Classic CLI literacy
Classic CLI literacy
Goal: Map classic Nix commands and channel habits to the modern CLI—once—so legacy docs are readable without making classic tools your daily driver.
This chapter is literacy, not a lifestyle change. After it, default back to modern nix + flakes.
Why this chapter matters
Half the internet still says nix-env -iA nixpkgs.hello and nix-shell -p. If you cannot translate, you thrash. If you only learn classic, you fight 2026 defaults. Translation table + one dual run is enough.
Ops motivation: production runbooks, ancient Stack Overflow answers, and coworker muscle memory will keep showing classic commands. Translation skill is cheaper than rewriting the internet.
Theory 1 — Same store, two UIs
| Layer | Shared | Differs |
|---|---|---|
| Store | /nix/store, derivations, GC roots |
— |
| Eval | Nix language | Entry points, purity defaults |
| UX | — | Flag names, flake awareness, UX polish |
Classic tools predate flakes. They still work. They are not the pedagogy default of this book.
┌──────────────┐
│ /nix/store │
└──────▲───────┘
│
┌────────────┴────────────┐
│ │
classic CLI modern `nix`
(nix-build, …) (nix build, …)
Theory 2 — Command map (core)
| Classic | Modern analogue | Notes |
|---|---|---|
nix-build -A hello |
nix build nixpkgs#hello |
Attr selection differs |
nix-build on default.nix |
nix build -f . / flake packages |
|
nix-shell -p hello |
nix shell nixpkgs#hello |
Ad-hoc tools |
nix-shell (dev shell.nix) |
nix develop (flake) / nix-shell still |
Project shells |
nix-env -iA nixpkgs.hello |
nix profile install nixpkgs#hello |
Prefer not to |
nix-env -q |
nix profile list |
|
nix-env -e |
nix profile remove |
|
nix-instantiate |
nix eval / derivation show |
Different shapes |
nix-store -q --requisites |
nix path-info -r |
Both fine |
nix-store -q --tree |
(still useful) / path-info | Keep classic for trees |
nix-channel --update |
nix flake update (project) |
Different model |
nix-collect-garbage |
nix store gc / nix-collect-garbage |
Still common |
nix-prefetch-url |
nix store prefetch-file etc. |
Variants evolve |
Intent-first map
| Intent | Prefer modern | Classic you may see |
|---|---|---|
| Run once | nix run |
nix-shell -p … --run |
| Project tools | nix develop |
nix-shell |
| Realize output | nix build |
nix-build |
| Pin project | flake.lock | channel + NIX_PATH |
| Query closure | nix path-info -r |
nix-store -q --requisites |
Theory 3 — Channels as history
Channel model
nix-channel --add https://nixos.org/channels/nixos-26.05 nixpkgs
nix-channel --update
nix-env -iA nixpkgs.hello
| Property | Effect |
|---|---|
| Mutable “latest on channel” | Drift between machines/days |
NIX_PATH / <nixpkgs> |
Ambient dependency |
| User or root channels | Confusing multi-user stories |
Flake model (preferred)
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# flake.lock pins exact rev
| Property | Effect |
|---|---|
| Lock file | Shared truth |
| Explicit inputs | No ambient channel required |
| Per-project pins | Different projects can differ safely |
This book: channels = read-only literacy. Do not build new projects on channels.
When channels still appear
| Context | Why |
|---|---|
| Old tutorials | Written pre-flakes default |
| Some NixOS install defaults | Historical paths |
| Angle-bracket imports | import <nixpkgs> {} |
Theory 4 — shell.nix / default.nix legacy layouts
Classic project shell
shell.nix:
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
packages = [ pkgs.hello pkgs.git ];
}nix-shell # enters shell.nixModern
flake.nix → devShells.default → nix develop
You may keep a thin shell.nix for non-flake users, but your path is flakes.
Dual support pattern (optional)
Some repos provide both: flake for modern users, shell.nix that re-exports for classic. Only do this if you must support both audiences.
Theory 5 — nix-env and imperative profiles
nix-env -iA nixpkgs.hello
nix-env -e hello
nix-env --list-generationsProblems as a primary workflow:
- Not recorded in project git
- Easy to create unreproducible laptops
- Diverges from NixOS declarative model
nix profile is the modern imperative cousin—same caution.
| Imperative tool | Declarative alternative |
|---|---|
nix-env -i |
flake packages / NixOS systemPackages / HM |
Random nix-shell -p forever |
devShells |
| Channel update as SoT | flake.lock review |
Theory 6 — Purity and flakes
Classic ad-hoc eval often sees your cwd and NIX_PATH freely. Flake evaluation is more pure by default (limited access to paths outside the flake). That is why git-tracked files matter (flakes chapter).
When classic examples “just import ./.”, flake equivalents may need self or explicit file inclusion.
| Classic habit | Flake analogue |
|---|---|
import ./default.nix |
outputs / packages |
Ambient <nixpkgs> |
inputs.nixpkgs |
| Untracked helper file | Must be added to git |
Theory 7 — Reading old error messages
| Old-doc symptom | Translation |
|---|---|
attribute 'foo' missing in -A |
Wrong attr path / channel content |
file 'nixpkgs' was not found |
NIX_PATH / channel not set |
nix-env permission errors |
multi-user profile ownership |
| Blog says enable flakes differently | Check current Nix 2.34 docs |
error: experimental Nix feature |
modern CLI without features |
Theory 8 — GC and store tools (both eras)
| Task | Classic | Modern |
|---|---|---|
| GC | nix-collect-garbage |
nix store gc |
| Delete gens aggressively | nix-collect-garbage -d |
same caution |
| Query refs | nix-store -q --references |
nix path-info |
| Roots | nix-store --gc --print-roots |
still common |
Do not GC aggressively on machines with important generations until the host generations chapter.
Theory 9 — Decision policy for this book
| Situation | Policy |
|---|---|
| New personal project | Flakes + modern CLI only |
| Reading a 2019 blog | Translate; do not adopt channels |
Coworker pastes nix-env |
Offer modern equivalent once |
| Debugging store graphs | Either store tool family is fine |
| NixOS host management | nixos-rebuild --flake (host part) |
Worked examples bank
Example A — Build hello both ways
# modern
nix build nixpkgs#hello --print-out-paths --no-link
# classic-ish
nix-build -E 'with import <nixpkgs> { }; hello' --no-out-link(If <nixpkgs> missing, skip classic path or install channel only for the drill.)
Example B — Shell both ways
nix shell nixpkgs#hello -c hello
nix-shell -p hello --run helloExample C — Query store both ways
P=$(nix build nixpkgs#hello --print-out-paths --no-link)
nix-store -q --references "$P"
nix path-info -r "$P" | headExample D — Profile vs run
# avoid if possible
# nix-env -iA nixpkgs.jq
# prefer
nix run nixpkgs#jq -- --versionExample E — Channel list observation
nix-channel --list || trueEmpty can be good on a flakes-first machine.
Lab 1 — Build the translation table
mkdir -p ~/lab/nixos-book/concepts/classic-literacy
cd ~/lab/nixos-book/concepts/classic-literacyCreate CLASSIC-MODERN.md with at least 10 rows you personally verified (or attempted):
| Task | Classic command | Modern command | Result notes |
|---|---|---|---|
| run hello | |||
| … |
Lab 2 — Dual path for a project shell
Take your flakes chapter project (or recreate):
Modern (already works):
cd ~/lab/nixos-book/concepts/flakes/myproject
nix develop -c git --versionClassic parallel: write shell.nix that mirrors the tool list using <nixpkgs> or a pinned fetch if you know how—only for the drill:
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
packages = [ pkgs.git pkgs.hello pkgs.jq ];
}nix-shell --run 'git --version'Journal: which is easier to share with a teammate on another machine?
Lab 3 — Channel inspection (optional, disposable)
Only if you are willing to touch channels on a lab account:
nix-channel --list
# do not --update production machines casuallyIf empty: good—note “no channels; flakes only.”
Lab 4 — Doc archaeology
Find one external tutorial that uses classic CLI (search your history or web). Rewrite its first three commands into modern equivalents in your notes without running random untrusted scripts as root.
Lab 5 — Commitment statement
Write three sentences:
- When I will still use classic tools
- When I will refuse to (prefer modern)
- How I will handle future blog posts that only show
nix-env
Exercises
- Expand your table to 15 rows including GC and store queries.
- Translate
nix-env -iA nixpkgs.hellointo three modern alternatives (run/shell/profile).
- Explain why
nix-shell -pis not a project SoT.
- Convert a classic
default.nixpackage build to a flakepackagesoutput (sketch).
- Document
NIX_PATHon your machine; plan to ignore it for new work.
- Pair-read an old tutorial with a highlighter: classic vs still-valid concepts.
- Practice
nix-store -q --treevs modern path-info on the same path.
- Write a “please use flakes” snippet for your team README.
- List risks of
nix-collect-garbage -din classic blogs.
- Map
nixos-rebuildclassic channel workflows to--flake(preview host part).
- Deliberately avoid installing channels even if a blog demands it—note the workaround.
- Quiz yourself: flashcard classic → modern for one week of reading.
Common pitfalls
| Symptom | Theory |
|---|---|
| Following classic blog on NixOS host | May fight flakes/module setup |
| Installing channels “to fix” flakes | Usually wrong direction |
nix-shell vs nix develop confusion |
Different project conventions |
| Profile pollution from drills | Remove packages / use disposable user |
| Assuming channels == 26.05 pin | Channel lag vs flake lock rev |
| Mixing both as SoT | Pick flakes for new work |
| Treating literacy chapter as daily driver | Revert to modern defaults |
Checkpoint
- Personal 10-row translation table
- Same task run modern + classic once
- Can explain channels vs flake locks
- Can explain why
nix-envis not the goal
- Default workflow remains modern
- Commitment statement written
Concepts part — exit criteria
Before the NixOS host part:
- Three faces of Nix clear
- Closures and GC roots clear
- Attrsets + functions + multi-file Nix workable
- Derivation idea + trivial build done
- Modern CLI fluent
- Project flake + lock + devShell done
- Classic docs are readable, not authoritative
Further depth (this book)
| Topic | Chapter / part |
|---|---|
| Host management CLI | Part NixOS host |
| Packaging classic attrs | Part Packaging |
| Deeper flake layout | Part Home and flake layout |
| Restart Concepts map | Concepts index |