This repository has been archived on 2026-07-30. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
nixos/docs/auto-installer.md
T
beatzaplentyandClaude Sonnet 5 b0ccbb1162 Migrate host-key tooling from nix-auto-installer, bake keys into images
Finishes the nix-auto-installer migration: scripts/prepare-host-key.sh
and the local host-keys/ directory (gitignored, private key material,
never committed — moved as plain files, not through git history)
weren't carried over in the initial migration.

Also implements automatic key staging, replacing the manual
scp-after-boot step:

- modules/installer/host-keys.nix reads host-keys/ via
  builtins.getEnv, which Nix silently returns as "" under normal
  (non---impure) evaluation — the module is a no-op by default, safe
  for CI, until explicitly opted into:

    NIXOS_HOST_KEYS_DIR=$(pwd)/host-keys nix build .#iso --impure

  When built this way every key present gets baked into the image at
  /etc/host-keys/, and auto-install.sh installs whichever one matches
  the flake target selected at install time — no manual per-host scp.

- This deliberately includes the PXE netboot variant, even though
  pxe-boot serves it unauthenticated over LAN HTTP: accepted
  explicitly as a reasonable trade-off for a network that sits behind
  LAN-only infrastructure, not the open internet. auto-install.sh
  still falls back to /root/host-keys (manual scp) if a key isn't
  baked in, so images built without --impure keep working exactly as
  before.

- docs/auto-installer.md replaces nix-auto-installer's README,
  updated for in-repo paths and the new build flow.

Verified: normal `nix eval` (no --impure) evaluates identically across
all 19 nixosConfigurations + 4 packages with zero host-keys/* entries
(CI-unaffected); with --impure + the env var set, all three installer
variants (installer/ISO, proxmox-lxc, pxe) correctly embed every key
in host-keys/.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01La55Nsss8jZ7ZuzUV9mfot
2026-07-20 04:41:34 +10:00

7.3 KiB

Auto-installer

This flake builds a self-contained NixOS installer environment that can install any host exposed by its own nixosConfigurations. It was migrated from a formerly-separate nix-auto-installer repo — everything it did now lives here.

The installer provides a small NixOS install environment (ISO/netboot/Proxmox LXC) with SSH access, Git support, and an interactive installation script. Logging in as any user (root or nixos) runs /etc/auto-install.sh, discovers available hosts from this same flake, lets the operator choose a target, applies that host's Disko storage configuration, installs NixOS, and reboots.

Layout

  • modules/installer/common.nix — shared by every installer target: SSH access, users, the generated /etc/auto-install.sh script, and the programs.bash.loginShellInit hook that runs it on login.
  • modules/installer/iso.nix — ISO/netboot-specific: imports the stock installation-cd-minimal.nix module plus common.nix. Also used, paired with netboot-minimal.nix, to build the PXE netboot variant (see docs/pxe-boot.md).
  • modules/installer/proxmox-lxc.nix — Proxmox LXC-specific: disables bootloader options (containers don't need one) plus common.nix.
  • modules/installer/host-keys.nix — optionally bakes pre-generated SSH host keys into the image; see "Host keys" below.
  • scripts/prepare-host-key.sh — admin-workstation pre-flight tool, see "Creating a New Machine" below.

Flake outputs:

nixosConfigurations.installer     # ISO/netboot installer image
nixosConfigurations.proxmox-lxc   # Proxmox LXC-based installer image

packages.x86_64-linux.iso   # installer ISO/netboot image
packages.x86_64-linux.lxc   # Proxmox LXC installer tarball
packages.x86_64-linux.pxe   # netboot-ipxe + netboot-initrd + netboot-kernel, bundled
packages.x86_64-linux.all   # all three of the above, bundled
nix build .#iso
nix build .#lxc
nix build .#pxe
nix build .#all

The pxe variant is also built automatically as part of the pxe-boot host itself (modules/pxe-boot/stage-installer-artifacts.nix) and served over iPXE — see docs/pxe-boot.md.

Host keys

sops-nix derives each host's decryption key from its own /etc/ssh/ssh_host_ed25519_key, generated at activation time — before systemd would otherwise generate one on first boot. Without pre-seeding this key, secrets (including the root/nixos login password) fail to decrypt on a genuinely fresh install.

Generated host keys live in host-keys/ at the repo root (ssh_host_ed25519_key

  • .pub pairs per hostname). This directory is gitignored on purpose — private key material must never be committed — which also means flakes can't see it through a normal relative path. modules/installer/host-keys.nix reads it through builtins.getEnv, which Nix silently returns as an empty string under normal (non---impure) evaluation, so the module is a no-op — safe by default, including in CI — unless explicitly opted into:
NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build .#iso --impure

When built this way, every key currently in host-keys/ is baked into the image at /etc/host-keys/<hostname>_ssh_host_ed25519_key(.pub), and auto-install.sh automatically installs whichever one matches the flake target selected at install time — no manual per-host scp step needed.

Trade-off, accepted deliberately for this LAN-only setup: baking keys in means every key present in host-keys/ at build time becomes readable by anyone who can reach the built image — including, for the PXE variant, anyone who can reach the pxe-boot host's unauthenticated HTTP server. This is considered acceptable here because pxe-boot sits behind LAN-only network infrastructure, not the open internet. If that ever changes, reconsider this default.

auto-install.sh still supports the older manual path as a fallback: if a host's key isn't baked in (/etc/host-keys), it checks /root/host-keys next, where you can scp a key in after boot, same as before this migration.

Storage

Disk partitioning is handled by Disko — the installer has no hardcoded parted/mkfs/mkswap/mount commands. Each host defines its own layout via disko.devices; a host without one is installed in place via bind-mount (this is how every LXC container target works, since they have no raw block device to partition).

Installer process

/etc/auto-install.sh:

  1. Queries nixosConfigurations from this flake over the network (git+https://<lanDomain>/beatzaplenty/nixos.git) — this happens at install time, not build time, so a generic installer image always sees whatever hosts are currently committed, without needing a rebuild.
  2. Presents them as a menu; confirms the choice.
  3. Skips the nix-cache substituter when installing a nix-cache host itself (consistent with that host's own runtime config).
  4. Runs disko --mode destroy,format,mount if the target has a Disko config, otherwise bind-mounts / onto /mnt.
  5. Installs the target's SSH host key from /etc/host-keys or /root/host-keys (see "Host keys" above).
  6. Runs nixos-install --flake <url>#<choice> --no-root-password.
  7. Cleans up and reboots.

Creating a new machine

Do this instead of jumping straight to a plain install whenever the target host consumes any sops-nix secret — as of this writing, that's every host (modules/common/configuration.nix puts the root/nixos password hash and the GitHub token behind sops-nix for all of them).

  1. On your admin workstation, decide the new machine's flake target name (<platform>-<buildtype>, e.g. proxmox-server) and generate + register its host key:

    ./scripts/prepare-host-key.sh <hostname>
    

    This generates host-keys/<hostname>_ssh_host_ed25519_key(.pub) and prints the .sops.yaml snippet to add, which secrets files to re-encrypt it into, and the next steps below.

  2. Edit .sops.yaml with the printed snippet, then re-encrypt every secrets file you added the new host to:

    nix-shell -p sops --run 'sops updatekeys secrets/common.yaml'
    
  3. Commit and push. The flake build the installer uses has to see the new recipient before you install, or decryption fails on first boot regardless of the next step.

  4. Build the installer image with keys baked in (or reuse an already-serving pxe-boot host, which does this automatically once redeployed):

    NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build .#iso --impure
    
  5. Boot it on the target machine, log in, select the new host's flake target from the menu, confirm. auto-install.sh finds the baked-in key, runs Disko + nixos-install, and reboots.

  6. Verify after reboot:

    ssh <new-host> ls /run/secrets/
    

    If that's empty or login fails, the host's age key most likely wasn't in .sops.yaml (or wasn't re-encrypted into the secrets file it needs) when nixos-install ran — fix .sops.yaml/secrets/*.yaml, push, then re-run nixos-install --flake .#<hostname> --no-root-password from a rescue environment against the existing /mnt, or just redo the install.

Safety

This installer is destructive: disko --mode destroy,format,mount erases any disk defined by the selected host's Disko configuration. Always verify the selected host profile and target machine before confirming.