Reading NixOS.org Infra as a Pattern Source
Reading NixOS.org Infra as a Pattern Source
The public tree at github:NixOS/infra is how the NixOS project runs hydra, ofborg, builders, DNS, and terraform. The boring default is: steal the shapes (keys file, host factory, critical/non-critical split, restricted builder SSH, channel table) and re-express them on the desk fleet — do not clone the repo and do not deploy their hosts.
This part is labs. The capstone already has five desk roles. Here we add the org-scale habits that tree actually uses in 2026.
Mental model
| Path in NixOS/infra | What it is | Desk equivalent |
|---|---|---|
flake.nix |
Live contract: 26.05-small, follows, extra substituter |
One lock, one cache |
keys.nix |
People and machines as SSH/age groups | keys.nix for desk-ops |
builders/ |
mkNixOS factory + common modules |
lib/mkHost.nix |
non-critical-infra/ |
Separate Colmena hive + sops | hive-apps vs hive-core |
channels.nix |
Hydra job → channel name + status | Attic/Cachix “tested” attr |
terraform/ |
OpenTofu withPlugins for the cloud envelope |
IaC chapters, same wrapper |
checks/ |
Group host toplevels by arch for CI | checks.x86_64-linux.hosts |
docs/inventory.md |
Stale (NixOps, Packet, DataDog) | Do not copy; flake wins |
The README still says “managed using NixOps.” The flake says Colmena, Disko, sops-nix, agenix, nix-darwin 26.05, OpenTofu. Trust the flake. Inventory docs rot; flake.lock does not.
They use flake-parts. We do not. The factory is a function in flake.nix. Same hosts, no extra DSL.
They pin https://channels.nixos.org/nixos-26.05-small/nixexprs.tar.zst (the small channel). The desk default stays github:NixOS/nixpkgs/nixos-26.05 unless you have measured that you only need the small jobset.
NixOS/infra (read)
keys, factory, split hives, restricted SSH, channel table
│ rewrite
▼
desk flake (write) — 26.05, Colmena, sops, Disko
Worked examples
Case 1: Map the live flake, ignore the stale README
On a throwaway clone (read-only; you will not colmena apply):
git clone --depth 1 https://github.com/NixOS/infra.git /tmp/nixos-infra
cd /tmp/nixos-infra
rg -n 'nixos-26.05|colmena|nixops|disko|sops' flake.nix README.md | headOutput (shape):
flake.nix: nixpkgs.url = "https://channels.nixos.org/nixos-26.05-small/nixexprs.tar.zst";
flake.nix: colmena = { url = "github:zhaofengli/colmena"; ...
flake.nix: disko = { url = "github:nix-community/disko"; ...
flake.nix: sops-nix = { url = "github:Mic92/sops-nix"; ...
README.md:All the hosts are currently managed using NixOps.
README lost. Flake won. That is the first lab skill: the deploy tool is the flake output, not the first paragraph of the README.
nix flake metadata --json 2>/dev/null | jq -r '.locks.nodes.nixpkgs.original.url // .locks.nodes.nixpkgs.locked.rev' | headYou should see a 26.05-small tarball or its narHash, not nixpkgs-unstable as the default OS pin. Unstable is a second input (nixpkgs-unstable) for tools that need it.
Case 2: Extra substituter on the flake, daemon still needs the key
They set:
# flake.nix fragment — pattern only
{
nixConfig.extra-substituters = [ "https://nixos-infra-dev.cachix.org" ];
nixConfig.extra-trusted-public-keys = [
"nixos-infra-dev.cachix.org-1:OvwhqPPs81cInrtRAX0K7dG6lw8wXcQEX4xyp4AnSXw="
];
}nixConfig in a flake is a hint. Untrusted users need accept-flake-config or the same keys in /etc/nix/nix.conf. The desk already puts the team cache in nix.settings. Do not copy their Cachix name onto the desk fleet; copy the habit: one extra cache, key next to URL, never a signing secret in nixConfig.
Case 3: follows is how a 20-input flake stays on one nixpkgs
Their colmena, disko, sops-nix, srvos, agenix all inputs.nixpkgs.follows = "nixpkgs". Darwin follows nixpkgs-darwin. That is the same follows rule as the tooling-flake chapter, at org scale.
# in the clone
rg 'follows = "nixpkgs"' flake.nix | wc -lA two-digit count is success. An input that does not follow is a second stdenv. Hunt those on purpose (hydra, ofborg) — they are the exceptions, documented by existing.
Case 4: Desk flake that looks like infra without flake-parts
Save as flake.nix:
# flake.nix
{
description = "Desk fleet — shapes from NixOS/infra, not their hosts";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, disko, sops-nix }:
let
mkHost = name: extraModules:
nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
{ networking.hostName = name; system.stateVersion = "26.05"; }
disko.nixosModules.disko
sops-nix.nixosModules.sops
] ++ extraModules;
};
in
{
nixosConfigurations = {
gw-01 = mkHost "gw-01" [ ./hosts/gw-01.nix ];
app-01 = mkHost "app-01" [ ./hosts/app-01.nix ];
ops-01 = mkHost "ops-01" [ ./hosts/ops-01.nix ];
};
};
}mkHost is their mkNixOS without flake-parts. Three hosts are enough for the labs in this part. Capstone still has five.
nix flake shownixosConfigurations
├──── app-01
├──── gw-01
└──── ops-01
Case 5: What we will not copy
| NixOS/infra | Why not on the desk |
|---|---|
| Hydra of all of nixpkgs | A queue-runner for the world. Desk CI is nix flake check. |
| ofborg eval fleet | GitHub PR eval for nixpkgs. Not your app. |
srvos as a required input |
Fine later; not the first module. |
| flake-parts | Extra DSL. A function is enough. |
Their keys.nix values |
Public SSH keys of their operators. Never paste into yours. |
isoImage.contents of key.txt |
Private keys stay off git. |
| NixOps | The README; not the flake. |
Open keys.nix in the clone, look at the shape (ssh.users, ssh.groups, ssh.machines, age.recipients), then close it. Next chapter you write your keys.
The trap
The trap is git clone + colmena apply against staging-hydra.nixos.org. That hive is not yours. Read-only clone. Rewrite patterns into the desk flake.
The other trap is treating docs/inventory.md as current. Packet.net, NixOps, and DataDog in that file are archaeology. The flake and non-critical-infra/ are the live map.
The boring rule
- Flake is the contract. README and inventory can lie.
- Steal shapes: keys file, host factory, hive split, restricted builder SSH, channel table.
- Do not steal hosts, secrets, flake-parts, or Hydra-for-nixpkgs.
followson every input that can. Extra substituter + key, never a signing secret.- Desk pin stays
nixos-26.05unless you have a reason for-small.
Try this
- Clone NixOS/infra at depth 1. Diff
README.mdvsflake.nixfor the deploy tool. - Count
follows = "nixpkgs"inflake.nix. rg NixOpsthe repo; note which files still say it.- Write
mkHostas in Case 4 in a throwaway dir;nix flake show. Do not add their Cachix.