How do I use my flake inputs from other NixOS module files

If you're using NixOS with Nix flakes, you're probably going to hit a point where you want to pull in a third-party module or package from another flake and use it in your NixOS config.

If you are doing everything inside flake.nix, this is pretty easy to do. Let's use agenix as an example (but this applies to any flake input you want to use in this way):

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    agenix.url = "github:ryantm/agenix";
    agenix.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = { nixpkgs, agenix, ... }: {
    nixosConfigurations.bulbasaur = nixpkgs.lib.nixosSystem {
      modules = [
        ({ pkgs, ... }: {
          imports = [
            ./bulbasaur/hardware-configuration.nix
            agenix.nixosModules.default
          ];

          networking.hostName = "bulbasaur";
          nixpkgs.hostPlatform = "x86_64-linux";

          environment.systemPackages = [
            pkgs.git
            agenix.packages.${pkgs.system}.default
          ];
        })
      ];
    };
  };
}

To use agenix in this way, we:

1. Add an agenix input to our flake.

2. Add agenix to the list of arguments for our outputs.

3. Reference the agenix argument in our NixOS configuration.

But putting everything in flake.nix isn't sustainable, so we're eventually going to want to move our NixOS config into a separate file (maybe even multiple files eventually). So let's do that:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    agenix.url = "github:ryantm/agenix";
    agenix.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = { nixpkgs, agenix, ... }: {
    nixosConfigurations.bulbasaur = nixpkgs.lib.nixosSystem {
      modules = [ ./bulbasaur ];
    };
  };
}

# bulbasaur/default.nix
{ pkgs, ... }: {
  imports = [
    ./hardware-configuration.nix
    agenix.nixosModules.default
  ];

  networking.hostName = "bulbasaur";
  nixpkgs.hostPlatform = "x86_64-linux";

  environment.systemPackages = [
    pkgs.git
    agenix.packages.${pkgs.system}.default
  ];
}

Unfortunately, now we can't reference agenix anymore. It's not in scope. It was available as an argument to outputs in flake.nix, but that doesn't cross the boundary to evaluating another file. To be able to use agenix from bulbasaur/default.nix, we need to find a way to pass it along explicitly.

Nix is a pretty flexible language, so there's more than one way to do this. I'm going to show the way that I think is best and that I prefer.

We're going to take advantage of a feature of the NixOS module system called specialArgs. When modules are evaluated, they can optionally accept various arguments provided by the module system. Common ones include pkgs, lib, and config. These arguments are passed to every module, and we can extend this list with arguments of our own:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    agenix.url = "github:ryantm/agenix";
    agenix.inputs.nixpkgs.follows = "nixpkgs";
  };

  # change 1
  outputs = { nixpkgs, ... }@inputs: {
    nixosConfigurations.bulbasaur = nixpkgs.lib.nixosSystem {
      modules = [ ./bulbasaur ];
      # change 2
      specialArgs = { inherit inputs };
    };
  };
}

Two changes here:

1. We added @inputs after the list of argument names for outputs. The { nixpkgs, … } syntax will pull out just the nixpkgs attribute from the argument, but adding @inputs will also give us a variable called inputs that contains all of the attributes. So in this case, inputs will include keys for self, nixpkgs, and agenix.

2. We pass specialArgs = { inherit inputs }; to nixosSystem. This means all of our modules will be able to take an inputs argument that includes all of our flake's inputs.

Now we can update our NixOS configuration to use the new argument:

# bulbasaur/default.nix

# change 1
{ pkgs, inputs, ... }: {
  imports = [
    ./hardware-configuration.nix
    # change 2
    inputs.agenix.nixosModules.default
  ];

  networking.hostName = "bulbasaur";
  nixpkgs.hostPlatform = "x86_64-linux";

  environment.systemPackages = [
    pkgs.git
    # change 3
    inputs.agenix.packages.${pkgs.system}.default
  ];
}

We added inputs to the list of arguments alongside pkgs, and then updated our references to agenix to get it from inputs.

That's it! If we split up the config into even more modules, any of them can use inputs as needed to pull in flake inputs. And if we add more inputs, those will also be available to our modules in the exact same way.

Proxy Information
Original URL
gemini://midna.dev/nix/faq/flake-inputs.gmi
Status Code
Success (20)
Meta
text/gemini;lang=en-US
Capsule Response Time
18.699224 milliseconds
Gemini-to-HTML Time
0.277991 milliseconds

This content has been proxied by September (UNKNO).