Reference · reference

Lab file: template and source

9 min read · 2026-09-02 · vmlab 0.9

This chapter is the field-by-field reference for the template {} block and its required source {} child. A template block describes how to build a sealed base image for the store; Templates and the store explains the build flow and vmlab template the verbs that run it. Template blocks live at the top level of a vmlab.wcl, beside a lab {} or in a file of their own with no lab at all. `vmlab template build reads ./vmlab.wcl unless --file` names another.

template {}

One buildable template. The build creates a working disk from the source, boots it with the hardware declared here, runs the provisions and playbooks, shuts the VM down and seals the disk into the store under <arch>/<name>/<version>/. The hardware fields are recorded in the template's metadata and become the inheritance layer for every VM cloned from it.

wcl
template "<name>" {
  arch        = "x86_64"
  version     = "24.04"
  registry    = "ghcr.io/owner/ubuntu-24.04"
  profile     = "linux-modern"
  cpus        = 2
  memory      = 4GiB
  disk        = 20GiB
  display     = "virtio-vga"
  firmware    = "ovmf"
  tpm         = false
  secure_boot = false
  nested      = false
  gui         = false
  qemu_args   = []
  first_boot  = "scripts/first-boot.ws"
  agent       = true
  source "…"  { … }
  media       { … }
  provision "…" { … }
  playbook "…"  { … }
  nic         { … }
  disk "…"    { … }
}
FieldTypeDefaultMeaning
nameutf8 (label)requiredTemplate name, for example linux-modern; the inline block label.
archutf8requiredArchitecture. Selects the QEMU system emulator.
versionutf8requiredVersion string, non-empty. Name, arch and version together are unique.
registryutf8noneFull OCI repository to publish to and to version-bump against.
profileutf8noneGuest OS profile supplying hardware defaults for the build VM.
cpusi64from profilevCPU count for the build VM. Inherited by clones.
memoryByteSizefrom profileRAM for the build VM, for example 8GiB. Inherited by clones.
diskByteSizefrom sourceWorking disk size for the build, for example 64GiB. Required for a scratch source.
displayutf8from profileQEMU display device string for the build VM.
firmwareutf8from profileFirmware: ovmf or seabios.
tpmboolfrom profileEnable a TPM 2.0 device.
secure_bootboolfrom profileEnable secure boot; OVMF only.
nestedboolfalseEnable nested virtualisation for the build VM.
guiboolfalseWatch the build VM in a VNC viewer.
qemu_argslist<utf8>noneRaw QEMU flags for the build VM. The escape hatch.
first_bootutf8nonewscript run on the first instantiation of a clone, before it turns ready.
agentbooltrueBake the vmlab-agent service into the image.
source {}childrequiredWhat the build starts from. Exactly one of four forms.
media {}childrennoneISO or floppy images attached to the build.
provision {}childrennoneProvision scripts that drive the build.
playbook {}childrennoneconfig-weave playbooks applied to the build VM, interleaved with provisions in declaration order. Steps stream as structured build progress.
nic {}childrennoneNICs for the build VM. Optional; the build VM may be air-gapped.
disk {}childrennoneAdditional disks attached during the build.

media, provision, playbook, nic and disk are the same blocks a vm carries; see vm and its children.

Versions and the registry

The declared version is a fixed prefix naming the upstream identity, such as an OS release. Each build appends a counter: the stored version is <version>.<N>, where N is one higher than the highest existing build with that prefix, or 0 for the first. When registry is set the existing builds are read from its tags, so the counter continues across machines; otherwise the local store decides. Changing the prefix restarts the counter at .0. vmlab template build --version pins an explicit version instead. registry is also the repository vmlab template push publishes to; see Distributing templates over registries.

The first-boot script and the agent

first_boot is read at build time and baked into the template's metadata, so a clone runs it on its first start without the lab needing the file. It runs before the VM turns ready. With agent = true the build waits for the guest-installed agent's handshake before sealing and records the agent version in the metadata. With agent = false, or on a profile with no agent channel, the check is skipped and the build log says so.

Validation

examples/templates/ubuntu-24.04/vmlab.wclwcl
// Ubuntu Server 24.04 template (PRD §6.1). The installer ISO is downloaded
// and sha256-verified into the artefact cache; cloudinit/ is packed into a
// CIDATA ISO that subiquity picks up as a NoCloud autoinstall source. The
// provision script answers the autoinstall confirmation and waits for the
// installer to power the VM off. Build with:
//
//   vmlab template build        (run from this directory)

import <vmlab.wcl>

template "ubuntu-24.04" {
  arch    = "x86_64"
  version = "24.04.4"
  profile = "linux-modern"
  cpus    = 2
  memory  = 4GiB
  disk    = 20GiB

  source "iso" {
    url    = "https://releases.ubuntu.com/24.04/ubuntu-24.04.4-live-server-amd64.iso"
    sha256 = "e907d92eeec9df64163a7e454cbc8d7755e8ddc7ed42f99dbc80c40f1a138433"
  }

  media { kind = "iso" from = "./cloudinit/" label = "CIDATA" }
  nic { nat = true }

  provision "scripts/install.ws" { }
}

source {}

What the build starts from. The inline label is the kind, and the kind decides which fields apply. Exactly one source {} per template; the schema rejects a template without one.

wcl
source "iso"      { path = "./installer.iso" }
source "iso"      { url = "https://…/installer.iso" sha256 = "…" }
source "qcow2"    { path = "./base.qcow2" }
source "template" { from = "x86_64/[email protected]" }
source "scratch"  { }
FieldTypeDefaultMeaning
kindutf8 (label)requiredSource kind: iso, qcow2, template or scratch; the inline label.
pathutf8noneLocal file path, for iso and qcow2. Mutually exclusive with url.
urlutf8noneRemote artefact URL, for iso and qcow2. Requires sha256.
sha256utf8noneSHA-256 of the remote artefact. Required with url.
fromutf8noneSource template <arch>/<name>[@<version>], for kind template: a layered build.

The four kinds:

Validation requires exactly one of path and url for iso and qcow2, sha256 whenever url is set, a path file that exists, and a from template that is in the store. An unknown kind is rejected.