Blog ·
What a backup does not contain
The same missing option means yes on a virtual machine and no on a container. Here is how we measured Proxmox backup coverage instead of promising it.
By Provibr
Ask a control panel whether a machine is backed up and you usually get a yes. The interesting question is narrower and much harder: which parts of it are in the archive, and which parts are not. A container with a bind mount to a directory on the host has data that no vzdump will ever touch, and a panel that says "backed up" over that configuration is not being optimistic, it is making a promise on behalf of somebody else's software.
So we stopped promising and started measuring. Every backup and every restore point now carries an observation of what the guest looked like at the time, and the panel derives coverage from that observation instead of from the fact that a backup succeeded.
The same absence, two opposite meanings
The reason this needs measuring rather than reasoning is one line of Proxmox behaviour that goes the opposite way for the two guest types.
For a container, a mount point is in the archive only when backup=1 is written on it. The documentation is explicit: to include a mount point in backups, enable the backup option for it. Absence means no.
For a virtual machine, a disk is in the archive unless backup=0 is written on it. That is not in the manual in so many words; it is in the function that hands vzdump its list, where the value is read as the drive's backup flag defaulting to one. Absence means yes.
| Config line | In the archive? |
|---|---|
rootfs: local-lvm:vm-101-disk-0,size=8G | yes, always; the rootfs has no backup field at all |
mp0: local-lvm:vm-101-disk-1,mp=/data,backup=1 | yes |
mp0: local-lvm:vm-101-disk-1,mp=/data | no |
mp0: /srv/data,mp=/data (bind mount) | no; contents of bind mount points are not backed up |
mp0: /dev/sdb1,mp=/disk (device mount) | no; the same holds for device mount points |
scsi0: local-lvm:vm-100-disk-0,size=32G | yes |
scsi1: local-lvm:vm-100-disk-1,backup=0 | no |
ide2: local:iso/debian.iso,media=cdrom | not applicable |
efidisk0: … with bios: ovmf | yes |
efidisk0: … without bios: ovmf | no |
tpmstate0: … | yes |
scsi2: /dev/sdb (passthrough) | yes |
unused0: local-lvm:vm-100-disk-4 | no |
virtiofs0: dataset-a | no |
The two bold lines are the same absence. Fold them into one sentence and one of your two screens is lying, and neither of them errors.
Reading the source, not the intuition
Three of those rows came out differently from what the plan for this work assumed, and all three were settled by reading Proxmox rather than by arguing.
A passthrough disk was assumed to be host data that stays behind, by analogy with a container's device mount. It is not. The function that builds the backup list never looks at the path, and the preparation step turns a volume id with no storage behind it into a block device and includes it. Getting that wrong in the safe looking direction would have told customers that data was outside their archive when it is inside, which sounds harmless until you notice it forces an acknowledgement on every restore and thereby teaches people to click through acknowledgements.
Detached disks and shared host directories were simply missing from the plan. An unusedN volume is real customer data sitting on the storage with no bus attached, and vzdump logs that it skips it. A virtiofsN entry is a directory on the host and never a volume, which makes it the virtual machine equivalent of a bind mount. Both are genuine gaps in coverage and both would have counted as complete.
The disk key prefixes and their counts come from the same place: four IDE slots, six SATA, thirty one SCSI, sixteen virtio, one EFI disk, one TPM state, two hundred and fifty six unused slots, ten virtiofs. A key outside those ranges is not a disk to Proxmox, so it must not quietly become one here. And virtiofs is tested before virtio, because virtiofs0 starts with virtio: with the order reversed, a shared host directory would be read as a disk and reported as backed up.
Unknown is never called complete
The words on the screen for an unmeasured guest are "not established", spelled out as: what a backup holds has not been established. Never "complete", never "not in the backup". There are three ways to land there, and all three are real: the guest is a type we have not taught the agent to read, the agent predates the release that reads configs at all, or the agent tried and could not read it.
That last case is why the observation carries an explicit "I read the config" flag. Without it, an empty list of mount points is indistinguishable from a container with no extra mounts, except that the second one does not exist: there is always a rootfs. An agent that could not look would otherwise read as a guest with nothing outside the archive, which is precisely the failure this work exists to prevent.
Two more rules push the same way. A container config that was read but contains no rootfs is unknown and not complete, because we evidently read something that was not a container config. And a virtual machine config with nothing in it that raises a coverage question at all, nothing, or only cd-rom drives, is unknown for the same reason: complete means everything is in, and saying that about zero things is a claim with no measurement under it. The price is known and accepted, and it is that a genuinely diskless machine now reads as unestablished and asks for an acknowledgement on restore. That is the side where being wrong costs nothing.
The verdict is also ordered on purpose: demonstrably outside beats not measured. A container with one bind mount and one unreadable line is partial, not unknown, because the part that matters is proven. The unreadable line does not disappear; it keeps its own row with its own word.
Not applicable is not excluded
A cd-rom drive gets its own coverage state, and the distinction is not pedantry, it is the action that follows.
Excluded means customer data is outside the archive and there is a checkbox in Proxmox that changes it. A cd-rom carries no data belonging to this machine: it is an image from a library, or the cloud-init disk that is rebuilt on every start, and Proxmox skips it before it evaluates anything else. If a cd-rom were called excluded, every machine with an ISO in its drive would be partial and every restore would ask for an acknowledgement about a drive with nothing on it. People would learn that the acknowledgement means nothing, and then it would mean nothing.
The row still appears in the table with its own word. A row that is missing raises the question of whether we looked, which is exactly the ambiguity the read flag exists to remove.
The acknowledgement is fail closed
Restoring an archive whose coverage is anything other than a measured "complete" requires ticking a box that names what was outside. Including unknown, which is the deliberate part: an old restore point, or an archive somebody else made, may well be missing host data, and the tick is where the customer learns to go and check. Only a measured complete skips it, because then there is genuinely nothing to acknowledge.
The same sentence guards two doors, the handler and the function behind it, because a screen is not a boundary. And the confirm button is not disabled while the sheet is open: a disabled element cannot hold focus, so a dialog that disables its own opener cannot hand focus back when it closes, and a keyboard user lands on the document body. Busy state and dimming do the same job without breaking that.
Whatever fell outside the archive is also written as an event on the restore, at warning level, with the mount points in its payload. A restore that quietly succeeded while leaving three directories behind should be findable a month later by somebody who was not in the room.
What we would write down
- Measure coverage, do not infer it. "The backup succeeded" and "your data is in the backup" are different statements about different things.
- Check whether a default is the same on both sides of your abstraction. Here one missing option means yes for one guest type and no for the other, and a shared sentence would be wrong on exactly one screen with no error anywhere.
- Read the code that actually builds the list, not only the manual. Three of our rules were wrong until we did, and one of them was wrong in the direction that feels safe.
- Give "not measured" its own word and never let it collapse into either yes or no.
- Give "no question here" its own word too. A state that forces a meaningless confirmation trains people to ignore confirmations.
- Put the observation and the judgement in different places. The agent reports what it saw; one function decides what it means, with tests beside it and no way for the reporter to claim completeness.
For clarity about what this is: customer backups sit behind a rollout flag that is off by default, so none of this is purchasable today, and the rules were verified against Proxmox's documentation and source rather than against a live hypervisor of our own. Three things are unproven or deliberately out of scope. The coverage of an individual restore point is inherited from the run that made it rather than read from the archive itself. Virtual machine disks carrying backup=0 are visible in this table but nowhere else in the product. And a genuinely diskless machine will read as unestablished, which is a cost we chose over the alternative.
From the backup work on Provibr. Why an untested restore is not a backup is in a backup that was never restored, and what the three vzdump modes actually guarantee is in Proxmox vzdump modes: what they guarantee.