Using nix without root

Created On:

The nix package manager is a purely functional package manager that enforces a declarative and hermetic style of building packages. I wanted to use nix to create per project development environments and what I learned is conventional nix installs come in two flavors.

  1. Via NixOS where it is the default and only package manager.
  2. Via an installer script which requires root access on Linux or macOS to create the /nix folder and in mutli-user situations a persistent daemon.

NixOS is a non starter for creating per project development environments and requiring root to create a development environment is not ideal. Instead I discovered it’s possible to setup nix as a non root user and the rest of the post outlines how to do that.

Getting the binary

The first problem to solve is getting the nix binary. The easiest way to get a nix static build from the hydra CI system1. A fully static binary is available for the 2.14 branch of the nix command by running:

$ curl -L https://hydra.nixos.org/job/nix/maintenance-2.14/buildStatic.x86_64-linux/latest/download-by-type/file/binary-dist > nix
$ chmod +x nix
$ ./nix --version
nix (Nix) 2.14.1

Setting up the /nix directory

The next problem is ensuring the nix binary has access to /nix directory for storage. Although nix can be configured to use any directory for storage, the official hydra CI system build all nix packages with the nix store in /nix. Using the same directory allows nix to download cached artifacts instead of re-building every package locally. There are two options to setup the /nix directory.

Option 1: Using the built in chroot

The first option is to use the built in logic that creates ~/.local/share/nix/root and uses linux namespaces to bind mount that as /nix. This means simply running the following will build GNU Hello and re-use cached artifacts if available.

$ ./nix run 'nixpkgs/release-22.11#hello'

Running the above for the first time nix will output.

warning: ‘/nix/var/nix’ does not exist, so Nix will use ‘/home/zmanji/.local/share/nix/root’ as a chroot store

Subsequent commands will reuse that folder as the nix store and nix will use user namespaces to mount that directory as /nix for each command.

Commands like nix store ls also work as expected as well:

$ ./nix store ls /nix/store/hrqijlzk6b56ij6g86vdh2b8mkv1i469-hello-2.12.1/bin/hello

The drawback to this option is the directory is not customizable.

Option 2: Running with bubblewrap

The other option use to use the bubblewrap tool to setup the mounts instead of relying on the built in features of nix. This allows customization of the underlying directory for the /nix mount and can also prevent nix from accessing certain directories as a security measure.

This can be done with the following command.

$ mkdir -p ./mynixroot/var/nix
$ bwrap --unshare-user --uid $(id -u) --gid $(id -g) --proc /proc --dev /dev --tmpfs /tmp \
        --ro-bind /bin/ /bin/ \
        --ro-bind /etc/ /etc/ \
        --ro-bind /lib/ /lib/ \
        --ro-bind /lib64/ /lib64/ \
        --ro-bind /run/ /run/ \
        --ro-bind /usr/ /usr/ \
        --ro-bind /var /var \
        --bind $(pwd) $(pwd) \
        --bind ~/.config/nix ~/.config/nix \
        --bind ./mynixroot /nix \
        ./nix build 'nixpkgs#'

This creates the /nix store under ./mynixroot and bind mounts most of the system as read only. nix will use the ./mynixroot for the nix store. This can be useful in select environments where using ~/.local/share/nix/root is not appropriate.

Other Alternatives

One alternative is the nix-user-chroot tool which also uses user namespaces to setup the /nix directory. However at the time of writing it has a bug which prevents strict sandboxing which means there is no guarantee builds will be hermetic. I think the built in chroot functionality or relying on bubblewrap is superior as it still allows nix to enable strict sandboxing for builds.

Conclusion

It’s very easy to setup nix without switching to NixOS or without root access. Just with one static binary it’s possible to use nix for a specific project.


  1. Strangely enough this is not mentioned on the download page at all↩︎