Lesson 17 of 30 intermediate 5 min read

Before this:Container networking & volumes

Backups & persistent data

Key takeaways Split your system into stateless parts (lose nothing on restart) and stateful parts (own data that must survive). Only the stateful data needs backing up — but it needs it seriously. Back up the volumes and databases, follow the 3-2-1 rule (three copies, two media, one off-site), and — the step everyone skips — test your restores. A backup you’ve never restored is a guess, not a backup.

Networking & volumes showed how a volume keeps data alive when a container is replaced. That protects you from an upgrade; it does not protect you from a deleted volume, a corrupted database, or a dead disk. Backups do — and knowing what’s stateful tells you exactly what to back up.

Stateless vs stateful: know what to protect

The first question is what actually holds data. Sort every part of your system into two buckets:

  Stateless Stateful
Loses data on restart? No Yes
Examples The app binary, a reverse proxy, a stateless API A database, GopherTrunk’s calls.db, its recordings
Recovery Redeploy the artifact Restore from backup
Backup needed? No — rebuild from the image Yes — this is the data that matters

The GopherTrunk binary is stateless: lose the container and you just pull the image again. Its calls.db and recordings are stateful: lose those and the history is gone forever. Your backup plan targets the stateful bucket and ignores the stateless one.

Back up the volumes and databases

For a file-based store like GopherTrunk’s, a backup is a consistent copy of the volume. For a SQLite database, use the database’s own backup command so you capture a coherent snapshot rather than a file caught mid-write:

# SQLite call database — .backup takes a consistent snapshot even while in use
sqlite3 /var/lib/gophertrunk/calls.db ".backup '/backups/calls-$(date +%F).db'"

# Recordings directory — a plain compressed archive
tar czf /backups/recordings-$(date +%F).tar.gz -C /var/lib/gophertrunk recordings

For a server database like Postgres you’d use pg_dump instead — same idea, a consistent dump rather than copying live files. Schedule it with a systemd timer or cron so it runs unattended.

Follow the 3-2-1 rule

One copy on the same disk as the original is barely a backup — the disk that dies takes both. The 3-2-1 rule is the durable minimum:

  • 3 copies of the data (the live one plus two backups),
  • on 2 different media or storage types,
  • with 1 copy off-site — another machine, another building, or object storage.
# push the local backup off-site to object storage
aws s3 sync /backups s3://my-gophertrunk-backups/ --storage-class STANDARD_IA

The off-site copy is the one that survives fire, theft, ransomware, or a fat-fingered rm -rf on the host.

Test your restores — really

This is the step that separates real backups from hopeful ones. A backup you have never restored is an untested assumption. Practice the restore on a scratch host on a schedule:

# restore into a throwaway location and confirm it opens and has data
sqlite3 /tmp/restore-test.db ".restore '/backups/calls-2026-07-23.db'"
sqlite3 /tmp/restore-test.db "SELECT count(*) FROM calls;"

If that count looks right, your backup works. If the file won’t open, you just found out during a drill instead of during a disaster. Restore time is also worth measuring — knowing it takes 20 minutes shapes how you handle an incident.

Retention: don’t keep everything forever

Backups cost storage, so decide how long to keep them. A common shape is grandfather- father-son: keep daily backups for a week, weekly for a month, monthly for a year. Prune older ones automatically so cost stays bounded while you retain enough history to recover from a problem you didn’t notice for a while.

Quick check: what is the step most often skipped that makes a backup trustworthy?

Recap

  • Split the system into stateless (rebuild from the image) and stateful (must be backed up) parts.
  • Back up volumes and databases with the store’s own consistent-snapshot tool, on a schedule.
  • Follow 3-2-1: three copies, two media, one off-site.
  • Test restores on a schedule — an unrestored backup is a guess.
  • Set a retention policy so backups don’t grow without bound.

Next up: locking the whole host down — production hardening.

Frequently asked questions

What is the 3-2-1 backup rule?

Keep at least 3 copies of your data, on 2 different types of media or storage, with 1 copy off-site. The three copies survive a single deletion or corruption, two media types survive a whole class of failure, and the off-site copy survives fire, theft, or a compromised host. It’s the minimum bar for data you’d be upset to lose.

What's the difference between stateful and stateless?

A stateless service keeps no important data of its own — restart it or replace it and nothing is lost, because its state lives elsewhere. A stateful service owns data that must survive, like a database or GopherTrunk’s recordings and call log. Stateless parts are easy to scale and redeploy; stateful parts are the ones you must back up and restore carefully.