NixOS specialArgs pattern

Borrowed from here

problem: you want to get the home-manager nixos module from the home-manager flake into a nixos config:

the home-manager documentation says this: https://nix-community.github.io/home-manager/index.html#sec-flakes-nixos-module

{
  description = "NixOS configuration";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs@{ nixpkgs, home-manager, ... }: {
    nixosConfigurations = {
      hostname = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ./configuration.nix
          home-manager.nixosModules.home-manager
          {
            home-manager.useGlobalPkgs = true;
            home-manager.useUserPackages = true;
            home-manager.users.jdoe = import ./home.nix;

            # Optionally, use home-manager.extraSpecialArgs to pass
            # arguments to home.nix
          }
        ];
      };
    };
  };
}

I find this ugly, because it forces to have the home manager include (modules = [ ... home-manager.nixosModules.home-manager ... ];) in the flake.nix, because only there the inputs or home-manager attrset is in scope

In "old" configurations, with niv or plain fetchTarball, you would have done this in the configuration.nix of the respective host(, or a common.nix, if it should be included on all hosts) imports = [ <home-manager/nixos> ]; actually anywhere in any nixos config file

The solution is the following:

flake.nix


{
  description = "NixOS configuration";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    home-manager.url = "github:nix-community/home-manager";
    home-manager.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs@{ nixpkgs, ... }: {
    nixosConfigurations = {
      hostname = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        specialArgs = { inherit inputs; };
        modules = [
          ./configuration.nix
        ];
      };
    };
  };
}

configuration.nix

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

{
  networking.hostName = "foo";
  [...]

  imports = [
    inputs.home-manager.nixosModules.home-manager
  ];
  home-manager.useGlobalPkgs = true;
  home-manager.useUserPackages = true;
  home-manager.users.jdoe = import ./home.nix;
}

specialArgs means, the inputs attrset is now available in the module args in every nixos module. just add it to the function parameters anywhere you need it, like you do it with pkgs, lib and config. specialArgs also means that in contrast to _module.args this parameter to the module system is fixed, and can not be changed by nixos modules themselves. this prevents infinite recursions when using stuff from the inputs attrset in nixos module imports (which is exactly what we want to do).

and like this using flake inputs in nixos configs becomes much easier and more natural. in many cases you can rewrite "old" configs 1:1 to this new pattern without moving the includes to flake.nix