pve1 runs non-NixOS guests (FreeIPA domain-controller, Proxmox Data Manager, etc.) alongside NixOS ones. Validate each discovered hostname against the set of hostnames defined in nixosConfigurations before adding it to the gc list. Brings back the flake eval but uses it correctly: extract all hostname values (not target-name keys) to build a filter set, then only include pve1 guests whose name matches a flake-managed NixOS host. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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-router,tor-relay,ha-server
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 and ha-server currently only exist on lxc/proxmox, 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-router / proxmox-tailscale-router / lxc-tailscale-router |
Tailscale subnet router + MagicDNS forwarder for the LAN |
lxc-tor-relay |
Tor middle relay |
proxmox-ha-server-1 / proxmox-ha-server-2 |
HA file-server cluster nodes — DRBD + XFS + iSCSI + NFS, managed by Corosync + Pacemaker |
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. 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; only used by the auto-installer environment for pre-seeding SSH host keys before first boot — see docs/auto-installer.md. All deployed hosts use clan vars (vars/per-machine/<target>/openssh/) instead |
vars/per-machine/ |
Clan vars: committed, sops-encrypted SSH host keys for all deployed hosts; read by create-proxmox-resource.sh at deploy time |
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-cacheserves substitutes over HTTP and can act as a remote builder for client hosts.pxe-bootserves 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 forlinode-*, whose disks Linode itself provisions ahead of time). lxc-*targets: not installed at all — build a ready-to-run container tarball andpct createit as a CT template directly.docs/auto-installer.mdcovers why (and the installer's menu excludes them for the same reason).proxmox-*targets: can alternatively be built as a standalone.rawdisk image and attached to a new VM with no install step — seedocs/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. All deployed hosts use clan vars
(vars/per-machine/<target>/openssh/, committed and sops-encrypted) for
their SSH host keys.
This repository's git history still contains secrets committed before the sops-nix migration — those are being scrubbed and rotated separately; don't treat the repo as safe to make public until that's finished.