Declarative Workflow with VMFile.kdl
This tutorial shows how to define VMs in a configuration file and manage them with vmctl up/down.
Write a VMFile
Create VMFile.kdl in your project directory:
vm "webserver" {
image-url "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
vcpus 2
memory 2048
disk 20
cloud-init {
hostname "webserver"
}
ssh {
user "ubuntu"
}
provision "shell" {
inline "sudo apt-get update && sudo apt-get install -y nginx"
}
provision "shell" {
inline "echo 'Hello from vmctl!' | sudo tee /var/www/html/index.html"
}
}
Bring It Up
vmctl up
vmctl will:
- Discover
VMFile.kdlin the current directory. - Download the Ubuntu image (or use the cached copy).
- Generate an Ed25519 SSH keypair for this VM.
- Create a QCOW2 overlay with 20GB disk.
- Build a cloud-init ISO with the hostname and generated SSH key.
- Boot the VM.
- Wait for SSH to become available.
- Run the provision steps in order, streaming output to your terminal.
Connect
vmctl ssh
When there's only one VM in the VMFile, you don't need to specify the name.
Make Changes
Edit VMFile.kdl to add another provisioner, then reload:
vmctl reload
This destroys the existing VM and recreates it from scratch with the updated definition.
To re-run just the provisioners without recreating:
vmctl provision
Bring It Down
Stop the VM:
vmctl down
Or stop and destroy:
vmctl down --destroy
Filtering by Name
If your VMFile defines multiple VMs, use --name to target a specific one:
vmctl up --name webserver
vmctl ssh --name webserver
vmctl down --name webserver