PostgreSQL Backup and Recovery#
A backup you have never tested restoring is not a backup. This covers the main backup tools, when to use each, point-in-time recovery, and automation.
Logical Backups: pg_dump and pg_dumpall#
pg_dump exports a single database as SQL or a compressed binary format. It takes a consistent snapshot without blocking writes.
# Custom format (compressed, supports parallel restore)
pg_dump -U postgres -Fc -d myapp -f myapp.dump
# Directory format (parallel dump)
pg_dump -U postgres -Fd -j 4 -d myapp -f myapp_dir/pg_dumpall exports every database plus cluster-wide objects. In practice, dump roles separately and per-database for flexibility:
pg_dumpall -U postgres --roles-only > roles.sql
pg_dump -U postgres -Fc -d myapp -f myapp.dumpRestoring:
# From custom format with parallel workers
createdb -U postgres myapp_restored
pg_restore -U postgres -d myapp_restored -j 4 myapp.dumpLogical backups are ideal for databases under ~100 GB and for cross-version migrations. They are too slow for multi-terabyte databases.
Physical Backups: pg_basebackup#
pg_basebackup copies the entire data directory at the filesystem level. Much faster for large databases and required for streaming replication setup.
pg_basebackup -U replicator -h primary-host -D /backup/base \
--checkpoint=fast --wal-method=stream -PPrerequisites on the primary: wal_level = replica, max_wal_senders = 5, a replication role, and a pg_hba.conf entry for replication.
Point-in-Time Recovery (PITR)#
PITR restores to any specific moment by combining a base backup with continuous WAL archiving.
Configure the primary to archive WAL:
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f'To restore to a point in time: stop PostgreSQL, replace the data directory with the base backup, create recovery.signal, and configure recovery:
restore_command = 'cp /var/lib/postgresql/wal_archive/%f %p'
recovery_target_time = '2026-02-22 14:30:00 UTC'
recovery_target_action = 'promote'Start PostgreSQL. It replays WAL up to the target time, then promotes to read-write.
pgBackRest#
pgBackRest is the standard tool for production backup management – incremental backups, parallel compression, rotation, and remote storage.
Configure /etc/pgbackrest/pgbackrest.conf:
[mydb]
pg1-path=/var/lib/postgresql/16/main
[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=2
repo1-retention-diff=7
compress-type=zstSet PostgreSQL to archive through pgBackRest:
archive_command = 'pgbackrest --stanza=mydb archive-push %p'Initialize and take backups:
pgbackrest --stanza=mydb stanza-create
pgbackrest --stanza=mydb --type=full backup # full
pgbackrest --stanza=mydb --type=diff backup # differential
pgbackrest --stanza=mydb --type=incr backup # incrementalRestore (including PITR):
pgbackrest --stanza=mydb --delta restore
pgbackrest --stanza=mydb --delta --type=time --target="2026-02-22 14:30:00" restoreAutomated Backup Schedule#
Full weekly, differential daily, WAL archiving continuous:
0 2 * * 0 pgbackrest --stanza=mydb --type=full backup >> /var/log/pgbackrest.log 2>&1
0 2 * * 1-6 pgbackrest --stanza=mydb --type=diff backup >> /var/log/pgbackrest.log 2>&1For pg_dump setups with 7-day retention:
0 3 * * * pg_dump -U postgres -Fc -d myapp -f /backup/myapp_$(date +\%Y\%m\%d).dump && find /backup -name "myapp_*.dump" -mtime +7 -deleteTesting Your Restores#
Restore into a temporary instance at least monthly:
docker run -d --name pg-test -v /backup:/backup -e POSTGRES_PASSWORD=test postgres:16
docker exec pg-test pg_restore -U postgres -d postgres --create /backup/myapp.dump
docker exec pg-test psql -U postgres -d myapp \
-c "SELECT COUNT(*) FROM users; SELECT MAX(created_at) FROM orders;"
docker rm -f pg-testScript this on a schedule. If row counts or timestamps are wrong, alert immediately.
Choosing Your Strategy#
| Database Size | Tool | PITR Capable |
|---|---|---|
| Under 10 GB | pg_dump | No |
| 10-500 GB | pgBackRest | Yes |
| 500 GB+ | pgBackRest with S3 | Yes |
| Cross-version migration | pg_dump | No |
For production, use pgBackRest with continuous WAL archiving. Logical backups supplement for portability.