Changelog: an important security fix, nix top, faster garbage collection, parallel instantiation, and more

We at Determinate Systems are pleased to announce the release of Determinate Nix version 3.24.1, based on upstream Nix 2.35.3. This post covers everything that’s changed since 3.23.3, which means Determinate Nix versions 3.24.0 and 3.24.1:
- This release includes two important security fixes: Determinate Nix 3.24.1 fixes the Broccoli2Shell vulnerability in the Brotli decoder, and 3.24.0 stops trusting a compromised root certificate, so we strongly recommend that you upgrade as soon as feasible.
- The new
nix topcommand shows a live view of your active builds, whilenix psnow reports memory usage. - Large evaluations no longer spend most of their time in garbage collection.
- With parallel evaluation enabled, Nix now instantiates derivation dependencies in parallel, which cuts the time needed to evaluate a single package roughly in half.
- Garbage collection and path registration are now up to 32% faster on large stores.
builtins.wasmno longer deadlocks with parallel evaluation.- Clients of the Nix C API no longer slow down quadratically during evaluation.
And as always, we’ve included a batch of bug fixes and smaller improvements to round out the lineup.
Security fixes
Determinate Nix 3.24.0 and 3.24.1 each include a security fix. We strongly recommend that you upgrade as soon as feasible.
- Broccoli2Shell (CVE-2026-111497).
Determinate Nix 3.24.1 updates Brotli to 1.2.1, which fixes an out-of-bounds write in the Brotli decoder that can be triggered by malicious data.
That bug can crash the process and may even allow remote code execution.
Nix decompresses Brotli data from untrusted sources, such as binary cache files that use Brotli compression and HTTP responses with
Content-Encoding: br, so this affects every Determinate Nix user. - Determinate Nix no longer trusts the emSign ECC Root CA - G3 certificate. After the compromise of an eMudhra intermediate CA key, Network Security Services (NSS) 3.131.1 removed the emSign ECC Root CA - G3 root certificate. Determinate Nix 3.24.0 updates the CA certificate bundle in its installer tarball and Docker image to NSS 3.131.1, so those installations no longer trust TLS certificates issued under that root when they connect to binary caches or fetch flake inputs.
Relevant pull request
Watch your builds with nix top
nix ps, which we introduced in Determinate Nix 3.14.0, gives you a one-time snapshot of all active Nix builds on the current host.
Version 3.24.0 introduces the new nix top command, which gives you a continuously updating, top-like full-screen view of the same information, including the CPU and memory usage of each build and each of its processes:
nix topYou can scroll, collapse process trees, sort builds by CPU usage, memory usage, wall time, or name, and change the refresh interval.
Alongside nix top, we’ve also made some improvements to nix ps.
It now reports memory usage in a new RSS (resident set size) column and in new rss fields in its JSON output.
On Linux with use-cgroups enabled, a build’s memory usage comes from its cgroup.
nix ps is also more accurate:
- It no longer shows build processes as owned by
root(they never ran as root). - It no longer drops the rest of a build’s processes when one of them exits while it’s reading them.
- It no longer shows a daemon process that’s about to start a builder as part of the build.
Garbage collection regression fix
Since Determinate Nix 3.23.0, many large evaluations have spent most of their time in garbage collection. That release picked up a change to the Boehm-Demers-Weiser garbage collector that allowed only one 16 MiB heap expansion per collection, so once the heap grew beyond its 4 GiB initial size, every 16 MiB of allocation triggered a full collection. In one of our CI jobs, evaluation went from about 2 minutes to about 40 minutes.
Determinate Nix 3.24.1 restores the collector’s previous heuristic, so the heap can once again grow several times between collections. Across the 249 flakes in our flake regression tests, evaluated on a single thread, total CPU time dropped from 7,014 seconds to 806 seconds. The gains are concentrated in flakes that need a large heap, at the cost of somewhat higher peak memory usage:
| Flake | Elapsed time | CPU time | Peak memory |
|---|---|---|---|
Mic92/sops-nix/8b89f44 | 283s → 52s | 3097s → 66s | 9.8 GiB → 11.0 GiB |
NixOS/nix/2.21.2 | 292s → 94s | 2582s → 119s | 9.5 GiB → 10.7 GiB |
zhaofengli/attic/0.1.165 | 37s → 33s | 90s → 35s | 4.7 GiB → 5.4 GiB |
Relevant pull request
Parallel instantiation of derivation dependencies
With parallel evaluation enabled, Determinate Nix now instantiates a derivation’s dependencies in parallel rather than one at a time. This should speed up any evaluation that instantiates derivations, even commands that otherwise run on a single thread:
nix eval nixpkgs#firefoxwent from 1.51 seconds to 0.77 seconds.- Evaluating Nix’s own development shell went from 2.65 seconds to 1.25 seconds.
When the evaluator threads are already busy, like when running nix flake show on a large flake, Nix skips this background work to avoid the overhead.
Relevant pull request
Faster garbage collection and path registration
Nix now keeps SQLite’s temporary data for the Nix store database in memory rather than in temporary files. Deleting or registering store paths with many references used to make SQLite spill its statement journals to temporary files, and from then on every write in that transaction went through them. Garbage-collecting a store with 192,000 paths and 7.9 million references wrote 13 million times to 166,000 temporary files, 34.4 GiB in all.
Now it doesn’t write to files at all, with the result that:
- Garbage-collecting the whole store is 17% faster (322.9 seconds to 269.3 seconds).
nix-store --register-validityof every path is 32% faster (323.3 seconds to 219.3 seconds).nix copyof 50,000 paths into an empty store is 20% faster (108.6 seconds to 86.7 seconds).
The memory cost is small for ordinary workloads (a few MiB at most), and SQLite frees it as soon as the statement that needed it finishes. Durability is unchanged. Thanks to @czawistowski-nvidia for this change, their first contribution to Determinate Nix.
Relevant pull request
No more builtins.wasm deadlocks
Previously, builtins.wasm occasionally deadlocked the parallel evaluator.
Building Nix with Nix (nix build ./packaging/nix-make --no-eval-cache --eval-cores 8), for example, hung about half the time.
A fiber inside a Wasm call blocked its worker thread rather than suspending, which stranded that thread’s other suspended fibers, and the value the blocked fiber was waiting for often depended on one of them.
Fibers now suspend normally inside Wasm calls, and Determinate Nix swaps each fiber’s list of active Wasm calls out of Wasmtime’s thread-local storage while it’s suspended. This release also updates Wasmtime to 48.0.7.
Relevant pull request
Linear-time waits for Nix C API clients
Clients of the Nix C API, such as FlakeBOM, wait for pending store path writes after nearly every operation. Beginning with 3.23.0, that wait copied the full set of paths written up to that point and checked each of them, which meant that clients would slow down quadratically as evaluation went on, to the point that evaluating Nixpkgs with a cold eval cache would outright hang. The wait now takes the same amount of time regardless of how many paths Nix has written.
Relevant pull request
Bug fixes
- Failed builds through upstream Nix daemons.
Since 2.34, upstream Nix daemons omit the results of builds that were cancelled or never started.
When connected to one of those daemons, as on NixOS 26.05,
nix buildaborted andnix flake checkfailed an assertion when a build failed. Both now print the real build error and list the other builds as cancelled. - Paths deleted out from under evaluation.
builtins.storePathdidn’t protect a path that was already in the store with a temporary garbage collector root, and neither didbuiltins.fetchurlorbuiltins.fetchTarballwhen the output for the given hash already existed. A concurrent garbage collection could delete those paths mid-evaluation, and evaluation then failed withpath '…' is not valid. Nix now registers the root before checking validity. - Out-of-order generation timestamps.
nix profile wipe-history --older-than,nix-env --delete-generations, andnix-collect-garbage --delete-older-thanaborted partway through if generation timestamps didn’t increase with the generation number, for example because the clock was wrong when a generation was created. They now skip those generations with a warning. nix copyto a binary cache.nix copycould abort withmap::atwhen a path’s validity changed during the copy.nix copywith a full$TMPDIR.nix copy --to file://...crashed, rather than printing an error, when writing the compressed Nix Archive (NAR) failed.- Home path literals.
The parser didn’t canonicalize path literals like
~/..the way it canonicalizes other path literals, so using one, for example innix replorbuiltins.readDir, failed an assertion. - Build hook output. Messages from the build hook that aren’t JSON log messages no longer corrupt the progress bar, and the daemon now forwards them to the client.
Relevant pull requests
Smaller improvements
- Correlated crash reports.
We updated Sentry’s SDK from 0.13.5 to 0.17.4.
Crash reports now include a random per-installation ID, stored in
~/.cache/nix/sentry/installation_id, so that we can correlate reports from the same installation and count how many installations an issue affects. - Fewer allocations in internal caches, which also shortens how long the OpenTelemetry logger holds a lock.
- Provenance documentation.
The manual now documents the provenance JSON format that
nix path-info --jsonuses. - Help output.
nix store --helpandnix config --helpno longer list some subcommands under an empty heading. - Building with
-march. Builds of Determinate Nix with-marchno longer fail on the Wasmtime test suite. Thanks to @n0099 for the fix, their first contribution to Determinate Nix. - Dependency updates.
Determinate Nixd and Determinate Nix Installer both update their
rustls-webpkidependency.
How to get Determinate Nix
If you already have Determinate Nix installed, you can upgrade to 3.24.1 with one Determinate Nixd command:
sudo determinate-nixd upgradeIf you don’t yet have Determinate Nix installed, you can upgrade or migrate to Determinate Nix on macOS using our graphical installer:
Install Determinate Nix on macOS
With support for Apple Silicon (aarch64-darwin)
On Linux:
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | \ sh -s -- install --determinateOn NixOS, we recommend using our dedicated NixOS module or our NixOS ISO (NixOS installer for x86_64, NixOS installer for ARM) with Determinate Nix pre-installed.
On GitHub Actions:
on: pull_request: workflow_dispatch: push: branches: - main
jobs: nix-ci: runs-on: ubuntu-latest # Include this block to log in to FlakeHub and access private flakes permissions: id-token: write contents: read steps: - uses: actions/checkout@main - uses: DeterminateSystems/flake-checker-action@main with: fail-mode: true - uses: DeterminateSystems/determinate-nix-action@v3 - uses: DeterminateSystems/flakehub-cache-action@v3 - run: nix flake checkIn Amazon Web Services:
data "aws_ami" "detsys_nixos" { most_recent = true owners = ["535002876703"] # Commercial # owners = ["579351485434"] # GovCloud # owners = ["129194717446"] # European Sovereign Cloud
filter { name = "name" values = ["determinate/nixos/epoch-1/*"] }
filter { name = "architecture" values = ["x86_64"] }}Written by
Graham is a Nix and Rust developer with a passion and focus on reliability in the lower levels of the stack. He is a co-founder of Determinate Systems, alongside Eelco Dolstra, as well as its Chief Technology Officer.
