Nix Troubleshooting Primer
- Type: Tutorial
- Status: Current
- Scope: Learning to classify Nix/configuration failures in
nixos-configbefore taking operational action- Canonical sources:
flake.nix;justfiles/deploy.just;AGENTS.md; Testing and CI Contracts; Safe Testing and Deployment; Monitoring and Service Investigation; NixOS manual- Last verified: Source commit
556df88494686003b1c4f20c8e0b99b5afc16a6e(2026-09-12); source HEADb2831d33132967b4c4fb69e6068b2b05fff8fdffchanges onlyflake.lock. Current check/build/test recipes and source layout checked 2026-09-13; no failure was induced or host changed.- Review triggers: Validation commands, flake outputs, module layout, deployment wrappers, incident runbooks, or upstream diagnostic guidance
Learning goal and prerequisites
Read an error as evidence about which stage failed, not as an invitation to try random overrides. Work through NixOS Modules and Option Merging first. The NixOS manual has the general recovery reference; this is a safe classification exercise, not a live incident runbook.
Place the failure on the lifecycle
| Stage | What a failure usually means | First safe check |
|---|---|---|
| Parse/evaluation or flake check | Syntax, missing file/attribute, undeclared or conflicting option, assertion, or a flake check | Read the first meaningful error and its file/line; run just check after staging new files |
| A specific host build | The selected closure or package cannot be constructed, even if flake checks pass | Build only the affected nixosConfigurations.<host>.config.system.build.toplevel with --no-link and inspect the failed derivation |
| Test activation or deployment | The built system did not activate cleanly, or the wrong target/mode was chosen | Confirm exact revision, target, active specialisation, hold, and wrapper output; use Safe Testing and Deployment |
| Runtime/client path | A unit starts or fails, but the real user path, mount, secret, route, or external dependency is wrong | Collect unit and client evidence using Monitoring and Service Investigation |
Do not equate a green nix flake check with an all-host closure build or
a successful deployment. Do not use a NixOS generation rollback as a data
restore. Testing and CI Contracts defines
what each gate actually covers.
Read the first useful error
Nix often prints a long stack of evaluations. Find the first concrete
error: and the nearest path and line in this repository, then trace the
import/option chain. Typical interpretations:
syntax erroror unexpected token: inspect braces, semicolons, list spacing, strings, and the cited line. Format/lint only after understanding the parse failure.attribute ... missingorundefined variable: inspect the exact attribute path and function argument header. A flake input with nopackages.<system>.defaultis not fixed by inventing that output; inspect its realpackages,legacyPackages, ornixosModulesshape.option ... does not exist: check spelling and whether the module that declares the option is in the host import graph. See Repository Structure and Import Chain.- conflicting definitions: find both definitions and determine ownership.
mkDefaultandmkForceare deliberate priority tools, not a generic way to suppress a duplicate mistake. - infinite recursion: find which option value depends on itself through
another definition. Consider whether an import or default is using
configtoo early; follow the dependency rather than addingmkForce. - missing source path after creating a file: Git-backed flakes do not see
untracked
.nixor encrypted YAML files. Stage the exact new file, verify its path, then reevaluate.
Warnings also matter. Ignore only a warning already understood and
documented as benign, such as the expected dirty-tree notice in a local
working tree; review every other diagnostic under AGENTS.md.
Guided exercise
In a clean, read-only checkout, trace the declared Jellyfin service from
hosts/lyra/default.nix into modules/services/jellyfin.nix. Imagine
the error The option nixosConfig.jellyfin.enable does not exist.
Identify the host import and option declaration you would inspect; do not
add a second declaration or activate a build. Next imagine the option
evaluates but Jellyfin cannot read /mnt/media: why does that belong to
mount/startup/runtime investigation rather than Nix syntax debugging?
Use the module and Service Migration and
Cutover to check the answer.
For real work, reproduce only the narrowest safe gate, record the exact source revision and error class, and escalate to the appropriate engineering or operations page. Never paste secret-bearing logs into a public issue. If a change affects boot, storage, networking, or keys, stop before speculative activation and use its recovery runbook.