Skip to main content
RunBook Academy

PostgreSQLXV · High Availability, Failover and Disaster RecoveryHA

Validating a failover, and rejoining a failed primary with pg_rewind

Advanced⏱ ~35 min🧪 Lab requiredpg_rewindpg_ctlpsql

What you'll learn

  • Rehearse a failover in a way that would find real problems
  • Rejoin a diverged former primary with pg_rewind
  • State pg_rewind prerequisites and what it discards
  • Decide between rewind and rebuild

Prerequisites

Practice

Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27

Not yet marked complete on this device.

A failover you have not rehearsed is a procedure you are testing during an incident. This lesson covers rehearsing one, and the tool that makes the aftermath cheap.

Rehearsing

A rehearsal that always succeeds is testing the rehearsal. A useful one:

Use the real mechanism. If production fails over with Patroni, fail over with Patroni, not by hand.

Have real traffic running. A failover with an idle database proves nothing about client behaviour, and lesson XV-06 established that client behaviour is most of the RTO.

Measure four things, from the moment the primary is killed:

NumberWhat it tells you
Time to detectionWhether the health checks work
Time to promotionThe stack’s decision path
Time to first successful client writeThe real RTO
Rows lostThe real RPO

The third and fourth are the ones that matter and the ones that get skipped in favour of “the standby was promoted in 89 milliseconds”, which is true and nearly irrelevant.

Kill it the way it will actually fail. pg_ctl stop -m fast is a polite shutdown that a real failure will not resemble. Prefer SIGKILL, a hypervisor power-off, or a network partition. Lesson XII-06 established that PostgreSQL recovers from SIGKILL correctly, so this is safe on a rehearsal cluster.

Run it to completion, including the rejoin. A rehearsal that stops at “the new primary is up” has not tested the half that takes longest.

pg_rewind

After a failover the old primary has diverged: it contains WAL the new primary never had, so it cannot simply stream from it. The choices are a full base backup or pg_rewind.

pg_rewind finds the divergence point and copies only the blocks that changed since, turning a full rebuild into a partial one.

Prerequisites

One of these, set before you need it:

  • wal_log_hints = on (a restart parameter), or
  • data checksums enabled at initdb

Without either, pg_rewind cannot work at all and the answer is a full base backup. This is why lesson XIV-03 puts wal_log_hints in the build procedure.

It refuses an unclean target

Read-only / Saferunning pg_rewind against a target that is still running
$ pg_rewind --target-pgdata=$PGDATA --source-server="host=newprimary ..." --dry-run
pg_rewind: executing "/usr/lib/postgresql/18/bin/postgres" for target server to complete crash recovery
pg_rewind: error: target server must be shut down cleanly

It attempted a single-user crash recovery pass on the target itself, then refused because the control file still recorded the cluster as in production.

Stop the old primary cleanly first, and confirm Database cluster state: shut down in pg_controldata.

Performing it

Destructivepg_rewind against a cleanly shut down target
$ pg_rewind --target-pgdata=/var/lib/postgresql/18/docker \
--source-server="host=rbpg-sb port=5432 user=postgres ..." --progress
pg_rewind: connected to server
pg_rewind: servers diverged at WAL location 0/43CCB2E0 on timeline 1
pg_rewind: rewinding from last common checkpoint at 0/43C807B8 on timeline 1
pg_rewind: reading source file list
pg_rewind: reading target file list
pg_rewind: reading WAL in target
pg_rewind: need to copy 196 MB (total source directory size is 376 MB)
   0/201171 kB (0%) copied
201171/201171 kB (100%) copied
pg_rewind: creating backup label and updating control file
pg_rewind: syncing target data directory
pg_rewind: Done!

Read the second and third lines. pg_rewind identified the divergence at 0/43CCB2E0 — the exact LSN the promotion happened at, matching the history file from lesson XV-02 — and rewound from the last common checkpoint before it.

196 MB copied out of 376 MB. A fresh pg_basebackup would have copied all 376. On a multi-terabyte cluster that diverged by minutes, that ratio is the entire argument for the tool.

Then rejoin

# appended to postgresql.auto.conf on the rewound node
primary_conninfo = 'host=rbpg-sb port=5432 user=repl application_name=oldprimary'
primary_slot_name = 'oldprimary'
touch $PGDATA/standby.signal
pg_ctl -D $PGDATA start
Read-only / Safethe rejoined node's log
$ tail /tmp/rw.log
LOG:  starting backup recovery with redo LSN 0/43C80728, checkpoint LSN 0/43C807B8, on timeline ID 1
LOG:  entering standby mode
LOG:  started streaming WAL from primary at 0/43000000 on timeline 2
LOG:  consistent recovery state reached at 0/43CFF2F0
LOG:  database system is ready to accept read-only connections

It starts recovery on timeline 1 and streams on timeline 2: it replays forward through the branch point and then follows the new timeline, which is the timeline history mechanism from lesson XIII-06 doing exactly what it exists for.

Rewind or rebuild?

SituationChoose
Divergence is small, wal_log_hints or checksums onpg_rewind
Neither prerequisite was setRebuild — no choice
The old primary’s storage is suspectRebuild. A rewind preserves whatever was wrong
Divergence is enormousRebuild; the rewind approaches a full copy anyway
You are not certain which side should winNeither yet. Copy first, decide second

What to take from this

  • Rehearse with real traffic, a real kill, and the real mechanism — through to the rejoin.
  • Measure time to first successful client write and rows lost. Those are the RTO and RPO.
  • pg_rewind needs wal_log_hints or checksums, set in advance.
  • It refuses a target that was not cleanly shut down.
  • Measured: divergence found at the exact promotion LSN, 196 MB copied of 376 MB.
  • It is destructive to the target. Copy first if in doubt.
  • Rewind fixes divergence. It does not fix corruption — rebuild for that.

Cross-course references

  • Linux for Production Sysadmins — Part LXIV (Rolling maintenance) covers rehearsing a failover as routine work rather than discovering it during an incident.
  • Observability for Production Sysadmins — Part CX (Observability during major incidents) covers what you must be able to see while a failover is in progress, as distinct from afterwards.

Quiz

Knowledge check · 6 questions

  1. Q1. How does pg_rewind determine which blocks to copy from the source?

  2. Q2. A primary failed because of intermittent storage errors. After failover it is a candidate for pg_rewind. What should you do?

  3. Q3. A failover rehearsal reports 'promoted in 89 ms' and is declared a success. What has it failed to establish?

  4. Q4. What must be true before pg_rewind can be used? Select all that apply.

  5. Q5. pg_rewind is destructive to the target and there is no way to recover its divergent data afterwards.

  6. Q6. Design a failover rehearsal that would find real problems.

Passing score: 75%. Answers are checked in this browser.