Reading NixOS.org Infra as a Pattern Source

Updated

September 12, 2026

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 | head

Output (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' | head

You 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 -l

A 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 show
nixosConfigurations
├──── 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.
  • follows on every input that can. Extra substituter + key, never a signing secret.
  • Desk pin stays nixos-26.05 unless you have a reason for -small.

Try this

  1. Clone NixOS/infra at depth 1. Diff README.md vs flake.nix for the deploy tool.
  2. Count follows = "nixpkgs" in flake.nix.
  3. rg NixOps the repo; note which files still say it.
  4. Write mkHost as in Case 4 in a throwaway dir; nix flake show. Do not add their Cachix.