← All guides

Backing up Postgres running in Docker (without the footguns)

docker exec pg_dump done right: version pinning, streaming out of the container, volume snapshots vs logical dumps, compose examples.

The basic pattern

docker exec -i my_pg pg_dump -U postgres --format=custom mydb \
  > backup_$(date +%F).dump

docker exec uses the pg_dump INSIDE the container — automatically version-matched to the server. Piping stdout means the dump never fills the container's writable layer.

Footguns

Backing up the volume directory with tar while Postgres runs → torn, inconsistent copies (only safe with pg_backup_start/filesystem snapshots). docker exec without -i on some shells → truncated output. Dumping through docker compose exec adds a TTY by default — use -T:

docker compose exec -T db pg_dump -U postgres -Fc mydb > db.dump

Get the dump off the host

A backup on the same disk as the database survives exactly none of the interesting failures. Stream to S3/R2 (encrypted), keep retention, and restore-test into a throwaway container:

docker run -d --name rt -e POSTGRES_PASSWORD=x postgres:16
docker exec -i rt pg_restore -U postgres --no-owner -d postgres < db.dump

Whatever tool made your backup, the only way to know it works is to restore it. Firedrill does that automatically for every backup — or try a one-off free drill on a dump you already have.

A backup you've never restored is a hope, not a backup.

Firedrill restore-tests every backup it takes — on real infrastructure, with the report to prove it.