Modern Nix CLI
Modern Nix CLI
Goal: Prefer the modern nix CLI for all daily tasks: build, run, develop, shell, flake inspection, store queries—and know which command matches which intent.
Why this chapter matters
Docs and blog posts mix eras. Muscle memory on the wrong CLI makes flakes feel harder than they are. This chapter is deliberate fluency so flake authoring and host work feel like product work, not archaeology.
Ops motivation: incident response on a Nix machine is mostly “pick the right verb.” build vs run vs develop vs eval is how you avoid thrash under pressure.
Theory 1 — One CLI, many intents
| Intent | Modern command | Rough meaning |
|---|---|---|
Build installable → result |
nix build |
Realize outputs |
| Run app without installing | nix run |
Build/realize + exec |
| Project dev environment | nix develop |
Shell with build inputs (flake devShell) |
| Ad-hoc package shell | nix shell |
Temporary PATH with packages |
| Explore flake outputs | nix flake show |
Schema of outputs |
| Update lock | nix flake update |
Refresh inputs (careful) |
| Metadata / eval | nix eval |
Evaluate expressions |
| Store facts | nix path-info |
Size, refs, sigs |
| Search | nix search |
Find packages |
| Profile (user env) | nix profile |
Generational user installs |
| Format / check (later) | nix fmt, nix flake check |
Quality gates |
| Store GC | nix store gc |
Collect unreferenced paths |
Classic cousins (nix-build, nix-shell, …) wait until the classic CLI chapter.
Theory 2 — Installables: how you name what you want
Modern CLI takes installables:
| Form | Example | Meaning |
|---|---|---|
| Flake output | .#greet |
Current flake, package greet |
| Flake + attr | .#packages.x86_64-linux.greet |
Fully spelled |
| Indirect flake | nixpkgs#hello |
Registry/indirect nixpkgs |
| Git flake | github:NixOS/nixpkgs/nixos-26.05#hello |
Explicit |
| Path flake | ./myproj#default |
Local |
| File / expr | -f file.nix, --expr '…' |
Non-flake (still useful) |
# splits flake ref from attribute
nixpkgs#hello
│ │
│ └─ attribute path inside outputs (with defaults)
└─ flake reference
Ambiguity and defaults
nix build .# often means default package. If show is empty, check system keys under packages.<system>.
Theory 3 — nix build deep enough for daily use
nix build .#greet
nix build .#greet --print-out-paths
nix build .#greet -o greet-result
nix build nixpkgs#hello| Flag | Role |
|---|---|
--print-out-paths |
Print store paths |
-o name |
Name the result symlink |
--no-link |
Realize without result |
-L / --print-build-logs |
Stream logs |
--rebuild |
Force rebuild (when applicable) |
--show-trace |
Eval error detail |
Discipline: temporary builds → --no-link or rm result to avoid GC root clutter (store chapter).
Theory 4 — nix run vs nix shell vs nix develop
nix run
nix run nixpkgs#hello
nix run .#greet
nix run nixpkgs#cowsay -- "moo"Builds/downloads if needed, runs the package’s main program, does not require a permanent profile entry. Args after -- go to the program.
nix shell
nix shell nixpkgs#git nixpkgs#jq
# temporary shell with git + jq on PATH
nix shell nixpkgs#jq -c 'jq --version'Ad-hoc tools. Great for “I need jq once.”
nix develop
nix develop # .#devShells.default or similar
nix develop .#name
nix develop -c cargo --versionEnters the flake’s devShell—meant for project dependencies, compilers, env vars. The flakes chapter makes one.
| Command | Sticky? | Typical use |
|---|---|---|
run |
No | One-shot CLI tools |
shell |
Until exit | Ad-hoc package mix |
develop |
Until exit | Project toolchain |
profile install |
Yes | User-global tools (use sparingly early) |
Theory 5 — Flake subcommands
nix flake init # scaffold (optional)
nix flake new mod # template
nix flake show # outputs tree
nix flake metadata # resolved inputs, locks
nix flake check # run checks (later quality)
nix flake update # update all inputs
nix flake update nixpkgs # selective update
nix flake lock # ensure lock matches urlsshow vs metadata
| Command | Answers |
|---|---|
show |
What outputs exist? |
metadata |
What are inputs pinned to? |
JSON for scripting
nix flake metadata --json | head -c 400
nix flake show --json 2>/dev/null | head -c 400Theory 6 — Eval and debugging eval
nix eval .#packages.x86_64-linux.greet.name
nix eval --raw .#packages.x86_64-linux.greet
nix eval --json nixpkgs#hello.meta | head
nix eval --show-trace --expr '1 + "x"' # intentional failnix repl
:lf .
:lf nixpkgsREPL remains the best theory laboratory.
| Goal | Tool |
|---|---|
| Print a string attr | nix eval --raw |
| Explore interactively | nix repl |
| Deep stack on error | --show-trace |
Theory 7 — Store queries you will reuse
nix path-info -Sh ./result
nix path-info -rS ./result | tail
nix store gc --dry-run # careful; prefer dry concepts early
# nix store delete … # advanced; skip casuallynix-store -q still works; modern path-info is friendlier.
| Query | Command family |
|---|---|
| Size | path-info -S / -rS |
| Closure | path-info -r |
| Roots (classic) | nix-store --gc --print-roots |
Theory 8 — Profiles without making them a lifestyle
nix profile list
nix profile install nixpkgs#jq
nix profile remove … This book’s preference: project flakes + devShells over growing a huge imperative profile. Profiles are fine for a few personal tools; they are not a substitute for declarative hosts (NixOS host part).
Theory 9 — Search and discovery
nix search nixpkgs ripgrep
nix search nixpkgs#legacyPackages.x86_64-linux hello # more explicit forms existFirst search can be slow (indexing/eval). Prefer known attribute names when you have them.
Online companion: https://search.nixos.org/packages (select 26.05 when available).
Theory 10 — Error class → command checklist
| Error class | First commands |
|---|---|
| experimental features | nix config show |
| attr missing | nix flake show |
| pure eval path missing | git status / flake files |
| build failed | nix build -L / nix log |
| wrong system | builtins.currentSystem / show packages |
Worked examples bank
Example A — Same package, three ways
nix run nixpkgs#hello
nix shell nixpkgs#hello -c hello
nix build nixpkgs#hello --print-out-paths --no-linkExample B — Flake introspection
cd ~/lab/nixos-book/concepts/derivations # or your flake
nix flake show
nix flake metadataExample C — Logs streaming
nix build nixpkgs#hello -LExample D — JSON for scripting
nix flake metadata --json | head -c 400
nix path-info --json nixpkgs#hello | head -c 400Example E — Non-interactive develop
nix develop -c bash -lc 'git --version; echo $name'Lab 1 — Cheat-sheet construction
mkdir -p ~/lab/nixos-book/concepts/modern-cli
cd ~/lab/nixos-book/concepts/modern-cliCreate CHEATSHEET.md and fill by running each command at least once:
build: …
run: …
shell: …
develop: … (may wait until flakes chapter if no shell yet)
show: …
metadata: …
eval: …
path-info:…
search: …
Paste the exact successful command lines you used.
Lab 2 — Operate a local flake with modern CLI only
Reuse the derivations flake (or recreate a minimal package flake).
Tasks:
nix flake show
nix build .#greet --print-out-paths
nix run .#greet
nix path-info -rS ./result | tail(if result exists)
rm -f resultand rebuild with--no-link
Lab 3 — Ad-hoc nix shell workflow
nix shell nixpkgs#jq nixpkgs#ripgrep -c bash -c 'jq --version; rg --version'Solve a tiny task: query a JSON blob with jq without installing jq into a profile permanently.
echo '{"ok":true}' | nix shell nixpkgs#jq -c 'jq .'Lab 4 — Search + run loop
nix search nixpkgs cowsay | head
nix run nixpkgs#cowsay -- "modern cli"Record the attribute name you used.
Lab 5 — Intent mapping drill (closed book)
Write the best command for each:
- Temporarily need
ffmpeg
- See outputs of current flake
- Build package without leaving
result
- Run project’s default app
- See what nixpkgs revision lock pins
Exercises
- Build with
-o custom-resultand remove it; confirm root behavior.
- Compare
nix runvsnix shell -cfor a multi-arg program.
- Export
nix flake metadata --jsonand extract locked rev (manual orjq).
- Use
nix evalto printhello.namefrom nixpkgs.
- Intentionally pass a bad attr; read the error; fix via
flake show.
- Time a cold vs warm
nix run nixpkgs#hello(subjective notes OK).
- List profile generations; avoid installing anything permanent if possible.
- Write a 15-line operator runbook: “package missing on PATH.”
- Use
--show-traceon a deliberate eval error.
- Document registry vs project lock difference for
nixpkgs#.
- Practice
nix build+path-info -rSclosure report for two packages.
- Map five classic commands (from memory) to modern ones; verify next chapter.
Common pitfalls
| Symptom | Theory |
|---|---|
experimental features errors |
nix-command / flakes not enabled |
| Attribute not found | Wrong system triple / output layout |
nix run can’t find binary |
Package has no main program meta |
Mixing nix-env -i habits |
Prefer run/shell/develop |
flake show empty packages |
system key mismatch |
Slow nix search |
First-time indexing/eval |
| Sudo changes flake path cwd | Use absolute flake refs when needed |
| Profile becomes SoT | Move tools into flakes/HM later |
Checkpoint
- Cheat-sheet written from real command output
- Can choose run vs shell vs develop correctly
- Built with and without
resultlink
- Used
flake show+metadata
- Used
path-infoon a real path
- No new dependency on classic CLI for daily work
Further depth (this book)
| Topic | Chapter |
|---|---|
| Author flakes + devShells | Flakes and project devShells |
| Classic translation table | Classic CLI literacy |
| Host rebuild CLI | Host: Rebuild and generations |
nix flake check quality |
Home/flake layout part |