pg_dump is fine for small databases, but restores are slow and you can only go back to the moment of the dump. pgBackRest takes physical backups plus continuous WAL archiving, giving you fast restores and point-in-time recovery (PITR): restore the database to exactly 14:32:10 yesterday, just before someone ran the wrong DELETE.
Concepts
- Full backup — a copy of the whole data directory.
- Differential — changes since the last full.
- Incremental — changes since the last backup of any type.
- WAL archive — every write-ahead log segment, shipped continuously. Replaying WAL on top of a backup reaches any point in time.
- Stanza — pgBackRest’s name for one PostgreSQL cluster’s configuration.
- Repository — where backups go: a local path, another server over SSH, or object storage (S3-compatible, Azure, GCS).
1. Install
sudo apt install -y pgbackrest
(From the PGDG repository for the newest version.)
2. Configure a repository
Object storage keeps backups off the database server — the best default. Example with S3-compatible storage (AWS S3, MinIO, others), /etc/pgbackrest/pgbackrest.conf:
[global]
repo1-type=s3
repo1-s3-endpoint=s3.example.com
repo1-s3-bucket=pg-backups
repo1-s3-region=us-east-1
repo1-s3-key=ACCESS_KEY
repo1-s3-key-secret=SECRET_KEY
repo1-s3-uri-style=path
repo1-path=/web1
repo1-retention-full=2
repo1-retention-diff=6
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=a-long-random-encryption-passphrase
compress-type=zst
process-max=2
start-fast=y
log-level-console=info
[main]
pg1-path=/var/lib/postgresql/17/main
sudo chown postgres:postgres /etc/pgbackrest/pgbackrest.conf
sudo chmod 640 /etc/pgbackrest/pgbackrest.conf
repo1-retention-full=2keeps two full backups (and the WAL needed for them).- Encryption (
repo1-cipher-*) protects backups at rest. Store the passphrase somewhere safe outside the server — without it backups are useless. - For a local repository, use
repo1-path=/var/lib/pgbackrestinstead of the S3 settings (and ideally a second, remote repository asrepo2).
3. Enable WAL archiving in PostgreSQL
postgresql.conf:
archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'
wal_level = replica
max_wal_senders = 3
sudo systemctl restart postgresql
4. Create the stanza and check
sudo -u postgres pgbackrest --stanza=main stanza-create
sudo -u postgres pgbackrest --stanza=main check
check verifies that WAL archiving works end to end.
5. Take backups
sudo -u postgres pgbackrest --stanza=main --type=full backup
sudo -u postgres pgbackrest --stanza=main --type=diff backup
sudo -u postgres pgbackrest --stanza=main info
Schedule them (as the postgres user’s crontab):
30 2 * * 0 pgbackrest --stanza=main --type=full backup
30 2 * * 1-6 pgbackrest --stanza=main --type=diff backup
Expiry of old backups happens automatically after each backup according to retention.
6. Restore
Stop PostgreSQL first. Latest state (after losing the server, restore onto a fresh install with the same config):
sudo systemctl stop postgresql
sudo -u postgres pgbackrest --stanza=main --delta restore
sudo systemctl start postgresql
--delta only replaces files that differ — fast when the data directory still exists.
Point-in-time recovery:
sudo -u postgres pgbackrest --stanza=main --delta \
--type=time "--target=2026-11-04 14:32:00+00" --target-action=promote restore
sudo systemctl start postgresql
PostgreSQL replays WAL up to that timestamp and then opens for writes.
7. Test restores regularly
Restore to a separate server or a different data directory (--pg1-path) monthly and run sanity queries. An untested backup is a hope, not a backup.
Monitoring
pgbackrest infoshows backups, sizes and WAL range.- Alert if the latest backup is too old or archiving fails (
pg_stat_archiver.failed_count). - Heartbeat monitors on the cron job — see Uptime Kuma push monitors.
Alternatives
Barman (from EnterpriseDB) and WAL-G offer similar physical backup and PITR features. For small databases, scheduled pg_dump is simpler.
Related: database backup automation and PostgreSQL replication.