Runtimes: kind vs qemu¶
A lab's runtime.type picks which backend astrona spins up. Every command (run, submit, destroy, test) works the same way regardless of which one a lab uses — they all resolve to the same LabEnvironment shape internally.
kind (default)¶
A local Kubernetes cluster via kind, on whichever container engine (Docker or Podman) astrona finds first on your PATH. This is the default and needs no runtime: block at all — every pre-existing lab config with no runtime.type keeps working unchanged.
bootstrap.manifests/testing.manifestsapply against the cluster viakubectl --context kind-<cluster-name>.validation.checksof typeresourceExists/podReadyrun against the same context.- Scripts (
bootstrap.init,teardown.init,validation.script) run on the host — there's no VM to SSH into.
qemu¶
Boots one or more full virtual machines from a base image instead of a container-backed cluster — for labs that need a real OS: kernel modules, systemd, package managers, multi-host networking, anything a container can't give you.
runtime:
type: qemu
qemu:
- image:
type: url # "file" | "url" | "oci"
source: "https://cloud-images.ubuntu.com/.../noble-server-cloudimg-amd64.img"
checksum: "sha256:..." # optional but strongly recommended
arch: amd64
cpus: 2
memoryMB: 2048
diskSizeGB: 20
Key differences from kind:
- There's no
KubeContext—bootstrap.manifests/testing.manifestsare rejected with an error if set, since there's no kubectl-reachable cluster. - Every script (
bootstrap.init,teardown.init,validation.script) runs inside the VM over SSH, as a dedicatedastronasuperuser account (passwordless sudo, key-auth only) that astrona provisions on every VM — separate from the human-facingstudentaccount. Each account gets its own ephemeral ed25519 keypair, generated and removed on teardown. Script content is piped over stdin tobash -s— never interpolated into a shell string. astrona ssh <lab-name>opens an interactive session into a running VM (name as shown byastrona list) asstudentby default — override with--user. Root SSH login is disabled on the VM.studentcan be locked down (sudo removed, password auth disabled) without affecting bootstrap/testing/teardown, since those always run as the independentastronaaccount.- Base images are cached under
~/.astrona/cache/images— inspect withastrona images list. Checksum verification is strongly recommended (image.checksum/image.checksums) but not required; an unverified image falls back to an online freshness check plus the existing cache.
Image sources¶
image.type is one of:
| Type | source is |
Notes |
|---|---|---|
file |
a local path (relative to the lab config's base directory) | no download, no freshness check |
url |
an http(s):// URL to a .qcow2/cloud image |
downloaded and cached; checksum/checksums recommended |
oci |
an OCI registry reference (e.g. ghcr.io/...) |
pulled via oras |
Single VM vs multi-VM¶
runtime.qemu is always a list. A single-VM lab is a one-element list whose entry has no name — the original shape every qemu lab used. Add a second entry (each one now named) to make it a multi-VM lab:
runtime:
type: qemu
networks:
- name: internal
cidr: 10.10.0.0/24
qemu:
- name: jumphost
image: { type: url, source: "...", checksum: "sha256:..." }
networks:
- { name: internal, ipv4: 10.10.0.10 }
sshAccess: [backend] # passwordless SSH from jumphost into backend
- name: backend
image: { type: url, source: "...", checksum: "sha256:..." }
networks:
- { name: internal, ipv4: 10.10.0.11 }
In a multi-VM lab:
- Every VM gets an implicit host-only management NIC (astrona's own control channel) in addition to any declared
networks. sshAccesswires up passwordless SSH from one named VM into another — both must already share aruntime.networkssegment; it doesn't create connectivity on its own, only trust over a path that already exists.- The lab's shared root
bootstrap/validationrun once per VM, inruntime.qemu's order; a VM's own nestedbootstrap/validationblock (if set) runs after that, scoped to just that VM.
Choosing one¶
Default to kind unless a lab specifically needs a full OS, kernel-level behavior, or multi-host networking that a Kubernetes cluster can't model — it's faster to boot and has no VM image to manage.