I ran a Proxmox homelab for years with a backup job that reported success every night. It had been created for "selected VMs" and every guest I built after that was simply not in the list. The job was fine. It was protecting the wrong things.
This is the setup I run now for a home Proxmox cluster with OPNsense and Home Assistant on it. Nothing exotic: vzdump, Proxmox Backup Server, rclone, and a small amount of discipline around proving it works.
1. The job covers everything by default
Datacenter → Backup → Add. The settings that matter:
- Selection mode: All, with your scratch guests under Exclude. New guests are protected the day they are created.
- Mode: Snapshot, Compression: ZSTD, nightly at a quiet hour.
- Retention: use storage defaults, so the numbers live in one place.
- Notification mode: Notification system (not legacy email), so matchers can route failures to your phone.
- Notes template:
{{guestname}} on {{node}}, so the backup list is readable at 2 AM.
The same thing on the command line, which is what the GUI writes into /etc/pve/jobs.cfg:
vzdump --all 1 --exclude 900,901 --mode snapshot --compress zstd \ --storage nas-backup --notes-template '{{guestname}} on {{node}}' \ --notification-mode notification-system Put the archives on something that is not the host's own disk. An NFS export on the NAS is the simplest:
pvesm add nfs nas-backup --server 192.168.10.40 --export /volume1/proxmox \ --content backup --options vers=4.1 pvesm set nas-backup \ --prune-backups keep-last=2,keep-daily=7,keep-weekly=4,keep-monthly=3 Install the QEMU guest agent in every VM (apt install qemu-guest-agent, enable it in the VM's Options). With it, snapshot mode asks the guest to flush its filesystems first, which is the difference between a consistent backup and one that needs fsck on restore.
Add --bwlimit 50000 if the nightly run makes anything laggy. It did for me.
2. Retention: keep-daily=7 does not mean seven days
keep-daily=7 means "keep the last backup from each of the last seven days that have a backup". If the job failed for a month and then ran once, that single run satisfies keep-daily, keep-weekly and keep-monthly at the same time, and pruning deletes the older ones. The rules never promise coverage of time.
So the weekly and monthly buckets matter more than the daily ones, and keep-last is the safety margin for "I just took a backup before an upgrade". What I use:
| Guest type | keep-last | daily | weekly | monthly |
|---|---|---|---|---|
| Databases, Home Assistant | 2 | 14 | 8 | 6 |
| Ordinary services | 2 | 7 | 4 | 3 |
| OPNsense (barely changes) | 2 | 0 | 8 | 6 |
Ten minutes with the Prune Simulator in the PBS docs fixed a misunderstanding I had carried for years.
Before a risky change, take a manual backup and mark it Protected (the shield icon). It survives every prune until you clear the flag.
3. Proxmox Backup Server, with the key saved somewhere else
vzdump copies the whole guest every night. PBS splits every disk into chunks, stores each unique chunk once, and after the first run only ships what changed. Thirty nightly backups of a 32 GB Home Assistant VM take a little more space than one.
Put PBS on its own box, or at least its own disks. Then:
# on PBS proxmox-backup-manager datastore create backups /mnt/datastore/backups proxmox-backup-manager user create backup@pbs proxmox-backup-manager acl update /datastore/backups DatastoreBackup \ --auth-id backup@pbs proxmox-backup-manager cert info | grep Fingerprint # on the Proxmox node pvesm add pbs pbs01 --server 192.168.10.30 --datastore backups \ --username backup@pbs --password '...' --fingerprint 'aa:bb:...' pvesm set pbs01 --encryption-key autogen That last line encrypts every chunk on the client before it leaves the node, and writes the key to /etc/pve/priv/storage/pbs01.enc. Copy that key somewhere that is not this cluster (password manager, printed QR). Without it, every encrypted backup is unrecoverable. With it, you can restore onto a brand-new Proxmox install. It is the one mistake in all of this with no undo.
On the PBS side, schedule three jobs under the datastore: a prune job with the same keep-* numbers, daily garbage collection (pruning only unlinks; GC frees chunks, after a 24-hour grace period), and a weekly verify job with "skip verified" on and "re-verify after" set to 30 days.
4. Off-site: rclone with a crypt layer
Everything above is still in one house. The cheapest third copy I know of is an encrypted rclone sync to object storage. Two remotes: the raw bucket, and a crypt remote on top so the provider only ever sees encrypted names and content.
rclone config # n) name: b2raw, type: b2, paste the key ID and key (scoped to one bucket) # n) name: b2, type: crypt, remote: b2raw:homelab-backups, two passwords chmod 600 ~/.config/rclone/rclone.conf rclone sync /mnt/backups/dump b2:dump \ --transfers 4 --bwlimit "08:00,2M 23:00,off" --fast-list \ --log-file /var/log/rclone-b2.log --log-level INFO sync mirrors deletions, which is right for a dump directory that prunes itself. Set a lifecycle rule on the bucket so hidden old versions expire after ~30 days, and use rclone copy (never deletes) for anything irreplaceable. rclone check b2:dump /mnt/backups/dump compares without transferring; run it monthly.
The crypt passwords go in the same place as the PBS key.
If a friend runs PBS too, a push sync job over Tailscale is even better: proxmox-backup-manager sync-job create ... --sync-direction push. Client-side encryption means neither of you can read the other's data.
5. The alert you actually need is "nothing succeeded recently"
A job that never runs never fails. The off-site sync that stopped in February will not send a single error until the day you need it. Alerting on failure is not enough; you need a dead-man switch that expects a ping.
vzdump has hook scripts for exactly this. In /etc/vzdump.conf:
script: /usr/local/bin/vzdump-hook.sh #!/bin/bash # vzdump calls this with the phase as $1 URL=https://hc-ping.com/your-uuid-here # healthchecks.io, or an Uptime Kuma push monitor case "$1" in job-end) curl -fsS -m 10 --retry 3 "$URL" >/dev/null ;; job-abort) curl -fsS -m 10 --retry 3 "$URL/fail" >/dev/null ;; esac exit 0 Set the check's period to a day and its grace to a few hours. A night the job does not run, for any reason, becomes a phone notification by breakfast. Do the same with a && curl at the end of the rclone cron line.
Hooks prove the job ran; they don't prove it covered every guest. This weekly script flags any guest whose newest backup is older than two days, and any guest with no backup at all (the gap a hand-picked job list leaves):
#!/bin/bash STORE=pbs01; MAXAGE=2; now=$(date +%s) API=/nodes/$(hostname)/storage/$STORE/content pvesh get $API --content backup --output-format json \ | jq -r '.[] | "\(.vmid) \(.ctime)"' | sort -k1,1n -k2,2nr | sort -u -k1,1n \ | while read -r vmid ctime; do age=$(( (now - ctime) / 86400 )) [ "$age" -gt "$MAXAGE" ] && echo "STALE: guest $vmid, last backup ${age}d" done ids="$(qm list | awk 'NR>1{print $1}') $(pct list | awk 'NR>1{print $1}')" for id in $ids; do pvesh get $API --content backup --vmid "$id" --output-format json \ | jq -e 'length > 0' >/dev/null || echo "NONE: guest $id has no backup" done Mail or push the output only when it is not empty.
6. Restore something every quarter
A PBS verify job proves chunks match their hashes. zstd -t proves an archive is complete. Neither proves the guest boots. Four times a year, one evening: restore a VM from PBS to a new ID with the network unplugged, restore a container from the vzdump copy, pull one file with File Restore, and pull one archive back from off-site. Write down how long each took. Then destroy the test guests.
qmrestore pbs01:backup/vm/101/2026-10-08T01:30:00Z 9101 \ --storage local-zfs --unique 1 qm set 9101 --net0 virtio,bridge=vmbr0,link_down=1 qm start 9101 # look at it, then qm destroy 9101 --purge The first time you watch a restored VM boot, the whole system stops being a hope and becomes a fact.
I wrote all of this up properly, with Home Assistant and OPNsense config backups, the backup → verify → destroy cleanup routine, and a weekend checklist. One chapter plus the checklist is free as a PDF: https://payhip.com/b/qbkVG
The full book, Never Lose Your Data: A Homelabber's Backup Playbook (41 pages, PDF + EPUB), is $19: https://payhip.com/b/1iHXQ

