The second storage tier nobody was writing to
- date
- 20260920
- what happened
- Proxmox Backup Server, the disk-level backup layer for the virtualisation cluster's guests, was running with a datastore configured on the central storage server, but a live audit found the cluster's backup scheduler completely empty: no scheduled job for any guest, and exactly one manually triggered test backup from 20260904 in the datastore.
- what it cost
- Roughly two weeks (20260904 to 20260920) in which the designed disk-level backup layer provided zero coverage for any production guest. Every stateful guest happened to have independent coverage of its own, a restic repository or a filesystem snapshot, which is the only reason nothing was lost.
- what changed
- Closed on 20260920, the day it was found. The datastore was given the nightly guest job it had never had, covering every guest except two test ones, so a new guest is covered the day it exists; rules now page if the job disappears or the newest archive passes 30 hours; and a restore was proven the same night. The gap is recorded here as a finding rather than quietly patched, because the two weeks of green checks are the lesson.
- the check now
- The cluster's backup job list and the scheduler's cron entry are checked directly, and the age of the newest archive per source is what pages, rather than inferring backup health from a datastore existing and being reachable.
Some infrastructure gaps are loud: something breaks, an alert fires, someone investigates. This one was silent in the most complete way possible. A whole layer of a backup design existed, was correctly built, had a working restore path proven against it, and simply had no job configured to ever put anything into it. Nothing about that state produces an error. The storage server reports healthy. The datastore reports available capacity. The one backup that ever ran, a manual test weeks earlier, succeeded and sat there looking exactly like evidence the system worked, when it was the entire extent of what the system had ever done.
The only reason this near-miss carried no real cost is that a separate, redundant mechanism already covered every guest that mattered, a fact true by coincidence of how those guests had been set up rather than by any design connecting the two layers.
Finding it required a deliberate audit asking what this component’s actual scope, actual schedule and actual proof of a successful restore were, rather than an alert-driven investigation. Nothing about the component’s own behaviour signalled anything missing. A backup system being reachable, healthy, and holding at least one successful entry is not the same claim as a backup system being scheduled to run against the things that matter, and only an audit that asks about schedule and scope explicitly catches the difference.
The stance changed with the job. Before, a guest was backed up if someone had added it to a plan; after, every guest is backed up unless it is explicitly excluded, and the job selects all guests and names the two test ones it leaves out. The difference is where the silence lands. Under the old stance a forgotten guest was silently unprotected; under the new one a guest that should not be backed up shows up in the archive until somebody says so, which is a cost measured in disk rather than in loss. A new guest is covered the day it exists without anyone remembering to add it, and the exclusion list is short enough to read at a glance in the audit. The same default applies to the seed: a backup plan selects everything and excludes by name, never the reverse.
Source: node0 lessons v0.1, lesson 3.21. Sanitized: checklist v0.1, 20260921; cluster and node names; voice pass 20260921. Part of oznog.com/node0.
