Patroni Cluster Backup
This page walks the complete procedure for backing up a Patroni HA cluster:
getting the backup infrastructure up with pg backup setup, taking
full/incr/diff backups with pg ha snapshot, and one operational pitfall — when a
replica’s Replay LSN stalls and pg ha status shows members disagreeing on LSN,
how to pull it back with patronictl reinit. Command and mechanism details live in
Patroni HA and Backup;
this page strings them into a path you can run end to end.
Model: one stanza per cluster, the backup container finds the primary
A Patroni cluster (scope) maps to one pgBackRest stanza: pgcli_<scope>
(matching the namespace-qualified DCS scope, so two pgcli namespaces sharing one S3
bucket never collide). The stanza is cluster-wide — the backup-side config lists
every member as a pg*-host, and pgBackRest SSH-probes them, locates the current
primary itself, and keeps following it across failovers with no config change.
Therefore:
- There is no per-member backup.
full/incr/diffall operate on the one cluster stanza; pgBackRest snapshots whatever host is the leader at that moment. - Backups run from the shared backup container, reaching the primary over SSH.
Cross-host members are reachable too (trust is wired up automatically by
setup). pg ha snapshotcommands never need you to name the leader — pass the scope; pgBackRest resolves the primary.
Prerequisite: pg backup setup
One idempotent run brings up the whole backup path and gives the cluster archiving:
What setup does (when an S3 repo is configured, it first runs an endpoint preflight — a plain TCP dial that fails the run immediately on a mistyped host or a store that is down, rather than after the image pull / container start buries the real cause):
- Build/pull the pgbackrest image, create the network and dirs, generate the
backup-container
pgbackrest.confand the member-local archive viewpgbackrest-archive.conf. - Start the shared backup container, then verify repository connectivity: a
pgbackrest repo-lsproves the whole stack (TLS, credentials, bucket) and maps a failure to an actionable hint — a cert error points atpg backup fetch-ca, access-denied at the credentials, a refused/timeout connection at the endpoint. - Wire up cross-host backup trust: each host publishes its backup public key
into the cluster’s etcd registry at
pg ha create;setupmerges every member’s key into one per-clusterauthorized_keysbind-mounted into each member container. sshd re-reads that file on every login and the merge rewrites it in place, so hosts that join later are trusted without a restart. The S3 repo CA follows the same path: a host withca_filepublishes it into the registry, joiners with it empty pull it. (Details: HA → Backing up a cross-host leader.) - Enable WAL archiving: when a member’s archive config is stale, pause the
cluster, recreate stale members replicas-first / leader last (a recreate is exactly
the postmaster restart
archive_modeneeds), resume, and render the samearchive_commandinto every member’spatroni.yml. - Run
stanza-create+checkfor every cluster stanza.
HTTPS is mandatory. pgBackRest rejects plaintext S3. A MinIO meant to receive cluster archives must serve TLS — install it with
pg addon install minio --tls(pgcli’s self-signed CA; point--s3-ca-fileat itsca.crt). When the MinIO is on another machine, pull its CA with one TLS handshake —pg backup fetch-ca <store-host>:<port>— no scp needed (see MinIO → Getting the CA onto a remote host).
Confirm after setup:
Once the backup container is Up and the stanzas are ready, start backing up.
Create members after setup, on every host. A member’s archiving is fixed at creation time: the container only mounts
pgbackrest-archive.confif that file already exists, and the renderedpatroni.ymlonly carries the archive GUCs if an S3 repo is configured. A memberpg ha created beforepg backup setupon its host therefore starts without WAL archiving —pg ha snapshot/pg ha restorecannot cover it until a latersetupflags it stale and recreates it inside a pause window.pg ha createwarns about this up front; runpg backup setupfirst on each host for a backup-ready create.
Snapshot operations: pg ha snapshot
These act on a cluster (scope), running pgBackRest against the current primary from
the shared backup container. This is the Patroni-cluster counterpart of pg snapshot
(single instance).
Notes:
- Before each command,
pg ha snapshotre-renders the backup-containerpgbackrest.conffrom the current topology, so member adds/removes and port changes are reflected in the stanza immediately — the file is bind-mounted, no recreate. - You can only delete a non-unique full: pgBackRest keeps at least one full, so deleting the only one is refused (take a new full first).
- create connects to whatever host is leader at the time; after a failover it still succeeds — that is the point of the cluster-wide stanza.
Pitfall: a stuck replica LSN → patronictl reinit
Symptom. In pg ha status app one replica’s Replay LSN trails its own
Receive LSN (or visibly differs from another replica) and never converges over
time. Note Patroni’s Lag column is relative to the leader, so every number moves
when the leader’s LSN advances; to spot a real stall compare a replica’s own recv vs
replay, or watch whether its Replay LSN stays frozen across checks.
Confirm. Three signals point to the same conclusion — the replica’s WAL replay is at a hole and streaming has actually stopped:
record with incorrect prev-link + a repeating waiting for WAL to become available
means the WAL stream has a hole/discontinuity at a segment boundary and the startup
process is dead-waiting for the next segment. Patroni often keeps logging
“no action. I am … a secondary, and following a leader” each cycle — it believes it
is following while the postmaster’s walreceiver has actually stopped, so it will not
self-heal. This is a replication runtime issue, unrelated to backups.
Fix: reinit the replica. Have Patroni take a fresh pg_basebackup from the leader
and restart replication. This destructively rebuilds that replica’s data directory
(the leader and other replicas are untouched), so Patroni requires --force:
-f does not work: patronictl reinit only accepts the full --force (unlike
switchover/failover’s --yes). Success: reinitialize for member node1 means it
was dispatched.
Verify recovery. reinit wipes and rebuilds the data dir, then basebackups + replays.
Poll until it is recv == replay and status=streaming:
After reinit completes, run pg ha snapshot create app --type full once to realign the
snapshot baseline with the healthy topology.