Nix 2.30 stops placing builds in a shared temporary directory
Nix now stages builds under its state directory by default. The change protects host-side build paths and makes storage placement and daemon configuration more explicit.
A build's temporary directory is an active part of the build environment. Other processes must not be able to replace or modify it while the daemon is using it. Nix 2.30 changes the default location because putting these directories under a shared temporary directory allowed interference, including sandbox escapes when combined with other conditions.
The July 7 release notes say build-dir no longer defaults to TMPDIR or /tmp. Its new default is builds under NIX_STATE_DIR, normally /nix/var/nix/builds. The notes explicitly identify accidental build impurities alongside malicious interference.
Nix's declared-input model is one reason I use it for personal infrastructure. That model depends on the implementation preventing unrelated host activity from influencing a build. Moving the working directory under a controlled parent supports that requirement at the filesystem level, where declared input rules alone cannot stop another process from changing a path.
The parent directory affects who can interfere
The 2.30 local-store configuration definition explains the constraint directly. build-dir must not name a world-writable directory. Such a parent can let other users access or modify build data currently in use, which can create impurities and has contributed to sandbox escapes.
The issue is the relationship between an active child directory and its parent. A build directory can have restrictive permissions while the surrounding namespace still permits unwanted interference under some circumstances. Selecting a parent controlled for this purpose removes the assumption that a general temporary directory provides the necessary protection.
That is why the default changes at the host level. The daemon needs a place to create temporary build directories whose parent is suitable for managing untrusted work. Naming that place under Nix's state directory gives it a purpose distinct from temporary files belonging to arbitrary host users and services.
The setting documentation says Nix creates the directory automatically with suitable permissions if it does not exist. If it already exists, its permissions need to permit traversal by all users for non-sandboxed builds to work. Traversal permission is different from permission to create or replace entries there.
This distinction matters when configuring an alternative mount. A directory can allow another user to reach a child path without allowing that user to change the parent directory's contents. The documented requirement is therefore not a demand that the directory become world-writable. That would contradict the protection the same setting describes.
The daemon's configuration chooses the build location
The setting reference notes that builds are often performed by the Nix daemon, so the daemon's build-dir applies. A shell's temporary-directory environment is not a reliable description of where a daemon-run build will stage its files. The process doing the work owns that configuration decision.
This is a practical change for workflows that used TMPDIR to influence build placement. The release explicitly removes that variable as the default source of the location. The declared setting and the daemon's state directory now explain the path, unless an explicit override applies.
An administrator selecting another filesystem needs to preserve both its storage properties and its access control. A large disk and a RAM-backed filesystem have different capacity characteristics, but neither is intrinsically the right permission model. The protection follows the parent directory's control, not its storage medium.
The default can also change where build storage is accounted for. An installation that put /tmp on a separate mount may now write temporary build content on the filesystem holding Nix's state. That follows from the documented relocation, and it matters when separating persistent store capacity from temporary working space.
The host path and the sandbox path are distinct
The sandbox setting definition describes sandbox-build-dir as the directory inside the sandbox, backed by build-dir on the host. Its default path is /build. Changing the host's staging parent therefore does not require a corresponding change to every sandboxed builder's visible working path.
This separation is useful for reproducibility. The host can select a suitable storage location while the sandbox presents a consistent directory name. A derivation's view of its workspace and the daemon's placement of its backing storage serve different purposes.
The setting documentation also names the exception. Without sandboxing, or on a platform without sandboxing through bind mounts, the builder environment contains the host directory instead of the virtual sandbox location. The release's host-path change can therefore be visible to those builders in a way that it is not for a bind-mounted sandbox.
That makes a precise account preferable to saying all builds now run at /nix/var/nix/builds. Host staging defaults there, while the path visible during execution depends on the sandbox arrangement. Both paths can be correct descriptions of the same build at different levels.
I find this separation useful because it keeps machine-specific storage policy out of the ordinary sandbox workspace. An operator can reason about disk placement, while a builder sees the path chosen for its isolated environment. The release repairs the default host policy without erasing that distinction.
Failed-build retention follows the same setting
The local-store documentation identifies build-dir as the location where --keep-failed leaves files. The relocation therefore changes an operational inspection path too. A failed build retained for investigation belongs under the configured build parent, rather than whichever temporary location an older workflow expected.
Keeping a failed directory does not make it an ordinary completed store output. It preserves working files for examination. The setting's access-control requirements still apply because the parent also hosts build work and should retain its controlled role.
This detail helps explain why the setting is part of the operator interface. It affects active execution and the artifacts left after failure. Documentation or automation locating those artifacts needs to use the configured path, particularly where a daemon's setting differs from a client's shell environment.
Evaluation gains a way to show where time goes
Nix 2.30 also introduces a stack-sampling evaluator profiler. The release notes describe --eval-profiler flamegraph and a profile output file, with a default sampling frequency of 99 Hz. This measures evaluation activity, separately from the builders using the temporary directories discussed above.
The profiler emits collapsed call-stack information for flamegraph-compatible tools and includes function names where available. That gives configuration authors a way to identify evaluation work before assuming that a slow invocation spends all its time compiling or downloading packages. The stage being measured is explicit in the setting name.
The same release adds warnings for import-from-derivation through trace-import-from-derivation. The notes distinguish observing those operations from denying them through the existing setting. This permits a workflow to identify their use without immediately turning every occurrence into a failure.
These additions are useful alongside the directory change because evaluation and realization have different operational causes. Moving build storage cannot by itself explain or repair expensive evaluation. Conversely, a profile of evaluator stacks does not establish that a build's host directory is protected from interference.
Explicit interfaces replace incidental behavior
Another release-note deprecation concerns manually setting the __json environment variable to manufacture structured attributes. The supported language declaration is __structuredAttrs = true in builtins.derivation. The notes say the alternate route relied on how the feature happened to be implemented.
This is a different change, but it shares the release's interest in making boundaries explicit. A derivation should request structured attributes through the declared mechanism. A daemon should choose temporary build placement through a setting whose permissions and ownership requirements are documented. Relying on an incidental environment path or implementation detail makes those requirements harder to maintain.
The profile command also receives a naming change. nix profile install becomes nix profile add, with the earlier spelling retained as an alias. The notes explain that adding an entry to a profile does not imply installation hooks or every behavior associated with installation in another package manager. That change clarifies the command's operation while preserving its old invocation, unlike the deliberate change to temporary-directory selection.
For my infrastructure use, the substantive July change is the protected staging default. The relevant configuration is the one used by the process performing the build. Its host directory must provide controlled temporary storage, while the sandbox can continue presenting its own consistent workspace path and retained failures remain discoverable under the configured parent.