PostgreSQL backups you can restore anywhere

backupd runs pg_dump against an exported snapshot on your schedule, then restores the backup into a sandbox and counts every table’s rows against your database.

Restore test

app-prod-2026-10-15-0300.pgdump.zst.age
Restorable
Tables
5 of 5
Rows
1,766,383
Took
41s
Sandbox
postgres 16
  1. Fetch the backup612 MB from storage6s
  2. DecryptUnlocked and checksum matched3s
  3. RestoreInto a temporary Postgres 16 database27s
  4. Count rowsEvery table matches the backup4s
  5. Clean upTemporary database deleted1s
Counts per table, backed up against restored
TableBacked upRestoredResult
public.sessions1,402,3771,402,377match
public.order_items241,880241,880match
public.orders96,51296,512match
public.users18,20418,204match
public.invoices7,4107,410match
9.2 – 18
PostgreSQL 9.2 through 18.
pg_dump -Fc
pg_dump’s custom format, from one snapshot.
Restore-tested
Restored and counted on our sandbox.
Yours to open
Open the file with age, zstd and pg_restore.

How a backup runs

  1. Dump from one snapshotWe open a read-only transaction, export its snapshot, and run pg_dump in custom format against it, counting rows inside the same snapshot.
  2. Compress and encrypt on the wayThe dump is compressed with zstd and encrypted with age as it streams to storage in 64 MiB parts. Nothing is written to a disk on the way.
  3. Check the file we storedThe stored file is read back, decrypted and decompressed, and both checksums are compared before the backup counts as good.

Backup

app-prod-2026-10-16-0900.pgdump.zst.age
Succeeded
  1. Dumppg_dump -Fc from an exported snapshot
  2. Compresszstd, 2.9 GB to 612 MB
  3. Encryptage, to 3 keys
  4. Upload10 parts of 64 MiB, nothing on disk
  5. CheckRead back, both checksums match

Took 1m 54s · Scheduled

The restore test

On your plan’s cadence, the backup is restored into a temporary database on a sandbox Postgres server, never into yours.

Every table is counted and compared with the counts taken when the backup was made. If a table is short, you get an alert and the backup’s page names it. Then the temporary database is dropped.

How restore tests work

What backupd needs

  • A postgres:// or postgresql:// connection string.
  • TLS is required: sslmode require, verify-ca or verify-full.
  • A read-only user is enough. On a primary, backupd exports a snapshot so the dump and the row counts see the same moment.
  • Your password is passed to the tools through the environment, never on the command line.

Add database

backupd connects over TLS. Use a read-only user.

Connection string

postgresql://backup:••••@db.example.com:5432/app

Schedule

Every hour

Keep backups

30 days

CancelTest and add

Restore it yourself

Every backup is encrypted with age to your key as well as ours. Download it, and open it with free tools on your own machine:

Terminal

# PostgreSQL 17 and later
$ age -d -i backupd.agekey app.pgdump.zst.age | zstd -d \
  | pg_restore --no-owner --single-transaction -d "$TARGET_URL"

# PostgreSQL 9.2 to 16
$ age -d -i backupd.agekey app.pgdump.zst.age | zstd -d \
  | pg_restore --no-owner -f - | grep -v '^SET transaction_timeout' \
  | psql --single-transaction -v ON_ERROR_STOP=1 "$TARGET_URL"

Restoring without backupd

Not yet

  • Row counts are exact up to 25 million rows in total and when a snapshot can be exported, which needs a primary. Past that, or on a standby, they are estimates and those tables are not compared.
  • Row counts cover the 1,000 largest tables. Smaller tables beyond that are backed up but not counted.
  • Point-in-time recovery and incremental backups are not available yet; every backup is a full copy.
  • Private networks and SSH tunnels are not supported yet.

Questions

Yes, as long as the database is reachable from the internet over TLS and the user you give us can run pg_dump.

We use pg_dump 18, which reads every server from 9.2 to 18. For servers before 17, restore through psql: the docs give the line.

We restore into a temporary database on our sandbox Postgres server, count every table with count(*), compare with the counts taken inside the same snapshot as the dump, then drop it.

Back up PostgreSQL tonight

Paste a connection string, pick a schedule and add your key. Starter is free for 14 days.