Getting started · tutorial
Install
This tutorial puts the vmlab binary and its guest-side assets on your host, installs the tools it drives, and explains what those assets are. At the end vmlab --version prints a version and your host is ready for Your first lab.
Before you start
- A Linux host, or Windows with WSL 2. There is no macOS build, because vmlab drives QEMU/KVM.
- /dev/kvm present and writable by your user. Without it every guest is emulated, which is far slower. On WSL 2 this needs the nested-virtualisation setting described at the end of this chapter.
- A package manager that can install QEMU. The package names below are Debian and Ubuntu names. Other distributions ship the same tools under different names.
- curl or wget for the installer, and a Rust toolchain only if you build from source.
Install the CLI
The installer downloads a prebuilt Linux x86_64 binary from the GitHub release and places it in ~/.local/bin. From the same release it then installs the guest assets into ~/.local/share/vmlab/guest/, which the section on guest assets below describes. vmlab publishes pre-releases only, so pass --pre. Without it the installer looks for a stable release, finds none, and stops with a message telling you to add the flag.
| To pin a version, pass --version <X> with a release tag instead of --pre, or set VMLAB_VERSION. To install somewhere other than ~/.local/bin, pass --bin-dir <dir> or set VMLAB_INSTALL_DIR. If the target directory is not on your PATH, the installer prints the export line to add.
--no-guest installs the binary alone. --guest-dir <dir>, or VMLAB_GUEST_DIR, puts the guest assets somewhere other than ~/.local/share/vmlab/guest/; vmlab does not search an arbitrary directory, so the installer then prints the VMLAB_GUEST_ASSET_DIR setting that points vmlab at it.
Build from source instead
The prebuilt binary exists for Linux x86_64 only. On any other Linux architecture, or if you want the current main branch, build with cargo. The source tree pins its WCL and wscript dependencies by git revision, so no sibling checkouts are needed.
From a checkout, just build runs cargo build and just install runs cargo install --path . --locked, which places the binary in ~/.cargo/bin. A source build installs no guest assets; the section on guest assets below says how to build them.
To build everything a release ships without installing a single toolchain on your host, use the build container. It needs Docker and nothing else. The image holds every toolchain on Ubuntu 24.04, the distribution CI builds on. Your checkout is mounted into it and the build runs as your user, so nothing in the checkout ends up owned by root.
target/dist/ then holds the release binary vmlab-<version>-linux-x86_64, the guest bundle vmlab-guest-<version>.tar.gz with its .sha256, and the BPF objects rebuilt under bpf/. The first run builds the image and every target from cold. Later runs are incremental, because cargo's caches and target directories persist in Docker volumes. just buildbox::shell opens a shell in the same container, where just ci::check runs. build/README.md describes the setup.
Confirm the binary runs.
vmlab is installed
vmlab --version prints a version string. If the shell reports command not found, add the install directory to your PATH and open a new shell.
Install the runtime tools
vmlab does not bundle QEMU. It finds the emulators and helper tools on your PATH. UEFI firmware is the exception: the guest assets carry vmlab's own OVMF and AAVMF, secure boot included, so the distribution's firmware packages are optional on x86_64 and aarch64. The package list below covers every guest architecture and every feature described in this manual.
| Package | Provides | Needed for |
|---|---|---|
| qemu-system-x86, qemu-system-arm, qemu-system-misc | qemu-system-x86_64, qemu-system-aarch64, qemu-system-riscv64 | Running guests. Install only the architectures you use. |
| qemu-utils | qemu-img | Every disk operation: clones, snapshots, template builds |
| seabios | BIOS firmware | Booting firmware = "seabios" guests; usually pulled in by QEMU itself |
| qemu-efi-riscv64 | UEFI firmware for riscv64 | riscv64 guests; vmlab bundles no riscv64 firmware |
| ovmf, qemu-efi-aarch64 | The distribution's UEFI firmware | Optional. vmlab boots its bundled firmware first and falls back to these only when the guest assets are missing |
| swtpm | A software TPM 2.0 | Guests with tpm = true, which Windows 11 and Server 2025 require |
| passt | Userspace networking helper | The network fabric |
| xorriso, mtools, dosfstools | ISO and floppy builders | media {} blocks and the bootstrap ISO every template build attaches |
| samba | smbd | Shared folders on guests without virtiofs support |
| tesseract-ocr | tesseract | vmlab vm ocr and wait_for_text in scripts |
Two more tools are optional. sqfstar from squashfs-tools is required the first time you run a container, because vmlab flattens the image's layers into a squashfs. A VNC viewer is required for vmlab console and gui = true; remote-viewer from virt-viewer is preferred because it dials the display's unix socket directly, and gvncviewer or vncviewer work over a local TCP bridge. Shared folders use virtiofsd when the host and guest both support it and fall back to the bundled smbd otherwise, so installing both covers every guest. Install virtiofsd 1.13.0 or later for every feature: online snapshots of a VM with a virtiofs share need 1.11.0, and read-only virtiofs shares need 1.13.0. Ubuntu 24.04 ships 1.10.0, and the install script says so when it finds one that old.
vmlab checks for a missing tool when it first needs it, not at install time. vmlab validate probes the host only for virtiofsd, when a share demands virtiofs and the installed one cannot serve it. A missing emulator is reported by vmlab up, a missing sqfstar by the first container start, and a missing viewer by vmlab console.
Runtime tools are installed
qemu-system-x86_64 --version and qemu-img --version both print a version. ls /dev/kvm shows the device.
Get the guest assets
Two things run inside guests that are not part of the vmlab binary. The first is the micro-VM guest asset, a pinned Alpine kernel and an initramfs holding vmlab's own init, which boots every lab container. The second is the agent binary, vmlab-agent, one per guest OS and architecture, which a template build bakes into the image from a bootstrap ISO. vmlab looks for both under one directory tree, in this order.
| Location | When it is used |
|---|---|
| $VMLAB_GUEST_ASSET_DIR/ | An explicit override, when the variable is set |
| /usr/share/vmlab/guest/ | A system-wide install |
| ~/.local/share/vmlab/guest/ | The per-user data directory |
Inside that tree the micro-VM asset lives at <arch>/vmlinuz, <arch>/initramfs.img and <arch>/VERSION, and the agent at agent/<os>-<arch>/vmlab-agent or vmlab-agent.exe with its own VERSION.
The installer places them for you. Every release carries vmlab-guest-<version>.tar.gz, built by CI with every target: the micro-VM asset for x86_64 and aarch64, the Rust agent for Linux x86_64, aarch64, riscv64 and x86 and for Windows x86_64 and x86, and the legacy agents for Windows NT, Windows 9x, DOS, old 32-bit Linux and TempleOS. The installer downloads it from the same release as the binary, checks it against the .sha256 published beside it, unpacks it next to the target directory and renames it into place. The previous contents move aside whole and are deleted, so nothing from an older version survives an upgrade.
A failed download, a checksum mismatch, or a release published before the bundle existed costs only the guest assets. The installer keeps the binary, prints a warning, and names the source build below as the fallback.
Build the guest assets from source
Build them yourself when you work on vmlab, run a binary built from source, or installed with --no-guest. One recipe runs the three build scripts and copies the result into ~/.local/share/vmlab/guest/. It skips, with a warning, every target whose toolchain is missing, so a developer host builds what it can.
guest/build-asset.sh fetches pinned Alpine packages, verifies their checksums, and assembles the kernel and initramfs for x86_64 and aarch64. It runs without root and needs curl, tar, gzip, cpio, sha256sum, cargo, rustup and git on the host, plus the x86_64-unknown-linux-musl and aarch64-unknown-linux-musl Rust targets installed with rustup target add. guest/build-agent.sh cross-compiles the agent for Linux x86_64, aarch64 and riscv64 and for Windows x86_64 and x86. The Windows targets need x86_64-w64-mingw32-gcc and i686-w64-mingw32-gcc from mingw-w64 and are skipped with a warning when absent. The riscv64 target is best-effort and skipped the same way. guest/build-agent-legacy.sh builds the legacy agents: the Windows NT build with mingw-w64, the Windows 9x and DOS builds with OpenWatcom v2 (found through $WATCOM, else ~/.local/opt/open-watcom-v2), and the old-Linux build with the host cc at -m32 -static, which needs gcc-multilib on an x86_64 host.
just guest-package is the release build. It builds every target and packs the bundle into target/guest-package/, and it is strict: a target whose toolchain is missing fails it instead of being skipped. Setting VMLAB_REQUIRE_ALL_TARGETS=1 makes any of the three build scripts strict the same way. scripts/guest-toolchains.sh installs every toolchain the strict build needs on Ubuntu 24.04, and is what CI runs.
The Windows agents are built twice over for age. They target *-win7-windows-gnu rather than *-pc-windows-gnu, because the standard library's usual RNG entry point (ProcessPrng) arrived in Windows 10 1809 and an import the loader cannot resolve stops the process starting at all. Those targets are tier 3, so the build needs a nightly toolchain with rust-src; it reuses the channel ebpf/rust-toolchain.toml pins.
They also link against a msvcrt C runtime built by guest/build-mingw-msvcrt.sh, a one-time five-minute build into ~/.local/share/vmlab/toolchains/mingw-msvcrt. Distribution mingw-w64 packages are UCRT-only, and the UCRT ships with Windows 10: a binary that imports it will not load on Vista, 7, 8, 8.1, Server 2008, 2008 R2, 2012 or 2012 R2, which is most of the guests the 32-bit agent exists for. msvcrt.dll has been present since NT4. Without the prefix the agents still build and the script says what it cost you.
You do not need the assets for every workflow.
- A VM cloned from a template pulled from a registry needs neither. The agent is already inside the pulled image. Your first VM works on a host with no assets.
- A container needs the micro-VM asset for its architecture. Without it the first container start fails, naming every directory it searched and the build script to run.
- A template build from an ISO needs the agent binary for the guest's architecture, unless the template sets agent = false. Without it the build fails before booting anything, with the same kind of message.
Guest assets are in place
ls ~/.local/share/vmlab/guest/ lists aarch64, agent and x86_64. cat ~/.local/share/vmlab/guest/x86_64/VERSION prints a version stamp.
On WSL 2
vmlab treats WSL 2 as a first-class host. Its network fabric creates no tap or bridge devices and needs no privileges, which is what makes the Windows kernel a non-issue. Two things are specific to WSL 2.
Nested virtualisation must be on. /dev/kvm appears inside the WSL distribution only when the WSL VM itself exposes virtualisation. Add the setting to %UserProfile%\.wslconfig on the Windows side, then restart WSL with wsl --shutdown.
Viewers live on the Windows side. vmlab console --tcp bridges a VM's VNC display to a localhost port and prints the address, so a Windows VNC client can attach through WSL's localhost forwarding. Port forwards declared in the lab file reach the Windows side the same way.
vmlab creates $XDG_RUNTIME_DIR at daemon start when a WSL session lacks it. The full list of host settings, including the viewer choice and the template store location, is in Host configuration and WSL 2.
Emulation is not a substitute for KVM
If /dev/kvm is missing, vmlab still runs guests under pure emulation. A Windows installer under emulation takes hours rather than minutes, and template builds time out. Confirm the device exists before building anything.
Next steps
- Your first lab brings up a container on a NAT segment and walks through the lifecycle verbs.
- Your first VM pulls a template from a public registry and runs a Linux VM from it, with no template build.
- Host configuration and WSL 2 covers every host-level setting, including where the store and the guest assets live.