We at the Lix team are proud to announce our fifth major release, version 2.94 “Açaí na tigela”.
This release focuses on bugfixes, quality-of-life improvements, performance improvements and started integrating Lix with the Cap’n’Proto remote procedure call runtime, in order to replace the previous bespoke implementation.
Açaí na tigela is a sweet Brazilian snack food from Pará and Amazonas, made with the frozen and mashed fruit of the açaí palm.
Lix is a Nix implementation focused on reliability, predictability, friendliness, developed by a community of people from around the world. We have long term plans to incrementally evolve Nix to work in more places, to make it more reliable and secure, and to update the language and semantics to correct past mistakes and reduce errors, all the while providing an amazing tooling experience.
Upgrading from CppNix or previous Lix versions
The upgrade procedure depends on how you installed Lix or CppNix, and is fully described in the Lix installation guide.
If you are using Lix from nixpkgs on NixOS, you just need to upgrade your nixpkgs once the upgrade pull request has passed through the build farm into your channel; no other action is required.
If you want to help us test the next version of Lix, consider running main by following the beta guide.
Changes
Lix 2.94 builds on the work from Lix 2.93 in improving the daemon and language to make room for future evolution.
This release took longer than usual due to non-trivial fallouts from the CVE mitigation plus the team taking some time off.
Here are the highlights from the release notes. This is not a comprehensive list, and we are thankful for every contributor’s hard work in making this release happen.
News from RPC
As mentioned in previous communications, Lix pursues the goal of delivering a reasonable RPC protocol that replaces the bespoke and obsolete Nix daemon protocol.
Building on top of KJ was chosen because it provides access to Cap’n Proto and gives us a well-tested RPC substrate.
Lix 2.94.0 now ships Cap’n Proto protocol definitions for build hooks and for logging.
Build hooks are used during remote builds. When Lix performs a remote build (nix __build-remote), it spawns a hook program. This hook instance is a Cap’n Proto RPC server that speaks the new protocol.
This subsystem has been the first target of the ongoing RPC work.
These changes will be mostly invisible to users. The main visible improvement is that multiple build hook processes may now wait concurrently. In the old protocol only one could wait at a time.
Flakes enters freeze period
As announced in https://wiki.lix.systems/books/lix-contributors/page/flakes-feature-freeze, Flakes-related changes are now frozen unless a core team member grants an explicit exception (mostly for bugfixes).
The Lix project has received many Flakes-related changes in the past, often driven by the CppNix project. The quality of these changes did not match the usual Lix standards and forced the core team to spend considerable effort evaluating their interactions with the existing feature set. Several inconsistency issues slipped through review. This is unsurprising because Flakes remain an experimental feature with semantics that change in practice.
Now that there are at least three separate implementations of Flakes, the Lix project cannot reasonably maintain a third flavor inside core.
The Flakes implementation in the Lix codebase has also been a recurring source of maintenance headaches.
We intend to remove Flakes from the core entirely and ship them as a plugin that is included by default.
Future Flakes improvements can then happen in that subproject without affecting Lix core.
Extracting Flakes is a 2.95.0 objective.
If you are confident with C++, please consider helping us with this migration.
Breaking changes
A significant amount of technical debt has been cleared to allow safer evolution of Lix.
Language
Lix strings may now contain NUL bytes
Lix can now manipulate strings that contain arbitrary binary data, including NUL bytes. The previous behavior was inconsistent and unintentional. Examples in the release notes show where this caused incorrect behavior.
Many thanks to eldritch horrors for this.
Function equality semantics are more consistent, but still bad
Functions always compare as not equal in Nixlang except when they come from the same memory location. This optimization exists to speed up comparisons of large attribute sets and had to be extended to functions stored inside attribute sets.
While reworking the evaluator, Lix made this behavior more consistent, although still undesirable.
For example:
let s.f = f; in s.f == s.fnow evaluates totrue.Lix intends to remove this optimization later.
Function equality is undefined behavior and should not be relied upon in portable Nixlang code.
Many thanks to eldritch horrors for this.
Stores and builds
Remove support for daemon protocols before 2.18
This affects clients connecting to the local daemon socket or remote builders configured using the ssh-ng protocol. Builders using the ssh protocol are still supported for older clients such as Nix 2.3.
Maintaining these older protocols required too much effort and lacked test coverage.
Many thanks to eldritch horrors for this.
Remove impure derivations and dynamic derivations
The
impure-derivationsanddynamic-derivationsexperimental features are removed.New impure or dynamic derivations can no longer be created. Existing ones cannot be read or built. Their outputs remain valid until garbage collected. The
.drvfiles may only be garbage collected.Many thanks to eldritch horrors for this.
A new cgroup delegation model for the
cgroupsexperimental featureBuilds using cgroups (
use-cgroups = trueandexperimental-features = cgroups) now always receive a delegated cgroup tree with permission to manage controllers in that subtree.This can cause visible breakage because the build process (daemon or direct store access) now requires to be run inside a cgroup tree that was already delegated by the caller: service manager or system administrator for example.
The
uid-rangeexperimental feature now depends oncgroups.The release notes contain guidance on setting up the tree and working around issues if you get stuck.
Many thanks to Raito Bezarius, eldritch horrors and lheckemann for this.
Fixed output derivations now run using pasta network isolation
Following the mitigation of CVE-2025-46416, fixed-output derivations are now isolated from the host network using Pasta.
This is a breaking change. We learned a number of operational details while deploying Pasta at scale.
If your DNS setup is healthy (first server in
/etc/resolv.confresponds quickly) and the derivation only needs TCP or UDP, this change should not affect you.Many thanks to eldritch horrors and puck.
Enable zstd with a high compression level instead of xz for binary cache uploads
Binary cache uploads now use zstd instead of xz. This significantly improves upload time on modern systems and high-speed links, enabling gigabit link saturation while uploading to fast Garage S3 implementations.
The release notes contain a typo: reducing runtime from 77 seconds to 18 seconds is about a 75 % improvement, not 50 %.
On a 4.4GB NAR file, uploads can be 75 % faster at the cost of roughly 18 % larger output.
Many thanks to eldritch horrors and Raito Bezarius.
Features
HTTP/3
Lix can now fetch from binary caches using HTTP/3 when supported by both the server and the local curl stack. This can reduce latency, although throughput may vary as described in https://daniel.haxx.se/blog/2024/06/10/http-3-in-curl-mid-2024/.
HTTP/3 is off by default.
Many thanks to Raito Bezarius and eldritch horrors.
Experimental integer coercion
Lix adds an experimental feature that allows integers to be coerced where strings were previously required. This reduces boilerplate but changes language semantics, so it is off by default.
Many thanks to Raito Bezarius, eldritch horrors, delroth and winter.
nix-eval-jobs no-instantiate
nix-eval-jobsnow supports--no-instantiate, skipping derivation instantiation and improving performance of large evaluations.Hyperlinks in attribute set printing
The attribute set printer used by
nix repland in type errors now prints hyperlinks on attribute names to their definition sites when known.Example:
$ nix eval -f '<nixpkgs>' lib.licenses.mit { deprecated = false; free = true; fullName = "MIT License"; redistributable = true; shortName = "mit"; spdxId = "MIT"; url = "https://spdx.org/licenses/MIT.html"; }Many thanks to jade for this.
Improvements
Debugging
Ctrl-C rework
Interrupt handling has been improved so Ctrl-C behaves predictably across long evaluations and daemon interactions.
One Ctrl-C requests a graceful shutdown. A second Ctrl-C aborts immediately with no guarantee of data integrity.
❯ nix-instantiate --eval --expr 'let f = n: if n == 0 then 0 else f (n - 1) + f (n - 1); in f 32' ^CStill shutting down. Press ^C again to abort all operations immediately. ^C ❌130 ❯Many thanks to eldritch horrors.
Stack traces now summarize involved derivations at the bottom
Evaluation stack traces now end with a summary that collects derivations involved in the error, which helps identify which package triggered the failure in a dependency tripping an assertion such as unsupported, insecure or broken derivations.
error: … while calling the 'head' builtin at /nix/store/9v6qa656sq3xc58vkxslqy646p0ajj61-source/lib/attrsets.nix:1701:13: 1700| if length values == 1 || pred here (elemAt values 1) (head values) then 1701| head values | ^ 1702| else … while evaluating the attribute 'value' at /nix/store/9v6qa656sq3xc58vkxslqy646p0ajj61-source/lib/modules.nix:1118:7: 1117| // { 1118| value = addErrorContext "while evaluating the option `${showOption loc}':" value; | ^ 1119| inherit (res.defsFinal') highestPrio; (stack trace truncated; use '--show-trace' to show the full trace) error: Package ‘olm-3.2.16’ in /nix/store/9v6qa656sq3xc58vkxslqy646p0ajj61-source/pkgs/by-name/ol/olm/package.nix:37 is marked as insecure, refusing to evaluate. < -snip the whole explanation about olm's CVEs- > note: trace involved the following derivations: derivation 'etc' derivation 'dbus-1' derivation 'system-path' derivation 'nheko-0.12.1' derivation 'mtxclient-0.10.1'Many thanks to Qyriad.
Debugging builds
--keep-failedchown the build dir to the invoking userWhen using
--keep-failedorkeep-failed = true, Lix now reliably changes ownership of the failed build directory to the user who requested the build, including through the daemon.Many thanks to eldritch horrors.
Keeping mismatching fixed-output derivation artifacts
When fixed-output derivations fail because the produced output does not match the expected hash, both paths are printed. The offending output is added to the store so that you can inspect it, compute a new hash, or fetch a known-good output for comparison.
Many thanks to lheckemann.
Show tree with references that lead to an output cycle
Output cycles now include a reference tree showing exactly how the cycle arose.
Example:
error: cycle detected in build of '/nix/store/gc5h2whz3rylpf34n99nswvqgkjkigmy-demo.drv' in the references of output 'bar' from output 'foo'. Shown below are the files inside the outputs leading to the cycle: /nix/store/3lrgm74j85nzpnkz127rkwbx3fz5320q-demo-bar └───lib/libfoo: …stuffbefore /nix/store/h680k7k53rjl9p15g6h7kpym33250w0y-demo-baz andafter… → /nix/store/h680k7k53rjl9p15g6h7kpym33250w0y-demo-baz └───share/snenskek: …???? /nix/store/dm24c76p9y2mrvmwgpmi64rryw6x5qmm-demo-foo … → /nix/store/dm24c76p9y2mrvmwgpmi64rryw6x5qmm-demo-foo └───bin/alarm: …texttexttext/nix/store/3lrgm74j85nzpnkz127rkwbx3fz5320q-demo-bar abcabcabc… → /nix/store/3lrgm74j85nzpnkz127rkwbx3fz5320q-demo-barMany thanks to Ma27.
disallowedRequisitesnow reports chains of disallowed requisitesErrors now include the full chain of references leading to each forbidden path rather than only the immediate offender.
Example:
$ nix-build -A hello error: output '/nix/store/0b7k85gg5r28gb54px9nq7iv5986mns9-hello-2.12.2' is not allowed to refer to the following paths: /nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-glibc-2.40-66 Shown below are chains that lead to the forbidden path(s). /nix/store/0b7k85gg5r28gb54px9nq7iv5986mns9-hello-2.12.2 └───/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-glibc-2.40-66Many thanks to Ma27 and Robert Hensing.
Performance
Substituter query speed
Substituter queries now take advantage of asynchronous work introduced in 2.93. Caches with higher latency benefit more from these improvements.
Nonetheless, it was measured to result in roughly a 60 % reduction in query time for medium-sized closures like NixOS systems.
Many thanks to eldritch horrors.
Rate limiting
nix copyparallel operationsnix copypreviously hitToo many open fileserrors on main. We added a rate limit to avoid that. You can remove the limit by increasing the open files limit usingulimit -n <new number>.Many thanks to Raito Bezarius.
Pointer tagging, thunk state sharing and unreferenced Values
Lix implemented pointer tagging and reduced the
Valuestructure to a single machine word. Thunk state sharing was implemented, enabling more reuse ofValueobjects.Valueis now used as a reference-counted smart pointer to a heap object.This unblocks further optimizations and resulted in:
- 15 % memory savings and a 3 % evaluation time regression on system rebuild
- 17 % memory savings and a 7 % evaluation time improvement on nix search
Many thanks to eldritch horrors.
Reuse of special strings in the language
Common strings that occurs in evaluation such as the result of
builtins.attrNamesare now reused more efficiently, reducing allocations and slightly improving evaluation speed.Up to 11 % memory savings were observed in large NixOS deployments with a slight decrease in CPU usage.
Many thanks to eldritch horrors, xokdvium, Raito Bezarius, Tom Hubrecht and NaN-git for this.
Parallel marking in garbage collection
The Boehm-Demers-Weiser garbage collector supports parallel marking. Lix now enables this feature.
Contrary to the release notes, we did not observe a 38 % improvement on nix search.
Many thanks to Eelco Dolstra and Seth Flynn for this.
Fixes
build-dirno longer defaults totemp-dirTemporary build directories no longer default to
temp-dir(typically/tmp), fixing CVE-2025-46415.Many users use a tmpfs for
/tmp. The default build directory is now/nix/var/nix/builds. If you care about tmpfs semantics, bind-mount that directory onto a tmpfs.Compared to 2.93.3, additional changes were made so Darwin handles the new path length correctly, which allows reasonable derivations to connect to UNIX domain sockets in sandboxes. This will also be shipped in 2.93.4.
Many thanks to eldritch horrors, Emily and Qyriad for this.
Global CA are copied inside the builder environment
Global CA bundles are now correctly propagated to the builder environment. This fixes TLS verification issues for fetchers relying on system CAs.
This adds about 500 KB per fixed-output build in scratch locations.
Many thanks to Raito Bezarius and Emily for this.
Exponential backoff for downloads
In 2.93, we lowered the
connect-timeoutto 5 seconds. Some users have DNS setups where the first nameserver times out, causing resolution to exceed 5 seconds.We replaced linear backoff with exponential backoff to handle these cases more robustly.
Many thanks to Ma27 for this.
nix developfor derivations that reject dependencies with structured attributesRecent work in nixpkgs for bash-less systems added many
outputChecks.<output>.disallowedRequisitesdefinitions to packages such as systemd.This caused
nix develop nixpkgs#systemdto stop working, which is a common workflow for systemd development.The root cause was that
nix developonly understood non-structured output checks.Lix 2.94.0 adds support for structured output checks, so
nix develop nixpkgs#systemdworks again.nix-shellis unaffected by this problem.Many thanks to Raito Bezarius for this.
“My shell didn’t work” —
nix-shelldefault shell directory is not/tmpanymoreHistorically,
nix-shellstored internal shell files in$TMPDIRor/tmpand also used it for$NIX_BUILD_TOP. Many users have$TMPDIRunset, so/tmpwas consistently used.If you ran
sudo nix-shelland exited uncleanly, you could create/tmp/env-varswith root permissions, causing all subsequent shells for unprivileged users to fail silently. The workaround was to delete the file manually.Lix now creates a dedicated temporary directory for shell metadata that does not collide with other shells. Cleanup is handled by Lix itself after the shell exits.
Many thanks to Raito Bezarius for this.
Remove reliance on bash for remote stores via SSH
Lix 2.93 changed SSH remote store handling in a way that broke classical
ForceCommandand similar directives. We reverted the problematic part in 2.93.1 and carry the same fix here.Previous configurations work again out of the box.
Many thanks to Raito Bezarius for this.
You can read the full changelog in the manual.
Known issues
The release notes may contain imprecisions and typos, we are working to correct this without doing a point release.
No impactful known issues are yet known!
Credits
Thanks, as always, to the following groups:
The large community who beta tested the upcoming release by running
mainin production since the 2.94 branch-off. We really appreciate having immediate feedback on our work and the trust of running main alongside us means a lot to us. We know we tested the patience of some of you, we thank you for that.If you want to run Lix main yourself, see the beta guide for details.
Everyone who contributed by filing bugs and giving us feedback on Matrix.
All the first time contributors who made their first contributions to a Nix implementation in Lix. We are eternally grateful to everyone who helped us out on the numerous important but tedious issues.
All the contributors who have helped us with the backlog of bugs.
The CppNix contributors and CppNix team, without whom we would not have this software, and who wrote some of the improvements ported into this release.
A quiet but heartfelt note of gratitude goes to eldritch horrors for their steady guidance throughout this release, even in the face of its many challenges.
Onwards and upwards for the next release. We look forward to continuing to work together with everyone to build a better foundation for the evolution of Nix.