A PostgreSQL backup is two things: a dump of each database, and the roles that own them. This guide takes both from a
database running in Docker, restores them into a throw-away container, and checks that the data came back. The commands use
a container named db and a database named appdb; replace them with yours.
Do not copy the data directory of a running server
Copying the volume behind /var/lib/postgresql/data while PostgreSQL runs gives you files from different moments — the copy
may not start, or may start with silent damage. Use PostgreSQL’s own tools, which read a consistent snapshot while the
database keeps serving traffic.
1. Dump each database
The custom format (-Fc) is compressed, restorable selectively and readable by pg_restore:
docker exec db pg_dump -U postgres -Fc --lock-wait-timeout=120s appdb > appdb.dumpDo not add -t to docker exec: a pseudo-terminal rewrites line endings and corrupts binary output. pg_dump takes only
lightweight locks, but it waits for (and then blocks) schema changes, which is why the lock timeout is there.
2. Dump the roles
Roles, their memberships and privileges on the cluster are not part of pg_dump. Take them separately:
docker exec db pg_dumpall -U postgres --globals-only > globals.sql3. Check the dump before you trust it
pg_restore --list reads the archive’s table of contents; a truncated or corrupt file fails here:
docker exec -i db pg_restore --list < appdb.dump | head4. Restore into a scratch container
Start a server with the same major version (or newer) and restore into it:
docker run -d --name pg-restore-test -e POSTGRES_PASSWORD=scratch postgres:17docker exec -i pg-restore-test psql -U postgres -f - < globals.sqldocker exec pg-restore-test createdb -U postgres appdbdocker exec -i pg-restore-test pg_restore -U postgres -d appdb --exit-on-error < appdb.dumppsql reports that the postgres role already exists — that is expected. Then compare something you know about the data:
docker exec db psql -U postgres -d appdb -Atc "select count(*) from orders"docker exec pg-restore-test psql -U postgres -d appdb -Atc "select count(*) from orders"docker rm -f pg-restore-testVersion rules worth remembering
pg_dumpmust be the same major version as the server or newer — running it inside the database’s own container guarantees that.- Restore into the same major version or a newer one; restoring into an older major version is not supported.
- Extensions that need
shared_preload_libraries(TimescaleDB,pg_cron) must be configured on the server you restore to.
Where Xtic comes in
The steps above are what Xtic runs for PostgreSQL — the custom-format dump per database, the globals as a separate artefact, the format check — and then it adds what a cron job does not: encryption with your keys, a checksum of what was stored, a signed manifest, retention, an alert when a backup is missing, and scheduled restore drills that repeat step 4 for you. The PostgreSQL page lists exactly what Xtic runs and how it restores.
Try it on your own servers: the Free edition covers 3 servers, forever.