Hardening prevents most attacks, but nothing is bulletproof. A ransomware infection, a failed drive, or even a botched update can wipe out months of work. This guide covers how to build a backup strategy that lets you recover from anything — quickly and confidently.
1. The 3-2-1 Backup Rule
The gold standard for backup strategy. Simple to remember, hard to beat.
| Rule | What It Means | Example |
|---|---|---|
| 3 copies | Your data should exist in 3 places | Live NAS + local backup + cloud |
| 2 different media | Use at least 2 different storage types | NAS (HDD) + USB drive or cloud (object storage) |
| 1 off-site | At least 1 copy must be physically separate | Cloud storage, or a drive at a family member's house |
The 3-2-1 rule protects against: hardware failure (different media), ransomware (off-site copy is unreachable), theft or fire (off-site location), and human error (multiple copies to restore from).
2. What to Back Up
Not everything needs the same backup strategy. Prioritise by how painful it would be to lose.
| Priority | Data | Frequency | Retention |
|---|---|---|---|
| Critical | Docker compose files, .env files, databases | Daily | 30 days |
| Critical | Photos, documents, personal files | Daily | 90 days |
| Important | VM configs, Proxmox backups | Weekly | 4 weeks |
| Important | Home Assistant config, Pi-hole settings | Daily | 14 days |
| Nice to have | Media library (movies, music) | Not backed up — re-downloadable | — |
3. Automated Snapshots
Manual backups do not happen. Automate everything.
Proxmox VM backups (vzdump)
# Automated via Datacenter > Backup in the Proxmox UI
# Or via cron:
vzdump 100 101 102 --mode snapshot --compress zstd \
--storage local-backup --mailnotification always
Docker volume backups with a script
#!/bin/bash
# backup-docker.sh — run nightly via cron
BACKUP_DIR="/mnt/backup/docker/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Stop, backup, restart each service
for service in nextcloud vaultwarden homeassistant; do
docker compose -f /opt/$service/docker-compose.yml stop
tar czf "$BACKUP_DIR/$service.tar.gz" -C /opt/$service .
docker compose -f /opt/$service/docker-compose.yml start
done
# Database dumps (no downtime needed)
docker exec postgres pg_dumpall -U postgres | \
gzip > "$BACKUP_DIR/postgres.sql.gz"
# Clean backups older than 30 days
find /mnt/backup/docker -maxdepth 1 -mtime +30 -exec rm -rf {} +
Cron schedule
# Run at 3am daily
0 3 * * * /opt/scripts/backup-docker.sh >> /var/log/backup.log 2>&1
4. Off-Site Sync
Your local backups protect against hardware failure. Off-site protects against everything else.
Option A: Cloud (Backblaze B2 + rclone)
- Backblaze B2 costs ~$6/TB/month — affordable for homelab-scale backups.
- Use
rclone syncto push encrypted backups to B2 nightly. - Enable rclone's
cryptremote so data is encrypted before it leaves your network.
rclone encrypted sync to Backblaze B2
# Configure remotes (one-time)
rclone config
# Create: b2-remote (Backblaze B2) → b2-crypt (crypt wrapping b2-remote)
# Nightly sync
rclone sync /mnt/backup/docker b2-crypt:docker-backups \
--transfers 4 --fast-list --log-file /var/log/rclone.log
Option B: Second location (rsync over SSH)
- If you have a friend or family member with a NAS, trade backup space.
- Use
rsync -avz --delete -e sshover a WireGuard tunnel for encrypted transport. - Schedule nightly with cron. Use SSH keys — no passwords.
5. Encryption at Rest
Backups contain your most sensitive data. Encrypt them.
- LUKS — encrypt your backup drives with LUKS full-disk encryption. The backup script mounts, writes, and unmounts automatically.
- rclone crypt — for cloud backups, rclone's crypt remote encrypts filenames and content before upload.
- GPG for individual files —
gpg -c --cipher-algo AES256 backup.tar.gzfor one-off encrypted archives. - Store the key separately — your encryption key should not live on the same machine as the backups. Print it, store it in a safe, or save it in your password manager.
6. Test Your Restores
A backup you have never tested is not a backup. It is a hope.
- Monthly restore test — pick one service each month. Restore it to a test VM or container and verify it works. Document the process.
- Full disaster drill — once a quarter, pretend your server died. Time how long it takes to restore everything from scratch to a fresh install. Your target: under 4 hours.
- Verify integrity — checksums matter. After backup, generate a SHA256 hash. Before restore, verify it matches. Bit rot is real.
- Document the process — write a recovery runbook. When disaster strikes at 2am, you will not remember the steps. Store the runbook off-site too (printed copy, password manager note, or a separate Git repo).