Discovering Nix

What is Nix?

I recently had the opportunity to play with Nix, which is a package manager and an OS based on this package manager. Nix is special in the sense that it claims to only make reproducible builds that can’t break the system. But how?

The package manager is “purely functional”, which means that the packages are built without side effects and then stored in the Nix store in their own folder. Each packet folder is final and will never be updated.

$ ls /nix/store
0003ysdgsqrw3cgd1d14fd3vwvk8xrx8-audit-2.8.5.drv 
001gp43bjqzx60cg345n2slzg7131za8-nix-nss-open-files.patch 
0063ldk3vc900dpwhx8gnxrf224xr56g-no-ipx.patch.drv 
00cyl30mqdn7b1322glr4sw31y1wm8j3-xcb-proto-1.14.1.tar.xz.drv 
00qr10y7z2fcvrp9b2m46710nkjvj55z-update-autotools-gnu-config-scripts.sh 
00r7an2p7s0zlpfp5r8325l9m5rxn409-itstool-2.0.6.tar.bz2.drv 
00yn5hiyh9csf4l54y9xkjmb87pbcq4z-python3.10-tomli-w-1.0.0.drv 
00ynjyb2b6pv7xn5c1rjb7arsy47jjps-CVE-2021-38185-3.patch.drv 
010as9z275bhz34yxxrwnj0ka98psqsa-yarl-1.8.1.tar.gz.drv 
013p8z1zp7hcx98w265vabd2by5c5r5a-python3.10-olefile-0.46.drv 
014p0xh9nnkcdgk0p8mz9kb3n9ghf14i-source.drv 
015zhkkz8n5ihjngldmzb4ra6myyz2p2-python3.10-cython-0.29.32.drv 
016vrlkmk8h2rv8c1f41d2aw11xbpkc6-python3.11-packaging-21.3 
018wjyikq7bw1nxc056gasasdgxpn9k8-libedit-20210910-3.1.tar.gz.drv 
01916ddyq0xn0r7b5sn4akbdfd7gs6qv-diffutils-3.8.tar.xz.drv 
01n3wxxw29wj2pkjqimmmjzv7pihzmd7-which-2.21.tar.gz.drv 
01zi2f11shjvlwlvp2ys0rf3n5f0s8ld-scp-0.14.4.tar.gz.drv 
022znrrav9r39x7wmpj4kgpz3xgihwq2-xcb-proto-1.14.1.tar.xz.drv 
0242hg6z6r1cz1axpknc12j6n25dvh0i-libbfd-2.39.drv 
026hln0aq1hyshaxsdvhg0kmcm6yf45r-zlib-1.2.13 
0276hsd59xk54z4k5k2dgvdac0s0j3xz-python3.10-twisted-22.10.0.drv
...

Any change in the package dependencies or inputs for a given packet will trigger a complete new build for it. This means that you can have multiples versions of every package. That’s also why you won’t break another package in your system by updating some dependencies. You get immutable builds in exchange for high disk usage for storing all versions of all the dependencies. You also get history and rollback for each packages thanks to this design.

Most packages are also built from source, including all their dependencies. No pre-built binaries, so everything should be built right for your system. But the first installation will take more time than with a classic package manager.

Using Nix

As a user, you get access to some parts of the nix store depending on what you install. If you only want to try some packages you can just run it as a one-off:

$ nix run "nixpkgs#bat" README.md

This references the nixpkgs repository which is the official repository. But technically any Github repository can serve as a nix repository.

It has to be noted that I’m using the flakes experimental feature along with the unified CLI. This puts all commands under nix instead of having to use nix-env, nix-shell, nix-build

When I want to use a package long-term, I can install it in my profile, which basically only needs to put a symlink to the nix store.

$ nix profile install nixpkgs#bat
$ bat README.md

Nix also has the nix develop command which allows you to enter a development environment from a local or remote definition. This means that you can enter an environment with pre-installed dependencies in your PATH (python, ansible, git…), environment variable definition, specific configuration… Creating your own development environment involves using Nix’s own functional language which has a steep learning curve but allows you to customize your builds.

I’m not really familiar with all of the Nix ecosystem yet, but I like the overall philosophy, and I can see the benefits of the development environment to synchronize a lot of dependencies between the devs (contrary to using a docker container just to run python or relying on a lot of version managers).

— Morgan