Respect nixpkgs input in custom module

I have a custom NixOS module that I use in my flake and I have an issue with the nixpkgs input not being respected.

My module flake has the following (shortened for brevity):

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
  };

  outputs =
    { self
    , nixpkgs
    }:
    {
      homeManagerModule = import ./modules/home-manager;
      nixosModules = {
        earth-view = import ./modules/nixos;
        default = self.nixosModules.earth-view;
      };
    };
}

As you can see, I specifically set nixpkgs input to be on nixos-23.11. However, when I use the module in my nix-config flake, it takes the nixpkgs input defined in my nix-config, not the one defined in the module…

I have to mention that I didn’t use the inputs.nixpkgs.follows option, so I don’t understand why is this happening? Is it because the module flake uses nixpkgs for the input name, as does the nix-config flake (and somehow the nixpkgs defined in nix-config takes precedence)?


In order to work around this, I had to do the following in my nix-config flake:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-23.11";

    earth-view = {
      url = "github:nicolas-goudry/earth-view";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, ... }@inputs: {
    nixosConfigurations = {
      g-xps = let
        system = "x86_64-linux";
      in inputs.nixpkgs.lib.nixosSystem {
        inherit system;
        specialArgs = inputs;
        modules = [
          ./hosts/g-xps
          {
            _module.args = {
              pkgs-stable = inputs.nixpkgs-stable.legacyPackages.${system};
            };
          }
        ];
      };
    };
  };
}

(I tried using inputs.nixpkgs.follows = "nixpkgs-stable"; but it didn’t work…)

I updated the custom module definition to do this:

{ config, lib, pkgs, pkgs-stable ? pkgs, ... }: {}

Is it possible to force the module to use the nixpkgs input defined in its flake, while still allowing users to use inputs.nixpkgs.follows to override the default?

Aside from the _module.args option I couldn’t find anything relevant in the Nix docs. And it seems to be user-oriented, not module developer oriented.

1 Like

I would consider it bad practices to force the use of the flake nixpkgs in the module, as this creates an additional instances of the 1000 evaluation of Nixpkgs.

Modules are working within the evalModules function which define what is the pkgs argument given to every modules and from which packages should be taken from. Failures to use this argument, and attempts to use a different Nixpkgs set can yield some various weird issues such as unexpected opengl versions, where the system has one global state and any miss-match would cause unexpected behavior.

If you want a single version of Nixpkgs for the whole system:

  • Then create a module to set the version of Nixpkgs:
  myNixpkgsModule = { config, ...}: {
    nixpkgs.pkgs = import nixpkgs { # flake input.
      inherit (config.nixpkgs) config overlays localSystem crossSystem;
    };
  };

If you need multiple versions of Nixpkgs, I can see 3 ways to work around this issue:

  • The program you are looking for can be carried in an overlay, in which case making an overlay which cherry-pick the few packages of interest and use nixpkgs.overlays.
  • Use of the virtualisation options to create a small sub-system which would be working with whatever specific version of Nixpkgs you need, which can be specified like the single version of Nixpkgs, as above, but within the scope of the virtualized environment.
  • Embrace the risk in case of global version miss-match, like opengl, then you can set the pkgs option of some services, or replace the package using an overlay:
  myImportedPackage = { config, ... }: let
    myPkgs = import nixpkgs { # flake input.
      inherit (config.nixpkgs) config overlays localSystem crossSystem;
    };
  in {
    nixpkgs.overlays = [ (final: prev: { package = myPkgs.package; })];
    services.packaged.pkgs = myPkgs.package;
  }
2 Likes