vms: disks.os.persistMode (copy|backing) + a GC-root for the backing chain

persistMode=backing seeds the persistent OS disk as a thin qcow2 overlay
(`qemu-img create -b <storeImage>`) instead of a full cp, so many VMs share one
base image and each holds only its deltas. Because the overlay reads through a
store path that nix does not otherwise pin (the disk lives outside the store),
and because even a cp'd Windows disk backs onto the store chain, a per-VM
oneshot `vm.vmix-gcroot@<name>` reads the overlay's ACTUAL backing_file at boot
(not the config's current image, which drifts after a rebuild) and symlinks it
under /nix/var/nix/gcroots. It runs before the VM service and outside its
ProtectSystem sandbox. Fires whenever the OS disk is persistent, covering copy
too. Default stays copy, so existing VMs are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
This commit is contained in:
Git Sagar 2026-09-16 12:23:45 -03:00
parent 60013006d6
commit c40f4460e3
2 changed files with 47 additions and 2 deletions

View file

@ -173,6 +173,17 @@ with lib;
default = "";
description = "Mutable path for the persistent OS disk (e.g. /storage/vms/myvm/os.qcow2). Required when persist = true.";
};
disks.os.persistMode = mkOption {
type = types.enum [ "copy" "backing" ];
default = "copy";
description = ''
How the persistent OS disk is seeded from the store image (persist = true).
copy: a full cp of the store image; the mutable disk holds everything.
backing: a thin qcow2 overlay backing onto the shared store image, so many
VMs share one base and each holds only its own deltas. The store image (and
its backing chain) must then survive GC -- vmix pins it via a per-VM gcroot.
'';
};
disks.iso.file = mkOption {
type = types.nullOr (types.either types.path types.str);
description = "Path to the ISO file. Can be a Nix store path or a string path to a local file.";