Manual PITR Drill
This tutorial performs a PostgreSQL PITR drill in Pigsty v4.5.0’s four-node sandbox.
It runs pgsql-pitr.yml in the sequence down → pitr → up.
Separate validation steps precede data overwrite, timeline promotion, and HA reconstruction.
For one current node, use pig pitr.
For direct pgBackRest control, see the low-level pg-pitr utility.
Recovery stops Patroni/PostgreSQL and overwrites the target PGDATA with pgbackrest --force restore.
The up stage deletes the target cluster’s etcd prefix and rebuilds Patroni state.
The playbook prints a plan but has no interactive confirmation.
Before production use, the operator must state and confirm the exact cluster and recovery point.
Verify a recent usable backup that has been independently tested.
Run --check with exactly the same -l, variables, and tags, and schedule a maintenance window.
This tutorial does not authorize these commands in a production environment.
Prepare an Isolated Sandbox
Use Vagrant or another disposable four-node lab and select the ha/full template, which includes a Silo backup repository:
ha/full defines a single-node pg-meta, a three-node pg-test, and a Silo/pgBackRest repository.
This tutorial uses the exact target pg-meta.
Before you copy that selector to another environment, resolve its inventory.
Initial deployment and backup both change sandbox state. Production environments require their own deployment and backup approval process.
Establish Recovery Evidence
Start with read-only topology, backup-chain, and WAL-range checks:
info must show at least one usable backup with status: ok.
Archived WAL must cover the intended target.
check validates the current stanza and archive path.
It does not replace a real restore drill or validation of an independent copy.
In the sandbox, run Pigsty’s heartbeat helper to create an easy-to-verify time series:
Record, then stop the workload:
- the timezone-qualified timestamp you intend to recover to;
- heartbeat, LSN, and transaction boundaries around that point;
- current primary, timeline, and backup label;
- target cluster
pg-metaand target node.
Inspecting real application tables requires separate authorization. This tutorial uses only sandbox heartbeat data.
Declare the Recovery Task
Declare the target under pg-meta.vars in the sandbox inventory:
clusteris the source backup stanza and defaults to the targetpg_cluster.action: pausepauses PostgreSQL at the target for a human validation gate.archive: truepreserves archive settings.backup: trueis not a safe-backup substitute: it deletes an existing<pg_data>-backupbefore moving current PGDATA, so this drill keeps itfalse.
You can also pass the object temporarily with -e.
Preflight and all three stages must use exactly the same valid JSON to prevent variable differences.
Full Preflight
Before any stop or write action, check the complete workflow against the same target:
Confirm that Ansible resolves exactly pg-meta, then review the output for:
- source stanza, recovery type, time, timeline, and action;
- destination
pg_data, port, and repository; - tablespace/link mappings;
archiveandbackupbehavior.
--check validates inventory, variables, and task selection.
It cannot prove that a pgBackRest backup is restorable.
If the target, backup, or variables change, repeat preflight.
Stage One: Stop
Only after the operator reconfirms exact target pg-meta, recovery point, and maintenance window, run:
down attempts to pause Patroni automatic failover and stops Patroni on every target member.
If PostgreSQL remains running, it uses immediate shutdown.
Then verify every target node independently of the playbook result:
The expected results are inactive and “server is not running.”
If any member remains active, stop and diagnose the problem.
Do not start the restore.
Stage Two: Restore and Validate
Recheck pg_pitr and the target nodes before running the destructive stage:
This stage:
- renders
/pg/conf/pitr.confand/pg/bin/pg-restore; - optionally moves old PGDATA according to
backup; - creates the destination and runs pgBackRest restore with
--forceanddelta=y; - starts PostgreSQL directly and waits for a consistent recovery state in the log;
- prints a
pg_controldatasummary.
Control data proves only that the directory contains readable control state.
It does not verify the time, XID, or application boundary.
With action: pause, confirm that WAL replay reached and paused near the target:
Then inspect only the smallest authorized data scope. In the sandbox, check heartbeat rows. If the target is wrong:
- keep every Patroni member stopped;
- stop the manually started PostgreSQL;
- adjust the target and rerun the complete
--check; - rerun the
pitrstage.
Do not run up or allow replicas from the old timeline to rejoin.
Promote and Stage Three: Rebuild HA
Promote only after the operator accepts the recovered result and the creation of a new timeline:
The expected result is f.
Promotion changes state, and you cannot undo it without loss.
With every Patroni member still stopped and exact target still pg-meta, run:
For the primary, up deletes the /pg/pg-meta/ prefix from etcd.
The effective prefix also depends on pg_namespace and Citus settings.
It stops the manually started PostgreSQL and starts Patroni on the primary.
It then starts replicas one at a time and resumes HA.
The etcd deletion task tolerates errors.
Thus, a successful playbook result does not prove that it removed stale DCS state correctly.
Post-Recovery Acceptance
Verify each layer. Service startup alone does not complete recovery:
Also confirm:
- exactly the intended member is primary and replicas stream from the new timeline;
- HAProxy/VIP/DNS and application traffic point only to accepted instances;
- data and event boundaries around the recovery target are correct;
archive_mode,archive_command, and new-WAL archiving work;- monitoring, alerts, and the backup repository contain no stale cluster state.
After the new timeline is stable, create and verify a new full backup under the applicable approval process:
If recovery used archive: false, it wrote archive-mode=off.
Validate recovery and confirm a maintenance window before you reset that override and perform a controlled restart.
The default archive: true does not require this step.
Multi-Node and Cross-Cluster Recovery
- Old-timeline replicas must not rejoin without validation.
upstarts replicas one at a time and waits for clone/recovery; monitor them to completion. - In cross-stanza recovery,
pg_pitr.clusteris the source while-lis the destination being overwritten. Record and state both separately. - Cross-cluster recovery should normally use
archive: falseso a test destination cannot write WAL into the source stanza. Enable its own archiving only after acceptance and post-clone stanza cleanup. link_map,data,port, and a temporaryrepochange the actual storage and data targets; include all of them in both--checkand human review.
Related
- Complete Restore Operations Reference
- PITR Concepts and Target Semantics
- Backup Chains and Recovery Window
- Clone and Low-Level
pg-pitr pig pitr
Was this page helpful?
Thanks—your feedback helps us improve this page.
What got in the way? (optional)