Clone and Side-Restore a PostgreSQL Instance
Pigsty v4.5.0 provides two local shell utilities:
pg-forkcopies a PostgreSQL data directory and gives the copy a separate port.pg-pitrinvokes pgBackRest to restore a stopped data directory to a selected target.
They are useful for sandbox drills, side-channel investigation, and temporary testing. They are not complete Patroni-cluster recovery orchestrators. Prefer pig pitr for a managed instance and staged pgsql-pitr.yml for a multi-node cluster.
pg-fork recursively removes an existing destination directory; pg-pitr overwrites the destination with backup data. Both can execute without a prompt in a non-interactive environment. Before a real run, verify source and destination absolute paths, ports, tablespaces, exact cluster/instance identity, and an independent recent backup that has been tested. A newly created CoW clone is not an independent backup.
pg-fork
pg-fork copies a PostgreSQL data directory on the current node. Run it as the database OS user—normally postgres, or at least a member of the postgres group:
Parameters
| Parameter | Meaning | Default |
|---|---|---|
<FORK_ID> |
One digit from 1 to 9, used to derive the default directory and port |
Required |
-d, --data <path> |
Source data directory | $PG_DATA or /pg/data |
-D, --dst <path> |
Destination data directory | /pg/data<FORK_ID> |
-p, --port <port> |
Source instance port | $PG_PORT or 5432 |
-P, --dst-port <port> |
Destination instance port | <FORK_ID>5432 |
-s, --skip |
Skip the online-backup API and force cold-copy mode | No |
-y, --yes |
Skip interactive confirmation | No |
The script rejects equal normalized source and destination paths, but it cannot know whether a custom destination contains other important data. If the destination directory exists, it is recursively removed before copying.
Hot Backup and Cold Copy
By default, the script connects to the source port and uses one psql session to run:
CHECKPOINT;pg_backup_start();rm -rf <destination>followed bycp -a --reflink=auto;pg_backup_stop(wait_for_archive => false).
If the source cannot be reached on the selected port, the script automatically falls back to a cold copy instead of aborting. -s also forces a cold copy. A cold copy is safe only after you independently confirm that the source instance is fully stopped; a postmaster.pid warning is only a clue, not proof of process state.
On the same filesystem, the implementation recognizes these as fast CoW modes: XFS with reflink enabled, Btrfs, Bcachefs, and OCFS2. Other filesystems or cross-filesystem destinations still use cp --reflink=auto but may fall back to a full copy. The script’s help text mentions ZFS more broadly than its detector; the v4.5.0 implementation does not classify ZFS as a confirmed fast-CoW mode.
Clone Configuration
After a successful copy, pg-fork:
- removes
postmaster.pid,postmaster.opts, andstandby.signalfrom the destination; - clears physical replication-slot files in the destination;
- writes a separate
port,archive_mode=off, and a locallog_directoryto the destination’spostgresql.auto.conf; - removes
primary_conninfo,primary_slot_name, and oldrecovery_target*overrides.
It does not check whether the destination port is free or resize memory settings. Before starting the copy, inspect at least:
cp -a preserves symlinks under pg_tblspc; pg-fork does not copy or remap tablespaces outside PGDATA. Starting such a clone can access or modify the source instance’s tablespaces. If external tablespaces exist, independently copy and remap every one of them, or do not use this script to create a writable clone.
Interaction Boundary
The script asks Proceed with fork? [y/N] only when standard input is a terminal and -y was not used. Pipes, CI, cron, and other non-interactive invocations do not receive that prompt. Automation must therefore enforce a strict absolute-path allowlist and destination-existence check before invoking the script; do not add -y by default merely for convenience.
pg-pitr
pg-pitr is a low-level pgBackRest restore wrapper. It does not pause or start Patroni, stop or start PostgreSQL, clear DCS state, or rebuild replicas.
Recovery Targets
Understand and select a recovery target before execution. Invoking the command without arguments only shows help:
| Parameter | pgBackRest semantics |
|---|---|
-d, --default |
Set no stop target and replay to the available end of WAL |
-i, --immediate |
Stop when the selected backup becomes consistent |
-t, --time <timestamp> |
Recover to a timestamp |
-n, --name <restore-point> |
Recover to a named restore point |
-l, --lsn <lsn> |
Recover to an LSN |
-x, --xid <xid> |
Recover to a transaction ID |
-S/--set (with compatibility alias -b/--backup) only selects which backup set recovery starts from; it is not a stop target. For example, -S 20251225-120000F -d still replays to the end of WAL. Combine -S ... -i to stop as soon as that backup becomes consistent.
For time, name, lsn, xid, and immediate, pgBackRest’s effective default action is to pause at the target; -P/--promote changes it to automatic promotion. Use -X/--exclusive only with a precise boundary such as time, lsn, or xid.
Other Options
| Parameter | Meaning |
|---|---|
-D, --data <path> |
Absolute destination data directory; default /pg/data |
-s, --stanza <name> |
pgBackRest stanza; defaults to the first non-global stanza in the config |
-T, --timeline <value> |
latest, current, or a positive numeric timeline |
-P, --promote |
Automatically promote recovery methods that have a stop target |
-v, --verbose |
Enable pgBackRest info-level console logging |
-c, --check, --dry-run |
Print the command without executing it |
-y, --yes |
Skip the five-second countdown |
-- <args> |
Pass additional arguments directly to pgBackRest |
-c is a command-rendering check. It does not prove that the backup/WAL is usable or that PostgreSQL and Patroni are stopped. The wrapper also does not filter conflicting native arguments; review the final command carefully when passing repository, tablespace, or link-mapping options.
Safe Execution Sequence
This example shows only the low-level flow for one isolated destination. Use the complete runbook for production cluster recovery:
Real execution refuses root and aborts whenever postmaster.pid exists in the destination. Even a stale PID file requires the operator to confirm that PostgreSQL is stopped before removing it. There is no y/N question: an interactive terminal gets only an interruptible five-second countdown; a non-interactive invocation skips the countdown and enters restore immediately.
After restore, the operator starts and validates the instance:
Promote only after the recovery target, authorized application data, timeline, and archive settings are all verified. Promotion creates a new timeline; it is not a reversible “inspect” operation. pg-pitr does not itself disable archiving. Do not mechanically follow its generic final “enable archive_mode” hint—inspect the effective value first and correct only an override introduced by this recovery.
Additional Side-Restore Risks
When restoring into a custom directory such as /pg/data1, pgBackRest can restore postgresql.auto.conf from the backup and overwrite the separate port written by pg-fork. Recheck port, archive_mode, sockets, logging, and memory settings before startup.
If the backup contains external tablespaces or links, a side restore can also use the original paths. For isolation, pass reviewed pgBackRest --tablespace-map, --link-map, or related arguments after -- and inspect the rendered command. Otherwise, do not start the restored copy on the same host as production.
Recommended Clone-Validation Flow
- Verify the source instance, destination absolute path, destination port, tablespaces, and independent backup.
- Run
pg-fork <id>in an interactive terminal and confirm that the plan shows hot backup rather than an unintended cold-copy fallback. - Before starting the clone, run
pg-pitr -D <clone> ... -cand inspect the recovery command. - Execute restore only after explicit destination confirmation, then recheck the clone’s port and every external path.
- Start the clone on the isolated port and verify recovery state and only the application data you are authorized to inspect.
- Promote only if the clone is intentionally becoming a new primary; otherwise stop it and clean up only the exact, verified path.
Side validation reduces direct writes to the current PGDATA, but still uses the same backup repository, consumes host resources, and may touch external tablespaces. It is not a risk-free sandbox.