Import from files/dirs

I got a flake that defines a nixos configuration “foo”

  outputs =
    { self
    , nixpkgs
    , ...
    } @ inputs: {

    nixosConfigurations = {
      foo = nixpkgs.lib.nixosSystem {

Now I would like this into a separate file which should work like this I guess

      foo = import ./machines/foo.nix { inherit nixpkgs; };

and

{ nixpkgs, ... }: # does this need to match { inherit nixpkgs; }; ?
{
  nixpkgs.lib.nixosSystem {

    modules = [
    ]

    networking.hostName = "nixos";
    networking.domain = "local";
    system = "aarch64-linux";
    system.stateVersion = "23.05";

  }
}

Now I got a couple of questions:

Is there an easy way to import multiple nixosConfigurations similar to this?
I am not sure how one would define the name/reference “foo”.

    nixosConfigurations = import ./machines/*.nix { inherit nixpkgs; };

Or would I need to implement this myself?

And about this:

  outputs =
    { self
    , nixpkgs
    , ...
    } @ inputs: {

Why the self?
What defines the number of variables? Why is it self and nixpkgs here?
Why the ...?
Why the @ inputs:

Maybe I have missed the details in docs.
Either a pointer or explanation would be great.

Since you have ..., you can leave it out. self allows you to access values you are outputting.
Like if you output an overlay from your flake as overlays.default, in other attributes you can access it as self.overlays.default.

You always get at least self and your flake inputs. Any extra parameters you have are implicit inputs.

That means that the function has at least self and nixpkgs, but more is okay. If you leave out the ..., then you have to list all the parameters. For example if you have only the nixpkgs input, { self, nixpkgs } would work, as well as { nixpkgs, ... }, but { nixpkgs } would error due to unhandled self parameter.

That allows you to refer to the whole set of arguments as inputs. inputs.self is the same as self and inputs.nixpkgs is the same as nixpkgs.

You could implement it by getting the directory contents, and mapping them to the values. Or use genAttrs if you are okay with listing the names.

For available functions, can see: Nix (builtins) & Nixpkgs (lib) Functions

If you want it done for you, you could use a flake library. I would recommend GitHub - nix-community/flakelight: Framework for simplifying flake setup [maintainer=@accelbread] (though I am biased as its author).

2 Likes

That was really helpful input. Thank you very much.

Right now I am trying to use a function:

{
  description = "my servers";

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

  outputs =
    { self
    , nixpkgs
    , ...
    } @ inputs:

    let
      mkHost = import ./lib/mkHost.nix { inherit inputs; };
    in {

      nixosConfigurations.utm-arm = mkHost {
        system = "aarch64-linux";
        hostName = "nixos";
      };

    };
}

From what I understood the inherit inputs on the import should pass in the nixpkgs.
And with this file lib/mkHost.nix

{ nixpkgs }:
{
  system,
  hostName,
}: nixpkgs.lib.nixosSystem {

  # specialArgs = { inherit inputs; };

  # system = system;
  # system.stateVersion = "23.05";

  modules = [
    ./hosts/utm-arm/hardware-configuration.nix
  ];

  networking.hostName = hostName;
  networking.domain = "utm";

}

It should work - but I am getting

function '**anonymous lambda**' called without required argument '**nixpkgs**'

Using

mkHost = import ./lib/mkHost.nix { inherit nixpkgs; };

improves things but there I am getting

function 'anonymous lambda' called with unexpected argument 'networking'

So obviously some more inputs are missing.

Another problem that raises the question of attribute context. This cannot really work

system = system;
system.stateVersion = "23.05";

but how do I reference the one system or the other?
It seems one really is nixpkgs.lib.nixosSystem.system and the other system.stateVersion.

Ha! Seems like I got it:

  outputs =
    { self
    , nixpkgs
    , ...
    } @ inputs:

    let
      mkHost = import ./lib/mkHost.nix { inherit nixpkgs; };
    in {

      nixosConfigurations.utm-arm = mkHost {
        hostPlatform = "aarch64-linux";
        hostName = "nixos";
      };

    };
{ nixpkgs }:
{
  hostPlatform,
  hostName,
}: nixpkgs.lib.nixosSystem {

  modules = [
    ../hosts/utm-arm/hardware-configuration.nix

    {
      nixpkgs.hostPlatform = hostPlatform;
      networking.hostName = hostName;
      networking.domain = "utm";
      system.stateVersion = "23.05";
    }
  ];
}

Still not quite sure why inherit inputs doesn’t work though.

{ inherit inputs; } is equivalent { inputs = inputs; } so in mkHost you’d need { inputs }: and to use inputs.nixpkgs.

You probably want the following instead:

  outputs =
    { self
    , nixpkgs
    , ...
    } @ inputs:

    let
      mkHost = import ./lib/mkHost.nix inputs;
    in {

      nixosConfigurations.utm-arm = mkHost {
        hostPlatform = "aarch64-linux";
        hostName = "nixos";
      };

    };
{ nixpkgs, ... }:
{
  hostPlatform,
  hostName,
}: nixpkgs.lib.nixosSystem {

  modules = [
    ../hosts/utm-arm/hardware-configuration.nix

    {
      nixpkgs.hostPlatform = hostPlatform;
      networking.hostName = hostName;
      networking.domain = "utm";
      system.stateVersion = "23.05";
    }
  ];
}
1 Like