NixOS module using system `nixpkgs` instead of the `nixpkgs` specified in the module's flake

I am using flakes in my system config. I am trying to add a myApp flake into my inputs and then use it. To do so I have to set programs.myApp.package to be the package from the myApp flake (inputs.myApp.packages.x86_64-linux.default). If I were not to do this, myApp would fail to build since my “main” nixpkgs from the system flake would be used (the version of Rust there isn’t new enough which will cause compilation errors) instead of the ones from myApp flake (where Rust is new enough)

I’ve asked about this on Matrix and it has been suggested this can be fixed by amending myApp flake. Would anyone be able to recommend a way to do so?

flake.nix

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
  };
  outputs = {nixpkgs, ...}: let
    pkgs = import nixpkgs {system = "x86_64-linux";};
  in {
    packages.x86_64-linux = {
      default = nixpkgs.legacyPackages.${"x86_64-linux"}.callPackage ./nix/package.nix {inherit pkgs;};
      devShells.default = import ./nix/shell.nix;
    };
    nixosModules.default = import ./nix/module.nix;
  };
}

nix/module.nix

{
  lib,
  config,
  pkgs,
  ...
}: let
  cfg = config.programs.myApp;
in
  with lib; {
    options.programs.myApp = {
      enable = mkEnableOption "myApp";
      package = mkOption {
        type = types.package;
        default = pkgs.callPackage ./package.nix {};
        description = ''
          The myApp package to use.
        '';
      };
    };
    config = mkIf cfg.enable {
      environment.etc."myApp.toml".text = ''...'';
    };
  }

nix/package.nix

{
  lib,
  pkgs,
}: let
  cargoTomlPath = ../Cargo.toml;
  cargoTomlContent = builtins.fromTOML (builtins.readFile cargoTomlPath);
in
  pkgs.rustPlatform.buildRustPackage rec {
    pname = "myApp";
    version = cargoTomlContent.workspace.package.version;
    src = lib.cleanSource ../.;
    cargoToml = cargoTomlPath;
    cargoLock = {
      lockFile = ../Cargo.lock;
    };
    nativeBuildInputs = with pkgs; [
      ...
    ];
    buildInputs = with pkgs; [
      ...
    ];
    LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
    LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}";
  }

Yes, you export a function that expects pkgs as an input and gets it from the system where the module is being used. So you get whatever nixpkgs is being used by the host system.

I solved this in the past by wrapping the module in another function layer where I pass in the flakes self-reference. So in your case, this would look something like this:

{self, system}:
{config, pkgs, lib, ...}:
[ ... All your stuff from nix/module.nix as before ... ]

Then change your module definition line to something like:

nixosModules.default = import ./nix/module.nix { inherit self system; };

Note: This kinda implies that you define a system variable, you can also hardcode "x86_64-linux" like you did before, but IMHO this makes things a lot easier if you ever wanna support other systems. Also, I added self to the list of arguments here, you will need that in any case:

outputs = { self, nixpkgs, ... }: let
    system = "x86_64-linux";
    pkgs = import nixpkgs { inherit system; };
  in

Then you can do something like this in your module.nix

package = mkOption {
  type = types.package;
  default = self.packages.${system}.default;
};

And it’s going to use the package with nixpkgs from your flake.

This works well for me, but still feels a bit clunky. I also wonder if there is a better solution available.

1 Like

This did the trick - thank you!!!

1 Like