Decoding Nix Store Paths: Why That Cryptic Hash Actually Makes Sense
markdown formatted blog content
You've seen them. Those intimidating directory names lurking in /nix/store that look like someone fed a package name through a cryptographic blender:
/nix/store/s0psayl7zvkvwdcqc8fy1sbv8rlf1yq8-bash-5.3p9
The human-readable part is obvious—bash-5.3p9 tells you exactly what's inside. But those 32 characters before the hyphen? That's where the mystery lives. And here's the thing: most explanations you'll encounter are incomplete.
The Common (and Wrong) Explanation
"The hash comes from the inputs" is what most people will tell you. They're not wrong, exactly—but they're missing crucial nuance. The real question is: which inputs? Are we hashing the source files you wrote? The build recipe that Nix generates? The bytes produced by the build? The dependencies?
The honest answer is: it depends.
Two Naming Schemes, One Directory
Nix actually uses two different naming mechanisms for store paths, and both live happily together under /nix/store. Conflating them is where the confusion starts.
Input-Addressed: Names Before Build
For most package builds, Nix works like an architect reviewing blueprints. When you write a .nix expression, Nix evaluates it into a derivation—a concrete specification of what to build, including:
- The builder script
- Build arguments
- Environment variables
- Declared outputs
- Dependencies
With this recipe in hand, Nix can compute a unique identifier before the build even runs. The finished binaries don't exist yet, but Nix already knows what to call them.
This is why editing a Nix expression doesn't always change the store path. If your changes don't affect the underlying build recipe, Nix produces the same identity. Two different expressions can evaluate to identical recipes, so they share the same store path. Nix names what it will build, not the spelling of your code.
Content-Addressed: Names from Bytes
Nix can also name store objects directly from their contents. When you run nix store add, you're handing Nix something that already exists, and it hashes the bytes immediately. No evaluation, no recipe—just content.
Here, the path is determined by what's actually in the file. Change a single byte, and the hash changes.
Why This Distinction Matters
Understanding these two naming schemes makes several Nix behaviors suddenly obvious:
Predicting path changes. If you're wondering "will editing X affect the store path?", ask yourself: does X change the build recipe or the actual content? Edit a comment in a Nix expression? Probably no new path. Change a dependency version? New path guaranteed.
Understanding coexisting builds. Why can an old build result sit beside a new one in the store? Because they have different identities. Nix doesn't overwrite—it simply doesn't need to.
Cache reuse makes sense. A binary cache can confidently serve a pre-built path because the name uniquely identifies the build inputs. Same name means same content, guaranteed.
More Than Just a Name
A store path is never really alone. Built packages reference other store paths (dynamic libraries, interpreters, shared assets), and Nix tracks these relationships as a graph.
This isn't just metadata—it's a complete dependency map. From any path, you can trace its closure: everything that must travel with it to function on another machine. This is how nix-copy-closure works, and why Nix deployments are reliable.
The same database lets Nix verify integrity. It can check whether the bytes on disk still match what it recorded when the path was created. Store paths aren't opaque directory names—they're queryable identities with a full audit trail.
The Takeaway
Those cryptic hashes aren't arbitrary. They're the result of a deliberate design choice: Nix names derivations by their inputs and contents by their bytes. This dual approach is what makes Nix reproducible, verifiable, and surprisingly predictable once you understand it.
So next time you see a store path, don't panic. Read from right to left—that's the human part. The hash on the left is just Nix's way of making a promise: this name means this build, and nothing else.
Ready to dive deeper? Whether you're deploying containers, managing infrastructure, or just curious about reproducible builds, understanding your system's internals always pays off. At NameOcean, we believe the best developers are the ones who understand what's actually happening under the hood—not just which commands to run.
Happy building.