4 minutes
Written: 2021-07-21 02:01 +0000
Updated: 2024-08-06 00:53 +0000
Nix, Channels and the NUR
A short exploration of multi-user
nix
and interacting with the Nix User Repository withoutroot
Background
For multi-user nix
installations 1, the NIX_PATH
variable is empty. Here I briefly go over two approaches to mitigate this, one with nix-channel
and the other by manual pinning. Note that this post will eventually be superseded for most cases by a better flake
workflow.
Channels
The idea behind using a channel is essentially that the tar
at a particular commit / tag will be downloaded and stored, typically at $HOME/.nix-defexpr/channels
. This is in-fact a symlink typically as seen below.
1tree -L 2 ~/.nix-defexpr/
2/users/home/rg/.nix-defexpr/
3├── channels -> /nix/var/nix/profiles/per-user/rg/channels
4└── channels_root -> /nix/var/nix/profiles/per-user/root/channels
This is in keeping with the profile data, since by default in a multi-user setup we have the equivalent of running nix-env --switch-profile /nix/var/nix/profiles/per-user/rg/profile
.
With that aside out of the way, we can start by setting up baseline channels:
1# Bad idea, see notes
2# nix-channel --add https://github.com/NixOS/nixpkgs/archive/nixos-21.05.tar.gz nixpkgs
3nix-channel --add https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz unstable
4nix-channel --add https://github.com/nix-community/NUR/archive/master.tar.gz nur
5nix-channel --update
This leads to:
1tree /nix/var/nix/profiles/per-user/rg/channels
2/nix/var/nix/profiles/per-user/rg/channels
3├── manifest.nix -> /nix/store/smh16xjbbhcrlzhrlbl71nm16dhz7pb5-env-manifest.nix
4├── nixpkgs -> /nix/store/c1hfsx2v1cy50j7ax3y34gwwpzzvrvm5-nixpkgs-21.05/nixpkgs
5├── nur -> /nix/store/za2fqbj1z9q6hiip39642hjfa0a59mls-nur/nur
6└── unstable -> /nix/store/a4bb70ihv8x8zxj0zw4wdafl62n9a92q-unstable/unstable
Now we can use these names ('<nur>'
, '<unstable>'
and '<nixpkgs>'
) to write a relatively straightforward config.nix
as follows:
1# Uses the channel form of the installation
2# Goes in ~/.config/nixpkgs/config.nix
3{
4 # NUR setup
5 pkgs = import <unstable>; # nixos-unstable channel
6 packageOverrides = pkgs: {
7 nur = import <nur> { # Also defined as a channel
8 inherit pkgs;
9 };
10 };
11}
The above snippet can be modified to swap out unstable
for nixpkgs
as well. It is generally a good idea to define nixpkgs
anyway to make sure we can do things like nix-env -iA cowsay -f '<nixpkgs>'
. This allows us to now interact with nur
packages without any trouble.
1nix-shell -p nur.repos.mic92.hello-nur
2hello
3> Hello, NUR!
Notes
- We need to define
pkgs
since by defaultNIX_PATH
is empty here - For more on
packageOverrides
, there’s a pill for that - For a refresher on
profiles
, there’s thenix
manual, specifically chapter 10 - Defining
nixpkgs
as a channel causeswarning: name collision in input Nix expressions
and it gets skipped - The problem with channels is of course that it is hard to keep control of update which occur when users run
nix-channel --update
Pinning Packages
To mitigate the impurity introduced by nix-channel --update
we can opt to instead pin both the pkgs
and nur
to a particular hash. The benefit is that we can dispense with any channel
management and still be guaranteed to have the same behavior across different machines.
The star snippet here is focused around builtins.fetchTarball
, which takes a url
and a sha256
hash. Using this, our config.nix
now becomes:
1# A config.nix
2{
3 pkgs = import (builtins.fetchTarball {
4 url = "https://github.com/NixOS/nixpkgs/archive/2588a6c9f1a3279d523e7bd0068da4e12db76ca8.tar.gz";
5 sha256 = "0x1q5sf04qk6d05gn4rr2hmlfz17mfkd9fsxwc0kdbhx5yl65zwy";
6 });
7 packageOverrides = pkgs: {
8 nur = import
9 (builtins.fetchTarball {
10 url = "https://github.com/nix-community/NUR/archive/4a81d63c672ffdbbb999eb2549c0eb0934b3384b.tar.gz";
11 sha256 = "09bg0xifdk6rw35w472rk0dj69w08c1ksb3a3qrzay0ww69495zk";
12 }
13 )
14 {
15 inherit pkgs;
16 };
17 };
18}
To update things in a controlled manner, use a commit from the repository as the URL, and then:
1url="https://github.com/nix-community/NUR/archive/3a6a6f4da737da41e27922ce2cfacf68a109ebce.tar.gz"
2nix-prefetch-url --unpack $url
Which will emit the required hash. With this setup the usage is as before:
1nix-env -f '<unstable>' -iA nur.repos.mic92.hello-nur
2hello
3> Hello, NUR!
Note that the uninstall is still best done after checking the name with nix-env -qa
:
1nix-env -e hello # not hello-nur
Bonus
Locale Errors
For some, dropping into nix-shell
causes bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)
. In this case add the following to the .bashrc
or its equivalent rc
file.
1export LOCALE_ARCHIVE="$(nix-build --no-out-link "<nixpkgs>" -A glibcLocales)/lib/locale/locale-archive"
Conclusions
Much of the channel workflow is discussed directly in this issue. The main usage of this is actually to setup and use user defined packages in a more natural way, and to move away from channels
which typically only update NIX_PATH
when run as root
, which in turn causes much confusion. NUR is also flake
compatible and this will be explored further in a another post.
Like the setup on the
elja
super-computing cluster of the Icelandic Research High Performance Compute cluster ↩︎