store paths
how store paths are computed and what they encode.
how store paths are computed and what they encode.
every path in /nix/store has the same structure. take curl:
three parts. the hash is the one that does the work.
you might expect the 32-character hash to be a hash of the built binary: check the output, derive the name. it is not. you do not have the output yet when you compute it. you have not built anything.
the hash is computed from the inputs. specifically, from the derivation.
a derivation is a build description. it lists everything nix needs to produce the output. nix derivation show prints it:
$ nix derivation show nixpkgs#curl
{
"/nix/store/2glid80bsdhnxxp08z2xq7kwypvdnbk6-curl-8.18.0.drv": {
"outputs": {
"out": { "path": "/nix/store/xhp149abalfmj232yjhgbqaw281ba8np-curl-8.18.0" },
"bin": { "path": "/nix/store/6jw31n10mjbjzn7ki2vyf6fs1cxif2qh-curl-8.18.0-bin" },
"dev": { "path": "/nix/store/z5whd1pqpv87x503w2hp8cgpgyp1k69z-curl-8.18.0-dev" },
"man": { "path": "/nix/store/rbyqrgnyg5wf4zhc20783mjgr0pdis35-curl-8.18.0-man" }
},
"inputSrcs": [
"/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"
],
"inputDrvs": {
"/nix/store/2grbn00snzph0l4znd2rl5c8a3pwmij2-openssl-3.6.1.drv": { "outputs": ["dev"] },
"/nix/store/c37654gji4m71dfyrmipfsx2lk15d961-zlib-1.3.1.drv": { "outputs": ["dev"] },
"/nix/store/hxrxlk9mavifyxb3bwk8sl5lwss0r8cp-bash-5.3p3.drv": { "outputs": ["out"] },
"/nix/store/yiz6d20f79vrdd5gvcapxjqds3zhpngx-curl-8.18.0.tar.xz.drv": { "outputs": ["out"] }
},
"system": "x86_64-linux",
"builder": "/nix/store/fwr62xmh06l8y8zfgc5m18pfap9b8az0-bash-5.3p3/bin/bash",
"args": ["-e", "/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"],
"env": {
"name": "curl-8.18.0",
"src": "/nix/store/79a3ki5zivmm28yxszqnfwcq6kwhr4j7-curl-8.18.0.tar.xz",
"out": "/nix/store/xhp149abalfmj232yjhgbqaw281ba8np-curl-8.18.0",
"system": "x86_64-linux"
}
}
}
the fields that matter:
systemtarget architecture and OS. the derivation is only valid for this platform.builderthe program nix runs to do the build. almost always bash.argsarguments passed to the builder.envenvironment variables the builder sees. $out is the output path, $src is the source, and all dep paths and build flags end up in here.inputDrvsother derivations this one depends on, specifying which output to use. each dep's hash is baked in here, so changing any transitive dependency changes this derivation's hash too.inputSrcsstore paths used directly as inputs, not built from a derivation. usually shell scripts and source archives.the hash of this entire structure, after computing and substituting all transitive inputs, is what becomes the 32-character prefix.
0123456789abcdfghijklmnpqrsvwxyz (no e, o, t, u)the result is 32 characters. that becomes the hash in the store path.
change anything: a dependency is patched, an env var differs, you bump the source version. you get a different hash. a different path. the old path is unaffected.
nix path-info --recursive shows you the full closure:
$ nix path-info --recursive nixpkgs#curl
/nix/store/p98zvq4nb98krxcv7ss2zr1qngfmi0f5-gcc-14.3.0-libgcc
/nix/store/2a3izq4hffdd9r9gb2w6q2ibdc86kss6-xgcc-14.3.0-libgcc
/nix/store/3rkccxj7vi0p2a0d48c4a4z2vv2cni88-libunistring-1.4.1
/nix/store/hxcmad417fd8ql9ylx96xpak7da06yiv-libidn2-2.3.8
/nix/store/vr7ds8vwbl2fz7pr221d5y0f8n9a5wda-glibc-2.40-218
/nix/store/0p8b2lqk47fvxm9hc6c8mnln5l8x51q1-gcc-14.3.0-lib
/nix/store/3qkwgkbjisvm8z1g826bvqfg6j4cr35x-nghttp2-1.67.1-lib
/nix/store/592kyxjw9fnl255vcxkdpd8iaymg8y8l-keyutils-1.6.3-lib
/nix/store/8dj39rr9xp8qpl3myqj8i04a8pwhyl60-openssl-3.6.1
/nix/store/b3vi2i22167nhmrnl85ks3xpzl8bjj56-krb5-1.22.1-lib
/nix/store/7w67asczqr61prk2i4c2yrc4xcwi0vbj-publicsuffix-list-0-unstable-2025-12-28
/nix/store/b7wbagl6c1xr79r6hbcc7fznjjibc8mr-libpsl-0.21.5
/nix/store/xdxxfabbd8w0dadijsd8rkgvnhpn3rkf-zlib-1.3.1
/nix/store/chqzzvliv0mldn2n6b96aivcmhvc0hb8-libssh2-1.11.1
/nix/store/fwr62xmh06l8y8zfgc5m18pfap9b8az0-bash-5.3p3
/nix/store/gwy8kliqcqspz7r56y6hn2a0k28r5hak-zstd-1.5.7
/nix/store/ka7244lkykijn9k6p4c8a2acz8yz9pnd-brotli-1.1.0-lib
/nix/store/mjwqy9bka7zma90b6q6vg4lc51wzrwda-ngtcp2-1.17.0
/nix/store/pdp6hrs98pkfymhg7869pzggw2xizskc-nghttp3-1.12.0
/nix/store/xhp149abalfmj232yjhgbqaw281ba8np-curl-8.18.0
/nix/store/6jw31n10mjbjzn7ki2vyf6fs1cxif2qh-curl-8.18.0-bin
/nix/store/rbyqrgnyg5wf4zhc20783mjgr0pdis35-curl-8.18.0-man
22 paths. every one was computed the same way. each hash encodes its own derivation, which references its dependencies' hashes, which reference theirs. the whole chain is content-addressed.
there is one exception: fetchers.
when you write fetchurl { url = "..."; sha256 = "..."; }, nix has no way to compute an input hash upfront. the source lives on the internet and could change. instead, nix lets the builder run with network access, then checks the output against the declared hash. if it does not match, the build fails.
src = fetchurl {
url = "https://curl.se/download/curl-8.18.0.tar.xz";
sha256 = "sha256-...";
};
these are called fixed-output derivations. the path is derived from the output hash, not the input hash. network is allowed. a mismatch is a hard failure, not a warning, not a fallback.
this is the only place in a normal nix build where the outside world can reach in. everything else is sealed.