vmlab
Declarative QEMU/KVM VM labs: machines and networks in one WCL file, reusable disk templates over OCI, and guest automation in wscript — unprivileged, from your terminal.
One file, one lab
The lab definition, found by walking up from the current directory (like git).
- A lab block declares the VMs and the networks that join them
- vmlab validate checks it; vmlab up boots it
- Clones, leases, and provisioning are wired automatically
Declaring machines
Declares one guest: its template, hardware, NICs, disks, shares and media.
- template = "x86_64/windows-server-2025" — boot from a stored disk
- cpus / memory (byte sizes: 8GiB), nic { segment = "corp" }
- depends_on orders boot; per-VM media folders become ISO/floppy
Vm: lifecycle & state methods
- vmlab up → linked clones boot, provisions run in order
- vmlab status / console / exec / logs while it runs
- vmlab down stops; vmlab destroy deletes the clones
Daemon model
A two-tier daemon: the supervisor vmlabd (one per user) plus one lab daemon per running lab, auto-started by the CLI.
Networking model
Networking is declarative: virtual L2 segments with daemon-supplied DHCP/DNS/NAT/routing/L3 filtering in userspace — no vmlab net CLI.
- segment blocks: a subnet with built-in DHCP and DNS
- Static IPs are reservations: nic { ip = "10.50.0.10" }
- nat = true, forward { host_port = … }, route, dns per segment
Unprivileged by design
vmlab runs unprivileged in Docker/Podman with only --device /dev/kvm; the network fabric is entirely userspace.
- The whole fabric is userspace — no bridges, no root, no capabilities
- Runs in containers and on WSL2 with only /dev/kvm
Templates
Sealed qcow2 disk images in the local store, referenced by <arch>/<name>[@<version>]; labs boot linked clones of them.
- A template is a reusable, versioned guest disk in the local store
- VMs boot linked clones — instant, and the template stays pristine
- Store refs: <arch>/<name>@<version> (x86_64/linux-modern@1.0)
Template build flow
A build resolves the source, boots a synthesised one-VM lab, runs the provisions, then flattens and seals into the store; a failed build leaves nothing behind.
- template blocks describe installer media + an install script
- vmlab template build runs the unattended install and stores the disk
- Builds are wscript-driven — keystrokes, screen matching, OCR included
Any OCI registry
Templates push/pull as OCI artifacts (not runnable images) through any OCI registry; chunked, multi-arch.
- vmlab template push / pull — chunked, multi-arch OCI artifacts
- Or reference the registry directly: template = "ghcr.io/…/alpine-3.23"
- Missing templates are pulled automatically on vmlab up
wscript: overview
A statically typed, Rust-flavoured scripting language; vmlab type-checks scripts at vmlab validate time.
- provision "scripts/setup.ws" runs after boot, in declaration order
- on "vm.crashed" { run = … } handlers react to lab events
- Scripts get the typed lab / vm / seg API objects
Drive guests without networking
- vm.exec(…), file copy, wait_ready — all over the guest agent
- Works before the guest has (or without it ever having) a network
Vm: screen, image matching & OCR methods
- Screenshot the console, match image templates, OCR the screen
- Combine with keystrokes to automate GUIs and installers end-to-end
Where to go next
- The golden path procedure in the book: template → lab → automation
- The reference book — every block, verb, and API call
- The Learn vmlab training course — hands-on lessons