Using flakes in home-manager module (especially for unfree packages)

Hi all-

I’ve been on a bit of an adventure converting my NixOS configuration to a flake, including my home-manager config. It has mostly worked, but I have run into some issues that I’m not sure how to fully resolve.

As part of my system configuration flake, I declaratively define flake registry entries:

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05";
  inputs.nixpkgs-unstable.url = "github:NixOS/nixpkgs";
  inputs.home-manager = {
    url = "github:nix-community/home-manager/release-22.05";
    inputs.nixpkgs.follows = "nixpkgs";
  };
  inputs.elgato.url = "github:waxlamp/elgato/flakes";

  outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, elgato }: {
    nixosConfigurations.kahless = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./configuration.nix

        home-manager.nixosModules.home-manager {
          home-manager = {
            useGlobalPkgs = true;
            useUserPackages = true;
            users.roni = import ./home.nix;

            extraSpecialArgs = {
              inherit elgato;
            };
          };
        }

        # Set up nix registry with nixpkgs flakes.
        ({ lib, ... }: {
          nix.registry.nixpkgs.flake = nixpkgs;
          nix.registry.nixpkgs-unstable.flake = nixpkgs-unstable;
          nix.registry.elgato.flake = elgato;
        })

        # Configure nixpkgs.
        ({ ... }: {
          nixpkgs.config = {
            allowUnfree = true;
          };
        })
      ];
    };
  };
}

so that I can use the experimental nix commands, but I’d like to use these same flakes to specify packages in both the configuration.nix and home.nix files. As you can see above, I am currently passing the elgato flake into the home-manager config via extraSpecialArgs, and I am able to use that flake to install the elgato package.

However, if I similarly pass in nixpkgs and nixpkgs-unstable, I cannot install, e.g., spotify or other unfree packages from those flakes (I get the error message about how to enable unfree packages, etc.). I know I can put nixpkgs.config.allowUnfree = true; into home.nix, and then use the module’s pkgs argument to install those packages, but I was hoping to be able to do so directly from my nixpkgs flake.

So, here are my questions:

  1. How is the pkgs argument passed into the system flake’s modules? This is largely a question about how the system flake runtime works. If I want finer control (or simply a better understanding) of how the pkgs argument reaches the modules, what should I do here? This is important because I may also want to install packages from a source other than the pkgs argument, for example, from my nixpkgs-unstable flake.
  2. How do I configure these flakes to allow for installing unfree packages from them?
  3. Is there a better way to pass flakes into my home-manager config than the extraSpecialArgs mechanism?

I can share other parts from my config if needed. Thanks!

  • You can pass specialArgs to nixpkgs.lib.nixosSystem, having the same effect as extraSpecialArgs does for home-manager. That should allow you to access inputs from wherever you want.
  • If you want to configure an extra branch of nixpkgs, you should be able to do it like this: import nixpkgs-whatever { system = ...; config.allowUnfree = true; } Note the lack of <...> around nixpkgs-whatever. This is importing the flake input, not a NIX_PATH lookup.
  • specialArgs is generally the best way to do it with flake inputs, though for certain other things, _module.args can be used instead. For flake inputs that’s a bad idea because it leads to infinite recursion if you try to add something to imports from it.
1 Like

Thank you, this worked beautifully!!

Could you explain how import works with those flake inputs? I thought a flake was “just” a special kind of attrset, but perhaps import treats those differently somehow?

It’s being automatically coerced to a string representing the nix store path of the flake. Any attrset with a .outPath attribute can be coerced like this. That’s how "${pkgs.hello}/bin/hello" works too, since derivations are also special attribute sets.

1 Like

Ah, wonders never cease! Thanks for this explanation.

Does this mean that if I want to be able to import a flake, I should supply a default.nix in its repository? And, secondary question: when I do import nixpkgs { ... }, is there special processing for the legacyPackages output attribute?

Yes, import flakename is importing that default.nix. You can also import arbitrary files with import "${flakename}/path/to/file.nix".

legacyPackages has nothing to do with import nixpkgs { ... }. legacyPackages is a flake output attribute, but using import like this is treating nixpkgs as a non-flake and entering from default.nix instead, so the entire flake output schema isn’t part of the equation.

In other words, import nixpkgs { ... } will evaluate to a pkgs-like value, not a flake output structure.

1 Like

Thanks–not sure how I missed that the source directory for the nixpkgs flake has a default.nix file. I think things make more sense now.