Nix and NixOS Version Reference

Updated

September 12, 2026

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.05

Run:

bash show-meta.sh

Output:

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.sh

Output:

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-infra

Run:

bash archive-inputs.sh

Output:

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' \
  --json

Run:

bash list-packages.sh

Output:

["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 \
  --rebuild

Run:

bash verify-repro.sh

Output (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 -- --check

Run:

bash fmt-check.sh

Output (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.sh

Output:

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#workstation

Output:

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#workstation

Output:

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-build

Output:

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#workstation

Output:

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.05

Output (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-workstation

Quick-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
}