How to get a derivation path for `<nixpkgs>`

I’m using nixops to deploy a few NixOS severs. One problem I have is that the channels of the servers are basically never used so they get left behind. This causes issues when using things like nix-shell or the command-not-found helper as the programs it fetch will be outdated. My goal is to configure the default Nix path to use the nixpkgs version used to deploy the server.

My initial attempt was something like this:

nix = {
	# Don't use mutable channels, always use the deployed version.
	nixPath = ["nixpkgs=${<nixpkgs>}"];
	channel.enable = false;
};

This is seemingly close to what I want. The nix-channel command is removed (unnecessary but nice to remind people) and the NIX_PATH var has any previous channels removed.

However the store path that is pointed to is a dangling symlink, causing evaluations to fail. If I do nixos=${<nixpkgs/nixos>} I do get a nixos path that works. But this doesn’t get nixpkgs. It seems that any sub-file of <nixpkgs> is fine, but referencing the root will get a dangling symlink.

Is there a way to make this work? Maybe we can add something like pkgs.nixpkgs to nixpkgs that points at its own root directory as a derivation?

I think pkgs.path is what you’re looking for, should give you a path to the nix store:

$ nix-channel --list
home-manager https://github.com/rycee/home-manager/archive/master.tar.gz
nixos https://nixos.org/channels/nixos-unstable
$ nix-instantiate --eval -E 'with import <nixpkgs> { }; pkgs.path'
/nix/store/nfmbc543kq6i6q16klizax5rgghwjlrp-nixos/nixos
$ ls /nix/store/nfmbc543kq6i6q16klizax5rgghwjlrp-nixos/nixos
CONTRIBUTING.md  COPYING  default.nix  doc  flake.nix  lib  maintainers  nixos  nixpkgs  pkgs  programs.sqlite  README.md  svn-revision
2 Likes

Thanks! pkgs.path was exactly what I was looking for.

However this revealed a poor interaction (bug?) between nix.nixPath and nix.channel.enable = false. It seems that setting nix.channel.enable = false sets a default value for nix.settings.nix-path which causes the NIX_PATH environment variable set by nix.nixPath to be ignored. For my case I could easily work around this by setting nix.settings.nix-path directly. But IDK if it is worth fixing this for everyone.

My final config looks like this. Now my servers will pull programs from their deployed version which is very nice.

nix = {
	channel.enable = false;
	settings.nix-path = ["nixpkgs=${pkgs.path}"];
};

programs.command-not-found.dbPath = "${pkgs.path}/programs.sqlite";
1 Like

Unfortunately nix.channel.enable is problematic in certain cases, see nix-channel: do not set empty nix-path when disabling channels by oxalica · Pull Request #273170 · NixOS/nixpkgs · GitHub

2 Likes

One problem that I noticed is when deploying with a Git checkout of nixpkgs this would include the .git dir and things like ./result if I did a build in the checkout. This would copy a huge amount of always different data. (Although store hard-linking optimizations likely helped avoid exploding the actually used storage space.) This isn’t an issue for channels because they are already “clean”.

I managed to work around this by using lib.fileset to remove untracted files from whatever nixpkgs is being used.

{lib, pkgs, ...}: let
	path = lib.fileset.toSource {
		root = pkgs.path;
		fileset = lib.fileset.gitTracked pkgs.path;
	};
in {
	nix = {
		channel.enable = false;
		settings.nix-path = ["nixpkgs=${path}"];
	};

	programs.command-not-found.dbPath = "${path}/programs.sqlite";
}

This works with both nixpkgs from git (like -I nixpkgs=/path/to/nixpkgs) and when using regular channels.