Getting started · tutorial

Install

7 min read · 2026-10-05 · vmlab 0.9

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

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.

sh
curl -fsSL https://vmlab.io/install.sh | sh -s -- --pre

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.

sh
cargo install --git https://github.com/VMLabDev/vmlab --locked

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.

sh
just buildbox::dist
ls target/dist/

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.

sh
vmlab --version

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.

sh
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
  qemu-system-x86 qemu-system-arm qemu-system-misc qemu-utils \
  seabios swtpm \
  qemu-efi-riscv64 \
  tesseract-ocr passt \
  xorriso mtools dosfstools samba
PackageProvidesNeeded for
qemu-system-x86, qemu-system-arm, qemu-system-miscqemu-system-x86_64, qemu-system-aarch64, qemu-system-riscv64Running guests. Install only the architectures you use.
qemu-utilsqemu-imgEvery disk operation: clones, snapshots, template builds
seabiosBIOS firmwareBooting firmware = "seabios" guests; usually pulled in by QEMU itself
qemu-efi-riscv64UEFI firmware for riscv64riscv64 guests; vmlab bundles no riscv64 firmware
ovmf, qemu-efi-aarch64The distribution's UEFI firmwareOptional. vmlab boots its bundled firmware first and falls back to these only when the guest assets are missing
swtpmA software TPM 2.0Guests with tpm = true, which Windows 11 and Server 2025 require
passtUserspace networking helperThe network fabric
xorriso, mtools, dosfstoolsISO and floppy buildersmedia {} blocks and the bootstrap ISO every template build attaches
sambasmbdShared folders on guests without virtiofs support
tesseract-ocrtesseractvmlab 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.

LocationWhen 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.

sh
git clone https://github.com/VMLabDev/vmlab
cd vmlab
just guest-install

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.

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.

.wslconfigini
[wsl2]
nestedVirtualization=true

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