Nix and NixOS Version Reference
Nix and NixOS Version Reference
A structured reference for Boring-NixOS readers tracking which Nix CLI features and NixOS system options became available in each release. Coverage spans Nix 2.18 – 2.35 and NixOS 23.05 – 26.05. This book’s examples target Nix 2.35 and NixOS 26.05 “Yarara”. Earlier rows are history so you can read old flakes.
Each section contains a table of reader-relevant changes followed by one short runnable example that exercises the most impactful addition in that release. All examples assume the infrastructure desk scenario: a shared flake that builds team tooling and drives a set of NixOS workstations.
Nix CLI Releases
Nix 2.18
| Area | Change |
|---|---|
nix flake metadata |
Prints flake description, inputs, and their resolved URLs; last-modified timestamp now shown per input. |
--log-format |
Accepts raw, internal-json, bar, bar-with-logs; useful for CI pipelines that consume structured output. |
| Evaluator | builtins.fetchTree no longer fetches tarballs eagerly when the hash is already in the store. |
| Error messages | Multi-line errors now include a source excerpt with a caret pointing at the offending token. |
Key feature — nix flake metadata
Save as show-meta.sh:
# show-meta.sh
nix flake metadata \
--log-format bar \
github:NixOS/nixpkgs/nixos-26.05Run:
bash show-meta.shOutput:
Resolved URL: github:NixOS/nixpkgs/nixos-26.05
Locked URL: github:NixOS/nixpkgs/a1b2c3d4e5f6...
Description: Nix Packages collection & NixOS
Path: /nix/store/xxxxxxxx-source
Last modified: 2024-11-30 12:00:00
Inputs:
└───nixpkgs: (this flake)
Nix 2.19
| Area | Change |
|---|---|
nix store gc |
Replaces nix-collect-garbage progressively; supports --max-freed <bytes> to cap space reclaimed per run. |
nix store gc --dry-run |
Reports paths that would be deleted without touching the store. |
nix profile |
nix profile diff-closures added; shows package diff between two profile generations. |
| Daemon | Store lock contention reduced for concurrent multi-user builds. |
Key feature — nix store gc --dry-run
Save as gc-preview.sh:
# gc-preview.sh
# Preview how much the desk workstation store can shrink
# before committing to deletion.
nix store gc --dry-run --max-freed $((10 * 1024 * 1024 * 1024))Run:
bash gc-preview.shOutput:
Would delete: /nix/store/aaa...-old-toolchain-2023
Would delete: /nix/store/bbb...-devshell-2023-11
...
Would free 7.4 GiB (limit was 10.0 GiB)
Nix 2.20
| Area | Change |
|---|---|
nix flake archive |
Copies all flake inputs into a binary cache or a local store, enabling fully offline evaluation afterwards. |
nix flake archive --to |
Accepts file:///path, s3://bucket, or ssh://host as destination. |
nix copy |
--substitute-on-destination flag added; destination fetches missing paths from its own substituters before failing. |
| Build sandbox | Linux sandbox now mounts /proc as tmpfs by default, improving isolation. |
Key feature — nix flake archive to a local mirror
Save as archive-inputs.sh:
# archive-inputs.sh
# Snapshot all flake inputs into a local binary cache so the
# desk's air-gapped build box can evaluate without internet.
nix flake archive \
--to file:///srv/nix-cache \
/home/ops/desk-infraRun:
bash archive-inputs.shOutput:
archiving input 'nixpkgs'
/nix/store/xxxxxxxx-source → file:///srv/nix-cache
archiving input 'home-manager'
/nix/store/yyyyyyyy-source → file:///srv/nix-cache
done — 2 inputs archived
Nix 2.21
| Area | Change |
|---|---|
nix eval --apply |
Applies a Nix function to the evaluated result before printing; enables one-liner transformations. |
nix eval --json |
Now handles null and true/false correctly in all output formatters. |
nix flake update |
--commit-lock-file flag commits the updated flake.lock with a machine-generated commit message. |
| Repl | :reload re-reads the current file without restarting the session. |
Key feature — nix eval --apply
Save as list-packages.sh:
# list-packages.sh
# Print just the names of packages exposed by the desk flake
# without writing a separate script.
nix eval \
/home/ops/desk-infra#packages.x86_64-linux \
--apply 'pkgs: builtins.attrNames pkgs' \
--jsonRun:
bash list-packages.shOutput:
["desk-tools","lint-runner","release-packager"]Nix 2.22
| Area | Change |
|---|---|
nix build --rebuild |
Rebuilds a derivation even if its output is in the store, then compares the new result to the cached one — a fast reproducibility check. |
nix copy --from / --to |
Both flags marked stable; no longer experimental. |
nix flake check |
Reports unfree license violations when --allow-unfree is absent. |
| Evaluator | builtins.readFileType added: returns "regular", "directory", "symlink", or "unknown". |
Key feature — nix build --rebuild
Save as verify-repro.sh:
# verify-repro.sh
# Rebuild the desk tool and confirm the output hash is stable.
# A mismatch here means a non-deterministic build is lurking.
nix build \
/home/ops/desk-infra#packages.x86_64-linux.desk-tools \
--rebuildRun:
bash verify-repro.shOutput (reproducible):
warning: performing a check build
[3/0/1 built] building desk-tools-1.4.0...
/nix/store/abc123...-desk-tools-1.4.0 (deterministic ✓)
Output (non-reproducible):
warning: performing a check build
error: derivation '/nix/store/...-desk-tools-1.4.0.drv' may not
be deterministic: output '/nix/store/abc123...' differs
from '/nix/store/def456...'
Nix 2.23
| Area | Change |
|---|---|
| Lazy trees | Flake inputs are fetched only when their subtree is actually evaluated; large monorepo inputs no longer block evaluation. |
nix fmt |
Declared stable; runs the formatter specified in flake.nix formatter output. |
nix repl :doc |
Prints the docstring attached to any Nix function or built-in. |
nix repl :log |
Shows the build log for a derivation path without leaving the REPL. |
nix flake update |
Accepts multiple named inputs: nix flake update nixpkgs home-manager. |
Key feature — nix fmt enforced in CI
Save as fmt-check.sh:
# fmt-check.sh
# Fail CI if any Nix file in the desk flake needs reformatting.
nix fmt /home/ops/desk-infra -- --checkRun:
bash fmt-check.shOutput (clean):
All Nix files already formatted.
Output (needs formatting):
Would reformat: /home/ops/desk-infra/modules/workstation.nix
error: 1 file would be reformatted
Nix 2.24
| Area | Change |
|---|---|
nix path-info --json |
Output is now a structured JSON array with path, narHash, narSize, references, registrationTime, and deriver fields. |
nix flake check |
Checks nixosConfigurations, darwinConfigurations, and homeConfigurations outputs for module evaluation errors. |
nix store diff-closures |
Declared stable; compares the full runtime closure of two store paths and reports added/removed/changed paths. |
nix build --print-build-logs |
Default changed to true in interactive terminals. |
Key feature — nix store diff-closures
Save as diff-release.sh:
# diff-release.sh
# Compare the desk-tools closure between the last release and
# HEAD so the team knows exactly what changed before shipping.
OLD=$(nix build \
"/home/ops/desk-infra?ref=v1.3.0#packages.x86_64-linux.desk-tools" \
--no-link --print-out-paths)
NEW=$(nix build \
/home/ops/desk-infra#packages.x86_64-linux.desk-tools \
--no-link --print-out-paths)
nix store diff-closures "$OLD" "$NEW"Run:
bash diff-release.shOutput:
desk-tools: 1.3.0 → 1.4.0
openssl: 3.2.1 → 3.2.2 (+12 KiB)
python3: 3.11.8 → 3.12.3 (+1.1 MiB)
curl: (unchanged)
added: zlib 1.3.1
NixOS Releases
NixOS 23.05 — Stoat
| Area | Change |
|---|---|
| systemd | 253; systemd-oomd enabled by default on desktop profiles. |
| Python | 3.11 is the default interpreter; python3 symlink resolves to python3.11. |
| Nix | Ships Nix 2.13; flake support via nix.settings.experimental-features is opt-in stable. |
| Module system | config.system.stateVersion warning added for stale values. |
| Security | services.fail2ban updated to 1.0; new ignoreIP list option. |
Key feature — enabling flakes on a Stoat workstation
Save as configuration.nix:
# configuration.nix
{ pkgs, ... }:
{
# Enable the two features that make flakes and the new nix
# command available on every desk workstation running 23.05.
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
# Pin the channel so every workstation evaluates identically.
nix.registry.nixpkgs.flake = import <nixpkgs> { };
environment.systemPackages = [ pkgs.git ];
}Run:
sudo nixos-rebuild switch --flake /etc/nixos#workstationOutput:
building the system configuration...
activating the configuration...
setting up /etc...
the following new units were started: nix-daemon.service
NixOS 23.11 — Tapir
| Area | Change |
|---|---|
| GNOME | 45; Mutter gains explicit sync support for tear-free rendering on variable-refresh monitors. |
| PHP | 8.2 is default; services.phpfpm pools now set pm.max_spare_servers automatically. |
| Kernel | 6.1 LTS; default for most hardware profiles. |
services.avahi |
openFirewall now defaults to false; must be set explicitly. |
| Nix | Ships Nix 2.18. |
Key feature — explicit Avahi firewall declaration
Save as configuration.nix:
# configuration.nix
{ ... }:
{
# 23.11 changed the default: openFirewall is now false.
# Desk printers and mDNS service discovery need this set
# explicitly or discovery will silently fail.
services.avahi = {
enable = true;
nssmdns4 = true; # allow getaddrinfo to resolve .local
openFirewall = true;
};
}Run:
sudo nixos-rebuild switch --flake /etc/nixos#workstationOutput:
activating the configuration...
setting up /etc...
reloading the following units: firewall.service
NixOS 24.05 — Uakari
| Area | Change |
|---|---|
| systemd | 255; systemd-boot gains UKI (Unified Kernel Image) signing support. |
| Python | 3.12 is default; python3.11 still available as explicit attribute. |
| Nix | Ships Nix 2.22 — nix build --rebuild and stable nix copy available out of the box. |
system.etc.overlay |
Experimental option; mounts /etc as an overlay for impermanence workflows. |
boot.initrd.systemd |
Available as an opt-in Stage-1; not the channel default yet (that lands in 26.05). |
services.postgresql |
enableJIT now defaults to true for 14+. |
Key feature — opt-in systemd initrd (pre-26.05)
Save as configuration.nix:
# configuration.nix
{ ... }:
{
# On 24.05 this is opt-in. 26.05 flips the default to true and
# deprecates the scripted Stage 1. Prefer the systemd path early
# if the desk host uses LUKS / TPM2 unlock.
boot.initrd.systemd.enable = true;
boot.initrd.luks.devices."cryptroot" = {
device = "/dev/disk/by-uuid/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
allowDiscards = true;
};
# Pin Nix to 2.22 features in this release.
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
}Run:
sudo nixos-rebuild boot --flake /etc/nixos#desk-buildOutput:
building the system configuration...
updating GRUB 2 menu...
Done. The new configuration will be used when the machine is rebooted.
NixOS 24.11 — Vicuna
| Area | Change |
|---|---|
| GNOME | 47; default font is now Cantarell 11; GNOME Color Manager updated. |
| Kernel | 6.6 LTS; amdgpu gains firmware for RDNA 3.5 integrated graphics. |
| Nix | Ships Nix 2.24 — nix path-info --json, nix store diff-closures stable. |
services.openssh.settings |
New sub-options: MaxSessions, PermitUserEnvironment, AllowGroups. |
security.apparmor |
enableCache option added; profile compile cache speeds boot on profile-heavy machines. |
| Python | 3.12 remains default; 3.13 available as python313. |
Key feature — locked-down SSH with services.openssh.settings
Save as configuration.nix:
# configuration.nix
{ ... }:
{
# 24.11 exposes AllowGroups directly in the settings attrset,
# eliminating the need for extraConfig string concatenation.
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
MaxSessions = 4;
AllowGroups = [ "ops" "developers" ];
};
};
# AppArmor with compile cache — useful on desk machines that
# load many profiles at boot.
security.apparmor = {
enable = true;
enableCache = true;
};
}Run:
sudo nixos-rebuild switch --flake /etc/nixos#workstationOutput:
activating the configuration...
reloading the following units: sshd.service apparmor.service
Nix 2.28 – 2.34 (bridge)
| Area | Change |
|---|---|
| Fetchers | Stricter NAR hash mismatches; prefer SRI hash = "sha256-…" over legacy sha256. |
| Flakes | Source trees copied into the store more lazily (completed in 2.35). |
nix store |
diff-closures, copy, ping remain the daily ops set. |
Pin nixos-25.11 only to read old machines. Do not start new desk hosts there: it reached EOL on 2026-06-30.
Nix 2.35
| Area | Change |
|---|---|
| Flake sources | Copied to the store lazily; unused flake files are not hashed into evaluation unless needed. |
| Sandbox | FreeBSD jail sandboxing; Linux sandbox unchanged. |
| Installer | Current upstream installer ships 2.35.2 (2026-08). |
| Security | Recursive-nix advisory fix from the 2.35.0 series. |
Key feature — current CLI on a 26.05 host
Stock 26.05 may print nix (Nix) 2.34.x. For this book’s 2.35+ baseline, install the upstream Nix installer on foreign Linux/macOS, or set nix.package = pkgs.nixVersions.latest; (or nix_2_35) on NixOS. Then:
nix --version
nix flake metadata github:NixOS/nixpkgs/nixos-26.05Output (shape, after pinning 2.35):
nix (Nix) 2.35.2
Resolved URL: github:NixOS/nixpkgs/nixos-26.05
NixOS 25.05 / 25.11
25.05 and 25.11 are previous stables. 25.11 “Xantusia” is deprecated (EOL 2026-06-30). If system.stateVersion is "25.11", leave it and move the flake input to nixos-26.05.
NixOS 26.05 — Yarara
| Area | Change |
|---|---|
| Support | Bugfix/security until 2026-12-31. |
| Initrd | Stage-1 systemd initrd is the default (not the old scripted initrd). |
| Toolchain | GCC 15; GNOME 50 on desktop ISOs. |
| Nix | Channel ships Nix 2.34; book CLI baseline remains 2.35+ (installer or nix.package). |
| Modules | New options; confirm names with nixos-option if a 24.11 snippet fails eval. |
Key feature — pin 26.05 in the desk flake
Save as flake.nix:
# flake.nix
{
description = "Desk workstation on NixOS 26.05";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
outputs = { self, nixpkgs }: {
nixosConfigurations.desk-workstation = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
{
networking.hostName = "desk-workstation";
system.stateVersion = "26.05";
}
];
};
};
}nix flake metadata
sudo nixos-rebuild dry-build --flake .#desk-workstationQuick-Reference Matrix
The table below maps each NixOS release to the Nix CLI it ships and highlights the single most operationally significant change per pair.
| NixOS Release | Nix Version Shipped | Most Impactful Addition |
|---|---|---|
| 23.05 Stoat | 2.13 | Flakes opt-in stable via experimental-features |
| 23.11 Tapir | 2.18 | nix flake metadata improvements; --log-format for CI |
| 24.05 Uakari | 2.22 | nix build --rebuild reproducibility check; systemd initrd opt-in |
| 24.11 Vicuna | 2.24 | nix store diff-closures stable; services.openssh.settings.AllowGroups |
| 25.05 | 2.28 | nix flake check / fetch-tree hardening; module cleanups |
| 25.11 Xantusia | 2.31 | EOL 2026-06-30; last train before Yarara |
| 26.05 Yarara | 2.34 (pin 2.35+) | Current baseline; systemd stage-1 initrd default; GCC 15 |
Nix 2.19–2.34 intermediate releases are available via pkgs.nixVersions even when the channel ships another version. Pin only when you need a CLI feature the channel does not have yet:
# configuration.nix (pin Nix version independently of channel)
{ pkgs, ... }:
{
nix.package = pkgs.nixVersions.latest; # or pkgs.nixVersions.nix_2_35
}