23 Oct 2026 · 3 min read · Ravi Sanchala
Why I run two backup strategies, not one
Continuous backups and daily dumps protect you from different failures. And the backup doesn't count until you've restored from it.
People are often surprised that I set up two independent backup systems for the same PostgreSQL database. Isn’t one enough?
One is enough for the failure you planned for. Two covers the failure you didn’t.
Different failures need different backups
Think about what actually goes wrong with databases:
| What happens | What you need |
|---|---|
| The server dies | A recent copy somewhere else |
Someone runs a bad DELETE at 14:32 | The database as it was at 14:31 |
| Data slowly gets corrupted over weeks | A clean copy from before it started |
| You need to move to a new server or version | A portable copy you can load anywhere |
| The backup tool itself has a bug | A second copy made a different way |
No single backup method handles all five well. So I use two.
Strategy 1: continuous backups with point-in-time recovery
PostgreSQL writes every change to its write-ahead log (WAL) before applying it. A tool like WAL-G ships those logs, plus regular base backups, to cloud storage continuously.
That lets you restore the database to any moment in the retention window, down to the second. On the setups I run, that window is 30 days.
This is what saves you from the bad DELETE. You restore to one minute before it happened.
Weak spot: it’s a physical copy. It’s tied to the PostgreSQL major version, and if corruption is written into the database, it’s faithfully shipped to the backups too.
Strategy 2: daily logical dumps
Once a day, pg_dump exports the database as SQL-level data. I keep those for 14 days.
A dump is portable. You can load it into a different server, a newer PostgreSQL version, or a laptop to investigate something. It’s also produced by a completely different mechanism than the WAL backups, so a bug in one doesn’t affect the other.
Weak spot: you can only go back to the moment the dump ran, so you can lose up to a day of changes.
Together, the two cover each other’s gaps.
Keep backups away from the thing they back up
A backup on the same disk as the database protects you from nothing that matters. Both strategies write to object storage (a bucket in a different service from the server), with access limited to the backup process.
A backup you haven’t restored is a guess
This is the part most teams skip.
Schedule restore tests. Restore the latest backup into a throwaway database, run a few queries to check the data looks right, and record how long it took. That number is your real recovery time, and it’s usually longer than people think.
I wrap both backup and restore in Make recipes, so they’re one command each and anyone on the team can run them:
backup-db: ## Take a logical backup now and upload it
./scripts/pg-dump-upload.sh
restore-db: ## Restore a backup into a scratch database (BACKUP=name)
./scripts/pg-restore-scratch.sh $(BACKUP)
Watch the backups themselves
Backups fail silently. A full disk, an expired credential or a changed bucket policy can stop them without anyone noticing.
Alert on backup age: if the newest backup is older than expected, someone hears about it today, not on the day you need it.
The short version
- Continuous backups for “undo the last hour”
- Daily dumps for “give me a portable, independent copy”
- Both stored away from the server
- Restores tested on a schedule
- An alert when a backup is late
It’s boring. That’s the point.