1 45 Adding a Host
Nimmo edited this page 2026-09-13 11:16:09 +01:00

Adding a Host

  • Type: Runbook
  • Status: Current
  • Scope: Engineering a new registered NixOS host target in nixos-config, before physical or VM provisioning
  • Canonical sources: AGENTS.md; flake.nix; scripts/new-host-template.nix; justfiles/hosts.just; lib/auto-update-policy.nix; modules/common/default-config.nix; modules/common/base.nix; modules/common/sops-host.nix; hosts/*/default.nix; potential/nova/default.nix; .sops.yaml; Host Provisioning; Security Baseline
  • Last verified: Source commit 556df88494686003b1c4f20c8e0b99b5afc16a6e (2026-09-12); source HEAD b2831d33132967b4c4fb69e6068b2b05fff8fdff changes only flake.lock. Builder, template, policy, staged Nova, and helpers checked 2026-09-13; no host was registered or provisioned.
  • Review triggers: New host or channel, makeNixosSystem changes, host template, inventory options, auto-update policy, SOPS rules/helpers, Disko layout, Home Manager ownership, or provisioning contract

Purpose and boundary

Create a reviewable NixOS configuration target and specify its consumers, state, access, and update policy. This page stops at the engineering handoff. Installing it on a machine is a separate, potentially destructive operation owned by Host Provisioning. A configuration under potential/ is staged source, not a flake target or evidence that NixOS is running on that machine. A registered target can build without ever being deployed; Host Inventory records that distinction.

New hosts change the fleet-wide validation and security surface. Open a Forgejo issue and use the branch/merge-request lifecycle for the target, with the intended role, hardware or VM platform, first-install plan, data preservation, access path, backup scope, update role, and canary evidence. Follow the Security Baseline for SSH, listeners, secrets, privilege, recovery, and monitoring gates.

Prerequisites and safety boundary

  • Choose a unique lowercase target name and confirm the intended target hardware/architecture, Nixpkgs branch, primary user, and whether the host will be a normal auto-update consumer. Do not assume a staged producer configuration is a safe default for a new machine.
  • Identify persistent disks, external mounts, encryption, application state, and what must survive installation. Neither a flake build nor a Git rollback can recover a disk that Disko reformats. Do not run just provision from this engineering checklist; use the dedicated runbook with target-identity and layout evidence.
  • Establish a console/rescue path and an off-host recovery route before the first installation. Plan the temporary provisioning identity and permanent SSH-derived host identity without placing private keys in Git, issues, shell history, or Nix strings.

Ordered procedure

1. Prepare the host composition

just add-host HOSTNAME creates and stages hosts/HOSTNAME/default.nix from scripts/new-host-template.nix. The template is desktop-oriented and sets role = "unspecified"; it is a scaffold, not an accepted fleet profile. It does not register the target in flake.nix, generate hardware facts, or write a reviewed Disko layout. You may instead keep an unfinished composition under potential/HOSTNAME/, where it is intentionally outside normal host builds, and promote it only after review.

In the entry module, choose the actual common/server/desktop profile imports, system user, networking, services, hardware modules, and disko.nix. Set networking.hostName and the nixosConfig.host inventory fields: role, purpose, optional fixed location, filterable tags, and gpu.vendors when relevant. modules/common/default-config.nix supplies defaults for primaryUser, userEmail, flakeRepo, and sshKeyPath; override them only for an actual different user or location. If primaryUser changes, ensure the corresponding home/users/<primaryUser>/ entry point exists, because flake.nix imports it for every registered host.

Choose system.stateVersion for the first NixOS installation and preserve it across routine upgrades. Do not copy another host's value without reviewing the target's intended initial release and state compatibility. Treat host hardware facts and disk layout as separate reviewed files; the Host Provisioning runbook covers capture and destructive layout validation.

2. Register the target with the shared builder

Add the host to flake.nix under nixosConfigurations. For a normal unstable target the current shape is:

newhost = makeNixosSystem {
  configName = "newhost";
};

newhost is an illustrative name; replace both occurrences with the chosen target. For a deliberately stable target, follow the current Cosmos/Chaos shape with pkgs = nixpkgs-stable; and hmFlake = home-manager-stable; in the same builder call. Do not copy the old inline nixpkgs.lib.nixosSystem example printed by just add-host; Forgejo issue #224 tracks that output. The shared builder already adds SOPS, Home Manager, Paseo, the standalone auto-update module, configuration revision, and cache settings. Do not duplicate those imports in the host.

Before promoting potential/nova, reconcile it against this builder and current host contract. Its staged default.nix still imports the updater directly and does not declare nixosConfig.host metadata. Without an explicit server role, the shared builder would pass isServer = false to Home Manager. Its producer role, network exposure, hardware, data, and disk plan require their own review. This is a source audit finding, not a claim about Nova's live OS or approval to install it.

3. Include the host in update and trust policy

Review every group in lib/auto-update-policy.nix whose input the new host actually consumes, including transitive common, profile, Home Manager, and hardware inputs. Add the host to the correct group hosts lists; do not add it blindly to every group or leave a shared input without the new consumer. The update producer uses those lists to select validation builds. Set the host's nixosConfig.autoUpdate.role deliberately; all currently registered hosts are consumers, whereas the staged Nova file declares producer intent. Changing producer topology is a separate control-plane review, not a side effect of registration.

Map only the secrets the host will consume. During first install, the provisioning age recipient must cover those encrypted files; after first boot, the host SSH public key can become the permanent age recipient in the relevant explicit .sops.yaml rules. Recipient scope is per file/service, not “all fleet secrets.” Until issue #224 is fixed, do not accept just add-secret or just add-secret-remote output as a complete or safe rule review. Follow Host Provisioning and Secret Editing and Recipient Rotation for the staged identity transition. Never commit a private provisioning or SSH key.

4. Validate the declared target before installation

Stage every new .nix file before flake evaluation; Git-backed flakes do not see untracked files. From the source checkout, run the non-activating gates:

just check
nix build .#nixosConfigurations.newhost.config.system.build.toplevel --no-link
just inventory newhost

Replace newhost with the registered name. The build checks that the target closure can be constructed; just inventory prints evaluated role, GPU, location, tags, and purpose. Neither proves disk safety or deployment. Review errors against the exact channel, architecture, imports, Home Manager user path, SOPS keys, and option assertions. Check required MR/CI results and publish the reviewed revision before provisioning from it.

When the target is ready, the operational Host Provisioning runbook owns hardware capture, stable disk-identity comparison, nixos-anywhere, first boot, permanent SOPS key, bootstrap-key removal, restore, and first verified backup. For a remote test after installation, create an auto-update hold first and use Safe Testing and Deployment. Record a genuine deployment and no-op follow-up for a multi-host or update-control change before closing the issue.

Success checks, rollback, and troubleshooting

  • The host appears exactly once in nixosConfigurations, uses the intended unstable or stable builder inputs, and has a complete host entry, hardware facts, Disko plan, user, metadata, and deliberate update role.
  • lib/auto-update-policy.nix names it for the root inputs it really consumes; secret recipient rules name only required files and independent recovery identities. Security and provisioning gates are recorded.
  • just check, the target build, inventory evaluation, and required MR checks pass. That is ready for reviewed provisioning, not “deployed.”
  • If the target is missing from inventory, check flake.nix registration; if an import is missing, check Git staging and relative paths; if Home Manager evaluation fails, check the selected primary-user directory and stable/unstable pairing. Correct the source and rebuild before proceeding.
  • Before installation, rollback is a reviewed revert/removal of the new target and policy changes. After Disko begins, recovery means restoring a tested backup and reprovisioning; a prior NixOS generation cannot recreate formatted data. Use the operational runbook, not an improvised retry.

Canonical source map

Concern Source
Registration, shared modules, channel pairing, Home Manager path flake.nix
Host scaffold and helper limitations scripts/new-host-template.nix, justfiles/hosts.just, issue #224
Inventory fields and current host examples modules/common/default-config.nix, hosts/*/default.nix
Input-to-host validation scope lib/auto-update-policy.nix
Host SOPS identity and recipient policy modules/common/sops-host.nix, .sops.yaml, Secret Editing and Recipient Rotation
Disk, first boot, key transition, backup Host Provisioning