Backup Strategies for a Self-Hosted Stack (3-2-1)
How to back up a self-hosted homelab properly using the 3-2-1 rule. What to back up, application-consistent database dumps, encrypted off-site copies with restic and Borg, and how to test restores.
Self-hosting moves your data onto hardware you own. That’s the point — but it also means the safety net is now yours to build. Cloud services quietly handled redundancy, snapshots, and off-site copies on your behalf. When you run Immich ↗, Vaultwarden ↗, Nextcloud, or any other service yourself, a single disk failure, a bad upgrade, a ransomware infection, or a rm -rf in the wrong directory can take it all with no one to call.
A good backup strategy is the difference between an annoying afternoon and a catastrophe. The framework that has stood the test of time is the 3-2-1 rule, and this guide turns it into a concrete plan for a homelab.
The 3-2-1 Rule
The rule is simple to state and the reasoning behind each number is what makes it robust:
- 3 copies of your data — the live data plus at least two backups. One backup isn’t enough, because the moment you’re restoring from your only backup, you have zero backups.
- 2 different media/devices — don’t keep both backups on the same disk or the same machine. A failure of one device shouldn’t be able to take out more than one copy.
- 1 copy off-site — at least one backup in a different physical location. Fire, theft, flood, and a power surge that fries everything plugged in don’t respect “but it was on a different drive.”
This guidance is echoed by organizations like CISA precisely because it survives the failure modes that destroy single-location backups. A homelab version: live data on your server, a local backup on a NAS or external drive, and an encrypted copy off-site (cloud storage or a drive at a friend’s house).
What to Actually Back Up
The most common backup mistake in a homelab isn’t how, it’s what. People back up the files but forget the database, or back up volumes while the database is mid-write and end up with a corrupt copy. For a typical Docker-based stack, your data falls into four buckets:
- Application data on disk — Docker volumes and bind mounts: photo libraries, document storage, config directories. Example: Immich’s
UPLOAD_LOCATION, Vaultwarden’svw-data. - Databases — Postgres, MySQL/MariaDB, SQLite. These often hold the structure of your data (which photo belongs to which album, who has access). A file backup of a live database can be inconsistent — you need a proper dump.
- Configuration — your
docker-compose.ymlfiles,.envfiles, reverse-proxy config. These are how you rebuild the stack. Keep them in version control as well. - Secrets —
.envvalues, API keys, certificates. Back these up securely (encrypted), and never commit them to a public repo.
Write down what each service stores and where. That inventory is itself part of your backup — when you’re rebuilding under stress, knowing exactly what to restore is half the battle.
Application-Consistent Backups (The Database Trap)
Copying a database’s files while the database is running can capture it in a half-written state. There are two reliable ways to avoid that:
Dump the database with its own tool. This produces a consistent logical backup regardless of in-flight writes. For Postgres in a container:
docker exec -t my-postgres pg_dump -U postgres mydatabase > db-$(date +%F).sql
For MariaDB/MySQL:
docker exec -t my-mariadb mysqldump -u root -p"$PASSWORD" mydatabase > db-$(date +%F).sql
Or stop the service briefly, then copy. For small services like Vaultwarden’s SQLite database, stopping the container for a few seconds while you copy the data directory guarantees consistency:
docker compose stop
tar czf vaultwarden-$(date +%F).tar.gz vw-data
docker compose start
The rule of thumb: dump databases, copy files. A backup of Immich that captures the photo files but a corrupt database leaves you with a folder of images and no albums, faces, or organization. Capture both, consistently.
Tools: restic and Borg
Two open-source tools dominate self-hosted backups, and either is an excellent choice. Both do deduplication (only changed data is stored, so daily backups are cheap), compression, and client-side encryption (your data is encrypted before it leaves the machine — essential for off-site copies).
restic
restic is a single static binary with no dependencies, supports a huge range of backends directly (local disk, SFTP, S3, Backblaze B2, and many more), and is famously easy to start with. Initialize a repository, then back up:
# one-time: create an encrypted repository (here, on a local NAS mount)
restic -r /mnt/nas/restic-repo init
# back up a directory
restic -r /mnt/nas/restic-repo backup ~/services
# back up to off-site object storage (env vars hold the credentials)
restic -r s3:s3.amazonaws.com/my-bucket backup ~/services
restic handles snapshots and pruning. A typical retention policy keeps recent backups densely and older ones sparsely:
restic -r /mnt/nas/restic-repo forget --prune \
--keep-daily 7 --keep-weekly 4 --keep-monthly 12
BorgBackup
Borg is the other heavyweight: deduplicating, compressing, encrypting, and very space-efficient. It’s a great fit when your off-site target is an SSH server you control (a VPS or a box at a friend’s house). Borg repositories are append-only-capable, which is a useful property against ransomware (more on that below). Pairing Borg with Borgmatic gives you a simple YAML config to declare sources, retention, and hooks (including running your database dumps before each backup).
Either tool gets you encrypted, deduplicated, scheduled backups. Pick one and learn it well rather than half-using both.
Mapping It Onto 3-2-1
Here’s a concrete homelab implementation:
- Copy 1 (live): your data on the server’s main disk.
- Copy 2 (local, different device): a nightly restic/Borg backup to a NAS or an external USB drive. Different physical device, fast to restore from.
- Copy 3 (off-site): the same backup pushed to encrypted cloud storage (Backblaze B2 and Storj are inexpensive and well-supported), or to a Borg repo on a VPS, or a rotated external drive you keep at another location.
Automate it with a cron job or a systemd timer that runs nightly: dump databases, run the backup tool to the local target, then sync/push to the off-site target. Many people run the local backup nightly and the off-site push slightly less often; both should be hands-off.
Defending Against Ransomware and Mistakes
A backup an attacker (or a buggy script) can delete isn’t a backup. Two defenses:
- Immutability / append-only. Use object-lock on cloud storage (Backblaze B2 and S3 support it) or an append-only Borg repository so that even a compromised server can write new backups but cannot delete or alter old ones.
- Pull, don’t push, for the most protected copy. If a separate, hardened machine pulls backups from your server rather than the server pushing out, credentials to delete the off-site copy never live on the box most likely to be compromised.
You don’t need all of this on day one. But for irreplaceable data — photos, password vaults — at least one immutable or pull-based copy is worth the effort.
Encrypt Everything Off-Site
Any copy that leaves your home should be encrypted before it goes. restic and Borg both encrypt client-side by default, so the storage provider only ever sees ciphertext. The catch: the encryption is only as good as your key handling. If you lose the repository password/key, the backup is permanently unrecoverable — that’s the design. Store the key in your password manager and in at least one other safe place. A backup you can’t decrypt is not a backup.
Test Your Restores — This Is the Whole Point
The single most ignored step, and the one that separates a backup strategy from a backup hope: a backup you have never restored is a backup you don’t have. Schedule periodic test restores:
# restic: restore the latest snapshot to a scratch directory
restic -r /mnt/nas/restic-repo restore latest --target /tmp/restore-test
# verify repository integrity
restic -r /mnt/nas/restic-repo check
Then actually use the restored data: spin up a throwaway copy of the service pointed at the restored volumes and database dump, and confirm it comes up and your data is there. Do a full dry run at least a couple of times a year. The first time you discover your database dumps were empty should not be the day your disk dies.
A Starter Plan You Can Build This Week
- Inventory what each service stores (files + database) and where.
- Script the dumps — a small shell script that
pg_dump/mysqldumps every database and copies/stops-and-copies file-only services into a staging directory. - Pick restic or Borg and back the staging directory up to a local NAS or external drive nightly.
- Add an off-site target — encrypted cloud storage or a remote repo — and push to it on a schedule.
- Enable immutability (object-lock or append-only) on the off-site copy.
- Test a restore now, and put a recurring reminder to test again every few months.
That’s a complete 3-2-1 backup strategy for a homelab. It’s not glamorous, and you’ll resent setting it up — right up until the day it’s the only reason you still have your data. Self-hosting gives you control; backups are the price of keeping it.
Sources
Related
The Beginner Homelab Guide: First 5 Services Worth Running
You don't need expensive hardware to start self-hosting. Here are five services that are worth the setup time and run comfortably on any low-power mini PC.
Self-Hosted Google Photos Alternatives
The best self-hosted replacements for Google Photos — Immich, Ente, PhotoPrism, Nextcloud Memories, and LibrePhotos — compared by auto-backup, search, sharing, and how much hardware they need.
Self-Hosted Notion and Docs Alternatives
The best self-hosted replacements for Notion, Google Docs, and Confluence — AppFlowy, Outline, Affine, Docmost, BookStack, and more — compared by editing model, collaboration, and setup effort.