Files
magnus919_agent-skills/postgres/references/04-backups-wal-pitr.md
T
Magnus HedemarkGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
cd14da26cc feat(skill): add PostgreSQL operational skill (#245) (#265)
Add a one-tool PostgreSQL operations skill: configuration review, index and
query-plan analysis, vacuum/bloat, WAL archiving + point-in-time recovery,
replication/failover, extensions, upgrades, and evidence-based diagnostics.
Ships the read-only pgdiag collector (stdlib, --json, --plan-for, --help
without a cluster), 9 dated references, tests, a human README, 6 eval cases,
and the top-level index + regenerated catalogs. Routes app data access to
backend-engineering and schema design to data-architect/data-engineering.

Closes #245

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-08-03 18:22:28 -04:00

96 lines
3.9 KiB
Markdown

# Backups: WAL Archiving and Point-in-Time Recovery
> **Last Updated:** 2026-08-03
This reference covers the operational side of backups for PostgreSQL: WAL
archiving readiness, base backups, and point-in-time recovery (PITR). The
cross-engine backup *strategy* (RPO/RTO targets, retention policy, off-site
copies) belongs to `data-engineering`; this skill owns the PostgreSQL
mechanics and their verification.
## The recovery model
A usable recovery point is a **base backup plus the WAL archive that follows
it**: restore the base, then replay archived WAL up to the target time or LSN.
Without continuous archiving you can only recover to the moment of the last
base backup.
## WAL archiving readiness
```sql
-- The three settings that gate archiving (wal_level change needs restart)
SELECT name, setting FROM pg_settings
WHERE name IN ('wal_level', 'archive_mode', 'archive_command', 'archive_timeout');
-- Archiver health: archived_count must grow, failed_count must stay flat
SELECT archived_count, failed_count, last_archived_wal, last_archived_time,
last_failed_wal, last_failed_time
FROM pg_stat_archiver;
```
The `pgdiag` `wal_archive` check reports the archiver row directly.
Readiness checklist:
- `wal_level = replica` (or higher) and `archive_mode = on`.
- `archive_command` succeeds for every segment; it must be idempotent and
return zero only on success (a failing command makes the server retry and
log failures).
- `pg_stat_archiver.failed_count` is not climbing and `last_failed_wal` is
stale. A rising `failed_count` means archiving is broken *right now*, and
every segment since then widens the PITR gap.
## Taking a base backup
```bash
# Consistent base backup with WAL included (the supported path)
pg_basebackup -h primary -D /backup/base-2026-08-03 -X stream -c fast -P
# Label and record it
echo "backup of primary at $(date -Is)" > /backup/base-2026-08-03/BACKUP_LABEL
```
- Use `-X stream` (or `-X fetch`) so WAL segments produced during the backup
are included — a base backup without its WAL is not restorable.
- Record the label and timestamp; a restore target is only as good as the
backup's metadata.
- Test restore: on a scratch instance, restore the base, point
`restore_command` at the archive, and verify the server reaches the expected
recovery point. This is the only evidence that the backup works.
## Restoring to a point in time
1. Restore the base backup to the target data directory.
2. Set `restore_command` (how to fetch archived WAL) and a recovery target —
`recovery_target_time` (e.g., `'2026-08-03 02:00:00'`),
`recovery_target_lsn`, or `recovery_target_xid`.
3. Start the server; it replays WAL to the target and stops (or enters
recovery if `recovery_target_action = promote` on 12+).
4. Verify the data at the boundary (the row/table the incident was about),
then promote when confident.
Recovery-target kinds: time-based (the `pitr|point.in.time` family — a
wall-clock moment) or position-based (an LSN or timeline marker). Choose the
kind that matches how you know *when things went wrong*; a wall-clock target
is the common case.
## Verification boundaries
| Claim | Minimum evidence |
|---|---|
| Archiving is healthy | `archived_count` increasing and `failed_count` flat over a window |
| A backup exists | Labeled base backup directory with matching WAL |
| Backups support recovery | A full restore-to-time drill on a scratch instance, logged |
| PITR target is reachable | Server reaches the target and the expected rows exist |
## Hard boundaries
- Never claim recoverability from a backup log alone; only a restore drill is
evidence.
- Never run a restore against the production data directory without an
explicit human directive; a restore is a mutation.
- Never let `archive_command` return success on failure — silent archiving
failure widens the recovery gap undetected.
- Never keep only base backups: without WAL there is no point-in-time
recovery, only point-of-backup recovery.