background grid image
Image for post changelog-determinate-nix-3120
Oct 31, 2025 by Eelco Dolstra

Changelog: introducing nix nario

Once you’ve built software with Nix, the logical next step in many cases is getting it onto another device, such as a production server. You typically do this using the nix copy command, which enables you to push the software to the target, or using a binary cache such as FlakeHub Cache, where the target pulls the software from the network.

But what if your target device isn’t connected to the network? In that case, you can neither nix copy to it nor have it fetch from a binary cache. Nix has long had a solution for this: nix-store --export and nix-store --import. The first enables you to serialize a closure (a store path and its dependencies) to a file:

Serialize the closure of GNU hello
nix-store --export $(nix-store -qR /nix/store/crrf4bysvarqp0g9pyhcrj47689g4i5l-hello-2.12.1) > file

This can then be imported on another machine:

Import the closure of GNU hello
nix-store --import < file

But these commands have a number of problems:

  • They are memory inefficient, requiring at least as much RAM as the largest store path in the archive. If, for instance, the archive contains a 30 GB store path (think of an AI model), then importing the archive requires at least 30 GB of memory. For embedded devices, this can cause the import to outright fail with an out-of-memory error.
  • They don’t support newer Nix features such as signatures and content-addressed paths.
  • They don’t provide a way to see what’s in the archive.
  • They have no equivalent in the new unified Nix CLI.

Enter nix nario

Determinate Nix 3.12.0 solves these problems by introducing a new nix nario command, where “nario” is simply the name we’re giving the file format produced by nix-store --export, which previously lacked a name. Analogous to the cpio file format, a nario is essentially a concatenation of NAR (Nix Archive) files, one per store path, with some metadata.

Creating a nario is simple. If you’re on NixOS, for example, you can serialize the entire current NixOS system closure (pointed to by /run/current-system) like this:

Serialize the entire current system closure as a nario
nix nario export \
--format 2 \
--recursive \
/run/current-system > system.nario

The flag --format 2 specifies that we want to use version 2 of the nario format. Version 1 is the old format generated by nix-store --export; version 2 fixes the memory inefficiency issue and adds support for path metadata such as signatures.

We can now copy the nario file to another device, for example by copying it onto a USB stick. On the target device, we can import it into the local Nix store:

Import closure from nario file
nix nario import < system.nario

Importing this particular system closure takes just 40 MiB of RAM using nario version 2. With version 1, using nix-store --import, it takes 2048 MiB! If you’re wondering why that may be: this inefficiency is an inherent result of how version 1 is laid out. Version 1 serializes store path contents (the NAR) before the store path itself, so deciding whether it can skip a store path requires Nix to first read the entire NAR into memory or write it to scratch space on disk.

The new format is also much faster when some store paths already exist. With version 1, for instance, repeating the import of the system closure above takes 7.3 seconds compared to 0.05 seconds with version 2.

A final feature worth mentioning is that nix nario can list the contents of a nario file, which was not possible with the old CLI:

List the contents of a nario
nix nario list < system.nario

That displays a list of store paths and metadata like this:

Terminal window
/nix/store/4j6p91af1bfgnn31agg1c9ijr0kyg6gi-gcc-14.3.0-libgcc: 201848 bytes
/nix/store/16hvpw4b3r05girazh4rnwbw0jgjkb4l-xgcc-14.3.0-libgcc: 201848 bytes
...
/nix/store/hb8nfbfxmlz9bq56nznvkcsb3c8f5cif-nixos-system-foo-25.05.20250925.25e53aa: 18968 bytes

Despite nario not being a random-access format, this is quite fast: just 0.05 seconds for this 15 GiB closure. We have an open pull request against NixOS/nix and hope to see this in upstream Nix soon.

Developer friendliness changes

nix nario brings a powerful new way to distribute software with Nix, but as with most of our releases, there’s some other fun stuff for Nix devs on offer.

nix flake clone now supports all flake references

The nix flake clone command now supports arbitrary input types. Previously, it supported most types but in version 3.12.0 we’ve added the ability to clone tarball flakes, such as flakes published to FlakeHub.

Relevant pull request

Print the Nix version when logging at the “talkative” level

When you run a Nix command, adding -vv sets the log level to “talkative,” which is useful for general debugging purposes. Beginning with Determinate Nix version 3.12.0, that log level now prints the Nix version, which can be helpful in situations where knowing which Nix is responsible could prove decisive in tracking something down.

When using -vv, Determinate Nix now prints the Nix version. This is useful when diagnosing Nix problems from the debug output of a Nix run.

Relevant pull request

How to get Determinate Nix

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

Upgrade command for version 3.12.0
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 graphical installer

Install Determinate Nix on macOS now

Apple Silicon and Intel

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@v5
- uses: DeterminateSystems/flake-checker-action@main
- uses: DeterminateSystems/determinate-nix-action@v3
- uses: DeterminateSystems/flakehub-cache-action@main
- run: nix flake check

In Amazon Web Services:

aws.tf
data "aws_ami" "detsys_nixos" {
most_recent = true
owners = ["535002876703"]
filter {
name = "name"
values = ["determinate/nixos/epoch-1/*"]
}
filter {
name = "architecture"
values = ["x86_64"]
}
}

Share
Avatar for Eelco Dolstra
Written by Eelco Dolstra

Eelco started the Nix project as a PhD student at Utrecht University. He is a co-founder at Determinate Systems and a member of the Nix team.

Would you like access to private flakes and FlakeHub Cache?

Sign up for FlakeHub