Skip to main content

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

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:

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.

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:

Watch active builds
nix top

You 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:

FlakeElapsed timeCPU timePeak memory
Mic92/sops-nix/8b89f44283s → 52s3097s → 66s9.8 GiB → 11.0 GiB
NixOS/nix/2.21.2292s → 94s2582s → 119s9.5 GiB → 10.7 GiB
zhaofengli/attic/0.1.16537s → 33s90s → 35s4.7 GiB → 5.4 GiB

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#firefox went 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.

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-validity of every path is 32% faster (323.3 seconds to 219.3 seconds).
  • nix copy of 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.

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.

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.

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 build aborted and nix flake check failed 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.storePath didn’t protect a path that was already in the store with a temporary garbage collector root, and neither did builtins.fetchurl or builtins.fetchTarball when the output for the given hash already existed. A concurrent garbage collection could delete those paths mid-evaluation, and evaluation then failed with path '…' 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, and nix-collect-garbage --delete-older-than aborted 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 copy to a binary cache. nix copy could abort with map::at when a path’s validity changed during the copy.
  • nix copy with 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 in nix repl or builtins.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.

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 --json uses.
  • Help output. nix store --help and nix config --help no longer list some subcommands under an empty heading.
  • Building with -march. Builds of Determinate Nix with -march no 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-webpki dependency.

How to get Determinate Nix

If you already have Determinate Nix installed, you can upgrade to 3.24.1 with one Determinate Nixd command:

Upgrade command for version 3.24.1
sudo determinate-nixd upgrade

If you don’t yet have Determinate Nix installed, you can upgrade or migrate to Determinate Nix on macOS using our graphical installer:

Logo for Determinate Systems graphical installer

Install Determinate Nix on macOS

With support for Apple Silicon (aarch64-darwin)

On Linux:

Install Determinate Nix on Linux
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | \
sh -s -- install --determinate

On 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:

.github/workflows/nix-ci.yaml
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 check

In Amazon Web Services:

aws.tf
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 Christensen

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.

Contact sales

Provide us with some basic details and we'll follow up with you about , usually within one business day.

We'll only use this to get back to you. See our privacy policy. This site is protected by reCAPTCHA and the Google Privacy Policy (opens in a new tab) and Terms of Service (opens in a new tab) apply.