This commit is contained in:
Hassan Abedi 2026-04-15 15:47:10 +02:00
parent d2814acdc9
commit 66d368555d
3 changed files with 75 additions and 75 deletions

View File

@ -4,47 +4,47 @@ Core vocabulary you'll hit in the first hour of Nix. Skim, don't memorize; come
## The Store and Builds ## The Store and Builds
- **Nix store**: `/nix/store`. Immutable, content-addressed directory where *everything* Nix builds lives. Each entry is prefixed by a hash of its inputs. - Nix store: `/nix/store`. Immutable, content-addressed directory where *everything* Nix builds lives. Each entry is prefixed by a hash of its inputs.
- **Store path**: a single entry under `/nix/store`, e.g. `/nix/store/abc123…-hello-2.12.1`. The hash makes it unique per set of build inputs. - Store path: a single entry under `/nix/store`, e.g. `/nix/store/abc123…-hello-2.12.1`. The hash makes it unique per set of build inputs.
- **Derivation (`.drv`)**: a *recipe* for a build, covering inputs, builder script, env vars, and outputs. Produced by evaluating Nix code. Not the built artifact itself. - Derivation (`.drv`): a *recipe* for a build, covering inputs, builder script, env vars, and outputs. Produced by evaluating Nix code. Not the built artifact itself.
- **Realisation / "realising a derivation"**: actually executing the `.drv` to produce the output store path(s). "Build" colloquially means this. - Realisation, or "realising a derivation": actually executing the `.drv` to produce the output store path(s). "Build" colloquially means this.
- **Output path**: the store path a derivation produces. A derivation can have multiple outputs (e.g. `out`, `dev`, `man`, `lib`). - Output path: the store path a derivation produces. A derivation can have multiple outputs (e.g. `out`, `dev`, `man`, `lib`).
- **Closure**: a store path plus all its runtime dependencies, transitively. What you need to copy to another machine to actually use something. - Closure: a store path plus all its runtime dependencies, transitively. What you need to copy to another machine to actually use something.
- **Hash**: content/input hash that names store paths. Changing any input changes the hash, which changes the path; this is how Nix guarantees no cache collisions. - Hash: content/input hash that names store paths. Changing any input changes the hash, which changes the path; this is how Nix guarantees no cache collisions.
- **Fixed-output derivation (FOD)**: a derivation whose output hash you declare up front (e.g. source tarballs, `fetchurl`). Allowed network access during build; everything else is sandboxed offline. - Fixed-output derivation (FOD): a derivation whose output hash you declare up front (e.g. source tarballs, `fetchurl`). Allowed network access during build; everything else is sandboxed offline.
## Language ## Language
- **Nix (the language)**: lazy, purely functional, dynamically typed. Every `.nix` file is one expression. - Nix (the language): lazy, purely functional, dynamically typed. Every `.nix` file is one expression.
- **Attrset**: `{ a = 1; b = "x"; }`. The main data structure. - Attrset: `{ a = 1; b = "x"; }`. The main data structure.
- **Lambda**: `x: x + 1` or `{ a, b }: a + b`. Functions take one argument; multi-arg functions are attrset-destructured. - Lambda: `x: x + 1` or `{ a, b }: a + b`. Functions take one argument; multi-arg functions are attrset-destructured.
- **`let ... in`**: local bindings, for example `let x = 1; in x + 1`. - `let ... in`: local bindings, for example `let x = 1; in x + 1`.
- **`with expr; body`**: brings `expr`'s attrs into scope for `body`. Common in `with pkgs; [ hello jq ]`. Useful but can shadow bindings, so use sparingly. - `with expr; body`: brings `expr`'s attrs into scope for `body`. Common in `with pkgs; [ hello jq ]`. Useful but can shadow bindings, so use sparingly.
- **`inherit`**: shorthand to pull names from outer scope into an attrset: `{ inherit pkgs system; }``{ pkgs = pkgs; system = system; }`. - `inherit`: shorthand to pull names from outer scope into an attrset: `{ inherit pkgs system; }``{ pkgs = pkgs; system = system; }`.
- **`import`**: evaluate another `.nix` file. `import ./foo.nix { }` imports and calls it. - `import`: evaluate another `.nix` file. `import ./foo.nix { }` imports and calls it.
- **`callPackage`**: a nixpkgs convention that auto-supplies function arguments from a package set. You'll see it everywhere in nixpkgs. - `callPackage`: a nixpkgs convention that auto-supplies function arguments from a package set. You'll see it everywhere in nixpkgs.
## Package Management ## Package Management
- **nixpkgs**: the community-maintained collection of Nix expressions for ~100k packages. Lives at `github:NixOS/nixpkgs`. - nixpkgs: the community-maintained collection of Nix expressions for ~100k packages. Lives at `github:NixOS/nixpkgs`.
- **Channel**: a named, periodically-updated snapshot of nixpkgs (e.g. `nixos-unstable`, `nixos-25.11`). Pre-flakes way to pin. Flakes mostly replace this with lockfiles. - Channel: a named, periodically-updated snapshot of nixpkgs (e.g. `nixos-unstable`, `nixos-25.11`). Pre-flakes way to pin. Flakes mostly replace this with lockfiles.
- **Overlay**: a function that extends or overrides a package set. Lets you patch, pin, or add packages without forking nixpkgs. - Overlay: a function that extends or overrides a package set. Lets you patch, pin, or add packages without forking nixpkgs.
- **Profile**: a symlink tree representing "what's installed" for a user or system. `nix-env`, `nix profile`, NixOS, and home-manager all manage profiles. - Profile: a symlink tree representing "what's installed" for a user or system. `nix-env`, `nix profile`, NixOS, and home-manager all manage profiles.
- **Generation**: a versioned snapshot of a profile. Every change creates a new generation; old ones stay until garbage-collected. This is how rollback works. - Generation: a versioned snapshot of a profile. Every change creates a new generation; old ones stay until garbage-collected. This is how rollback works.
- **Garbage collection (`nix-collect-garbage`)**: deletes store paths not reachable from any "GC root" (profiles, running processes, and `result` symlinks). - Garbage collection (`nix-collect-garbage`): deletes store paths not reachable from any "GC root" (profiles, running processes, and `result` symlinks).
## Flakes ## Flakes
- **Flake**: a directory with a `flake.nix`. Has pinned `inputs` and structured `outputs`. Reproducible, composable, and schema-driven. - Flake: a directory with a `flake.nix`. Has pinned `inputs` and structured `outputs`. Reproducible, composable, and schema-driven.
- **`flake.lock`**: JSON file pinning the exact revision (and hash) of each input. Commit it. - `flake.lock`: JSON file pinning the exact revision (and hash) of each input. Commit it.
- **Flake reference**: URL-like string identifying a flake: `github:NixOS/nixpkgs`, `path:./foo`, `git+https://…`, or `nixpkgs` (registry alias). - Flake reference: URL-like string identifying a flake: `github:NixOS/nixpkgs`, `path:./foo`, `git+https://…`, or `nixpkgs` (registry alias).
- **Registry**: short-name to flake-ref mapping. `nixpkgs` resolves via the global registry by default. - Registry: short-name to flake-ref mapping. `nixpkgs` resolves via the global registry by default.
- **Pure evaluation**: flakes evaluate in a sandbox with no env vars, no arbitrary filesystem reads, and only declared inputs. This is what makes them reproducible. - Pure evaluation: flakes evaluate in a sandbox with no env vars, no arbitrary filesystem reads, and only declared inputs. This is what makes them reproducible.
## NixOS and home-manager ## NixOS and home-manager
- **NixOS module**: a function `{ config, lib, pkgs, ... }: { options = …; config = …; }`. The unit of NixOS configuration. - NixOS module: a function `{ config, lib, pkgs, ... }: { options = …; config = …; }`. The unit of NixOS configuration.
- **home-manager**: user-level declarative config for dotfiles, per-user packages, and services. Works standalone or as a NixOS module. - home-manager: user-level declarative config for dotfiles, per-user packages, and services. Works standalone or as a NixOS module.
## CLI: Old vs. New ## CLI: Old vs. New

View File

@ -1,9 +1,9 @@
# Nix Primer # Nix Primer
Nix is **two things bundled**: Nix is two things bundled:
1. **A pure, lazy functional language** for describing builds. 1. A pure, lazy functional language for describing builds.
2. **A package manager** that takes those descriptions and produces content-addressed store paths. 2. A package manager that takes those descriptions and produces content-addressed store paths.
Confusingly, both are called "Nix." Separate them in your head and everything gets easier. Confusingly, both are called "Nix." Separate them in your head and everything gets easier.
@ -20,8 +20,8 @@ Every `nix build` or `nix develop` runs in two phases:
(pure, lazy) (on disk) (content-addressed) (pure, lazy) (on disk) (content-addressed)
``` ```
- **Evaluation** runs the Nix language. Pure, in-memory, fast. Produces `.drv` files. - Evaluation runs the Nix language. Pure, in-memory, fast. Produces `.drv` files.
- **Realisation** actually builds things. Sandboxed. Slow. Outputs go to `/nix/store`. - Realisation actually builds things. Sandboxed. Slow. Outputs go to `/nix/store`.
You can evaluate without building (`nix eval`, `nix-instantiate`) and you can build without touching the language if you already have a `.drv`. Most errors you'll see are *eval* errors: typos in attrsets, missing arguments, and undefined names. You can evaluate without building (`nix eval`, `nix-instantiate`) and you can build without touching the language if you already have a `.drv`. Most errors you'll see are *eval* errors: typos in attrsets, missing arguments, and undefined names.
@ -107,7 +107,7 @@ import ./foo.nix { x = 1; } # if foo.nix is a function, also call it
## 3. Derivations: The Core Primitive ## 3. Derivations: The Core Primitive
A **derivation** is "a build, described as data." You create one with `derivation { … }` (low-level) or `pkgs.stdenv.mkDerivation { … }` (the nixpkgs wrapper you'll actually use). A derivation is "a build, described as data." You create one with `derivation { … }` (low-level) or `pkgs.stdenv.mkDerivation { … }` (the nixpkgs wrapper you'll actually use).
Minimal example: Minimal example:
@ -129,10 +129,10 @@ pkgs.stdenv.mkDerivation {
Key ideas: Key ideas:
- **`$out`** is the output store path the build writes into. - `$out` is the output store path the build writes into.
- **Phases** (`unpackPhase`, `buildPhase`, `installPhase`, …) are shell snippets `stdenv` runs in order. Override the ones you need. - Phases (`unpackPhase`, `buildPhase`, `installPhase`, …) are shell snippets `stdenv` runs in order. Override the ones you need.
- **No network** in the build sandbox, except for fixed-output derivations (whose hash you declare up front). - No network in the build sandbox, except for fixed-output derivations (whose hash you declare up front).
- **Every input** (source, compiler, env vars) becomes part of the hash. Change anything and you get a fresh store path. - Every input (source, compiler, env vars) becomes part of the hash. Change anything and you get a fresh store path.
--- ---
@ -140,13 +140,13 @@ Key ideas:
Traditional package managers put files in `/usr/bin`, `/usr/lib`, and similar shared locations. One version at a time. Upgrades mutate shared state. Traditional package managers put files in `/usr/bin`, `/usr/lib`, and similar shared locations. One version at a time. Upgrades mutate shared state.
Nix puts every build in `/nix/store/<hash>-<name>/` with the hash derived from **all its inputs** (recursively). That gives you: Nix puts every build in `/nix/store/<hash>-<name>/` with the hash derived from all its inputs (recursively). That gives you:
- **Atomic upgrades and rollbacks**: switching "versions" is just switching symlinks. - Atomic upgrades and rollbacks: switching "versions" is just switching symlinks.
- **Multiple versions coexisting**: they live at different hashes. - Multiple versions coexisting: they live at different hashes.
- **Reproducibility**: same inputs produce the same hash and the same output (in theory; in practice, 99% there). - Reproducibility: same inputs produce the same hash and the same output (in theory; in practice, 99% there).
- **Binary caching**: if someone else already built exactly these inputs, you can download their output. This is what `cache.nixos.org` does. - Binary caching: if someone else already built exactly these inputs, you can download their output. This is what `cache.nixos.org` does.
- **Garbage-collectable**: anything not referenced by a "GC root" can be deleted safely. - Garbage-collectable: anything not referenced by a "GC root" can be deleted safely.
--- ---
@ -154,12 +154,12 @@ Nix puts every build in `/nix/store/<hash>-<name>/` with the hash derived from *
Ordered roughly by power-to-complexity: Ordered roughly by power-to-complexity:
1. **`nix run nixpkgs#cowsay -- moo`**: run a package without installing. 1. `nix run nixpkgs#cowsay -- moo`: run a package without installing.
2. **`nix shell nixpkgs#jq nixpkgs#ripgrep`**: throwaway shell with those tools. 2. `nix shell nixpkgs#jq nixpkgs#ripgrep`: throwaway shell with those tools.
3. **Dev shell via `flake.nix`**: per-project pinned toolchain. *This is the most common thing flakes are used for.* 3. Dev shell via `flake.nix`: per-project pinned toolchain. *This is the most common thing flakes are used for.*
4. **`nix build`**: produce artifacts (binaries, containers, and ISOs). 4. `nix build`: produce artifacts (binaries, containers, and ISOs).
5. **NixOS**: declare your whole OS config (packages, services, users, and network) in Nix. 5. NixOS: declare your whole OS config (packages, services, users, and network) in Nix.
6. **home-manager**: same idea, at the user level. 6. home-manager: same idea, at the user level.
Each step up locks in more of your environment. You don't need to adopt all of it at once. Each step up locks in more of your environment. You don't need to adopt all of it at once.
@ -189,12 +189,12 @@ nix why-depends ./result nixpkgs#glibc # trace dependency chains
## 7. Gotchas That Trip Up Newcomers ## 7. Gotchas That Trip Up Newcomers
- **Lists use spaces, not commas.** `[ 1 2 3 ]`. Commas are a syntax error. - Lists use spaces, not commas. `[ 1 2 3 ]`. Commas are a syntax error.
- **`rec { }`** makes an attrset self-referential. Without `rec`, attrs can't reference each other. You'll see this in package definitions. - `rec { }` makes an attrset self-referential. Without `rec`, attrs can't reference each other. You'll see this in package definitions.
- **Paths vs. strings.** `./foo` is a path (copied to store on use). `"./foo"` is a string (not auto-copied). Mixing them causes surprises. - Paths vs. strings. `./foo` is a path (copied to store on use). `"./foo"` is a string (not auto-copied). Mixing them causes surprises.
- **`builtins.*`** is the always-available low-level namespace. `lib.*` (from nixpkgs) is the high-level helper library. Both exist; they're different. - `builtins.*` is the always-available low-level namespace. `lib.*` (from nixpkgs) is the high-level helper library. Both exist; they're different.
- **Evaluation errors are lazy.** An error deep in an unused branch only fires when you actually use it. `nix-instantiate --eval --strict` forces full evaluation. - Evaluation errors are lazy. An error deep in an unused branch only fires when you actually use it. `nix-instantiate --eval --strict` forces full evaluation.
- **Hash errors on first build of an FOD**: you'll see `got: sha256-…`. Copy that hash into your expression. This is expected workflow. - Hash errors on first build of an FOD: you'll see `got: sha256-…`. Copy that hash into your expression. This is expected workflow.
--- ---

View File

@ -1,11 +1,11 @@
# Flakes # Flakes
A **flake** is a directory containing `flake.nix` (and, once evaluated, `flake.lock`). It's a packaging convention on top of regular Nix that gives you: A flake is a directory containing `flake.nix` (and, once evaluated, `flake.lock`). It's a packaging convention on top of regular Nix that gives you:
- **Pinned inputs** via `flake.lock` for reproducible builds. - Pinned inputs via `flake.lock` for reproducible builds.
- **A fixed output schema** so tools know where to look (`devShells.<system>.default`, `packages.<system>.default`, and so on). - A fixed output schema so tools know where to look (`devShells.<system>.default`, `packages.<system>.default`, and so on).
- **Composability**, since flakes depend on other flakes by URL. - Composability, since flakes depend on other flakes by URL.
- **Pure evaluation**, with no silent dependence on `NIX_PATH`, env vars, or random files. - Pure evaluation, with no silent dependence on `NIX_PATH`, env vars, or random files.
Flakes are still marked "experimental" but are now the way most new Nix code is written. Flakes are still marked "experimental" but are now the way most new Nix code is written.
@ -35,7 +35,7 @@ Two top-level keys matter: `inputs` and `outputs`. `description` is cosmetic.
### Inputs ### Inputs
Each input has a **flake reference** URL. Common forms: Each input has a flake reference URL. Common forms:
``` ```
github:owner/repo # latest default branch github:owner/repo # latest default branch
@ -81,10 +81,10 @@ Default attrs `default` are picked when you omit the name: `nix build` ≡ `nix
`flake.lock` pins the exact commit and NAR hash of every input, transitively. Generated or updated automatically on first use or via `nix flake update`. `flake.lock` pins the exact commit and NAR hash of every input, transitively. Generated or updated automatically on first use or via `nix flake update`.
- **Commit it.** Without it, "my flake" means different things on different machines. - Commit it. Without it, "my flake" means different things on different machines.
- **Update explicitly.** `nix flake update` (all inputs) or `nix flake update nixpkgs` (one input). - Update explicitly. `nix flake update` (all inputs) or `nix flake update nixpkgs` (one input).
- **Inspect.** `nix flake metadata` prints the resolved URLs and revisions. - Inspect. `nix flake metadata` prints the resolved URLs and revisions.
- **`follows`** deduplicates: if both `flake-utils` and `foo` want their own `nixpkgs`, `follows = "nixpkgs";` forces them to use yours. Without this, you can end up with multiple nixpkgs copies and subtle version skew. - `follows` deduplicates: if both `flake-utils` and `foo` want their own `nixpkgs`, `follows = "nixpkgs";` forces them to use yours. Without this, you can end up with multiple nixpkgs copies and subtle version skew.
--- ---
@ -149,9 +149,9 @@ Flakes evaluate in pure mode:
- No reading `NIX_PATH` or `<nixpkgs>`. - No reading `NIX_PATH` or `<nixpkgs>`.
- No arbitrary `builtins.getEnv` or reading files outside the flake. - No arbitrary `builtins.getEnv` or reading files outside the flake.
- `builtins.currentTime` and `builtins.currentSystem` are blocked. - `builtins.currentTime` and `builtins.currentSystem` are blocked.
- Source is pulled from the flake's input tree, not the working directory, unless you're in `self`. Files not tracked by git are **invisible** to the flake by default. - Source is pulled from the flake's input tree, not the working directory, unless you're in `self`. Files not tracked by git are invisible to the flake by default.
This last point surprises people: **`git add` a file before `nix build` can see it.** (`nix build --impure` escapes this, but then you're not reproducible.) This last point surprises people: `git add` a file before `nix build` can see it. (`nix build --impure` escapes this, but then you're not reproducible.)
In exchange: anyone with your `flake.nix` plus `flake.lock` gets bit-identical evaluation. In exchange: anyone with your `flake.nix` plus `flake.lock` gets bit-identical evaluation.
@ -223,11 +223,11 @@ overlays.default = final: prev: {
## 8. Debugging Tips ## 8. Debugging Tips
- **`nix flake show --all-systems`** to see outputs for every platform, not just yours. - `nix flake show --all-systems` to see outputs for every platform, not just yours.
- **`nix eval .#packages.x86_64-linux.default.drvPath`** to get the `.drv` without building. - `nix eval .#packages.x86_64-linux.default.drvPath` to get the `.drv` without building.
- **`nix eval --json .#devShells.x86_64-linux.default.buildInputs | jq`** to inspect shell deps. - `nix eval --json .#devShells.x86_64-linux.default.buildInputs | jq` to inspect shell deps.
- **`--show-trace`** on any command for full eval backtraces. - `--show-trace` on any command for full eval backtraces.
- **`nix repl .`** loads your flake's outputs into a REPL: useful for poking at attrs. - `nix repl .` loads your flake's outputs into a REPL: useful for poking at attrs.
- If an input seems stuck on an old rev, check `flake.lock`; `nix flake update <name>` fixes it. - If an input seems stuck on an old rev, check `flake.lock`; `nix flake update <name>` fixes it.
--- ---
@ -238,4 +238,4 @@ overlays.default = final: prev: {
- A tiny personal script: plain `default.nix` plus `nix-build` is still fine. - A tiny personal script: plain `default.nix` plus `nix-build` is still fine.
- Large monorepos with complex CI: consider `flake-parts` or hybrid setups (flake for the interface, regular Nix for guts). - Large monorepos with complex CI: consider `flake-parts` or hybrid setups (flake for the interface, regular Nix for guts).
Flakes are a **packaging interface**, not a requirement. The old non-flake world still works; flakes just compose better once you have more than one Nix project. Flakes are a packaging interface, not a requirement. The old non-flake world still works; flakes just compose better once you have more than one Nix project.