I feel like NIX_PATH creates more issues than it fixes

Two issues from my point of view:

  1. NIX_PATH gets cached since applications do not re-source /etc/set-environment due to this if statement located in /etc/profile
if [ -z "$__NIXOS_SET_ENVIRONMENT_DONE" ]; then
    . /nix/store/sm7s4dlgji6p2gzigh5xalxwly372ijg-set-environment
  1. PAM environment depends on NIX_PATH (I am not sure why :/) which means that it gets rebuilt each time NIX_PATH changes, and in my case it happens on each rebuild:
❯ echo $NIX_PATH | sed 's/:/\n/g'

Admittedly, I am planning on changing the implementation of the use cases I am handling here, so I guess it’s not much of an issue.

An alternative implementation of NIX_PATH:
If possible - should get directly passed to applications that require it via config.nix.nixPath - otherwise file is read when required from /etc/nix which contains its definition (similar to the registry)

Could it be that I am missing some use cases?

1 Like

Based on what you describe here you are probably better off using flakes already.


I am using flakes, but as I said - I will change my flakes implementation at least partially. But then again, it’s not like flakes deprecate NIX_PATH

Edit: now that I think about it, in the case when using flakes (nixUnstable) - the only use case is when using nix repl (assuming you don’t have any automagic script for it). Cant think of any other “legit” cases

Better use something like this:

  environment.etc."channels/nixpkgs".source = nixpkgs.outPath;
  environment.etc."channels/home-manager".source = home-manager.outPath;
  nixPath = [

where nixpkgs and home-manager are flake inputs.

The entries in /etc will be relinked during activation and your applications will be able to see the updated channels.


I don’t think that /etc is the correct place to place em but yeah, I was thinking about something similar.

I created this post, not in search of a workaround, but to create a discussion if we actually can consider getting rid of the NIX_PATH environment variable. I feel like this would be a quite an appropriate addition to Nix 3.0

An empty NIX_PATH works also with nix repl. Just use builtins.getFlake instead of :l or import.

$ NIX_PATH= nix repl
Welcome to Nix version 2.4pre20210308_1c0e3e4. Type :? for help.

nix-repl> :l <nixpkgs>
error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I)

nix-repl> flake = builtins.getFlake "/etc/nixos"

nix-repl> flake.inputs.nixpkgs.legacyPackages.x86_64-linux.hello
«derivation /nix/store/70vmydmb7hv290k7v459qlcqmnykpzxq-hello-2.10.drv»

I’ve actually used an empty NIX_PATH for a couple of days, but I’ve found myself using some legacy utilities like nix-shell once in a while and other tooling like nixpkgs-review does not work with flakes yet. This inconvenience wasn’t worth it for me and I went back to using NIX_PATH with flake inputs linked into /etc.


A few people and I discussed this on Discord a earlier. My side:

My <nixpkgs> is literally now just a wrapper around builtins.getFlake, so that nix-shell -p still works. It’s also a bit easier to use :l <nixpkgs> than :a (builtins.getFlake "flake:default").outputs.legacyPackages.${builtins.currentSystem}. If we can get nix-shell -p and nix repl to behave better with flakes, I think I can pretty much do without NIX_PATH.


@dramforever how do you accomplish this?

1 Like

Sorry for bumping an old thread, I reached here via a web search after I failed to make my NixOS flake setup use the nixpkgs flake input as the nixPath argument. This may work for someone:

# /etc/nixos/flake.nix
    outputs = { self, nixpkgs }: {
      HOSTNAME = nixpkgs.lib.nixosSystem {
         system = "x86_64-linux";
         modules = [ (import ./configuration.nix nixpkgs.outPath) ];

# /etc/nixos/configuration.nix
# an argument that for nixPath for evaluation without internet

{ config, pkgs, options, lib, ... }:

  # ....
  nix.nixPath = [
    # don't use `nixpkgs` as argument at the top - to not conflict with the configuration option nixpkgs

Link to what was evaluated today for my system:


What exactly do you mean when you say “did not work”?

Did you see any error? Was the NIX_PATH not set properly? Anything else?

It’s weird, it didn’t evaluate earlier but now I can’t reproduce., because I didn’t notice I used both nixpkgs as an argument at the top of configuration.nix (and not nixpkgs-path) and nix confused it with the nixpkgs nixos option.

I edited the original message.


I moved it to the flake.nix itself though, I prefer the locality:

      modules = [
        # Use the flake path for the nix path
        { nix.nixPath = [ "nixpkgs=${nixpkgs.outPath}" ]; }


Slight issue with that method. By doing "nixpkgs=${nixpkgs.outPath}", you’re hardcoding the path in the shell environment. So if you update nixpkgs, your open terminals will still see <nixpkgs> pointing to the old version.

Something similar I’m experimenting with that solves this:

  outputs = { nixpkgs, ... }@inputs: let
    flakeWrapper = name: builtins.toFile "${name}.nix" ''
      import (builtins.getFlake "${name}")
    registry = { 
      nix.registry = nixpkgs.lib.mapAttrs (n: flake: { inherit flake; }) inputs;
      nix.nixPath = nixpkgs.lib.mapAttrsToList (name: _: "${name}=${flakeWrapper name}") inputs;
  in {
    nixosConfigurations.kruphix = nixpkgs.lib.nixosSystem {
      system = "aarch64-linux";
      modules = [

I set the system flake registry so that builtins.getFlake "nixpkgs" always returns the nixpkgs that the current system was built with. And NIX_PATH is set to point <nixpkgs> to a wrapper file that will always import from builtins.getFlake, so that NIX_PATH doesn’t need to be updated for <nixpkgs> to be updated.

Only problem is that <nixpkgs> doesn’t actually point to a nixpkgs tree, so you can’t do stuff like <nixpkgs/nixos>. There’s workarounds though, like (import <nixpkgs> {}).path + "/nixos", but this isn’t a given for any flake, and it’s kinda ugly. So I dunno if I’m going to keep this.

Ah, old thread.
For the record at this point, this is how I am dealing with nix path and registry.
Extra feature: It allows me to browse my flake inputs code via /etc/nix/inputs/xyz

1 Like

Ah, that’s very good. I like it

1 Like

Thanks for this tip, yesterday I was running into this. I wound up pulling the store path of source and then imported that in nix-shell, but this is way cleaner.

Well - so what you’re saying is that open terminals need to access some global state.

Solution: put the flake sources in the system derivation and point the path to nixpkgs=/run/current-system/flakes/nixpkgs

1 Like

I did it a little bit differently…

I used tmpfile.d to maintain symlinks to the correct “pins” and let my NIX_PATH point to those links:

It is not yet perfect, though the best I was able to come up with in a hurry. I’d like if there wasn’t a need to specify everything 3 times or so…

1 Like

The more I think about it, the more sense it makes to me to tie the nixpkgs path to the current system, so that nix-shell and friends use what the rest of the current system is using.

Now I wonder how hard it is to express that in Nix.