by Andre Amorim
In modern infrastructure design, the shift toward declarative packaging has fundamentally changed how we reason about software state. Declarative package managers solve the assembly problem: mathematically pure derivation graphs produce bit-for-bit reproducible binaries without ambient host impurities.
Yet once binaries are built deterministically, an operational question remains: where and how should those binaries execute?
For over a decade, the default answer has been containerization. But when we examine the boundary between application sandboxing and hardware execution, containers reveal an architectural compromise. Enter microvm.nix: the convergence of declarative NixOS configuration and paravirtualized hardware isolation.
Containers are not virtual machines; they are ordinary Linux processes wrapped in kernel namespaces (pid, net, mnt), cgroups, and seccomp filters. While operationally lightweight, this architecture imposes distinct boundaries:
/proc or /sys leak immediately exposes the bare-metal host.sysctl parameters cannot modify their runtime environment without elevated capabilities (e.g. --privileged or CAP_SYS_ADMIN), collapsing the sandbox entirely.nixos-rebuild switch) still execute within a single global state machine. A service reload failure or glibc ABI conflict can introduce unintended collateral impact across co-located tenants.Traditional virtualization via full-system hypervisors (QEMU, VMware) historically felt too heavy for microservice architectures. Legacy VMs emulate decades of physical silicon: ACPI power management tables, PCI bus topologies, IDE controllers, and simulated motherboard BIOS firmware. Booting requires traversing legacy hardware initialization routines before the kernel even uncompresses.
MicroVMs discard hardware emulation altogether:
virtio interfaces (virtio-net, virtio-blk, virtio-fs). There are no emulated timer chips or simulated VGA adapters./nix/store/.../init, consuming memory footprints comparable to traditional container runtimes.microvm.nix Completes the StackAuthored by astro, microvm.nix bridges modern paravirtualization with the functional NixOS module system. Rather than managing hypervisor flags through ad-hoc bash wrappers or complex orchestrators, each guest virtual machine is declared as a native nixosConfiguration.
Traditional VMs require thick disk images (.qcow2 or raw disk images) containing duplicate root filesystems. Because Nix stores every library in content-addressable /nix/store paths, microvm.nix guests can mount the hostβs existing /nix/store directly via virtiofs in read-only mode:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Host Bare-Metal β
β Shared Read-Only /nix/store (Single Deduplicated Copy) β
ββββββββββββββββ¬ββββββββββββββββββββββββββββ¬βββββββββββββββ
β virtiofs β virtiofs
βΌ βΌ
βββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
β MicroVM 1 (Isolated) β β MicroVM 2 (Isolated) β
β Dedicated Kernel + vCPU β β Dedicated Kernel + vCPU β
β Independent Memory Space β β Independent Memory Space β
βββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββ
The guest requires no independent disk image for system packagesβonly a thin, ephemeral overlay for stateful data (/var, /etc).
Both the host hypervisor configuration and the guest payload are expressed cleanly within a single flake.nix:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
microvm.url = "github:microvm-nix/microvm.nix";
microvm.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, microvm }: {
# 1. The Host Configuration
nixosConfigurations.hypervisor-host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; # or aarch64-linux
modules = [
microvm.nixosModules.host
{
microvm.vms.edge-gateway = {
flake = self;
};
}
];
};
# 2. The Isolated MicroVM Guest
nixosConfigurations.edge-gateway = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
microvm.nixosModules.microvm
{
microvm = {
hypervisor = "cloud-hypervisor";
mem = 512;
vcpu = 2;
interfaces = [
{
type = "tap";
id = "vm-tap0";
}
];
shares = [
{
proto = "virtiofs";
tag = "ro-store";
source = "/nix/store";
mountPoint = "/nix/.ro-store";
}
];
};
# Dedicated, isolated service payload
services.openssh.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ];
}
];
};
};
}
Containers were designed for an era of mutable host systems, attempting to enforce consistency upon inherently drifting foundations.
By pairing pure Nix derivations with micro-hypervisors, microvm.nix shifts isolation from software convention to hardware guarantees. Each workload receives an independent Linux kernel, cryptographic memory boundaries, and dedicated hardware threadsβwithout sacrificing declarative determinism, build speed, or storage efficiency.
True reproducibility does not stop at how binaries are assembled; it encompasses the complete execution boundary.