Welcome to the nixB Codelab! In this interactive lab, we will explore the foundational problems of software reproducibility. We won't start by learning a tool; instead, we will start by experiencing the reason tools like Nix were built in the first place.
flake.nix to guarantee a reproducible environment.Let's dive in!
The Hook: Let's experience a classic failure. Imagine you're working on a Python project that requires a specific C library (libsecp256k1) to compile securely.
If you have Nix installed, we can simulate this environment failure without polluting your host system or needing to pre-install Python/pip:
Run this command in your terminal:
nix shell nixpkgs#uv --command uv run --no-binary --with secp256k1 python -c "import secp256k1"
Nix will dynamically fetch the uv toolchain, which automatically sets up a clean, isolated Python environment. The --no-binary flag forces uv to compile secp256k1 from source instead of downloading pre-compiled binaries. You will immediately encounter a build failure:
× Failed to build `secp256k1`
├─▶ The build backend returned an error
...
'pkg-config' is required to install this package (or 'secp256k1.h' file not found)
Why did this happen? Nix successfully provided a clean Python interpreter and uv dynamically, but because we did not declare the C library dependencies (pkg-config, secp256k1 development headers) in our environment shell, uv has no access to the system-level components needed to compile the C-extension. This illustrates that language-level package managers cannot resolve system-level dependencies.
Application-level package managers (like pip, uv, poetry, npm, or cargo) only manage their specific language ecosystems. They assume the underlying operating system provides the necessary libraries and compilers.
When we use sudo apt install or brew install, we mutate the global state of our operating system. Every command changes the machine in ways that aren't tracked or easily reversible. Over time, machines diverge, creating configuration drift.
(See our diagram:
reproducibility_gap_v1.0.mmd
)
This gap between "code works" and "system works" has real-world consequences, from security vulnerabilities to the deprecation of Cloud IDEs.
Technology | Reproducibility Level | System Isolation |
Virtualenv | Partial (Python only) | None |
Docker | High (Image state) | High (Container) |
Nix Flakes | Complete (Blueprint) | Perfect |
Nix is a "pure function" package manager. It treats packages like functions: given the exact same inputs (source code, compiler version, dependencies), it always produces the exact same output.
Key principles:
A flake.nix file is the blueprint of our environment. It explicitly declares inputs, outputs, and devShells. The associated flake.lock file pins the exact git hashes of every dependency, guaranteeing reproducibility.
Let's return to our failure from Step 2. We can fix it by wrapping the environment in a flake.nix that declares the system-level C library dependency.
buildInputs = [
pkgs.secp256k1
];
By running nix develop, Nix ensures secp256k1 is available in our path before the shell starts. Our build now succeeds. Same code, different approach!
Because Nix can manage everything from a C compiler to an entire operating system, it is the ultimate Infrastructure as Code (IaC) tool. Compare this to tools like Ansible, which rely on the unstable state of the target machine.
Using our flake.nix approach, we can orchestrate complex software like Bitcoin Core and Core Lightning. With a single command, you can spin up a reproducible, self-contained Lightning network node in regtest mode.
Reproducible builds ensure that the binary you are running exactly matches the source code. This eliminates an entire class of supply chain attacks, which is critical for financial software like Bitcoin.
Combining Git and Nix Flakes gives us a complete "time-machine". You can check out a commit from 3 years ago, run nix develop, and get the exact environment from that day. We also manage this project using ScrumMD!
Take a moment to reflect on the difference between imperative dependency management and declarative blueprints. Please prepare your portfolio submission detailing your findings.
You've completed the nixB Codelab! You've learned why reproducibility matters, how Nix solves the problem, and how to apply it. Next up: Explore NixOS or Home Manager!