the build sandbox
how nix isolates builds from the rest of the system.
how nix isolates builds from the rest of the system.
when nix runs a build, the builder does not get your environment. no HOME, no PATH from your shell, no network access, no /etc. it starts from nothing and gets only what you declared.
this is how store paths stay meaningful. if builds could reach outside their declared inputs, two machines with the same derivation could produce different outputs. the sandbox is the guarantee that they do not.
on linux, nix calls clone() with new mount, network, UTS, IPC, and PID namespaces. the build runs as a dedicated nixbld user with no special privileges.
from inside the build, it looks like a fresh machine.
/nix/store: declared inputs only, bind-mounted read-only from the host store. the builder can read any path it was given. it cannot see paths it was not given, even though they sit in the same directory on the host.
/tmp: a fresh tmpfs. writable, empty, wiped when the build finishes.
/dev: minimal devices only: null, zero, urandom, random. no block devices, no access to host hardware.
network: loopback interface only (lo, 127.0.0.1). no external connectivity. a curl https://example.com inside a build will fail.
/etc (or a minimal synthetic version with just enough for the build to run: /etc/passwd with the nixbld user, /etc/group)instead of inheriting your shell environment, nix injects a small fixed set:
| variable | value |
|---|---|
$out | output path. write your build here |
$src | source path in /nix/store |
PATH | constructed from bin/ dirs of declared deps |
HOME | /homeless-shelter |
NIX_BUILD_TOP | the build directory |
TMPDIR | /tmp |
HOME=/homeless-shelter is deliberate. any tool that tries to read ~/.config/something gets a path that does not exist. if your build depends on a dotfile, it breaks loud and early.
write your output to $out. that directory becomes the store path.
a standard nix build script (default-builder.sh) runs unpackPhase, configurePhase, buildPhase, installPhase: each one writing into $out by the end. nothing about this is magic. it is just shell.
when the build finishes, nix registers $out into the store, makes it read-only, and records it as the output of the derivation.
each of these fails immediately, with an error that points directly at what's missing:
/etc/ssl/certs for CA bundles and finds nothing. the build fails with a TLS error or a missing-file error.~/cache/something. HOME is /homeless-shelter, which does not exist. permission denied or file not found.all of these are features. they tell you the build has undeclared dependencies. fix the derivation, not the sandbox.
macos does not have linux namespaces. nix uses sandbox-exec with a generated Scheme profile instead.
same goal: block filesystem access outside declared paths, no network, restricted device access. the surface area differs slightly. some builds need additional allowances for macOS-specific paths. the principle is the same.
a build that passes in nix's sandbox will produce the same result on any other machine with the same inputs. the sandbox is what makes that true.
if nix builds could silently read from your home directory or phone home during compilation, store path hashes would be meaningless. two people building the same derivation could get different binaries with the same path. the whole model collapses.
the sandbox is not a security boundary. it is a correctness boundary. its job is to make builds reproducible, not to protect the build from you.
references and closures picks up where the sandbox leaves off. once a build completes and the output lands in the store, nix scans it for embedded store path hashes: that is how runtime dependencies are discovered without you declaring them explicitly.