nixos-btw
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/nixosworkflow - Flake work:
flake.nix,flake.lock, inputs, outputs,nix flake update,nix flake check nixos-rebuildflow: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 withnix-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, ornix 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 flakeswhere the user is already using flakes. -
nix-env -iis not the answer: installing into the per-user profile hides state fromconfiguration.nixand breaks reproducibility. Use declarativeenvironment.systemPackages,home.packages, or an ad-hocnix shellinstead. - No partial upgrade advice: on flakes-based systems, do not bump a single input without running
nixos-rebuild --flakeafter; on channels, do not change onlynixoswithout updating dependent channels too. - Rebuild verb is intentional:
switch,test,boot,dry-activate,build-vm, andbuilddiffer.testdoes not persist the boot entry;bootdoes not activate now;switchdoes both. - Known-good generation preserved: never remove the last known-good generation, and never
nix-collect-garbage -don 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-rootsfirst. - Unfree / insecure gates named explicitly: set
nixpkgs.config.allowUnfree = true;orallowUnfreePredicate, and list insecure packages underpermittedInsecurePackages. Do not default toNIXPKGS_ALLOW_UNFREE=1as the permanent answer. - Hardware module present: for fresh installs,
hardware-configuration.nixmust be regenerated withnixos-generate-configand 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.
optionsgraveyards drift between 23.11, 24.05, 24.11, 25.05, and 25.11 - verify against thenixpkgstag 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 switchvsnixos-rebuild switchvsdarwin-rebuild switchis not interchangeable. - Overlay placement sane: overlays belong in
nixpkgs.overlays(NixOS) or as a flake input overlay. Do not recommend global~/.config/nixpkgs/overlays.nixon a flakes system where that path is often ignored. - Determinate / Lix advice scoped: Determinate's
determinate-nixdand 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/nullwhen the error text is the evidence. Use2>&1 || truewhen gathering. - Version pins justified: if a pinned
system.stateVersionis suggested, explain why; do not changestateVersionon 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 .#hostbeforeswitchwhen the risk is nonzero. - Prefer
testoverswitchwhen the change might break login;testdoes 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.foobeat guessing. - On home-manager, run
home-manager switchseparately if it is standalone; otherwisenixos-rebuild switchpicks 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 mutatingpkgsin-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:
- Confirm the Nix lane (NixOS vs Nix-on-host, flakes vs channels, Determinate vs upstream vs Lix).
- Identify the failing layer: input state, evaluation, build, activation, or runtime service.
- Pull the right logs before changing config.
- Change one layer at a time and retest.
- 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.lockand 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.lockto isolate hardware vs config.
Default Decisions
- Declarative first.
nix-env -i,nix profile install, and one-offnixpkgs.configtweaks 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.automaticandnix.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.kernelPackagescasually.
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,mkMergeandmkForce, NixOS module systemreferences/flakes-and-channels.md- flakes,flake.nixanatomy, inputs and outputs,flake.lock,nix flakeverbs, channels vs flakes vs npinsreferences/rebuild-generations-and-rollback.md-nixos-rebuildverbs, generations,nix-env --list-generations, bootloader entries, rollback flowreferences/home-manager-and-darwin.md- home-manager (standalone, NixOS module, nix-darwin module), nix-darwin system configuration on macOSreferences/overlays-packaging-and-overrides.md- overlays,overrideAttrs,override,callPackage, writing derivations, patching upstreamreferences/store-gc-and-builders.md- Nix store,nix-storevsnix store, GC roots,nix-collect-garbage, optimise-store, remote builders, Cachix and atticreferences/dev-shells-and-direnv.md-nix-shell,nix develop,shell.nix,default.nix, flake dev shells,nix-direnv, per-project envreferences/disko-impermanence-and-imaging.md- disko partitioning, impermanence, nixos-anywhere, SD/ISO images with nixos-generatorsreferences/hardware-desktop-and-kernel.md- nixos-hardware, kernel selection, firmware, GPU drivers, Wayland and X11, display managersreferences/secrets-sops-and-agenix.md- sops-nix, agenix, activation-time decryption, age key management, avoiding store-world-readable secretsreferences/determinate-lix-and-lanes.md- Determinate Nix (enterprise lane, daemon differences, flakes-on-by-default), Lix (compat-focused fork), coexistencereferences/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
- 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.
- Declarative beats imperative. If the fix is
nix-env -i, reach forenvironment.systemPackages,home.packages, ornix shellinstead. Hidden profile state is a footgun. - Know the rebuild verb.
switch,test,boot,dry-activate,build-vm, andbuildare not interchangeable.testdoes not persist across reboot;bootdoes not activate now. - Preserve a known-good generation. Never GC aggressively right after a rebuild. Boot the new generation twice before pruning.
- Flakes are intentional. Enable
nix-command flakeswhere the user already uses them; do not push migration during troubleshooting. - GC roots are real state. Dev shells, direnv caches, and CI pins hold them. Check before mass deletion.
- Secrets never in the store. Use sops-nix or agenix with activation-time decryption; anything else ends up world-readable in
/nix/store. - 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 replor the options search before recommending. - Overlays compose, not mutate.
final: prev: { ... }scope. Do not recommend global profile-level overlays on flake systems. - Kernel and initrd agree. Do not flip
boot.kernelPackageson ZFS, NVIDIA, or custom-module systems without a fallback. - Do not touch
system.stateVersioncasually. It controls migration semantics and is not a "version bump" field. - Disko and impermanence are install-time decisions. Retrofit is a different, harder conversation.
- home-manager mode is sticky. Pick standalone, NixOS module, or darwin module per host and stay there.
- Determinate and Lix diverge at the edges. Name the lane before suggesting daemon or CLI flags.
- 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.