Day 1 — Why Nix exists
Day 1 — Why Nix exists
Stage I · ~4h (theory-heavy)
Goal: Separate the three meanings of “Nix,” understand the problem statement honestly, install the package manager with flakes + modern CLI, and evaluate first expressions in a REPL.
Today installs Nix language + package manager, not NixOS. The OS host arrives in Stage II.
Why this day exists
People fail Nix for social reasons as much as technical ones: they read a blog that mixes language, CLI, and NixOS modules, then paste commands from three eras of tooling. Theory first reduces that thrash.
Theory 1 — Three faces of the same ecosystem
| Layer | Question it answers | You touch it today? |
|---|---|---|
| Language | How do we describe packages and configs? | Yes — REPL |
| Package manager | How do we build/store/compose software? | Yes — install + nix run |
| NixOS | How do we describe an entire OS? | No — later |
Analogy (imperfect but useful)
| Idea | Rough analogy |
|---|---|
| Nix expression | A pure function recipe |
| Derivation | A build plan (inputs → output path) |
| Store path | The baked loaf with a serial number |
| Profile / shell | A lunch tray pointing at selected loaves |
| NixOS system | The whole kitchen layout as code |
Analogies break; the table is orientation, not law.
Theory 2 — Failure modes of traditional systems
Dependency hell
Classic package managers often assume one version of a library in a global prefix (/usr/lib/...). Applications A and B that need different versions conflict.
Example story
- App A needs
libssl.so.1.1
- App B needs
libssl.so.3
- Distro upgrade breaks A, or pinning freezes security updates for B
Snowflake servers
Imperative drift:
ssh prod
apt install foo
vim /etc/foo.conf
# six months later: nobody knows the full story
Language-level global installs
pip install --user something
npm install -g other
cargo install anotherEach ecosystem invents isolation. None composes cleanly with the others or with the OS.
What “reproducible” can mean (honesty ladder)
| Claim | Strength |
|---|---|
| “Same machine rebuilds the same store paths” | Often achievable with locks |
| “Same flake lock on two Linux machines → same paths” | Common with pure fixed-output inputs |
| “Bit-for-bit identical across OS/CPU forever” | Strong; not always true |
| “My data is reproducible” | Not Nix’s job unless you design state |
Nix optimizes for controlled, rebuildable software graphs. It does not delete ops.
Theory 3 — Nix’s core bet: pure builds + hashed store
Content-addressed intuition
If a build is a function of its inputs, then informally:
output path = f(build recipe, input paths, system, …)
In the common input-addressed world, the hash reflects the derivation inputs. (Content-addressed derivations are an advanced evolution—Stage VII.)
Coexistence
/nix/store/hAAAAA-libfoo-1.2.3
/nix/store/hBBBBB-libfoo-2.0.0
Both can exist. Package A references hAAAAA; package B references hBBBBB. No single /usr/lib/libfoo.so gladiator match.
Environments as compositions
A “shell” or “profile” is not a pile of files dumped into /usr. It is a set of store paths arranged on PATH, LIB, etc.
Example mental model
devShell = { go_1_26, git, hello }
→ PATH entries under those store paths
Theory 4 — Evaluation vs realization
| Phase | What happens | Failure examples |
|---|---|---|
| Evaluate | Nix language runs; produces derivation plans | Infinite recursion, missing attr, type errors |
| Realize | Build or download outputs into the store | Compile error, hash mismatch, network/cache miss |
Substituters (binary caches)
If a trusted cache already has the output path, realization becomes download, not compile. That is why first nix run nixpkgs#hello can be fast or slow depending on cache/network.
Example
eval: nixpkgs.hello → derivation → planned out path P
realize: P exists on cache.nixos.org? download : build
Theory 5 — Installer shapes and multi-user daemon
| Shape | Idea |
|---|---|
| Multi-user + nix-daemon | Builds run as daemon; shared store; recommended default |
| Single-user | Store owned by your user; simpler, less isolation |
| Determinate installer | UX wrapper; still Nix—record that you used it |
Why a daemon?
Sandbox builds need privileges to set up namespaces/mounts cleanly. Multi-user installs also protect the store from accidental mutation.
Config locations (theory)
| File | Role |
|---|---|
~/.config/nix/nix.conf |
User Nix settings |
/etc/nix/nix.conf |
System-wide (multi-user) |
Experimental features (nix-command, flakes) must be visible to the Nix that actually runs.
Theory 6 — Flakes as the modern pinning model (preview)
You enable flakes today; deep flake authoring is Day 8+.
| Concept | Meaning |
|---|---|
flake.nix |
Declarative inputs + outputs |
flake.lock |
Pinned revisions/hashes of inputs |
nixpkgs#hello |
Indirect flake reference to package hello |
Contrast with channels
| Channels | Flakes |
|---|---|
| Mutable “what is nixpkgs today?” | Lock file pins exactly |
NIX_PATH / <nixpkgs> |
Explicit inputs |
| Easy to drift | Designed for collaboration |
This volume: flakes default, channels as literacy only.
Theory 7 — CLI eras (so docs don’t gaslight you)
| Era | Examples | Status in this volume |
|---|---|---|
| Classic | nix-env, nix-build, nix-shell |
Literacy later (Day 9) |
| Modern | nix build, nix run, nix develop, nix flake |
Default |
Same underlying store; different UX and defaults.
Worked examples (conceptual)
Example A — “Install” without polluting a global profile
nix run nixpkgs#helloRuns hello from a realized store path without committing to a permanent user profile.
Example B — Why two machines diverge without locks
Machine A: nixpkgs as of Monday
Machine B: nixpkgs as of Friday
→ different hashes for "hello", different closures
Flake locks exist to end that story.
Example C — What Nix does not do
Declarative Postgres service ≠ automatic migration of your prod data
Encrypted secrets in git ≠ “Nix encrypts the store”
Lab 1 — Install Nix
Use current docs: https://nixos.org/download/
Or Determinate installer if you choose—write which.
# new shell after install
nix --versionDisk: leave tens of GB free for later stages.
Lab 2 — Enable modern features
~/.config/nix/nix.conf:
experimental-features = nix-command flakesVerify:
nix config show | grep -i experimental
nix flake --help | headLab 3 — First modern commands
nix search nixpkgs hello | head
nix run nixpkgs#helloREPL
nix repl1 + 1
{ a = 1; b = "x"; }.a
builtins.typeOf { x = 1; }Load nixpkgs when available (:lf nixpkgs or version-appropriate equivalent) and inspect pkgs.hello.
Lab 4 — Write the model
In your journal, answer without looking up:
- Language vs PM vs OS—one sentence each
- Why two
libfooversions can coexist
- Eval failure vs build failure—one example each
- Which installer you used
Common gotchas
| Symptom | Theory link |
|---|---|
nix not found |
Install/PATH; shell not reloaded |
| experimental feature errors | nix.conf not read by that Nix |
| Slow first command | Cold substituter / eval of nixpkgs |
| Permission errors | Single vs multi-user mismatch |
Old blog uses only nix-env -i |
Classic era; map later |
Checkpoint
- Can explain three faces of Nix
- Can state two problems Nix targets and two it does not
nix run nixpkgs#helloworks
- Flakes + nix-command enabled
- REPL evaluated an attrset
- Installer name recorded
Tomorrow
Day 2 — Store & closures: path anatomy, reference graphs, GC roots, and why deleting a project folder does not free the store.