nix-darwin linux-builder and peers (NixOS VM)
The classical way to build Linux on macOS: a persistent NixOS guest, reached over SSH, registered in nix.buildMachines. Two transports (QEMU and Virtualization.framework) and one open-source external-builders reimplementation (IOHK).
| Language | Nix + NixOS |
| License | MIT (nix-darwin / nixpkgs) |
| Module | nix-darwin/modules/nix/linux-builder.nix |
| Manual | nixpkgs darwin-builder section |
| Category | Persistent remote builder |
Overview
What it solves
darwin.linux-builderprovides a way to bootstrap a Linux remote builder on a macOS machine.
This host's /etc/nix/machines is missing. builders = @/etc/nix/machines therefore names nothing. nix-darwin does write external-builders for Determinate; it does not enable nix.linux-builder.
Design philosophy
A real NixOS system, with its own store image, SSH, and nix-daemon. The host Nix is a client. Persistence of /nix/store on the guest is the point: rebuilds are incremental.
How it works
QEMU path. pkgs.darwin.linux-builder → nixos/modules/profiles/nix-builder-vm.nix. launchd runs create-builder. SSH on localhost:31022, user builder, host key published in the module (a known private key — acceptable only because the VM is local). useNixStoreImage = true so the guest store is a snapshot, not a bind of the host store (otherwise the host's lockfile deadlocks the guest).
VZ path. pkgs.darwin.linux-builder-vz (macOS 13+, Apple silicon). Rosetta in the guest for x86_64-linux instead of TCG. Same SSH identity, so switching is a disk-format change (delete the qcow2; vz writes raw). Nested virt (virtualisation.vz.nestedVirtualization) on macOS 15+ / M3+ gives the guest /dev/kvm for NixOS tests.
IOHK nix-linux-builder (65ecc7687e0df4b3e634e3e026f8e7822a961800, v0.3.0) implements the same external-builders JSON protocol as Determinate, in-process with Virtualization.framework, no SSH. VirtioFS of /nix/store plus an ext4 loop for /build because VirtioFS does not honour DAC_OVERRIDE. UID mapping patches guest passwd to Darwin nixbld UIDs (351–382). nix-linux-shell is an interactive fail-into-VM. Prebuilt kernel/initrd on GitHub Releases break the bootstrap cycle. Not gated.
Analysis spine
- Execution model. Persistent VM (nix-darwin) vs per-drv VM (IOHK / Determinate).
- Nix integration. SSH
buildMachinesvsexternal-builders. - Architectures. QEMU: typically host-arch Linux. VZ / IOHK: both Linux arches via Rosetta.
- Store sharing. Isolated guest store (nix-darwin QEMU) vs VirtioFS host store (IOHK, Determinate).
- Availability. Not enabled here. IOHK is an opt-in flake input.
- Interactive vs derivation. SSH into
builder@linux-builderis interactive; the Nix integration is still derivation-only.
Strengths
- Known quantity; Hydra-style
supportedFeatures(kvm,big-parallel). - Guest NixOS can be reconfigured once the first builder exists.
- IOHK is the ungated Determinate-shaped tool.
Weaknesses
- First boot is a large NixOS closure; chicken-and-egg without substitutes.
- QEMU on macOS is slower than VZ; DNS over SLiRP needs a public nameserver workaround.
- Persistent disk grows;
ephemeralwipes it. - Does not by itself run
ci --testin a working tree.
Key design decisions and trade-offs
| Decision | Rationale | Trade-off |
|---|---|---|
| SSH remote builder | Works with every Nix | Keys, ports, launchd VM |
| Isolated guest store (QEMU) | Avoid store lock deadlocks | Duplicate closure |
IOHK ext4 loop for /build | VirtioFS DAC_OVERRIDE | Extra image, not a POSIX fs share |