Modern Nix CLI

Updated

July 30, 2026

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 --version

Enters 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 urls

show 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 400

Theory 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 fail
nix repl
:lf .
:lf nixpkgs

REPL 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 casually

nix-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 exist

First 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-link

Example B — Flake introspection

cd ~/lab/nixos-book/concepts/derivations   # or your flake
nix flake show
nix flake metadata

Example C — Logs streaming

nix build nixpkgs#hello -L

Example D — JSON for scripting

nix flake metadata --json | head -c 400
nix path-info --json nixpkgs#hello | head -c 400

Example 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-cli

Create 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:

  1. nix flake show
  2. nix build .#greet --print-out-paths
  3. nix run .#greet
  4. nix path-info -rS ./result | tail (if result exists)
  5. rm -f result and 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:

  1. Temporarily need ffmpeg
  2. See outputs of current flake
  3. Build package without leaving result
  4. Run project’s default app
  5. See what nixpkgs revision lock pins

Exercises

  1. Build with -o custom-result and remove it; confirm root behavior.
  2. Compare nix run vs nix shell -c for a multi-arg program.
  3. Export nix flake metadata --json and extract locked rev (manual or jq).
  4. Use nix eval to print hello.name from nixpkgs.
  5. Intentionally pass a bad attr; read the error; fix via flake show.
  6. Time a cold vs warm nix run nixpkgs#hello (subjective notes OK).
  7. List profile generations; avoid installing anything permanent if possible.
  8. Write a 15-line operator runbook: “package missing on PATH.”
  9. Use --show-trace on a deliberate eval error.
  10. Document registry vs project lock difference for nixpkgs#.
  11. Practice nix build + path-info -rS closure report for two packages.
  12. 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 result link
  • Used flake show + metadata
  • Used path-info on 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