NixOS modules, overlays, and templates for provisioning NixOS systems. The same module set drives several modes:
- imageless guest: a system closure that boots a VM directly —
tmpfs root, systemd initramfs, external kernel,
/nix/storeand/lib/modulesmounted via virtiofs from the host. No disk image and nothing persists across boots. - libvirt guest: a qcow2 disk image consumed by libvirt —
/dev/vdaroot, grub bootloader, NixOS-built kernel, DHCP from libvirt's default network. - baremetal: the imageless system activated on real hardware
with
switch-to-configurationinstead of booted in QEMU. - controller: a host that builds kernels, runs Kconfig and Ansible, and provisions the guests above.
All modes compose with the opt-in modules — user, the mounts
modules (shares, storage), the profiles (devel,
build-tools, monitoring, controller), and the per-suite
modules under testSuites.* — so a system can be minimal or
carry the full kernel and storage testing toolchain.
License: copyleft-next-0.3.1
The Nix package manager with flake support enabled:
mkdir --parents ~/.config/nix
echo 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.confValidate that both backends evaluate and build:
nix flake checkBuild a single backend closure:
nix build .#checks.x86_64-linux.imageless # or .libvirt
readlink --canonicalize resultThe result symlink points to the system closure. For imageless,
result/boot.json contains the init and initrd paths needed to
configure QEMU:
cat result/boot.jsonTo create a downstream configuration, use a template:
nix flake init --template "github:linux-kdevops/nixos-flake#imageless"
nix flake init --template "github:linux-kdevops/nixos-flake#libvirt"See docs/usage.md for customizing packages and NixOS options.
This backend builds two artifacts: a NixOS system closure and a
systemd initramfs. Booting requires an external kernel and QEMU
with virtiofsd sharing the host's /nix/store and /lib/modules
into the guest.
The external kernel must have the boot-critical virtio drivers
built-in (CONFIG_VIRTIO_FS=y, CONFIG_VIRTIO_PCI=y,
CONFIG_TMPFS=y). All other drivers can be kernel modules loaded
from /lib/modules after switch-root.
QEMU needs two virtiofsd instances sharing host directories into the guest with these tags:
store: the host's/nix/store(read-only)modules: the kernel build's/lib/modulesdirectory
The kernel command line:
root=tmpfs console=ttyS0,115200 console=hvc0 init=/nix/store/<hash>/init
systemd in the initramfs reads the NixOS-generated fstab, mounts
root (tmpfs), /nix/store (virtiofs tag store), and
/lib/modules (virtiofs tag modules), then switch-roots into
the system closure. The init= and initrd paths change on
every rebuild and are available in result/boot.json.
This backend builds a full NixOS system closure including its own
kernel. The consumer supplies a qcow2 disk image containing the
closure; libvirt presents it to the guest as /dev/vda. Grub on
the MBR loads the kernel, initramfs mounts the ext4 root from
/dev/vda1, and systemd activation proceeds normally. Networking
comes up via DHCP from libvirt's default network (typically
192.168.122.0/24).
The imageless backend builds a NixOS system closure. A VM is one way to run it; a real machine is another. Activate the closure on baremetal with the closure's own switch script — no QEMU, no VM:
<closure>/bin/switch-to-configuration switchThe host then runs the same NixOS system the imageless backend
builds. A baremetal host supplies its own root filesystem and
kernel, so it overrides the fileSystems and boot entries the
imageless module declares for the virtiofs case.
The profiles.controller module turns a NixOS host into a control node:
the toolchain to build a kernel and drive make menuconfig,
Ansible and its Python runtime, git, the QEMU and virtiofs
tooling, and system libvirt with the QEMU/KVM stack. Enable it on
top of a backend on a real machine:
modules = [
nixos-flake.nixosModules.backends.libvirt
nixos-flake.nixosModules.profiles.controller
{ nixos-flake.controller.enable = true; }
];The same host can also import a test-suite module and run the suite itself — the baremetal case above, driven from the controller.
| Document | Content |
|---|---|
| docs/usage.md | Configurations, modules, overlays, packages, home overlay, block-device filesystems |
| docs/design-decisions.md | Imageless and libvirt boot model, upstream references |
| docs/verifying.md | Pre-commit checklist: format, flake check, image and package builds |
- run-kernel. Rust init + NixOS boot via virtiofs. The direct inspiration for the imageless boot model.
- nixos-shell. Nix-based lightweight QEMU VMs with host mounts.
- kernel-development-flake. Nix flake for Linux kernel development with QEMU.