nix module and flake for declarative non-NixOS VMs
Add a macOS image pipeline mirroring the Windows one: unattended install,
generalization and user creation, driven end to end in QEMU on a KVM host.
lib/images/macos:
- makeOpenCore: OSX-KVM OpenCore ESP with a config.plist rewritten per image —
SMBIOS model + serial/MLB (macserial) + UUID + ROM=en0 MAC (built-in NIC pinned
to PciRoot(0x0)/Pci(0x12,0x0)) for Apple ID / iMessage / App Store; boot disk;
OpenCore self-entry hidden. ident.nix derives MAC+UUID from a seed so the NixOS
module and CLI know the NIC MAC at eval time.
- makeImage: one QEMU session driven by vm-driver.py (QMP + screenshot settle
detection + OCR of the menu bar) — boots the recovery via OpenCore, opens
Terminal (Ctrl-F2 -> Utilities -> Terminal), types the bootstrap command;
vmix-install.sh erases the disk as APFS, lays down the host-extracted installer
app skeleton + a byte-exact SharedSupport.dmg (raw disk mapped to that byte range
of the pkg, dd'd in — Recovery's xar truncates an 18 GB member), runs
startosinstall with the vmix agent pkg. OpenCore is then copied into the image's
ESP so it boots standalone with OVMF.
- customizeImage / templates: boot the image with a FAT-then-HFS+ VMIX volume; the
vmix agent (LaunchDaemon) runs a script as root, records status and powers off —
the macOS counterpart of Windows Audit Mode. generalize creates the admin user +
auto-login (kcpassword), suppresses Setup Assistant, sets hostname/timezone,
grows APFS, and assigns a fresh SMBIOS identity. Templates: noUpdates,
performance, remoteAccess (ssh + screen sharing).
- fetchRecovery: Apple recovery BaseSystem, retried until the pinned Tahoe build
(osrecovery load-balances Sequoia/Tahoe during the rollout). makeAgentPkg builds
a distribution flat pkg on Linux (xar+bom+cpio) for startosinstall --installpackage.
CLI: vmix build/copy/run for macOS (run --macos --vnc, reads the image's MAC from
its ESP), and a `vmix macserial` helper. NixOS module: disks.os.file carrying
_vmixOsType="macos" auto-enables the macOS QEMU profile (AppleSMC+OSK, Skylake
CPU spoof, AHCI system disk, VMware SVGA, pinned NIC); macos.{enable,cpu,mac}.
Status: proven through the installer prepare phase (SharedSupport.dmg mounts,
version 26.6.2 read, SU catalog loads). Two blockers remain, documented in
lib/images/macos/README.md: (1) startosinstall's OSISVerifyBaseSystemOperation
rejects the byte-identical plain-UDIF SharedSupport as "pkgdmg missing a footer"
in this Tahoe recovery/VM; (2) Apple's CDN unreliably serves the Tahoe recovery
during rollout (self-hosting the verified BaseSystem is the robust fix).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XsESshRCoBoUVWV9qKURUF
|
||
|---|---|---|
| lib | ||
| nixos | ||
| .env-export-vmix-cli-local | ||
| .gitignore | ||
| cli.nix | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
vmix
Composable QEMU VM image building and orchestration for NixOS.
What it does
vmix provides:
- Image building — reproducible Linux (Debian) and Windows qcow2 images via Nix derivations
- NixOS module — declarative VM management with QEMU in network namespaces
- CLI tool — build, copy-to-disk, and run images from the command line
Usage
As a flake input
# flake.nix
inputs.vmix.url = "git+https://git.sagar.ch/dotfiles/vmix.nix.git";
# In your NixOS configuration (or globally via serverFunctions)
imports = [ inputs.vmix.nixosModules.default ];
# Use vmixLib via overlay
nixpkgs.overlays = [ inputs.vmix.overlays.default ];
# Then: pkgs.vmixLib.linux, pkgs.vmixLib.windows, pkgs.vmixLib.network
As a CLI
# Enter dev shell
nix develop
# Build an image
vmix build --image windows.images.win10.laptop \
--generalize username=User,password=secret,hostname=PC
# Write to local disk
vmix copy --image windows.images.win10.laptop \
--generalize username=User,password=secret \
--to-disk /dev/sda
# Write to remote disk (streamed via SSH + LZ4)
vmix copy --image windows.images.win10.laptop \
--generalize username=User,password=secret \
--to-remote-disk root@10.10.10.100:/dev/nvme0n1
# Boot a built image with QEMU
vmix run ./result --mem 8192 --smp 8 --ahci
Flake outputs
| Output | Description |
|---|---|
overlays.default |
Nix overlay exposing pkgs.vmixLib |
nixosModules.default |
NixOS module for declarative VM management |
lib.x86_64-linux |
Library functions (images + network utilities) |
packages.x86_64-linux.default |
vmix CLI tool |
apps.x86_64-linux.default |
Runnable vmix app |
Repository structure
flake.nix # Flake entry point
module.nix # NixOS module export
overlay.nix # Nix overlay (vmixLib pinned to nixpkgs 25-11)
cli.nix # CLI tool (build, copy, run)
lib/
default.nix # Exports: images (linux + windows) + network
network.nix # IPv4/CIDR utilities
images/
linux/ # Debian image building + customization
windows/ # Windows image building + customization
helpers/ # makeImage, customizeImage, makeWinISO, etc.
templates/ # Registry tweaks, app installers, essentials
drivers/ # VirtIO, AMD GPU drivers
win10/ # Windows 10 LTSC images
win11/ # Windows 11 images
nixos/
default.nix # NixOS module entry point
networks/ # Network namespace management (LAN, WAN, macvtap)
vms/ # VM lifecycle management (QEMU, tap devices, DHCP)
Image building
Windows pipeline
makeImage— unattended install from upstream ISO via QEMUcustomizeImageFold— apply modular templates (registry, apps, drivers)generalize— sysprep + OOBE for deployment to real hardware
Linux pipeline
- Fetch upstream Debian cloud image
customizeImageFold— apply templates viavirt-customize
Key patterns
customizeImageFold—builtins.foldl'over templates for composable layered images_vmixOsType— all images carry"linux"or"windows"metadata for auto-detection- Offline registry — Windows templates use
ControlSet001for offlinevirt-win-reg --merge
NixOS module
Declare VMs and networks:
vmix.namespaces."lab" = {
networks.lan1 = {
subnet = "10.99.1.0/24";
dhcp.enable = true;
};
vms.myvm = {
image = pkgs.vmixLib.linux.images.debian.v12.upstream;
memory = 2048;
cores = 2;
interfaces.lan1 = { ip = "10.99.1.10"; };
autostart = true;
};
};
Features:
- Network namespaces with WAN (veth), LAN (bridge + dnsmasq), macvtap
- ACPI graceful shutdown via QMP socket
- 9p shares with auto-created mount targets
- Conditional macvtap service (only created when macvtaps are configured)