The /nix/store Anatomy

Updated

September 12, 2026

The /nix/store Anatomy

Everything Nix builds, downloads, or installs lands in /nix/store. The boring default is: treat the store as a read-only database of paths named by hash, and never move, rename, or chmod anything inside it.

Mental model

Every store path looks like this:

/nix/store/<hash>-<name>-<version>

Example:

/nix/store/z1k947f6y788sfaivs931h26hsvb3506-ripgrep-14.1.0
Piece Meaning
Hash 32 characters of Nix base32. Identity of this path.
Name + version Human label. Not identity. Two ripgrep-14.1.0 builds can coexist.
The directory A complete artifact: bin/, lib/, share/, or a single file.

The hash is computed from the inputs of the derivation (the usual case) or from the contents of the output (fixed-output and content-addressed paths). Either way, a different hash is a different path. Nothing is overwritten.

Permissions are read-only. Timestamps are the Unix epoch (Jan 1 1970) so two machines producing the same bytes do not disagree about mtime.

/nix
 ├── store/          immutable artifacts
 └── var/nix/
      ├── db/        SQLite index of those artifacts
      ├── profiles/  generations (GC roots)
      └── gcroots/   extra roots (result, direnv, …)

Your shell’s rg is not “the ripgrep in /usr/bin”. It is a symlink into a specific store path.

Worked examples

Case 1: Follow a binary into the store

which rg
ls -l $(which rg)
ls -ld $(dirname $(which rg))/..

Output (hashes will differ on your machine):

/run/current-system/sw/bin/rg
lrwxrwxrwx 1 root root 68 Jan  1  1970 /run/current-system/sw/bin/rg -> /nix/store/z1k947f6y788sfaivs931h26hsvb3506-ripgrep-14.1.0/bin/rg
dr-xr-xr-x 1 root root 4096 Jan  1  1970 /nix/store/z1k947f6y788sfaivs931h26hsvb3506-ripgrep-14.1.0

On a Nix-on-Linux user profile the first hop may be ~/.nix-profile/bin/rg instead of /run/current-system. The last hop is always /nix/store/….

Case 2: Read-only and epoch timestamps

ls -ld $(readlink -f $(which nix))

Output:

-r-xr-xr-x 1 root root 15728640 Jan  1  1970 /nix/store/7vqw9388aaaaaaaaaaaaaaaaaaaaaaaa-nix-2.35.2/bin/nix
  • Mode r-xr-xr-x (or r-xr-xr-x on the directory r-xr-xr-x): no write bit.
  • Date Jan 1 1970: not “this was built at the epoch”; the timestamp is normalised so it is not an input to the next hash.

Try to touch it:

touch $(readlink -f $(which nix))

Output:

touch: cannot touch '/nix/store/…-nix-2.35.2/bin/nix': Permission denied

Case 3: Two versions, no collision

nix build nixpkgs#hello --out-link hello-a
ls -ld hello-a

Output:

lrwxrwxrwx 1 deskadmin deskadmin 55 Jan  1 12:00 hello-a -> /nix/store/…-hello-2.12.1

hello-a is a GC root in the current directory (the result-style symlink). The store path is the real artifact. Installing a different nixpkgs revision of hello produces a second directory next to the first. Both stay until nothing roots them.

Case 4: What is not in the store

ls /usr/bin/rg 2>&1 || true
echo $PATH | tr ':' '\n' | head

On NixOS, /usr/bin is nearly empty (env, maybe sh). Tools live under /run/current-system/sw/bin and /nix/store. On Nix-on-Linux, /usr/bin still has the host distro; Nix tools are extra entries on PATH. Mixing them is how “it works in my shell” happens. Prefer the store path.

Case 5: Name a path without guessing the hash

nix path-info nixpkgs#hello

Output:

/nix/store/…-hello-2.12.1

nix path-info -Sh nixpkgs#hello adds closure size. You do not construct store paths by concatenating strings in a shell script.

The trap

The trap is sudo chmod -R u+w /nix/store/… so you can patch a file. The path’s hash promised those bytes. After you write to it, every later build that trusted that path is lying. The daemon may also refuse to register the path on --verify.

If a binary is wrong, rebuild it with different inputs. You get a new hash. The old path stays until GC.

A related trap: copying a store path to another machine with scp and dropping it under /nix/store by hand. Use nix copy so the database is updated. A directory that is not registered in db.sqlite is invisible to Nix and eligible for nothing useful.

The boring rule

  • All Nix artifacts live under /nix/store/<hash>-<name>.
  • Follow which until you see /nix/store. That is the real path.
  • Never rm, mv, chmod, or touch inside the store.
  • Use nix path-info / nix-store -q to name paths. Do not paste hashes from memory.
  • Treat result and profile symlinks as pointers, not as the software.

Try this

  1. Run readlink -f $(which bash) (or $(which sh)) and count how many symlink hops precede /nix/store.
  2. ls /nix/store | wc -l — that is how many artifacts this machine currently holds. Run it again after nix run nixpkgs#cowsay -- hi and see the count move.
  3. stat $(readlink -f $(which nix)) and confirm Access: (0555) (or similar, no write) and a 1970 timestamp.
  4. Try echo x >> $(readlink -f $(which nix)) and save the exact error. That error is the product.