Day 9 — Classic CLI literacy
Day 9 — Classic CLI literacy
Stage I · ~4h (literacy + dual drills)
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 day is literacy, not a lifestyle change. After today, default back to modern nix + flakes.
Why this day exists
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.
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 volume.
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-instantiate |
nix eval / derivation show |
Different shapes |
nix-store -q --requisites |
nix path-info -r |
Both fine |
nix-channel --update |
nix flake update (project) |
Different model |
nix-collect-garbage |
nix store gc / nix-collect-garbage |
Still common |
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 volume: channels = read-only literacy. Do not build Stage I projects on channels.
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.
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.
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 (Day 8).
When classic examples “just import ./.”, flake equivalents may need self or explicit file inclusion.
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 |
Worked examples bank
Example A — Build hello both ways
# modern
nix build nixpkgs#hello --print-out-paths
# 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" | headLab 1 — Build the translation table
mkdir -p ~/lab/90daysofx/02-nixos/day09
cd ~/lab/90daysofx/02-nixos/day09Create 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 day08 shell
Take your day08 project:
Modern (already works):
cd ~/lab/90daysofx/02-nixos/day08/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
Common gotchas
| 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 |
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
Commit
cd ~/lab/90daysofx/02-nixos/day09
git init 2>/dev/null || true
git add .
git commit -m "day09: classic to modern cli literacy table"Tomorrow
Day 10 — Stage I gate. Prove foundations: pinned flake shell, store/lock explanations, no channel dependency for the project.