The Complete Computer

A Custom NIX_PATH and local nixpkgs checkout

LifeTechEmacsArcology

Because of some silly crap that happened to my system in 2k22, I moved to using a local nixpkgs checkout synced between my systems with Syncthing . Here's why and how.

"The manual appears to depend on the location of Nixpkgs"

Some time in 2022 my computer began to experience an issue which perhaps no one had experienced before or since, where nixpkgs would fail to build with an absurd error and with steps that did not make sense to me:

shell source: 
<string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/rename.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/system/etc/etc.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/system/etc/etc.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/system/etc/etc.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/system/etc/etc.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/system/etc/etc.nix" /> [...redacting much of this] <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/virtualisation/qemu-vm.nix" /> <string value="3y2ymjdad71ilw7rpl71xwsj0pv9js36-nixpkgs/nixpkgs/nixos/modules/virtualisation/qemu-vm.nix" /> The manual appears to depend on the location of Nixpkgs, which is bad since this prevents sharing via the NixOS channel. This is typically caused by an option default that refers to a relative path (see above for hints about the offending path).

Something about my system's invocation of the subsystems which dynamically generate the OS configuration manpages man configuration.nix and co were failing to strip the nixpkgs path from the docbooks which were being generated, and I couldn't figure out why. I could disable the documentation but I use the documentation a lot when developing Arroyo , when assembling the Wobserver , when updating my system. I can't simply disable that and rely on "oh just grep the nixpkgs source code or use this webpage someone else hosts"... In fact I'm using it right now to draft this blog post which will then reintegrate in to my system!

But I couldn't update more than once:

  • nix channel --update

  • morph deploy # works

  • morph deploy # fails

  • nix channel --rollback to known-working nixpkgs+nixos

  • morph deploy # still fails!

  • switch-to-configuration to the previous build

  • morph build # works

Frustrating. And no one really has seen anything like this or could tell me much about how to fix it. I spent some weeks trying to figure out how to build My NixOS configuration as a nix flake since that would make it easier to bisect or narrow down problems without having to do all this nix-channel chicanery but I'm still not ready to go all-in on nix flakes so something else must be done. We'll rip out nix-channel and provide our own NIX_PATH.

Briefly: NIX_PATH

The Nix Pills guide talks about NIX_PATH in chapter 15.

NIX_PATH is used to resolve things like import <nixpkgs> and in the default configuration this points to a few places, and it'll go through them looking for a valid nixpkgs:

  • /home/rrix/.nix-defexpr/channels

  • nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos

  • nixos-config=/etc/nixos/configuration.nix

  • /nix/var/nix/profiles/per-user/root/channels

You can see how it works here by looking at the results of these four commands:

shell source: :exports both
nix-instantiate --eval -E '<nixpkgs>' NIX_PATH=/home/rrix/.nix-defexpr/channels:nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels nix-instantiate --eval -E '<nixpkgs>' NIX_PATH=/home/rrix.nix-defexpr/channels:nixpkgs=/home/rrix/Code/nixpkgs nix-instantiate --eval -E '<nixpkgs>' NIX_PATH=nixpkgs=/home/rrix/Code/nixpkgs nix-instantiate --eval -E '<nixpkgs>'
/home/rrix/Code/nixpkgs
/nix/var/nix/profiles/per-user/root/channels/nixos
/home/rrix/Code/nixpkgs
/home/rrix/Code/nixpkgs

Overriding my NIX_PATH to build with local nixpkgs

To bootstrap my system back together I updated my local nixpkgs git checkout to the version listed on status.nixos.org and then Morph 'd Meadow Crush , my GPD Pocket , as a development mule to validate that it wouldn't have the same issues.

 source: 
[nix-shell:~/Code/nixpkgs]$ export NIX_PATH=nixos=/home/rrix/Code/nixpkgs/nixos:nixpkgs=/home/rrix/Code/nixpkgs:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels; [nix-shell:~/Code/nixpkgs]$ morph deploy ~/org/cce/nixlib/nixops/laptops.nix switch --on=meadow-crush --passwd

And then I did it again. And then I did it from within Meadow Crush . The only problems are that a few GUI applications miss cache.nixos.org the Binary Cache, well okay, that's fine at least I'm not running out of date software with bugs fixed upstream.

I took the plunge and had Meadow Crush deploy back to my Framework laptop , Virtuous Cassette , and that worked:

 source: 
[nix-shell:~/Code/nixpkgs]$ cat /etc/os-release | grep BUILD_ID BUILD_ID="22.05.git.41cc1d5d958"

But I still cannot "simply" update my system channels and deploy! In theory I could spend some time tracking down the differences in the evaluation, but I simply would rather not. In fact, I have wanted to move away from using channels in general so that I could have a declarative pin for my nixpkgs version. I could just ... have that. With flakes you don't need to have a nixpkgs lying around to bootstrap your system and with this model you still do, though frankly I am not so concerned with that as I use Syncthing already to keep this all in a reasonable state of flow.

And so what if I just make my single-admin systems use the nixpkgs i already have locally cloned? you can set nix.nixPath to configure your system's NIX_PATH default value:

nix source: :tangle ~/arroyo-nix/nixos/nix-path.nix
{ ... }: let nixpkgs-unstable = builtins.getFlake "github:nixos/nixpkgs/nixpkgs-unstable"; in { nix.nixPath = [ "nixos=/home/rrix/Code/nixpkgs/nixos" "nixpkgs=/home/rrix/Code/nixpkgs" "arroyo=/home/rrix/arroyo-nix" "projects=/home/rrix/Code" "org=/home/rrix/org" "nixpkgs-unstable=${nixpkgs-unstable.outPath}" ]; imports = [ ./nix-flake-registry.nix ]; }

This allows one to (on non-flake builds) to reference things like <projects/feediverse/default.nix> to get to my Feediverse package, and you could do this too with some changes here. It does make bootstrapping a system a bit more complex, these paths need to be provided in nix-build invocations with -I if NIX_PATH isn't set..

Nix3 Flake Registry Configuration

Setting up nix3 (flake commands) to work with the local nixpkgs clone as well. There's two ways to do this and they both have drawbacks, I think, maybe in different ways.

Setting nix.registry.nixpkgs.flake to a flake path will work, but it'll copy the current nixpkgs to /nix/store, calculate its hash and that'll be what nix run nixpkgs#hello will use until the next deploy, and any future nix run commands will evaluate against the pre-computed /nix/store as opposed to the live path which nix-shell etc evaluate against. If you are building your NixOS configuration using a flake, this is ideal, because you'll have a nixpkgs variable in the top level scope of your outputs function and can just say nix.registry.nixpkgs.flake = nixpkgs and nix flake update to your heart's content. But alas.

Setting the from and to attribute sets will be directly written out to the registry.json file in the format that they're specified in the docs while the first option is not, it is only specified in man configuration.nix. But this way, it'll only realise a /nix/store path when you first invoke a nix run or other nix3 command. It does this every time, repeated copies of /home/rrix/Code/nixpkgs to /nix/store is not ideal even if they share the same address!

We'll eat the cost on the build end, if I end up needing to fix nixpkgs in Arroyo or Arcology builds or something I can deal with it there.

nix source: :tangle ~/arroyo-nix/nixos/nix-flake-registry.nix
{ pkgs, ... }: { nix.registry.nixpkgs.flake = builtins.getFlake "git+file:/home/rrix/Code/nixpkgs"; nix.registry.arroyo.flake = builtins.getFlake "git+file:/home/rrix/org/arroyo"; nix.registry.nixpkgs-unstable.flake = builtins.getFlake "github:NixOS/nixpkgs/nixpkgs-unstable"; # nix.registry.nixpkgs = { # from = { id = "nixpkgs"; type = "indirect"; }; # to = { type = "path"; path = "/home/rrix/Code/nixpkgs"; }; # }; # nix.registry.arroyo = { # from = { id = "arroyo"; type = "indirect"; }; # to = { type = "path"; path = "/home/rrix/arroyo-nix/"; }; # }; }

Keeping my nixpkgs checkout up to date

Updating the Nixpkgs checkout can be by invoking something like cce/update-nixpkgs-checkout:

emacs-lisp source: :results none :tangle ~/org/cce/nixpkgs.el
(defun cce/update-nixpkgs-checkout () (interactive) (let ((default-directory "/home/rrix/Code/nixpkgs/")) (with-current-buffer (find-file-noselect default-directory) (shell-command "git fetch --all") (magit-merge-plain "origin/nixos-25.11")))) (provide 'cce/nixpkgs)

This relies on Magit .