← Back to Course Index
Lesson 4: Understanding Derivations
Peek under the hood—how Nix builds packages
Learning Objectives
- Understand what a derivation is and why it matters
- Learn how Nix builds packages using
stdenv.mkDerivation
- Understand the Nix store and content hashing
- Read and interpret basic derivations
What is a Derivation?
A derivation is Nix's way of describing how to build something. It's a recipe that specifies:
- Inputs: What you need (source code, compiler, libraries)
- Build steps: How to transform inputs into outputs
- Outputs: What the result is (usually a compiled binary or package)
The key insight: A derivation is an expression that evaluates to a description of how to build something—not the built thing itself. Nix evaluates the derivation to get a recipe, then executes that recipe to get the actual artifact.
The Derivation Flow
Nix Expression (derivation)
↓
[Nix evaluates it]
↓
Build Recipe + Content Hash
↓
[Nix builds]
↓
Package in /nix/store/{hash}-{name}
A Simple Derivation: Hello World
Here's a minimal derivation that creates a simple script:
# The simplest possible derivation
let
pkgs = import <nixpkgs> {};
in
pkgs.stdenv.mkDerivation {
name = "my-hello";
unpackPhase = "true"; # Skip unpacking
buildPhase = ''
cat > hello << 'EOF'
#!/bin/bash
echo "Hello from Nix!"
EOF
'';
installPhase = ''
mkdir -p $out/bin
cp hello $out/bin/hello
chmod +x $out/bin/hello
'';
}
$out: A special variable that points to where your package's output should go in the Nix store. Everything you want to keep must go here.
Understanding mkDerivation
stdenv.mkDerivation is a function that takes an attribute set describing your build. Here are the key attributes:
Basic Attributes
{
name = "hello"; # Package name
version = "1.0"; # Version (optional but good practice)
src = ./src; # Source code location
buildInputs = [ pkgs.gcc ]; # Tools needed to build
propagatedBuildInputs = []; # Dependencies for packages that use this
}
Build Phases
Derivations follow a standard build process with phases you can customize:
unpackPhase # Extract source (from src)
patchPhase # Apply patches if needed
configurePhase # Run ./configure
buildPhase # Run make
installPhase # Install to $out
fixupPhase # Final cleanup
The Nix Store
Everything Nix builds goes into /nix/store, organized by content hash:
/nix/store/
├── 1a2b3c4d5e6f-hello-1.0/
│ ├── bin/hello
│ └── share/doc/hello.md
├── 9x8y7z6w5v4u-gcc-11.2.0/
│ ├── bin/gcc
│ └── ...
└── ...
Content hashing: The hash is derived from the derivation's inputs and build process. Same inputs = same hash. Different inputs = different hash. This enables deduplication and reproducibility.
Derivations Depend on Derivations
Building is really a graph of dependencies:
my-app derivation
├─ depends on gcc derivation
├─ depends on python derivation
└─ depends on source code
When you build my-app, Nix automatically:
- Builds gcc (if not already built)
- Builds python (if not already built)
- Uses those outputs as inputs to build your app
A More Realistic Example
Here's a derivation for a simple Python script:
let
pkgs = import <nixpkgs> {};
in
pkgs.python3.pkgs.buildPythonApplication {
pname = "my-tool";
version = "1.0";
src = ./src;
propagatedBuildInputs = with pkgs.python3.pkgs; [
requests
click
];
meta = {
description = "My Python tool";
license = pkgs.lib.licenses.mit;
};
}
Important Concepts
Hermetic Builds
Derivations are hermetic: they can only access explicitly-declared inputs. They can't reach out to the internet or access system libraries unless you tell them to. This isolation ensures reproducibility.
Build Inputs vs Runtime Dependencies
buildInputs = [ pkgs.gcc ]; # Needed at build time
propagatedBuildInputs = [ pkgs.openssl ]; # Needed at runtime
Try It Yourself
Explore an Existing Derivation
Search nixpkgs for a package and see its derivation:
nix eval 'import <nixpkgs> {}.hello.drvPath'
This shows the path to the derivation file
Show what a derivation looks like:
nix show-derivation 'import <nixpkgs> {}.hello'
You'll see the build recipe as JSON
Build a simple package:
nix build 'import <nixpkgs> {}.cowsay' -o result
This builds cowsay and creates a symlink called result in your current directory
Run what you built:
./result/bin/cowsay "Hello from Nix!"
Key Takeaways
- A derivation is a recipe for building something, not the built thing itself
- Everything goes in /nix/store, organized by content hash
- Builds are hermetic—only declared inputs are accessible
- Derivations form a graph—dependencies are automatically handled
- mkDerivation is the standard way to define build recipes