Programming · free software · the open webSubscribe via RSS

A Nix derivation describes a build; it does not prove reproducibility

A derivation records a build step and its declared dependencies, but matching that recipe does not guarantee identical output bytes when the process can observe undeclared inputs or nondeterminism.

A recipe can be named before it runs

A build system needs to describe work before the work has produced its result. In Nix, a derivation is a stored specification for running a builder to produce one or more store objects. It has a name, an input specification, an output specification, a system type and the fields needed to start the process. The store path of the serialized derivation names that specification; it is not a report of the bytes that a future build will emit. [1]

This distinction matters for dependency graphs. A downstream build can refer to an upstream derivation’s output by naming the derivation and output name, even while the output has not yet been realized and its path is not known. That lets Nix represent a plan as a graph rather than requiring every dependency to be built before the next step can be described.

Declared inputs are the boundary of the plan

The derivation’s inputs are deriving paths: references to store objects needed for the step. The builder executable, arguments and environment may also mention store paths, but the derivation creator is responsible for listing the corresponding objects in the input specification. Nix does not infer every dependency merely by scanning those strings. [1]

This is a useful boundary for reasoning about a build. If a compiler is declared as an input and invoked by a declared path, another machine can obtain the same compiler object and execute the specified step. But the process may still observe facts outside the declared store graph: a clock, randomness, network responses, machine-specific state or an environment detail the recipe did not capture. A declaration makes the intended inputs legible; it does not by itself prevent every other observation.

The builder is a process, not a proof

The process fields describe an executable, its arguments and environment variables, much like the ingredients of an execve call. Nix starts that process and expects it to leave files that become the specified outputs after post-processing. The system field describes where the builder is meant to run, which also lets build plans account for different platforms. [1]

Imagine two machines receive the same derivation and all its declared store inputs. Each invokes a compiler that embeds the current time in a generated version file. The recipes and declared graph match, yet builds started at different times can produce different output bytes. A builder could similarly enumerate filesystem entries in an unstable order or consult an undeclared file. Repeating a build and getting different results is evidence of a hidden or nondeterministic influence; it does not mean the derivation stopped describing the intended process.

Recipe identity and output identity answer different questions

The .drv store path identifies the serialized build specification. Nix’s documentation distinguishes that specification from the outputs produced when it is realized; output paths are not always known in advance, particularly in content-addressed cases. These mechanisms should not be collapsed into the claim that every store hash is simply a hash of output contents. [1]

Reproducibility is a stronger claim about execution: when the relevant inputs and conditions are held fixed, independent realizations yield equivalent output bytes. A derivation helps state and share the build plan, and the store can retain immutable results. To establish reproducibility, however, the builder must behave deterministically with respect to the declared inputs, and the build environment must keep undeclared influences from changing the result. The recipe gives you a place to investigate that boundary; its existence alone does not certify it.

References

  1. Nix: derivations

Suggest a correction · All posts