Day 1 — Why Nix exists

Updated

July 30, 2026

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.

Note

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

Three layers: Nix language, Nix package manager, and NixOS

Language, package manager, and NixOS
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 another

Each 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

Traditional single library slot versus multiple hashed store paths

Traditional FHS vs Nix store coexistence

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

Nix expression evaluation to plan then realization to store paths

Evaluate then realize
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#hello

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

Disk: leave tens of GB free for later stages.


Lab 2 — Enable modern features

~/.config/nix/nix.conf:

experimental-features = nix-command flakes

Verify:

nix config show | grep -i experimental
nix flake --help | head

Lab 3 — First modern commands

nix search nixpkgs hello | head
nix run nixpkgs#hello

REPL

nix repl
1 + 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:

  1. Language vs PM vs OS—one sentence each
  2. Why two libfoo versions can coexist
  3. Eval failure vs build failure—one example each
  4. 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#hello works
  • 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.