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.
beatzaplentyandClaude Sonnet 4.6 055577ee91
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m33s
fix(lxc): fix activation ordering and add boot-time sops reinstall
Two bugs prevented nixos-rebuild switch from working on lxc-* hosts after
first boot, both confirmed live on a deployed lxc-tor-relay container:

1. Ordering bug: preserveSshHostKey had no explicit deps, so the topological
   sort placed it at position 7 — after etc at position 5. By the time it
   tried to save the SSH key, etc had already removed it as "obsolete"
   (absent from the current generation's environment.etc when built without
   NIXOS_HOST_KEYS_DIR). Consolidate all four system.activationScripts entries
   into one block and add etc = { deps = ["preserveSshHostKey"]; } and
   setupSecrets = { deps = ["restoreSshHostKey"]; } to enforce the correct
   save→etc→restore→sops chain.

2. No boot-time secrets: /run/secrets is a tmpfs cleared on every reboot, and
   sops-nix does NOT generate a boot-time service in this configuration
   (confirmed live: no sops-nix.service in systemctl list-unit-files).
   Add nixos-lxc-sops-reinstall.service, modelled after sops-nix's own service
   placement (wantedBy/before sysinit.target, DefaultDependencies=false), so
   secrets are reinstalled before basic.target on every non-first boot.
   ConditionPathExists skips it on first boot; nixos-lxc-first-boot-activate
   handles that case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 19:07:30 +10:00
2026-07-20 17:49:35 +00:00
2026-07-21 20:07:30 +00:00
2025-07-16 13:33:23 +10:00
2025-07-16 03:22:31 +00:00
2026-07-24 05:38:30 +10:00
2026-07-25 15:55:04 +10:00

NixOS LAN Configurations

Flake-based NixOS configuration repository for Wayne's LAN servers and workstation.

Hosts

Targets are named <platform>-<buildtype>, generated from two orthogonal pieces composed in flake.nix:

  • Platforms (what it runs on): linode, proxmox, lxc, baremetal
  • Build types (what it's for): minimal, nix-cache, server, docker, gui, pxe-boot, tailscale-exit-node, tor-relay

Not every combination exists — pxe-boot has no linode variant, since PXE/DHCP/TFTP need LAN L2 adjacency that a Linode VPS doesn't have, tor-relay currently only exists as lxc-tor-relay, and baremetal currently only exists as baremetal-gui (the real gui-host hardware). The full list:

Target Purpose
linode-minimal Minimal NixOS host profile on a Linode VPS
proxmox-minimal Minimal NixOS host profile on Proxmox — previously the flat nix-minimal target
lxc-minimal Minimal NixOS host profile in a Proxmox LXC container
linode-nix-cache / proxmox-nix-cache / lxc-nix-cache Local Nix binary cache and remote builder — previously the flat nix-cache target
linode-server / proxmox-server / lxc-server Storage, NFS, backup, and monitoring exporter host — previously the flat server target
linode-docker / proxmox-docker / lxc-docker Docker host for the main container stack — previously the flat docker target
linode-gui / proxmox-gui / lxc-gui Cinnamon desktop workstation — previously the flat nixos target
baremetal-gui Same Cinnamon desktop workstation, on the real gui-host hardware — ZFS RAID0 root, systemd-boot
proxmox-pxe-boot / lxc-pxe-boot HTTP/iPXE boot asset host — previously the flat pxe-boot target
linode-tailscale-exit-node / proxmox-tailscale-exit-node / lxc-tailscale-exit-node Tailscale exit node
lxc-tor-relay Tor middle relay

Which variant of a given buildtype is actually deployed isn't tracked anywhere in this repo — that's live infrastructure state, not something a committed file can keep accurate, and it changes independently of the code. Check the Proxmox node itself, or /etc/flake-target on a running host (see below), if you need to know what's really out there right now. scripts/proxmox/create-proxmox-resource.sh's duplicate-host guard works the same way: it checks the Proxmox node directly rather than any file here. Real, production deployments live on pve1.sweet.home; there's a second node, pve-test.sweet.home, set aside purely for scratch/test resources — see scripts/env.sh (PVE1_HOST / PVE_TEST_HOST, and the --node/PROXMOX_HOST targeting they feed into) and CLAUDE.md's Proxmox section for which is which.

Each buildtype's hosts/<name>/host.nix carries the per-machine identity (hostname, hostId, per-machine secrets, system.stateVersion) that must stay fixed regardless of which platform it's built for — see flake-target-refactor-spec.md for the full rationale. Every deployed host stamps its own active target name into /etc/flake-target at build time, so nixos-rebuild switch --flake .#$(cat /etc/flake-target) always picks up the right one even after a platform migration changes the flake attribute name.

List hosts with:

nix eval --json .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]'

Layout

Path Purpose
flake.nix Flake inputs, the mkTarget platform × build-type generator, and nixosConfigurations outputs
variables.nix Single source of truth for shared values (LAN domain/CIDR, hostnames, timezone, primary username, storage root, NFS share subpaths/mountpoints, service ports, ...) — passed to every module and Home Manager config as the vars argument via specialArgs/extraSpecialArgs
hosts/<name>/host.nix Per-machine identity: hostname, hostId, per-machine secrets, system.stateVersion
hosts/nixos/home.nix Workstation-specific Home Manager config (used by the gui build type)
modules/platforms/ Platform-specific config: virtualisation guest tools, boot method, hardware config (linode.nix, proxmox.nix, lxc.nix, baremetal.nix)
modules/build-types/ Build-type-specific config: what makes a system minimal/server/docker/gui/pxe-boot/nix-cache
modules/common/ Shared NixOS config, Home Manager, aliases imported by every host
modules/nix-cache/ Binary cache and remote builder client/server modules
modules/installer/ Auto-installer environment (ISO, also served as PXE netboot) — see docs/auto-installer.md
host-keys/ Gitignored, locally-generated SSH host keys for the auto-installer — see docs/auto-installer.md
docs/ Operational notes for cache, builders, lock updates, boot services, the auto-installer, and Proxmox image builds
scripts/ Codex setup, validation, host-key, release-bump, and Proxmox resource helpers

Validation

Safe validation commands for Codex and local review:

bash scripts/codex-setup.sh
bash scripts/codex-maintenance.sh

codex-maintenance.sh with no flags (what CI runs on every push/PR) scopes fmt-check/statix/eval to files changed against a base ref — fast, but only as thorough as the diff. For the full sweep (every host, every package, fmt-check and statix over the whole tree — slow, CI never runs this):

bash scripts/codex-maintenance.sh --full-check
bash scripts/codex-maintenance.sh --full-check --dry-run

For individual host evaluation:

nix eval .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath --raw

Use nix build --dry-run --no-link when build planning is needed. Do not run deployment, install, disk formatting, mount, or reboot commands from automated review sessions.

Operations

  • Host rebuilds should consume the committed flake.lock.
  • Routine dependency updates should happen through the flake lock automation described in docs/flake-lock-automation.md.
  • nix-cache serves substitutes over HTTP and can act as a remote builder for client hosts.
  • pxe-boot serves iPXE boot files over HTTP from /srv/pxe.

Deploying a new host

Three different paths depending on target, none of them involving a manual nixos-rebuild switch from this repo:

  • Most hosts: boot the auto-installer, pick the target from its menu — see docs/auto-installer.md. Every menu target has a Disko config the installer formats unconditionally (docs/auto-installer.md's "Storage" section covers how this stays non-destructive for linode-*, whose disks Linode itself provisions ahead of time).
  • lxc-* targets: not installed at all — build a ready-to-run container tarball and pct create it as a CT template directly. docs/auto-installer.md covers why (and the installer's menu excludes them for the same reason).
  • proxmox-* targets: can alternatively be built as a standalone .raw disk image and attached to a new VM with no install step — see docs/proxmox-images.md.

scripts/proxmox/create-proxmox-resource.sh --type lxc|vm --host <name> automates either of the last two end to end (host-key registration, building the image directly on the Proxmox node itself, pct create/qm create), with --dry-run and a guard against duplicating an already-deployed host's identity. See its --help.

Security Notes

Do not commit tokens, private keys, live credentials, or new password hashes as plaintext. Secrets are managed with sops-nix: encrypted files live under secrets/, recipients (per-host age keys derived from each host's existing SSH host key, plus an admin key) are declared in .sops.yaml. To add or edit a secret:

nix-shell -p sops --run "sops secrets/<file>.yaml"

then reference it from a module via config.sops.secrets."<name>".path (or sops.templates for values that need to be embedded in a rendered config file, e.g. nix.conf's access-tokens). Never write a secret value directly into a tracked .nix file. A pre-commit hook (.githooks/, enabled via git config core.hooksPath .githooks, done automatically by scripts/codex-setup.sh) runs gitleaks protect --staged to catch mistakes before they're committed.

The auto-installer environment is the one deliberate exception to sops-nix-everywhere: it has a hardcoded login password instead (no stable per-boot host key for sops-nix to derive from on ephemeral media) — see "Host keys" in docs/auto-installer.md for why, and how the private keys it does pre-seed for target hosts stay out of git via the gitignored host-keys/ directory.

This repository's git history still contains secrets committed before this migration (see remove-sensetive-info-refactor.md) — those are being scrubbed and rotated separately; don't treat the repo as safe to make public until that's finished.

S
Description
No description provided
Readme MIT
17 MiB
Languages
Shell 64.5%
Nix 32.9%
Python 2.6%