# NixOS LAN Configurations Flake-based NixOS configuration repository for Wayne's LAN servers and workstation. ## Hosts Targets are named `-`, 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//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: ```bash 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//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//openssh/`) instead | | `vars/per-machine/` | Clan vars: committed, sops-encrypted SSH host keys for deployed LXC 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 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 bash scripts/codex-maintenance.sh --full-check bash scripts/codex-maintenance.sh --full-check --dry-run ``` For individual host evaluation: ```bash nix eval .#nixosConfigurations..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 ` 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](https://github.com/Mic92/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: ```bash nix-shell -p sops --run "sops secrets/.yaml" ``` then reference it from a module via `config.sops.secrets."".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//openssh/`, committed and sops-encrypted) for their SSH host keys. 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.