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.

What you'll learn

Prerequisites

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 to compile securely.

If you try to install the package using standard tools:

pip install -r requirements.txt

You might encounter this real-world error:

ERROR: Could not build wheels for secp256k1 [...] fatal error: 'secp256k1.h' file not found

Why did this happen? You have the exact same requirements.txt and the same Python version as your teammate, but it failed on your ARM64 machine! The reason is that pip cannot manage system-level dependencies like C headers (secp256k1.h).

Application-level package managers (pip, npm, cargo) only manage their specific ecosystems. They assume the underlying operating system provides the necessary libraries and compilers.

The "Jenga Tower" Mental Model

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!