Every virtual machine in the house is captured nightly, every container daily without stopping, and both copies leave the building: the machine history to Cloudflare R2, pulled by a box that can only read; the container archives and the photo library to Dropbox, behind six checks that stop a bad day from being mirrored. Nothing counts as backed up until it has been read back and matched. This page is the whole chain, with the numbers.
Three layers, in the order you would restore from them. The first two are point-in-time copies with retention; the third is verification, because a backup that has never been read back is a hope.
| What | Point in time, locally | Offsite | Checked by |
|---|---|---|---|
| Virtual machines the smart-home brain, the service host, the GPU server, the game servers, the security monitor | Proxmox Backup Server on the storage server, nightly, deduplicated; last five kept, and the hypervisor’s own configuration on a 14-daily / 8-weekly / 6-monthly ladder | Cloudflare R2, pulled each morning by a read-only token; 14 daily, 8 weekly, 6 monthly | Weekly verification that reads the chunks back from R2 and re-hashes them |
| The appliance’s guests the voice stack, the demo house, the AI NAS | Their own datastore on the same backup server, nightly | The same R2 bucket, in its own namespace so nothing collides | The same weekly verify |
| Containers 36 on the storage server: media, downloads, monitoring | A Btrfs snapshot of appdata at 08:30 while everything keeps running; one compressed archive per container; five days kept, with the flash drive and the container templates beside them | Dropbox at 11:00, mirroring the dated runs | A manifest per run, a notification on any failure, and archives opened from a scratch folder to prove they extract |
| The photo library | The array, behind dual parity | Dropbox, in the same daily mirror | The mirror’s own six guards |
| The backup server itself | Its system disk imaged daily at 06:00 by the Unraid VM Backup plugin, two kept, into the same backups share | Dropbox, with the rest of that share | — the data it protects is already in R2; the image only saves reinstalling it |
Everything on the household hypervisor, including Home Assistant, is a virtual machine, so the smart-home brain restores as a whole machine from any of its nightly snapshots.
Both hypervisors back their guests up to a Proxmox Backup Server that runs as a VM on the storage server, with its datastores on the array. Deduplication is what makes nightly full snapshots affordable: the household host’s store holds 671 GiB on disk for a dedup factor of 9.6, the appliance’s 197 GiB at 16×. The retention rules are simple locally — the last five snapshots per machine — because the long history lives offsite, where it is safer.
Two housekeeping facts worth knowing if you run one of these. A prune job with no depth limit recurses into every namespace beneath it, so a root-level “keep last five” will quietly flatten a longer ladder kept in a sub-namespace; each prune job here is pinned to its own depth. And a machine that stops sending backups keeps its last N snapshots forever, because prune only ever counts what arrives: retired guests are forgotten by hand, and the space comes back on the second garbage collection, after the 24-hour grace window.
The 36 containers on the storage server are backed up from a Btrfs snapshot of the appdata pool: every container is paused for the instant the snapshot commits — the same freeze Proxmox applies to a container in its own snapshot mode — then unpaused, and the archives are written from the frozen tree while the live one carries on. One .tar.zst per container, about 130 GiB a day, five days kept, restorable from the web GUI. The whole method, with its scripts and the exact plugin settings, is the appdata snapshots guide.
The backup server that holds the data holds no cloud credentials at all. A second Proxmox Backup Server, a small VM on the appliance with a 128 GB local cache disk, holds a read-only token for the first one and pulls each morning at 07:30 into an S3-backed datastore in R2. Whoever compromised the storage server would find nothing there that reaches the offsite copy. Deletions never propagate either: the sync jobs are set never to remove what has vanished at the source, so offsite retention is owned entirely by its own prune job — 14 daily, 8 weekly, 6 monthly, 28 snapshots per machine against 5 at home. That gap is deliberate: it is the dwell time a quiet intruder would need to outwait.
The provider was measured, not assumed. Round-trip times from Bangkok to the candidates: a Singapore object store 55 ms, two European ones around 190 ms, US regions 240–270 ms. R2 sits behind anycast, so its round trip says nothing; a real 2 GiB transfer measured 24 MiB/s up at default settings and 57 MiB/s with upload concurrency raised to 32, 50 MiB/s down. The Singapore option lost despite the latency: its 90-day minimum retention fights a backup server’s constant chunk churn, and its egress is capped against stored volume — which penalises verification, the one operation that matters most. R2 was chosen for zero egress, which makes reading every byte back as often as you like free; on a provider that charges for it, a documented backup-server case saw verification traffic alone multiply the monthly bill several times over.
What the first full verification taught. The first attempt at reading 868 GiB back over one connection surfaced a handful of dropped reads on the four largest disks — no hash mismatches anywhere, just transient connection errors that a re-read clears. The reader count was set from eight to four, and the baseline pass that followed came back 100 % clean: every chunk read from R2, every hash matched. From here the weekly job only re-reads what is new, with anything older than 30 days re-checked on its own cycle.
The backups share — the dated container runs, the backup server’s own disk image, the flash-drive images and the book catalogue’s database snapshots — and the photo library are mirrored to Dropbox by rclone from the GPU server, which runs the household’s scheduled jobs. The share is the source of truth and Dropbox follows it, deletions included; what makes that safe is that the share holds five point-in-time runs, not a live folder. A mirror mirrors deletions, so the dated, immutable archives are the layer you restore from and the mirror is the copy of them that lives elsewhere.
| Time | Job | Where |
|---|---|---|
| overnight | Every guest on both hypervisors to the backup server; the hypervisor’s own configuration at 03:00 on its long ladder; local prune at midnight | Storage server |
| 06:00 | The backup server’s system disk imaged, two kept | Storage server |
| 07:30 | Both datastores pulled to R2 | Appliance → R2 |
| 08:30 | Containers paused, snapshotted, unpaused; one archive per container written until about 10:25 | Storage server |
| 09:00 / 10:00 | Offsite prune, then offsite garbage collection | R2 |
| 11:00 | Backups share and photo library mirrored to Dropbox, about two hours | GPU server → Dropbox |
| Saturday 11:00 | Verification reads the week’s new chunks back from R2 and re-hashes them | R2 |
The order is the point: nothing reads a folder another job is still writing, and the offsite prune runs after the sync has delivered the day’s snapshots, never before.