Lightweight MicroVMs with microvm.nix
Lightweight MicroVMs with microvm.nix
Full libvirt guests take minutes to iterate. The boring default for local multi-node tests is: microvm.nix (or runNixOSTest) on nixpkgs 26.05; production still gets real machines or the capstone VMs.
Mental model
microvm.nix is a NixOS module that boots a guest in hundreds of milliseconds, often sharing the host store via virtiofs. It is a lab accelerator, not a tenant isolator. Do not put untrusted workloads there without reading the project’s trust model.
host flake
nixosConfigurations.desk-micro
microvm.hypervisor = "qemu" # or cloud-hypervisor
microvm.mem / vcpu
volumes you listed — nothing else persists
runNixOSTest is the CI twin: headless, assertions, no SSH session. Keep both only when they answer different questions (human poke vs PR gate).
/dev/kvm must exist on the host. Nested virt on a cloud VM is often off — that is a missing feature, not a Nix bug.
Worked examples
Case 1: Guest in the flake
Save as flake.nix:
# flake.nix
{
description = "Desk microVM lab";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
microvm.url = "github:astro/microvm.nix";
microvm.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, microvm }: {
nixosConfigurations.desk-micro = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
microvm.nixosModules.microvm
{
microvm.hypervisor = "qemu";
microvm.mem = 1024;
microvm.vcpu = 2;
networking.hostName = "desk-micro";
services.openssh.enable = true;
services.openssh.settings.PasswordAuthentication = false;
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI… deskadmin@ops-01"
];
system.stateVersion = "26.05";
}
];
};
};
}follows keeps the guest on the same 26.05 as the host flake.
Case 2: Run the declared runner
nix run .#nixosConfigurations.desk-micro.config.microvm.declaredRunner(The exact run attr is in microvm.nix docs for your pin.) You should get a console. Log in, then:
hostname
systemctl is-system-running
systemctl status sshd --no-pager
haltIf QEMU exits immediately, check ls -l /dev/kvm and that your user is in kvm.
ls -l /dev/kvm
id -nG | tr ' ' '\n' | grep -x kvm || echo 'not in kvm group — log out after extraGroups'Nested virt on a cloud workstation: the hypervisor UI must enable it. ls /dev/kvm missing is not a Nix eval error.
Case 3: Persist nothing unless listed
A default microVM is as ephemeral as a tmpfs root. If a test needs /persist, declare a volume in the module:
# volumes.nix
{
microvm.volumes = [
{
image = "persist.img";
mountPoint = "/persist";
size = 1024;
}
];
}Otherwise the next nix run starts clean — which is what you want for a lab.
Case 4: Host virtualisation (workstation)
Save as hosts/ops-lab.nix (fragment of the workstation):
# hosts/ops-lab.nix
{
virtualisation.libvirtd.enable = true;
users.users.deskadmin.extraGroups = [ "libvirtd" "kvm" ];
}ls -l /dev/kvm
groupskvm in the group list, crw-rw---- on /dev/kvm. Nested virt on a cloud workstation: enable it in the hypervisor UI or pick a metal builder.
Case 5: microVM vs runNixOSTest
| Need | Tool |
|---|---|
| SSH in, poke Caddy, read journals | microVM on the laptop |
| PR must fail if nginx does not listen | pkgs.testers.runNixOSTest in checks |
| Production desk-api | Colmena to real hosts |
Do not maintain both for the same scenario without a reason. CI does not SSH into your laptop guest.
The trap
The trap is running production desk-api in a microVM on someone’s laptop. Prod is Colmena to real hosts (or the capstone VMs). microVMs die when the lid closes.
The boring rule
- 26.05 +
microvm.inputs.nixpkgs.follows. - QEMU/KVM for labs.
runNixOSTestfor CI. - No production tenants on a laptop hypervisor.
stateVersion = "26.05"on new guests.- Share the store only when you understand the trust model. Volumes you did not list do not exist.
Try this
- Enable KVM, run Case 1,
hostnameinside the guest. systemctl status sshdinside it, thenhalt.- Compare wall-clock boot to a libvirt NixOS guest on the same box.
- Add a
checks.x86_64-linuxrunNixOSTestthat covers the samesshdenablement so CI does not need the laptop.