pgProofGet early access

Guide

pg_dump vs pg_basebackup vs PITR

Three different backups that recover three different disasters. Most setups need two of them, and almost nobody has thought about which two.

The comparison

pg_dump / pg_dumpallpg_basebackupBase backup + WAL archiving (PITR)
KindLogicalPhysicalPhysical + WAL
ScopeOne database (or all, with pg_dumpall)The whole clusterThe whole cluster
Restore granularityDatabase, schema, single tableWhole cluster onlyWhole cluster only
Restore to a newer major versionyesnono
Restore to a different CPU / OSyesnono
Worst-case data loss (RPO)Time since last dumpTime since last base backupSeconds to minutes
Restore speed on a large clusterSlow — replays SQL, rebuilds every indexFast — copies filesFast, then replays WAL
Needs server configurationnoyes — replication connectionyes — archive_mode / archiver

Two rows do most of the deciding. Logical is the only one that crosses versions and machines — a dump taken from Postgres 15 restores into 17, on a different architecture, on a different provider. Physical is the only one that gets your RPO under a day, because only WAL can replay what happened between backups.

pg_dump — the portable copy

A logical dump connects like any other client and writes out the statements needed to rebuild one database. It runs against a live server, takes a consistent snapshot of the instant it starts, and does not block writes.

pg_dump -Fc -d "$DATABASE_URL" -f mydb.dump
pg_dumpall --globals-only -d "$DATABASE_URL" -f globals.sql

Three properties matter, and they are the reason this is the backup you should have even when you have another one:

What it does not do: pg_dump covers a single database and skips cluster-wide objects. Roles, and the passwords and grants attached to them, come from pg_dumpall --globals-only — leave it out and the restore fails with "role does not exist". And the client version rule is strict: pg_dump must be at least the server's major version (the version rule in detail). Which archive format to pick is its own decision.

pg_basebackup — the cluster clone

A physical backup is a byte-level copy of the data directory, taken over the replication protocol. It needs a role with the REPLICATION attribute and a server with wal_level at replica or higher and a spare max_wal_senders slot:

pg_basebackup -h db.internal -U replicator -D /backup/base -Ft -z -P

# -X stream (the default) ships the WAL generated during the copy,
# which is what makes the result self-consistent and startable

The output is a cluster, not a database. You restore it by pointing a Postgres server of the same major version, built for the same platform at those files and starting it. There is no partial restore: no single table, no single database, no cross-version jump.

On its own, a base backup gives you exactly the same RPO as a nightly dump — you lose everything since it was taken. Its real job is to be the starting point for the third option, and to seed replicas.

PITR — base backup plus every WAL segment since

Point-in-time recovery is a base backup plus a continuous archive of the write-ahead log. Postgres restores the files, then replays WAL forward and stops exactly where you tell it:

# postgresql.conf on the primary
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'

# on the restored copy: recovery settings + a recovery.signal file
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2026-09-07 14:32:00'

That last line is the whole point. It is the only backup that answers "put the database back to one minute before the migration ran" — the failure mode that actually kills companies, because a bad DELETE at 14:33 is faithfully copied to every replica within milliseconds.

Do not hand-roll this in production. pgBackRest, Barman and WAL-G exist because retention, compression, parallelism, encryption and archive verification are all things the two-line cp above does badly. They take the base backup and manage the archive for you, and pgBackRest and Barman will verify checksums on the stored backup.

Note that recovery_target_time and friends belong in the configuration file on PostgreSQL 12 and later; the old recovery.conf was removed in 12, replaced by a recovery.signal file next to the normal config.

Two things that are not backups

How to choose

Whichever you pick, the file or the archive is a hypothesis until something has restored it. PITR setups fail on a broken archive_command silently filling pg_wal; dumps fail on missing roles and version drift. The only way to know is the drill.

Pick a method, then prove it restores

pgProof takes the backup on schedule, restores it into a disposable container matching your Postgres major version, and verifies counts, constraints and your own probe queries. The proof lands in your inbox weekly; failures alert the same day.

Join the early-access waitlist