26.05 is the latest stable NixOS release as of now (26.11 is still the in-development branch that nixos-unstable reports); the previous 25.05 pin was a release behind. - flake.nix / flake.lock: stable overlay tracks nixos-26.05. - neogaia system.stateVersion and the base home.stateVersion set to 26.05 (fresh install, so aligning to the current stable release). - Refresh the "latest stable release" references in the project docs to match. Other inputs are rolling branches (nixos-unstable, nixpkgs-unstable, chaotic nyxpkgs-unstable) or master (home-manager, per spec), so they carry no version to bump.
11 KiB
Problem Statement
I'm returning to NixOS after ~2 years away, and I want to start by moving my laptop (neogaia, a Dell XPS 13 9380 currently running CachyOS) onto it. My old config still exists but is stale and written in a style I no longer want to copy verbatim. Eventually this same config has to grow to cover my desktop and three servers, so whatever I build for the laptop has to be a clean, scalable foundation — not a throwaway.
Reimaging the laptop is destructive and I only get one machine, so I need a tightly-scoped, well-understood minimum viable install (MVI): the smallest config that boots the laptop into a usable state I can then iterate on live, without risking a half-defined system that strands me at a dead console.
Solution
Rebuild the Skeleton and a single neogaia Host to the point where the laptop:
- boots from an encrypted disk (LUKS + btrfs + zram),
- comes up on wifi,
- lets me log into a console as my user and run
nixos-rebuild switch, - and already carries my core terminal tooling (fish, tmux, nvim, Claude Code).
Everything graphical and everything multi-host is deliberately left for later iterative passes, which are safe because a mistake then is "edit and rebuild," not "reimage." The MVI is the one step that must be right before reimaging; the rest is reversible.
The install itself is done from the NixOS live ISO by cloning the repo from my Gitea and running a single disko-install against the neogaia Host, then setting a bootstrap password by hand.
User Stories
- As the operator, I want the
Skeletonrewritten around my old scalable ideas (theAuto-loader, theEnable convention, per-Hostlayout), so that the config stays legible and shareable across all five future machines without me copying stale code. - As the operator, I want the flake hand-rolled and cleaned up (no framework layer), so that the whole plumbing stays readable in one place for a config that only targets a handful of
x86_64-linuxmachines. - As the operator, I want every
Moduleauto-discovered and imported but inert until aHostsets itsenableflag, so that eachHostreads as a checklist of features. - As the operator, I want a
nixos-unstablebase with anunstable overlayand astable overlay, so that I can run rolling by default but reach up to bleeding-edge or down to rock-solid on a per-package basis. - As the operator, I want home-manager integrated as a NixOS module with global packages, so that one
nixos-rebuild switchbuilds both the system and my user environment atomically. - As the operator, I want my user modelled as an explicit option defaulting to
alexion(no impure environment lookup), so that the config is reproducible and honest about who the user is. - As the operator, I want the laptop's disk declared with
diskoas encrypted btrfs plus zram swap, so that the install is reproducible and the laptop is encrypted at rest. - As the operator, I want the system to prompt for the LUKS passphrase at boot via systemd-boot and the initrd, so that the encrypted disk unlocks on a normal boot.
- As the operator, I want the CachyOS kernel from chaotic-nyx with the chaotic binary cache wired in from the first build, so that I get the performance/feel I'm used to without compiling the kernel from source.
- As the operator, I want Intel microcode and the redistributable firmware for the QCA6174 wifi included, so that the laptop's hardware works out of the box.
- As the operator, I want NetworkManager enabled, so that I can join wifi easily from the console.
- As the operator, I want an SSH daemon running, so that I can drive the rest of the setup remotely if the console is inconvenient.
- As the operator, I want my user in
wheelwith a manually-set bootstrap password, so that I can log in and use sudo on first boot without committing any secret to a public repo. - As the operator, I want fish as my default login shell, configured natively via home-manager with my
cachyos-config.fishtranslated (greeting, bat-manpager,doneand bang-bang plugins, helper functions, eza/nav aliases) and all Arch/pacman-specific parts dropped or replaced with NixOS equivalents, so that my shell feels like home but is correct for NixOS. - As the operator, I want tmux configured natively via home-manager using my exact existing
tmux.conftext, so that my terminal multiplexer is identical to today with no plugin manager needed. - As the operator, I want my nvim config brought in verbatim (lazy.nvim managing its own plugins) via a writable out-of-store symlink, with
git/gcc/ripgrep/fdprovided by Nix, so that my editor is identical to today and lazy.nvim can still update and write its lockfile. - As the operator, I want Claude Code installed declaratively and authenticatable without a browser on the laptop, so that I can use it over the console/SSH via the paste-code flow or an API key.
- As the operator, I want timezone
America/New_York, localeen_GB.UTF-8, and console keymapusset, so that the base system matches my locale preferences. - As the operator, I want to install by cloning the repo from my Gitea onto the live ISO and running
disko-installagainstneogaia, so that I avoid self-signed-TLS/auth problems with flake fetching during install. - As the operator, I want the
Skeletondesigned so that per-Hostdisk layouts, per-Hostkernels, and preserved ZFS pools are all expressible, so that the same foundation extends to the desktop and the three servers later without restructuring.
Implementation Decisions
Skeleton
- Hand-rolled flake, rewritten and trimmed; no flake-parts.
Auto-loaderrewritten: recursively discovers and imports everyModuleunder the modules tree without the old null-placeholder traversal hack; a single discovery helper feeds theHostimports. The oldnixosModulesflake output is dropped.- Helper lib trimmed to the
Auto-loader, the host-builder, and the script-from-file helper.with lib.myreplaced by explicitinherits throughout.enableflags use the stdlib enable-option helper rather than bespoke sugar. nixos-unstableas the base channel. Anunstable overlayexposesnixpkgs-unstablepackages; astable overlayexposes the latest stable release (nixos-26.05). chaotic-nyx added as an input with its overlay and binary cache from the start.- home-manager sourced from
nix-community, tracking master with nixpkgs followed, integrated as a NixOS module with global packages and user packages. - User modelled as an explicit option defaulting to
alexion, inwheel, driving the system user and the home-manager user in lockstep.
neogaia Host
- Disk declared via
disko: LUKS-encrypted btrfs with subvolumes plus zram swap. systemd-boot on an EFI system partition; initrd LUKS unlock. - CachyOS kernel selected via a small per-
Hostkernel mechanism; chaotic substituter and trusted key in the Nix settings. - Intel microcode; redistributable firmware enabled for the QCA6174 wifi. NetworkManager for networking. A zram toggle
Moduleenabled here. - SSH daemon enabled. Baseline CLI (git, editor, flakes) present. Claude Code installed declaratively.
- fish
Module: native home-manager configuration; translated aliases/functions/plugins/init; set as the default login shell. tmuxModule: native home-manager, exact existing config text inlined. nvimModule: verbatim config placed as a writable out-of-store symlink with runtime dependencies provided by Nix. - Locale, timezone, and keymap set to the detected values.
Install flow
- Repo pushed to Gitea first. From the NixOS live ISO: join wifi, clone the repo locally, run
disko-installagainst theneogaiaHostwith the chaotic substituter passed to the install-time daemon, set bootstrap passwords vianixos-enter, reboot.
Secrets (design only in MVI)
- Per ADR 0001, secrets use
sops-nixwith age keys derived from eachHost's SSH host key. The MVI does not wire any secret, because aHost's age key does not exist until its first install generates the SSH host key. The bootstrap password is set by hand and never committed; moving passwords to ahashedPasswordFilebacked by a sops secret is the first post-boot task, out of scope here.
Testing Decisions
- A good test here asserts externally-observable evaluation/build success of the whole
Host, not the internals of any individualModule. - Primary seam (required): the
neogaiaHostevaluates and its system toplevel builds. Building the toplevel drives the entireSkeleton— theAuto-loaderdiscovering everyModule, all three overlays resolving, home-manager integration, and every enabled module's config merging without conflict — plus thediskolayout, which builds from the same tree. Nearly all config-authoring errors surface at this seam short of booting real hardware. - No unit-level tests of individual modules; the config-merge model makes the whole-
Hostbuild the meaningful unit, and it is the highest available seam. - Prior art: none in this repo yet (it starts empty); this build-the-toplevel check is the pattern to reuse for every future
Host. - The genuine end-to-end confirmation is the real reimage, which is manual and irreversible by nature and is not automated.
Out of Scope
- Any graphical environment: Wayland-vs-i3 choice, greeter/display manager, theming (Nord via Stylix or otherwise), fonts, terminal emulator, browser, general desktop apps, gaming (Steam/Lutris/proton-cachyos), emulation.
- Full
sops-nixwiring and moving passwords off the bootstrap value (immediate post-boot follow-up, but not MVI). - Migrating nvim to a native home-manager configuration with Nix-managed plugins.
- chaotic-nyx packages beyond the kernel (
mesa-git,proton-cachyos,scxschedulers). - Flatpak strategy (
nix-flatpakvs dropping the old imperative helper). - The desktop
Host(zeus), including Nvidia. - The three servers: deployment model, service migration (plex/arr/kavita/nfs/torrent-through-protonvpn), ZFS wiring and pool import, backups/monitoring, and per-server kernel/channel pinning.
- VM-based CI (
nixosTestboot assertions) — explicitly a future addition, not part of this deliverable.
Further Notes
- Bootstrap ordering: the flake must exist on Gitea before the install can consume it, and the manual password step keeps the public repo free of any secret while still yielding a login on first boot.
- Gitea is a bootstrap dependency: every NixOS install pulls the config from self-hosted Gitea, so the Gitea host must stay reachable during any install — relevant when sequencing the servers so the migration never locks the operator out of their own configs.
- chaotic cache at install time: the install-time Nix daemon on the live ISO must have the chaotic substituter configured, or it compiles the CachyOS kernel from source on the USB stick.
- Extends to future Hosts by construction: disk layout, kernel, and channel are all per-
Hostconcerns in theSkeleton, and existing ZFS pools are preserved by import rather than declared throughdisko. This is what lets the desktop and the three servers join later without reworking the foundation. - Theme target is Nord (the current CachyOS setup is Nord across terminal, tmux, and nvim), superseding the old repo's Dracula — relevant when the theming branch is grilled.