The 3-2-1 Backup Rule for Apps and Databases
Three copies of your data, on two kinds of storage, one off-site — and the modern additions: one immutable copy and zero restore errors. What counts as a copy, applying it to a Postgres-backed app, retention, encryption, ransomware and AI-agent mistakes, and testing restores.
The 3-2-1 rule is the oldest and most useful idea in backups:
- 3 copies of your data (the original plus two backups),
- on 2 different types of storage or systems,
- with 1 copy off-site.
The point is that no single failure — a dead disk, a deleted server, a fire, a compromised account, a bad command — can destroy every copy at once. (Backups for beginners)
The modern version: 3-2-1-1-0
Ransomware and account takeovers added two more numbers:
- 1 copy that's immutable or offline — it can't be changed or deleted, even by someone with your credentials, for a set period.
- 0 errors when you test a restore.
An untested backup is a hope, not a backup.
What counts as a "copy"
| Thing | Is it a backup? |
|---|---|
| A replica database | No — a DROP TABLE replicates instantly (Postgres read replicas) |
| RAID | No — protects against a disk failing, not deletion |
| A snapshot on the same provider/account | Partly — one copy, same failure domain |
| A dump in another provider's object storage | Yes — off-site |
| Versioned/object-locked bucket | Yes — and immutable |
Applying it to a typical app
Your app has two kinds of data: the database and files (uploads).
Copy 1: production
Your live Postgres database and file storage.
Copy 2: frequent backups, near the server
- Nightly logical dumps (
pg_dump) and/or continuous WAL archiving for point-in-time recovery. (Postgres backup and restore, Point-in-time recovery) - Server/volume snapshots from your provider.
Fast to restore; same provider.
Copy 3: off-site, immutable
Send backups to object storage at a different provider or region, with:
- versioning and object lock (immutability) for the retention period,
- separate credentials — the production server can write new backups but cannot delete old ones.
If an attacker (or an AI agent with too much access) gets into production, they can't wipe the off-site history. (Principle of least privilege, How to stop an AI agent deleting your production database)
Files
Uploads in object storage: turn on versioning and replicate or back up to a second provider. (Where should user uploads go?)
Retention
Keep backups long enough to notice problems. Data corruption or a bad migration may go unnoticed for weeks. A common scheme:
- daily backups for 7–14 days,
- weekly for 1–3 months,
- monthly for a year (if regulations or customers require).
Longer retention costs more storage; decide per dataset. Remember deletion requests (GDPR) apply to backups too — document how long backups are kept. (GDPR basics)
Encrypt backups
Backups contain everything: user data, password hashes, tokens. Encrypt them before they leave the server (or use storage-side encryption with keys you control), and keep the encryption key somewhere else — a backup you can't decrypt is useless. (Encryption at rest vs in transit)
Test restores — on a schedule
Monthly (or at least quarterly):
- Restore the latest backup to a separate server or database.
- Run the app against it, or at least check row counts and recent records.
- Time it — that's your real recovery time.
- Write down the steps; you'll be stressed when you need them.
Automate it if you can: a scheduled job that restores last night's dump into a scratch database and runs sanity checks.
Define your targets
- RPO (recovery point objective) — how much data can you afford to lose? Nightly dumps = up to 24 hours. WAL archiving = minutes.
- RTO (recovery time objective) — how long can you be down while restoring?
These decide how sophisticated your setup needs to be.
Checklist
- 3 copies, 2 kinds of storage, 1 off-site
- 1 immutable copy with separate credentials
- Database and uploads both covered
- Encrypted, key stored elsewhere
- Retention long enough to catch slow problems
- Restore tested, timed and documented
- Alerts when a backup job fails
EasySpawn takes daily backups of every server, kept for 7 days, with 30-day retention and snapshots available as add-ons — so the copies closest to your app are handled for you. See pricing or join the waitlist.
Related: Backups for Beginners · How to Back Up a Postgres Database · Postgres Point-in-Time Recovery · Encryption at Rest vs in Transit
Keep reading
Zero-Downtime Deploys for a Small App
You don't need Kubernetes to deploy without dropping requests. What actually causes downtime during a deploy — stopping before starting, no health checks, killed requests, and database changes the old code can't handle — and the four practices that fix each one.
Redis: When a Small App Actually Needs It (and When Postgres Is Enough)
Redis shows up in every architecture diagram, and AI tools add it by reflex. It's excellent at a few specific jobs — caching, rate limiting, ephemeral state, pub/sub — and unnecessary for many small apps. What it's for, what it isn't, and how to use it without losing data you cared about.