Repository navigation
A restored guest gets its own MAC address.
The plan now says what network identity a restore will wear. Reported as issue #82 by an adopter on PVE 9.2.2: pve_restore of a QEMU backup to a new VMID produced a copy with the original's MAC address and SMBIOS UUID, and the plan said only that a new guest would be created. The QEMU branch sent PVE {vmid, archive} and nothing else, on a docstring that called storage an LXC-only parameter. PVE's own API takes unique ("Assign a unique random ethernet address") and storage ("Default storage") on the create-from-archive call for both guest kinds, which the reporter confirmed by direct API call.
restore_guest now sends storage for both kinds and asks for unique=1 by default. The one exception is a force overwrite of the same VMID the archive came from, which keeps the archive's MAC, because that is what a restore in place means; a force restore of one guest's archive onto another VMID gets a fresh MAC, since the source may still be running. unique is a tool parameter now and an explicit value wins either way. storage is a behaviour change for QEMU: the old call sent none, so PVE put the disks back on the storages named in the archive; now they land on the storage you pass, and the plan's change line and blast radius say so. When the VMID exists and force is not set, PVE rejects the restore and the plan carries no identity note. The plan carries a note naming the network identity the restored guest will wear: a new MAC with everything inside the disks copied as-is (hostname, /etc/machine-id, which systemd-networkd derives its DHCP client id from, keys), with the reporter's own recipe for a test restore beside a running original (link_down=1 on the NIC, read it through the guest agent); or, with unique=false, the same MAC and the warning that two machines with one identity share the bridge. What unique does is read from the vendor's own source, not guessed: qemu-server 9.2.11's restore_update_config_line gives every netN a new random MAC and smbios1 a new UUID when unique is set, and regenerates vmgenid on every restore; without unique the MAC and the SMBIOS UUID come out of the archive as they went in. The plan note says MAC and SMBIOS UUID for QEMU and MAC alone for LXC, and hedges both the identity and the storage line with "if the restore proceeds" when the existence read failed. The in-place rule reads the archive's source VMID from both shapes PVE restores from, a vzdump file archive by its basename and a PBS snapshot by its vm/<vmid>/ or ct/<vmid>/ path, anchored so a decoy in the storage id or an earlier directory cannot name the wrong guest; an archive whose name carries no VMID is never in place, so it fails toward a fresh MAC.
Proved live, not only read. On a standalone PVE 9.2 lab node, through this library: a disk-less VM backed up with vzdump and restored twice. The default restore came back with a new MAC and a new SMBIOS UUID; the unique=false restore came back wearing the archive's MAC and UUID, which is the shape the issue reported; vmgenid was new on both. The proof found a second defect: PVE answers a status or config read for a VMID with no config on that node with HTTP 500 and the message "Configuration file 'nodes//<qemu-server|lxc>/.conf' does not exist", never with a 404 (read live, both kinds, both endpoints). The restore, delete, template-convert and migrate plans confirmed absence only on a 404, so on a real PVE they had never once confirmed a guest absent and called every new-VMID restore "could not verify". That file is per node, so the 500 alone means "not on this node": in a cluster a guest alive on node B answers the same through node A. backends.guest_absent now confirms absence only when the message names this guest's own config file and /cluster/resources?type=vm lists no guest with that VMID; a 404 stays absent; a roster that cannot be read, a roster that lists the VMID, or any other error stays unknown and hedged. Eight more 404-only existence reads for users, realms, pools and ACLs are untouched: different endpoints, not probed live.
Twenty-four new tests in tests/test_backup.py and three in tests/test_server_plan.py: the QEMU body carries storage; both kinds carry unique=1 for a new VMID, omit it on an in-place force overwrite, and carry it again on a force restore across VMIDs; an explicit unique wins on both kinds; the plan note names the identity in both shapes, names the SMBIOS UUID for QEMU only, and is absent when PVE will reject the restore; the plan names the storage; the tool threads unique to both the plan and the API call; PVE's 500 "does not exist" answer with an empty cluster roster reads as confirmed absence, the same 500 with the VMID alive on another node stays unknown (a force restore onto it is not "creates new"), another 500 or an unreadable roster stays unknown and hedged, and the delete, template-convert and migrate plans confirm absence on the live shape too. The confirm-sweep fixture learned the new unique key. Four adversarial lens rounds on this release found the force default wrong across VMIDs, the storage change undisclosed, the in-place rule blind to PBS snapshot volids, and the first cut of the absence rule blind to a guest alive on another cluster node; all four are folded here.
Full history for this release, commit by commit, is on the internal mirror; this
public branch carries one squashed commit per release by design.
Every change here is described in CHANGELOG.md.
Where to read more
- The two-deployment trust model, and the honest scope notes saying what does not hold: SECURITY.md
- Wiring it into an MCP client, least-privilege tokens, pinning the tool surface: docs/SETUP.md
- Every tool, per plane: docs/TOOLS.md
- Verifying the image signature, the SBOM and the provenance yourself: VERIFY.md
- Full change detail: CHANGELOG.md
Install: uvx proximo-proxmox · pip install proximo-proxmox · ghcr.io/john-broadway/proximo:0.44.2