nixos-btw

Installation
SKILL.md

NixOS BTW: NixOS, Nix, and Flakes Administration

Administer NixOS without falling back into imperative distro muscle memory. NixOS is declarative, functional, and atomic: the system is a value computed from a configuration, every change becomes a new immutable generation in /nix/store, and rollbacks are a bootloader entry away. This skill keeps that model intact, then layers in the practical stack: channels vs flakes, nixos-rebuild vs nix CLI, home-manager, nix-darwin, store hygiene, overlays, module writing, secrets, and the Determinate Nix and Lix lanes.

The places NixOS breaks are NixOS-shaped: channel drift, flake input staleness, garbage collection that nukes a needed derivation, overlays fighting, nix-env -i poisoning the user profile, hardware modules missing, or people treating /etc/nixos/configuration.nix like a normal Linux config file.

Why people run it. Atomic upgrades and rollbacks, bit-for-bit reproducible systems, disposable dev shells, fleet-wide configuration without a separate config-management tool, and a single language for a workstation, a server, a container image, a NixOS VM, and a macOS laptop via nix-darwin.

Versions worth pinning (verified May 2026):

Pin versions only when they shape compatibility or troubleshooting. For ordinary package work, trust the live channel or flake lock over a stale table.

Component Version or date Why it matters
NixOS stable 25.11 "Xantusia" (Nov 2025) current stable, maintained until 2026-06-30
NixOS upcoming 26.05 "Yarara" (May 2026) next release; do not target yet for production
Nix (CLI / daemon) 2.33 (Dec 2025) stable upstream; 2.32 introduced skip-substitutable-downloads
nixos-rebuild-ng default in 25.11 Python rewrite of nixos-rebuild, default for new installs
home-manager release-25.11 (Nov 2025) matches NixOS 25.11; unstable tracks nixos-unstable
nix-darwin tracks nixpkgs 25.11 and master active macOS module system (Intel + Apple Silicon)
Determinate Nix downstream, flakes-on by default validated distribution; parallel eval, lazy trees
Lix fork of Nix compatibility-focused fork; Meson build, improved errors
Kernel default for 25.11 Linux 6.12 LTS default linuxPackages; linuxPackages_latest tracks mainline (6.17 at 25.11 release, not LTS)

When to use

  • NixOS system administration: configuration.nix, modules, options, imports, and /etc/nixos workflow
  • Flake work: flake.nix, flake.lock, inputs, outputs, nix flake update, nix flake check
  • nixos-rebuild flow: switch, test, boot, dry-activate, build-vm, and rollback
  • Channels, pinning, and input management: nix-channel, nix registry, npins, niv, flake inputs
  • home-manager (standalone, NixOS module, or nix-darwin module) for user-level declarative config
  • nix-darwin on macOS: declarative system config, Homebrew integration, LaunchDaemons
  • Nix store and derivations: nix-store, nix store, GC roots, nix-collect-garbage, optimise-store
  • Dev environments: nix-shell, nix develop, shell.nix, default.nix, direnv with nix-direnv
  • Overlays, overrides, and package customisation: overlays, overrideAttrs, override, pin patches
  • Writing NixOS modules: options, config, assertions, conditionals, imports, mkMerge, mkForce
  • Secrets management: sops-nix, agenix, nix-sops, ragenix, activation-time decryption
  • Declarative disks and filesystems: disko, btrfs layouts, LUKS, ZFS, impermanence patterns
  • Remote installs and imaging: nixos-anywhere, nixos-install, nixos-generate, SD images, ISO images
  • Hardware enablement: nixos-hardware profiles, firmware, kernel choice, GPU drivers
  • Unfree and insecure packages: allowUnfree, permittedInsecurePackages, NIXPKGS_ALLOW_*
  • Boot and generations: systemd-boot vs GRUB, kernel selection, nix-env --list-generations, boot entries
  • Garbage collection strategy: retention, GC roots, nix.gc.automatic, auto-optimise-store
  • Determinate Nix lane: Determinate installer, flakes-on-by-default, enterprise backports
  • Lix lane: fork-specific behavior, coexistence, and migration notes
  • Integration with Docker, Kubernetes, and CI: nix build, image outputs, cachix, attic

When NOT to use

  • Arch or CachyOS administration - use arch-btw
  • Debian, Ubuntu, Mint, or Pop!_OS administration - use debian-ubuntu
  • Fedora, RHEL, CentOS Stream, Rocky, or Alma administration - use rhel-fedora
  • Kali Linux and offensive-tool distros - use kali-linux
  • Shell syntax, quoting, or portability outside Nix expressions - use command-prompt
  • Docker, Podman, image builds, or container runtime issues - use docker
  • Kubernetes cluster or manifest work - use kubernetes
  • Fleet-wide non-Nix Linux configuration via playbooks - use ansible
  • Terraform or OpenTofu infrastructure code - use terraform
  • Offensive or privesc testing - use lockpick
  • Defensive hardening and vuln review - use security-audit
  • OPNsense or pfSense appliance work - use firewall-appliance

AI Self-Check

Before returning NixOS or Nix commands, verify:

  • Lane identified: NixOS install, Nix on non-NixOS Linux, Nix on macOS (nix-darwin or plain Nix), WSL, Determinate Nix, or Lix. Advice diverges fast.
  • Channels vs flakes decided: confirm which the user has before prescribing nix-channel, nixos-rebuild --flake, or nix flake update. Mixing without intent creates channel-lock drift.
  • Flake status is current: flakes remain nominally experimental on upstream Nix but are enabled by default on Determinate Nix; recommend enabling experimental-features = nix-command flakes where the user is already using flakes.
  • nix-env -i is not the answer: installing into the per-user profile hides state from configuration.nix and breaks reproducibility. Use declarative environment.systemPackages, home.packages, or an ad-hoc nix shell instead.
  • No partial upgrade advice: on flakes-based systems, do not bump a single input without running nixos-rebuild --flake after; on channels, do not change only nixos without updating dependent channels too.
  • Rebuild verb is intentional: switch, test, boot, dry-activate, build-vm, and build differ. test does not persist the boot entry; boot does not activate now; switch does both.
  • Known-good generation preserved: never remove the last known-good generation, and never nix-collect-garbage -d on a system that just booted a new generation without verifying the new one survives a reboot.
  • GC roots respected: dev shells, direnv caches, and CI artifacts often hold GC roots. Do not recommend aggressive GC without checking nix-store --gc --print-roots first.
  • Unfree / insecure gates named explicitly: set nixpkgs.config.allowUnfree = true; or allowUnfreePredicate, and list insecure packages under permittedInsecurePackages. Do not default to NIXPKGS_ALLOW_UNFREE=1 as the permanent answer.
  • Hardware module present: for fresh installs, hardware-configuration.nix must be regenerated with nixos-generate-config and not hand-edited for filesystem UUIDs. For laptops, check nixos-hardware profile.
  • Kernel and initrd coherence: boot.kernelPackages, initrd modules, filesystems, and bootloader agree. Do not casually swap kernels on a system with ZFS, NVIDIA, or custom out-of-tree modules.
  • Module fields real: options named in advice exist in the version of nixpkgs the user has. options graveyards drift between 23.11, 24.05, 24.11, 25.05, and 25.11 - verify against the nixpkgs tag the user pinned.
  • Secrets not put in the store: anything under ./secret.age, ./sops.yaml, or similar must be decrypted at activation time by sops-nix or agenix, not embedded directly in a Nix string that ends up world-readable in /nix/store.
  • disko / impermanence claims match the install: declarative disk layouts change the recovery story. Confirm partition, filesystem, and subvolume layout before prescribing rollback or wipe steps.
  • home-manager mode identified: standalone, NixOS module, or nix-darwin module. home-manager switch vs nixos-rebuild switch vs darwin-rebuild switch is not interchangeable.
  • Overlay placement sane: overlays belong in nixpkgs.overlays (NixOS) or as a flake input overlay. Do not recommend global ~/.config/nixpkgs/overlays.nix on a flakes system where that path is often ignored.
  • Determinate / Lix advice scoped: Determinate's determinate-nixd and Lix's CLI diverge from upstream in subtle places. Name the lane before suggesting daemon or CLI flags.
  • Diagnostic errors are not silenced: do not hide useful output with 2>/dev/null when the error text is the evidence. Use 2>&1 || true when gathering.
  • Version pins justified: if a pinned system.stateVersion is suggested, explain why; do not change stateVersion on an existing system casually - it controls migration semantics.

  • Current source checked: dated versions, CLI flags, API names, and support windows are verified against primary docs before repeating them
  • Hidden state identified: local config, credentials, caches, contexts, branches, cluster targets, or previous runs are made explicit before acting
  • Verification is real: final checks exercise the actual runtime, parser, service, or integration point instead of only linting prose or happy paths
  • Channel/flake checked: advice matches stable, unstable, flake, home-manager, or nix-darwin context
  • Rollback identified: generation rollback, boot entries, and store/cache state are understood before changes

Performance

  • Use binary caches and substituters deliberately; local source builds can dominate iteration time.
  • Evaluate and build targeted attributes before rebuilding entire systems where possible.
  • Garbage-collect only after confirming new generations boot and required roots are preserved.

Best Practices

  • Keep hardware, secrets, user packages, and service modules separated enough to review changes safely.
  • Commit flake lock and configuration changes together for reproducible rebuilds.
  • Do not delete old generations or store paths until rollback is no longer needed.

Workflow

Step 1: Identify the Nix lane first

Lane Default stance What changes
NixOS installed (stable 25.11) configuration.nix or flake.nix drives everything full system is Nix-managed; rollbacks via bootloader
NixOS unstable / 26.05 pre-release treat as rolling-ish expect breakage; verify options still exist
Nix on non-NixOS Linux user-level only host distro still owns services and boot; no nixos-rebuild
nix-darwin on macOS declarative macOS config darwin-rebuild switch; host owns kernel and firmware
Plain Nix on macOS (no darwin) package manager only nix profile or nix shell for user tooling
WSL2 with NixOS full NixOS in WSL boot and init differ; host owns kernel
Determinate Nix flakes on by default, daemon differs determinate-nixd, parallel eval, lazy trees
Lix upstream-compatible fork CLI mostly matches; internals, error messages, and Meson build diverge

Step 2: Gather current system state

Start narrow. Widen only when the failing layer is still unclear.

Baseline checks for every NixOS or Nix case:

# Identify OS and Nix lane
cat /etc/os-release 2>&1 || true
nix --version
command -v nixos-version >/dev/null 2>&1 && nixos-version
command -v darwin-version >/dev/null 2>&1 && darwin-version
command -v determinate-nixd >/dev/null 2>&1 && determinate-nixd status
uname -a

# Nix config and features
nix config show 2>&1 | grep -E 'experimental-features|extra-experimental|substituters|trusted-users' || true
cat /etc/nix/nix.conf 2>&1 || true

# NixOS-specific
[[ -d /etc/nixos ]] && ls /etc/nixos
[[ -f /etc/nixos/flake.nix ]] && echo "flakes system" || echo "channels system (or flake elsewhere)"
command -v nix-channel >/dev/null 2>&1 && nix-channel --list 2>&1 || true
command -v nixos-rebuild >/dev/null 2>&1 && nixos-rebuild --help 2>&1 | head -5

# Generations and boot
command -v nix-env >/dev/null 2>&1 && nix-env --list-generations -p /nix/var/nix/profiles/system 2>&1 | tail -5 || true
[[ -d /boot/loader/entries ]] && ls /boot/loader/entries 2>&1 | tail -10
command -v bootctl >/dev/null 2>&1 && bootctl status 2>&1 | head -20 || true

# Store health and GC state
df -h /nix/store
du -sh /nix/store 2>&1 || true
nix-store --gc --print-roots 2>&1 | wc -l

Add subsystem probes only when the task needs them:

# home-manager
command -v home-manager >/dev/null 2>&1 && home-manager generations | head -5

# flake state
[[ -f flake.nix ]] && nix flake metadata 2>&1 | head -20
[[ -f flake.lock ]] && nix flake check --no-build 2>&1 || true

# services and logs (NixOS)
systemctl --failed 2>&1 || true
journalctl -b -p warning..alert 2>&1 | tail -30 || true

# hardware (NixOS)
lspci -k 2>&1 | grep -Ei 'vga|3d|display|network' || true
journalctl -b 2>&1 | grep -Ei 'nvrm|nvidia|amdgpu|i915|xe|drm|firmware' || true

Step 3: Load only the relevant reference

Task type Reference
configuration.nix, modules, options, assertions, mkForce/mkMerge references/configuration-and-modules.md
flakes, inputs, lockfile, nix flake, registry, input follows references/flakes-and-channels.md
nixos-rebuild verbs, generations, rollback, boot entries, dry-activate references/rebuild-generations-and-rollback.md
home-manager (standalone, NixOS module, nix-darwin module) references/home-manager-and-darwin.md
overlays, overrideAttrs, override, packaging, mkDerivation, patching references/overlays-packaging-and-overrides.md
Nix store, nix-store, GC roots, nix-collect-garbage, optimise-store, CAS references/store-gc-and-builders.md
dev shells, nix develop, shell.nix, direnv with nix-direnv references/dev-shells-and-direnv.md
disko, impermanence, nixos-anywhere, SD/ISO images, nixos-generators references/disko-impermanence-and-imaging.md
hardware, firmware, kernels, nixos-hardware, GPU drivers, Wayland desktop references/hardware-desktop-and-kernel.md
secrets: sops-nix, agenix, activation decryption, keyrings references/secrets-sops-and-agenix.md
Determinate Nix, Lix, lane-specific behavior, migration notes references/determinate-lix-and-lanes.md
recurring NixOS and Nix footguns, edge cases, upgrade breakage references/gotchas-and-special-situations.md

Do not load every reference by default. Pick the one that matches the failure mode.

Step 3.5: Stay here or hand off

Request shape Route
NixOS install, module, flake, rebuild, rollback, store, overlay, disko, secrets stay in nixos-btw
Build a Docker or OCI image from Nix (dockerTools, nix2container) stay here for the Nix build, hand to docker for runtime
Deploy a NixOS box into K8s (nixos-in-kube, custom images) start here, hand to kubernetes for the cluster side
Review Nix-produced container for OWASP / supply chain issues stay for the Nix build; use security-audit for the audit
Write a bash one-liner or zsh function that wraps nix calls use command-prompt

Step 4: Change one layer at a time

  • Fix flake or channel state before blaming nixos-rebuild.
  • Run nixos-rebuild dry-activate --flake .#host before switch when the risk is nonzero.
  • Prefer test over switch when the change might break login; test does not persist the boot entry, so a reboot returns to a known-good generation.
  • Fix option-name drift before blaming module logic: nix repl -f '<nixpkgs/nixos>' -I ... and :p config.services.foo beat guessing.
  • On home-manager, run home-manager switch separately if it is standalone; otherwise nixos-rebuild switch picks it up when it is wired as a module.
  • Avoid touching system.stateVersion. It anchors migration semantics.
  • Keep overlays composable: scope to final: prev: { ... }, not mutating pkgs in-place.
  • Validate before GC: nix-store --gc --print-roots | grep <path> before deleting.

Step 5: Validate before closing

# System state
nixos-version 2>&1 || true
nix-env --list-generations -p /nix/var/nix/profiles/system | tail -5
systemctl --failed 2>&1 || true

# Flake sanity
[[ -f flake.nix ]] && nix flake check --no-build

# Store sanity
nix-store --verify --check-contents 2>&1 | tail -20
df -h /nix/store

Reboot only when the boot path is understood and at least one known-good generation remains.


Troubleshooting Pattern

Keep triage cross-layer and boring:

  1. Confirm the Nix lane (NixOS vs Nix-on-host, flakes vs channels, Determinate vs upstream vs Lix).
  2. Identify the failing layer: input state, evaluation, build, activation, or runtime service.
  3. Pull the right logs before changing config.
  4. Change one layer at a time and retest.
  5. Prefer rollback to reinstall. Generations are the whole point.

Core log sweep on NixOS:

journalctl -b -p warning..alert
journalctl --user -b
dmesg --level=err,warn
journalctl -u nix-daemon -b
journalctl -u nixos-rebuild -b 2>&1 || true

Build and eval failures are loudest in the rebuild output itself; pipe through --show-trace and, for opaque errors, --print-build-logs:

nixos-rebuild switch --flake .#host --show-trace --print-build-logs 2>&1 | tail -200

When a problem looks "flake-only," compare one clean baseline:

  • delete flake.lock and re-lock, or
  • roll one input back to its previous rev via nix flake lock --override-input, or
  • build the same output on another machine with the same flake.lock to isolate hardware vs config.

Default Decisions

  • Declarative first. nix-env -i, nix profile install, and one-off nixpkgs.config tweaks are escape hatches, not workflows. If it survives across rebuilds, it belongs in the config.
  • Flakes when the user already has them. Do not migrate users off channels mid-troubleshoot. Do not migrate users onto flakes without naming tradeoffs (still experimental upstream, lockfile commits become a habit, registry overrides differ).
  • Generations are the rollback. Before debugging, confirm the previous generation boots. That is cheaper than chasing ghosts.
  • Store hygiene is boring and scheduled. Enable nix.gc.automatic and nix.settings.auto-optimise-store = true; rather than manual sweeps.
  • home-manager has three modes. Standalone, NixOS module, nix-darwin module. Pick one per host and stay there.
  • nix-darwin is opinionated. macOS system settings, LaunchDaemons, and Homebrew coexistence are what it owns. Do not treat it like NixOS with a different kernel.
  • Disko and impermanence are declarative recovery. They shine on fresh installs and nixos-anywhere deploys; retrofitting them onto a live system is a different, harder problem.
  • Secrets never go in the store. Use sops-nix or agenix with activation-time decryption.
  • Pick Determinate or Lix intentionally. Both are fine; both add behavior that diverges from upstream Nix in small but real ways. Name the lane.
  • Kernel changes are cross-layer. ZFS, NVIDIA, hardened kernel, and custom initrd modules all interact. Do not swap boot.kernelPackages casually.

Quick Triage Checklist

Symptom First checks
nixos-rebuild evaluation error --show-trace, check option name against the current nixpkgs tag, check imports paths
Build fails mid-derivation --print-build-logs, check sandbox violations, check unfree or insecure gates
Boot drops to emergency shell previous generation from bootloader menu, check hardware-configuration.nix, LUKS, kernel modules
Flake input won't update nix flake lock --update-input <name>, check inputs.<x>.follows, check registry override
System is huge, /nix/store fills disk nix-collect-garbage -d, nix-store --optimise, prune generations, check direnv GC roots
nix-env -i installed something that won't stick user profile vs system config; move to environment.systemPackages or home.packages
home-manager drift vs NixOS standalone vs module mode, which one owns the file, home-manager switch vs nixos-rebuild switch
Unfree package refuses to build nixpkgs.config.allowUnfree = true; or predicate, or NIXPKGS_ALLOW_UNFREE=1 nix-build --impure for one-off
Secrets in a module show up world-readable moved to sops-nix/agenix and re-deploy; rotate the exposed secrets
ZFS or NVIDIA breaks after kernel bump boot.kernelPackages = pkgs.linuxPackages_<pin>; or wait for out-of-tree module to rebuild
nix-darwin change did not apply darwin-rebuild switch, not nixos-rebuild; check users.users.<name>.home and nix.enable
Nothing makes sense check gotchas reference - channel vs flake mixing, stale lock, overlay collisions, GC of an active dev shell

Reference Files

  • references/configuration-and-modules.md - /etc/nixos/configuration.nix, modules, options, assertions, mkMerge and mkForce, NixOS module system
  • references/flakes-and-channels.md - flakes, flake.nix anatomy, inputs and outputs, flake.lock, nix flake verbs, channels vs flakes vs npins
  • references/rebuild-generations-and-rollback.md - nixos-rebuild verbs, generations, nix-env --list-generations, bootloader entries, rollback flow
  • references/home-manager-and-darwin.md - home-manager (standalone, NixOS module, nix-darwin module), nix-darwin system configuration on macOS
  • references/overlays-packaging-and-overrides.md - overlays, overrideAttrs, override, callPackage, writing derivations, patching upstream
  • references/store-gc-and-builders.md - Nix store, nix-store vs nix store, GC roots, nix-collect-garbage, optimise-store, remote builders, Cachix and attic
  • references/dev-shells-and-direnv.md - nix-shell, nix develop, shell.nix, default.nix, flake dev shells, nix-direnv, per-project env
  • references/disko-impermanence-and-imaging.md - disko partitioning, impermanence, nixos-anywhere, SD/ISO images with nixos-generators
  • references/hardware-desktop-and-kernel.md - nixos-hardware, kernel selection, firmware, GPU drivers, Wayland and X11, display managers
  • references/secrets-sops-and-agenix.md - sops-nix, agenix, activation-time decryption, age key management, avoiding store-world-readable secrets
  • references/determinate-lix-and-lanes.md - Determinate Nix (enterprise lane, daemon differences, flakes-on-by-default), Lix (compat-focused fork), coexistence
  • references/gotchas-and-special-situations.md - recurring NixOS and Nix failure patterns and edge cases

Output Contract

See skills/_shared/output-contract.md for the full contract.

  • Skill name: NIXOS-BTW
  • Deliverable bucket: audits
  • Mode: conditional. When invoked to analyze, review, audit, or improve existing repo content, emit the full contract -- boxed inline header, body summary inline plus per-finding detail in the deliverable file, boxed conclusion, conclusion table -- and write the deliverable to docs/local/audits/nixos-btw/<YYYY-MM-DD>-<slug>.md. When invoked to answer a question, teach a concept, build a new artifact, or generate content, respond freely without the contract.
  • Severity scale: P0 | P1 | P2 | P3 | info (see shared contract; only used in audit/review mode).

Related Skills

  • arch-btw - Arch and CachyOS administration. Use it when the host is Arch-family and Nix is a side tool, not the system.
  • debian-ubuntu - Debian-family administration. Use it when Nix runs on Debian/Ubuntu as a user-level package manager only.
  • rhel-fedora - RHEL-family administration. Use it when Nix runs on Fedora/RHEL as a user-level package manager only.
  • kali-linux - Kali distro workflow. Use it for Kali, not for Nix inside Kali.
  • command-prompt - shell syntax, aliases, and one-liners outside Nix expressions.
  • docker - container runtime and image concerns. This skill builds images with dockerTools; docker runs them.
  • kubernetes - cluster workloads once the build output leaves Nix.
  • ansible - fleet config management when Nix is not the chosen tool for the job.
  • terraform - IaC for cloud resources that host NixOS VMs; this skill owns the guest.
  • security-audit - defensive review of a Nix-built artifact.
  • update-docs - sync docs after substantial module or flake restructuring.

Rules

  1. Identify the Nix lane before prescribing commands. NixOS vs Nix-on-host, channels vs flakes, Determinate vs upstream vs Lix, standalone vs module home-manager - all change the answer.
  2. Declarative beats imperative. If the fix is nix-env -i, reach for environment.systemPackages, home.packages, or nix shell instead. Hidden profile state is a footgun.
  3. Know the rebuild verb. switch, test, boot, dry-activate, build-vm, and build are not interchangeable. test does not persist across reboot; boot does not activate now.
  4. Preserve a known-good generation. Never GC aggressively right after a rebuild. Boot the new generation twice before pruning.
  5. Flakes are intentional. Enable nix-command flakes where the user already uses them; do not push migration during troubleshooting.
  6. GC roots are real state. Dev shells, direnv caches, and CI pins hold them. Check before mass deletion.
  7. Secrets never in the store. Use sops-nix or agenix with activation-time decryption; anything else ends up world-readable in /nix/store.
  8. Module options must exist in the user's nixpkgs tag. 23.11/24.05/24.11/25.05/25.11 drift is real; confirm with nix repl or the options search before recommending.
  9. Overlays compose, not mutate. final: prev: { ... } scope. Do not recommend global profile-level overlays on flake systems.
  10. Kernel and initrd agree. Do not flip boot.kernelPackages on ZFS, NVIDIA, or custom-module systems without a fallback.
  11. Do not touch system.stateVersion casually. It controls migration semantics and is not a "version bump" field.
  12. Disko and impermanence are install-time decisions. Retrofit is a different, harder conversation.
  13. home-manager mode is sticky. Pick standalone, NixOS module, or darwin module per host and stay there.
  14. Determinate and Lix diverge at the edges. Name the lane before suggesting daemon or CLI flags.
  15. Reach for common Nix failure patterns before exotic explanations. Channel/flake mixing, stale lock, overlay collision, option drift, GC of an active dev shell, and unfree/insecure gates explain a large share of the chaos.
Related skills
Installs
7
GitHub Stars
6
First Seen
Apr 23, 2026