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