3
copies
2
media
1
offsite
For a homelab that’s the live data, a local backup target, and one copy geographically separate. Three files on the same array is one copy.
This is the general shape. What my own chain actually looks like today — which legs run when, and what deliberately isn’t backed up — is in the rack tour.
The stack#
| Layer | Example | What it’s for |
|---|---|---|
| Primary | Proxmox + Longhorn / local disks | Working data |
| Local backup | Synology NAS / Proxmox Backup Server | Fast restore, same building |
| Offsite | Cloud VPS, Hetzner StorageBox, S3-compatible storage | Survives the building |
The tools matter far less than people think. What matters is that the three copies exist, that the offsite one is encrypted, and that you have restored from at least one of them.
The ways it fails anyway#
- Never testing restores. A backup you haven’t restored from is a hope.
- Backups on the same system as the data. That’s a second copy of a single point of failure.
- No encryption offsite. You’ve handed someone else your files.
- No versioning. A live mirror replicates the encryption as fast as ransomware writes it.
- No offsite copy. One fire, one flood, one theft.
Ransomware looks for backup files specifically, and a mounted backup share is the easiest target in the house. Without versioning or a copy that’s genuinely offline, there is nothing to recover from.
What actually helps: snapshots with a retention policy, at least one copy that’s offline or air-gapped, MFA on whatever manages the backups, and an alert when a job fails. That last one is cheap and catches the most common failure — backups that quietly stopped months ago.
Proxmox Backup Server, from any Linux host#
Both halves, written now rather than during the outage. --keyfile is what makes the copy on the far end useless to anyone who takes the disk.
| |
| |
PBS_REPOSITORY takes the API token’s full ID, user@realm!tokenname, and proxmox-backup-client reads it and PBS_PASSWORD from the environment, so neither needs repeating on the command line. With set -e the restore script stops at the failing command, instead of printing a success line after it.
Longhorn PVCs don’t go through PBS. Those are restored from the S3 backup target instead.
When one layer dies#
| What failed | What you still have | What to do |
|---|---|---|
| Local NAS | Backup server + offsite | Replace hardware, rebuild the array, restore from whichever copy is closest, then confirm the next scheduled job succeeds |
| Offsite target | Everything local | New instance, redeploy the client, restore the encryption key from the password manager, run the backup once by hand, confirm the cron is back |
| Backup server | NAS + offsite | Reinstall, restore the datastore config and mount point, check the datastore structure is intact (.chunks, namespaces), then pull from a surviving copy |
Each row assumes the other two copies are healthy. The reason 3-2-1 works is that it never asks you to bet on one of them.
What lives in the password manager#
Restoring is gated on things that are not themselves in the backup:
- The encryption keyfile, e.g.
pbscloud_key.json— without it the offsite copy is noise - Token credentials for the backup system
- The backup paths for each layer
- The cron definitions, so the automation can be rebuilt from nothing
Keeping it honest#
Monthly, run an integrity check over a random subset and read the logs for jobs that failed quietly. Quarterly, do a real restore of at least one backup and write down how long it took — that number is your actual recovery time, and it’s usually longer than anyone’s estimate.
Alert on failed jobs and on capacity thresholds. Running out of space is the most common way a backup stops working without anyone noticing.