A hands-on technical reference for platform engineers operationalizing Nix-based SBOMs, CVE remediation, and supply chain controls in real C

Nix Sandbox Configuration for Network-Isolated Builds

Nix's network isolation ensures builds can't fetch undeclared dependencies.

· 10 min read
Cover illustration for “Nix Sandbox Configuration for Network-Isolated Builds”
Hermetic Build Patterns · October 11, 2026 · 10 min read · 2,222 words

A build process running inside a Nix sandbox can see almost nothing. It has the store paths its derivation declared and a filesystem assembled specifically for it, and beyond that boundary there is no home directory, no ambient system libraries, no live network connection, no inherited environment variable left over from whatever shell launched it. Nix assembles that guarantee from several distinct mechanisms.

When Nix realizes a derivation with sandboxing turned on, it assembles the restriction from several Linux kernel primitives at once. It constructs a fresh mount namespace that contains only the store paths the derivation declared. It creates a private network namespace with no route out. It sets up separate PID, IPC, and UTS namespaces, and it roots the whole thing in a chroot-style filesystem. No stray binary from the host, no leftover configuration file, no second copy of a library that happens to be a different version than the one the derivation specified.

The sandbox is not a single toggle because each of those namespaces is solving a distinct leakage problem. The PID and IPC namespaces stop a build from interacting with other processes running on the same machine. The network namespace, which gets its own treatment in the next section, stops the build from reaching anything outside the machine. Composed together, these primitives produce an environment in which the only variables that can affect the outcome of the build are the ones named in the derivation.

This matters because it is not incidental hardening bolted onto Nix for defensive reasons. Sandboxing behaves differently depending on the platform: on Linux it is the default behavior, while on macOS it is not, and the underlying isolation primitives differ enough that a configuration that holds on one platform cannot be assumed to hold identically on the other.

Why network isolation is the mechanism's load-bearing feature

Of all the namespaces composed into the sandbox, the network namespace carries the most weight in the reproducibility argument. Network access is a different category of risk, because it lets a build pull in state that changes over time, state that no amount of static inspection of the derivation would reveal.

A build with an open network connection can silently fetch something the derivation never declared. The entire premise of a reproducible environment, that every input is declared in advance and pinned, collapses the moment a build can reach outside its own closure and ask the internet for something it did not name up front.

The network ban is how Nix turns that declaration requirement from a convention into a mechanical guarantee. Build infrastructure that follows this model, such as nixbuild.net, runs each build inside an isolated sandbox without network access, with one deliberate carve-out for fixed-output derivations that is permitted network access by default.

The consequence reaches back to how derivations get written in the first place: any dependency that is not already sitting in the Nix store has to be declared before the build starts, which pushes the work of naming the full dependency closure to derivation-writing time. That shift, from discovery to declaration, is what makes the next piece of the mechanism necessary: something has to fetch the bytes that live outside the store, and the sandbox has to permit that without giving up the guarantee it just built.

Fixed-output derivations: the controlled exception that keeps fetches honest

A sandbox with no network access raises an obvious question: how does any external dependency get into the store? Source tarballs, vendored archives, anything fetched from outside Nix has to cross the network boundary at some point. The fixed-output derivation resolves the contradiction by relocating the trust anchor to the declared hash of the expected result.

A fixed-output derivation declares the hash of its expected result before the fetch ever happens, and the fetch either produces what was promised or it produces nothing.

The honesty of the fetch comes from the hash, not from the absence of confinement. For an ordinary derivation, the sandbox enforces purity by cutting off network access. A fixed-output derivation trades that removal for a cryptographic commitment: it accepts network access in exchange for a verifiable promise about what will come back across it. In nix.conf, sandbox = true governs every derivation by default, and fixed-output derivations get their network exception as a designed part of that default, not as a hole punched through it by misconfiguration.

The practical implication for anyone packaging something that fetches at build time is that disabling the sandbox is never the right move. And the allowed-uris setting in nix.conf adds a further layer of control on top of that: it restricts which URIs fixed-output derivations are permitted to contact at all, letting an operator narrow the controlled hole down to a known set of registries or internal mirrors rather than leaving it open to any URL a derivation happens to name.

Configuring the sandbox layer by layer: sandbox-paths, allowed-uris, and fallback

Each sandbox-related setting in nix.conf governs a different stage of the build pipeline, and the settings make the most sense read in the order the pipeline actually executes them: evaluation first, then the sandboxed build itself, then network access for fetches, then what happens when a binary cache cannot deliver.

The top-level sandbox setting governs the build phase itself and takes three values. sandbox = true enables full sandboxing for every derivation. sandbox = false turns it off. sandbox = relaxed enables sandboxing by default but allows individual derivations to opt out by setting __noChroot = true. Community guidance limits its legitimate use to internal, enterprise packaging where a dependency genuinely cannot yet be made reproducible and the cost of that gap is being tracked.

sandbox-paths governs what the build can see beyond the store itself. It is a list of additional host filesystem paths that get bind-mounted into the sandbox, making them visible to the build even though they were never Nix store paths. If the file at that path changes on the host, the build's output can change without the derivation's hash changing at all, which quietly reopens the exact drift problem the sandbox exists to close. The discipline here is keeping the list as short and as narrowly scoped as the build genuinely requires.

Fallback behavior governs the last stage: what happens when a binary cache, or substituter, cannot supply a requested store path. Nix can fall back to building from source, or it can fail. In a strictly network-isolated continuous integration environment, disabling fallback for fixed-output derivations means a cache miss produces a visible, diagnosable failure instead of the pipeline quietly reaching out to the network to compensate.

Where the sandbox's threat model ends

The sandbox is built to catch an honest mistake, not to stop a deliberate attack. Its entire design assumes the derivation is not actively trying to escape; it assumes the build might accidentally reach an undeclared file or an undeclared network endpoint, and it closes those paths off. That is a different threat model from an attacker who wrote the derivation specifically to break out of confinement, and confusing the two leads teams in opposite wrong directions: either trusting the sandbox to do a job it was never built for, or dismissing it as inadequate for a job it actually does very well.

The sandbox shares its shape with a container: namespaces plus a chroot-style root, with the build process making syscalls directly to the host kernel the entire time. The Nix manual states the purpose: the sandbox exists to improve purity and reproducibility, and it does not defend against a build that is genuinely malicious and trying to attack the host it runs on.

Part of the confusion traces back to the word itself. "Sandbox" in the broader security world means an isolation boundary strong enough to run code from an untrusted source safely. Those are two different guarantees wearing the same label, and most misreadings of what the Nix sandbox can promise come from collapsing that distinction.

The distinction stops being academic the moment a team has to build something it did not write with full trust: a contributor's pull request, a vendored third-party flake, a derivation generated by an automated agent. At that point the operative concern changes to what happens to the host machine if the build turns out to be hostile. The Nix sandbox, on its own, does not answer that second question, and it was never built to.

Composing the sandbox with a VM boundary for untrusted derivations

An untrusted derivation needs a separate isolation layer placed underneath the Nix sandbox, because the sandbox and a hardware isolation boundary solve two different problems and stack together.

The Nix sandbox isolates the build's inputs: it enforces hermeticity so that the build is pure and its result is safe to cache and share. Neither one substitutes for the other, and running both is not redundant.

Production infrastructure already demonstrates the pattern. nixbuild.net uses KVM-based virtualization to isolate builds from one another, paired with a build sandbox developed specifically to guarantee no network access and no physical disk access for each build. That is the two layers operating together: hermeticity enforced on the inside, hardware isolation enforced on the outside.

The architecture introduces its own problem, one specific to how Nix works. A fresh virtual machine spun up for every build is clean and empty, and the Nix store is not a disposable cache. Whether the store survives through a persistent volume, a binary cache, or a pre-baked image decides whether the VM layer is a manageable overhead or a pipeline-breaking one.

Sandbox-enforced hermeticity and supply chain provenance

The same discipline that makes a sandboxed build reproducible also makes its dependency closure fully computable, and that computability is what turns SBOM generation from a manual audit exercise into a direct output of the build graph. Because the sandbox forces every input to be declared before the build runs, there is no undeclared dependency hiding anywhere in the result; the complete closure is already sitting in the data Nix used to build it.

Tooling built specifically for the Nix ecosystem exploits exactly this property. sbomnix, from the Technology Innovation Institute, generates a software bill of materials from a Nix flake reference or a store path, determines the dependencies of that reference, and exports the result in both CycloneDX and SPDX formats; the project released version 1.8.0 in June 2026. It can operate against either build-time or runtime dependencies, a distinction that matters in Nix because the two dependency sets are frequently different, and it enriches the components it finds with metadata beyond bare names and relationships when enough information is available from Nixpkgs. A set of companion tools extends the same approach across the rest of the supply chain: nixgraph visualizes the dependency graph itself, vulnxscan runs vulnerability scans driven directly from the generated SBOM, and nix_outdated identifies outdated dependencies and lists them in priority order.

None of this works as a bolt-on retrofit against a build system that was not hermetic to begin with. The sandbox discipline covered earlier in this piece, the declared inputs, the hash-pinned fetches, the restricted evaluation, is the precondition that makes the closure computable. SBOM generation here is a downstream consequence of configuring the sandbox correctly, not a separate compliance feature layered on top of it.

That consequence arrives with a regulatory deadline attached. Teams that have already built hermeticity into how they configure their build pipelines are structurally ahead of teams still treating the SBOM as a document to generate after the fact. The sandbox also narrows the supply chain attack surface during the build itself: a compromised dependency running inside a network-isolated build has no outbound connection available to exfiltrate anything, closing off a vector that has been used in real compromises during the build phase.

Putting the layers together: a configuration posture for reproducible, auditable pipelines

None of the layers covered in this piece function as a single switch, and none of them substitute for the others. A coherent configuration posture treats each one as answering a distinct question and sets it deliberately rather than leaving it at whatever the installer defaulted to.

For a continuous integration environment where every derivation is trusted, the posture is straightforward: sandbox = true across the board, sandbox-paths kept to the minimum the builds operationally require, allowed-uris restricted to a known set of mirrors, and fallback disabled so that a cache miss produces a visible failure. For an environment that has to build derivations it cannot fully trust, contributor pull requests, vendored third-party flakes, build files generated by an automated agent, the posture adds a hardware isolation layer underneath: the Nix sandbox keeps running inside the VM to preserve hermeticity, while the VM boundary takes on the separate job of containing a build that turns out to be hostile. The __noChroot escape hatch belongs only inside internal, enterprise packaging work where a dependency is not yet reproducible and the gap is being tracked as debt, never in a derivation destined for shared infrastructure.

Teams that configure these layers this way are not fighting the sandbox's defaults or working around its restrictions. They are using the mechanism as it was built to be used, and in doing so they make reproducibility and auditability properties that the pipeline guarantees structurally, rather than properties that have to be checked after the fact.

More in Hermetic Build Patterns