Skip to main content
A guest customisation template is a boot script you save once and reuse: the user-data a Linux VM runs on first boot, or the autounattend.xml a Windows VM is sysprepped with. Both are stored in Celum’s own database (not on a supervisor), both are Go text/template documents, and both declare their own variables so the same template can produce a different result per VM. They are edited under Settings → Cloud-init templates, and selected on the Create VM form.
A template is not a VM image and not a cluster template. It customises a guest after it boots from an image you already have in the Image library; the cluster equivalent, which renders whole manifests and applies them to the supervisor, is on Cluster templates.

The two collections with almost the same name

This is the one thing worth reading before anything else, because the failure it causes is a 404 with no clue in it. Both are reachable from one MCP tool (list_vm_templates) under different kind values, which is exactly how the two get mixed up: kind=cloud-init returns the ids the rest of the flow expects, kind=vm-cloud-init returns names that 404 everywhere else. If a lookup fails with the id apparently right in front of you, check which kind produced it.

What a template is made of

A variable’s default is what the form pre-fills and what a render uses when the caller says nothing. Overrides are merged over the defaults, so a caller sends only what it wants to change.
Rendering uses missingkey=zero: a reference that matches no declared variable renders as empty, not as an error. A misspelled variable name therefore produces a template that renders happily and boots a VM with a blank where a hostname or a package name should have been. Render before you create — that is what the preview exists for.

How it reaches the VM

At create time the template is rendered, split on newlines, and prepended to the VM’s startup commands. Both image families consume it identically: cloud-init images and Ignition (Talos-family) images take the same list, so one template serves both. Two consequences of “prepended” that decide what you can write in one:
  • Template lines run before anything typed on the form, in the same boot script. A startup command the operator adds on the Create VM screen runs after the template, so a template can set up a repository or a user that a later ad-hoc line depends on — but not the other way round. On a cloud-init image the lines become one shell script, so shell state carries on too: a template that starts with set -e makes a failing startup command typed on the form stop the rest of the script.
  • The guest-agent install runs first of all. When it applies, the qemu-guest-agent install is prepended ahead of the template, so the agent is present before your script runs. It is skipped for ISO installs and Windows goldens, which never run cloud-init at all.

Windows: sysprep templates

The Windows sibling is a sysprep template — an autounattend.xml with the same Go-template variables, chosen with sysprepTemplateId and sysprepVariables. Leaving it empty uses the platform’s built-in answer file with the universal virtio driver-path list, which is what most golden clones want. Provisioning scripts do not belong in the answer file. A Windows VM takes a separate windowsSetupScript, delivered as setup.ps1 on the sysprep CD and run once at first logon — that is the Windows equivalent of a cloud-init startup command. Putting the same content in FirstLogonCommands instead looks equivalent and is not: the command-line length cap there will invalidate the whole answer file, and an invalid answer file means an unattended install that stops and waits for a human.

Preview before you commit

POST /api/cloud-init-templates/{id}/render takes a variables object and returns the exact user-data a VM would boot with, plus the resolved variable set. It creates nothing. This is the only way to see the missingkey=zero problem before a VM has already booted around it.
Settings → Cloud-init templates lists, creates, edits and deletes templates. The Create VM form shows the picker and renders a field per declared variable, pre-filled with its default.

Permissions

Rendering a template is authorised as a read — it changes nothing — so an operator who may list templates may also preview them.

What commonly goes wrong

Create a VM

Where a template is selected and its variables filled in.

Image library

The golden images a template customises — and the RWO/RWX decision taken at import.

Console & access

SSH keys, passwords and the guest agent a template can assume.

Cluster templates

The cluster-level equivalent, which renders manifests instead of boot scripts.