Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Imperative vs Declarative

vmctl supports two workflows for managing VMs.

Imperative

Use individual commands to create, configure, and manage VMs step by step:

vmctl create --name myvm --image-url https://example.com/image.img --vcpus 2 --memory 2048 --start
vmctl ssh myvm
vmctl stop myvm
vmctl destroy myvm

This is useful for:

  • Quick one-off VMs
  • Experimenting with different images
  • Scripting custom workflows

Declarative

Define your VMs in a VMFile.kdl and let vmctl converge to the desired state:

vm "myvm" {
    image-url "https://example.com/image.img"
    vcpus 2
    memory 2048

    cloud-init {
        hostname "myvm"
    }

    ssh {
        user "ubuntu"
    }

    provision "shell" {
        inline "echo hello"
    }
}
vmctl up          # create + start + provision
vmctl down        # stop
vmctl reload      # destroy + recreate + provision
vmctl provision   # re-run provisioners only

This is useful for:

  • Reproducible development environments
  • Multi-VM setups
  • Checked-in VM definitions alongside your project
  • Complex provisioning workflows

When to Use Which

ScenarioApproach
"I need a quick VM to test something"Imperative
"My project needs a build VM with specific packages"Declarative
"I want to script VM lifecycle in CI"Either, depending on complexity
"Multiple VMs that work together"Declarative