NixOS + home-manager config where both use flakes

Hi everyone, I wondering the following:

My home-manager config is a nix flake on github. How do I import it into my nixos config?

Setup:

I have 2 repositories, one containing my nixos config, one containing my home-manager config. Both are nix flakes and work perfectly individually.

My nixos flake looks as follows:


{
  description = "Nixos config flake";

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

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

    home-manager-config = {
      url = "/home/cgahr/.config/home-manager";
      # url = "git+ssh://git@github.com/cgahr/home-manager-config";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.home-manager.follows = "home-manager";
    };
  outputs = { self, nixpkgs, ... }@inputs:
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };
    in
    {
      nixosConfigurations.default = nixpkgs.lib.nixosSystem {
        specialArgs = {
          inherit inputs pkgs;
        };
        modules = [
          ./configuration.nix
          inputs.home-manager.nixosModules.default
          {
            home-manager.users."cgahr" = inputs.home-manager-config.homeConfigurations."cgahr";
            # home-manager.users."cgahr" = inputs.home-manager-config.nixosModules.home;
            home-manager.extraSpecialArgs = { inherit inputs; };
          }
        ];
      };
    };
}

My home-manager flake looks as follows:


{
  description = "Home Manager configuration of cgahr";

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

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

    nix-colors.url = "github:misterio77/nix-colors";
  };

  outputs =
    { home-manager
    , nix-colors
    , nixpkgs
    , ...
    }@inputs:
    let
      system = "x86_64-linux";

      pkgs = import nixpkgs { inherit system; };
    in
    {
      # nixosModules.home = import ./home.nix;
      homeConfigurations."cgahr" = home-manager.lib.homeManagerConfiguration {
        inherit pkgs;

        modules = [ ./home.nix ];

        extraSpecialArgs = {
          inherit inputs;
        };
      };
    };
}

As you can see, my personal home-manager config is a nix flake residing on github (private), but for debugging purposes I import the local folder.

What I have tried so far:

Running sudo nixos-rebuild switch --flake .#default --impure fails with the following error

error: Module `:anon-6:anon-1' has an unsupported attribute `activation-script'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: activation-script activationPackage extendModules newsDisplay newsEntries pkgs) into the explicit `config' attribute.

If I uncomment the commented line in each flake respectively, I get the following error:

error: attribute 'nix-colors' missing

I’m at a loss at what to do. Any help is appreciated!

Running sudo nixos-rebuild switch --flake .#default --impure fails with the following error

error: Module `:anon-6:anon-1' has an unsupported attribute `activation-script'.

I might be wrong, but homeConfigurations is an output that is special to home-manager; it behaves kinda like lib.nixosSystem and produces a computed configuration of a user. Perhaps with some lib.evalModule tricks you could convince your config flake to reuse that output from your home flake but the machinery would probably be very brittle.

If I uncomment the commented line in each flake respectively, I get the following error:

error: attribute 'nix-colors' missing

The commented lines is the approach that I have personally used to reuse homeConfigurations between flakes. The missing attribute happens because nix-colors is not passed to the module import site in your config flake.

Two ways to fix this:

  1. Bind nix-colors in your home flake where the home module is being declared as an output. To do this, turn your home.nix into a function:

    # home flake.nix
    {
      description = "Home manager flake";
    
      inputs = {
        nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
    
        home-manager = {
          url = "github:nix-community/home-manager";
          inputs.nixpkgs.follows = "nixpkgs";
        };
    
        nix-colors.url = "github:misterio77/nix-colors";
      };
    
      outputs =
        {
          self,
          home-manager,
          nix-colors,
          nixpkgs,
          ...
        }@inputs:
        let
          system = "x86_64-linux";
    
          pkgs = import nixpkgs { inherit system; };
        in
        {
          # Formerly commented line
          homeManagerModules.home = import ./home.nix nix-colors; # aka importApply pattern
          homeConfigurations."alice" = home-manager.lib.homeManagerConfiguration {
            inherit pkgs;
    
            modules = [ self.homeManagerModules.home ];
    
            extraSpecialArgs = {
              inherit inputs;
            };
          };
        };
    }
    
    # home.nix
    nix-colors:
    { config, ... }:
    {
      imports = [ nix-colors.homeManagerModule ];
      home.stateVersion = "24.05";
      home.username = "alice";
      home.homeDirectory = "/home/alice";
      colorScheme = nix-colors.colorSchemes.dracula;
    
      programs.fzf = {
        enable = true;
        colors.bg = "#${config.colorScheme.colors.base00}";
      };
    }
    

    And import the module in the config flake like so:

    home-manager.users.alice =
      { config, ... }: # config is home-manager's config, not the OS one
      {
        imports = [ inputs.home-flake.homeManagerModules.home ];
        home.stateVersion = "24.05";
      };
    
  2. Pass nix-colors in your config way the same way you are doing in the home flake – through special args. Chances are if you add nix-colors as an input – this might just work.

Also, I would recommend not using nixosModules for home manager modules. If you decide to write NixOS tests – such outputs would fail, since options are different between Home Manager and NixOS modules. While there’s no official recommendation that I am aware of, some projects are using homeManagerModules output.

Thank you very much for your pointers, I got it to work now. In addition to your changes, I had to export all overlays use with home-manager and provide the via overlays.default so that I can apply them in nixos, and I had to move all nix = {...} from home.nix into my flake.nix so that they don’t clash with the same config in nixos config:

# home-manager flake
...
outputs =
    { self
    , home-manager
    , helix_me
    , nix-colors
    , nixpkgs
    , ...
    }@inputs:
    let
      system = "x86_64-linux";

      pkgs = import nixpkgs {
        inherit system;
        overlays = [ self.overlays.default ];
      };
    in
    {
      overlays.st = (_: _: { st = inputs.st.packages.${system}.st; });
      overlays.helix_dev = (_: _: {
        helix_dev = inputs.helix_me.packages.${system}.helix;
      });
      overlays.zsh-plugins = inputs.zsh-plugins-overlay.overlay;

      overlays.default = final: prev: with prev.lib; foldl' (flip extends) (_: prev) [
        self.overlays.st
        self.overlays.helix_dev
        self.overlays.zsh-plugins
      ]
        final;

      homeManagerModules.home = import ./home.nix nix-colors;
      homeConfigurations."cgahr" = home-manager.lib.homeManagerConfiguration {
        inherit pkgs;

        modules = [
          self.homeManagerModules.home
          {
            nix = {
              package = pkgs.nix;
              settings.experimental-features = [ "nix-command" "flakes" ];
            };
          }
        ];
      };
    };

My nixos flake now looks as follows:

# nixos flake
    let
      system = "x86_64-linux";
      pkgs = import nixpkgs {
        inherit system;
        overlays = [
          inputs.home-manager-config.overlays.default
          (_: _: { dwm = inputs.dwm.packages.${system}.dwm; })
          (_: _: { st = inputs.st.packages.${system}.st; })
          (_: _: { slstatus = inputs.slstatus.packages.${system}.slstatus; })
        ];
      };
    in
    {

      nixosConfigurations.default = nixpkgs.lib.nixosSystem {
        specialArgs = {
          inherit inputs pkgs;
        };
        modules = [
          ./configuration.nix
          inputs.home-manager.nixosModules.default
          {
            home-manager = {
              users."cgahr" = inputs.home-manager-config.homeManagerModules.home;
              useUserPackages = true;
              useGlobalPkgs = true;
            };
          }
        ];
      };
    };

The only remaining problem has something to do with unfree licenses, but I think I’ll be able to fix this on my own. Thank you so much for your help!

1 Like

Awesome, glad you got it to work!

If you want to save a bit of boilerplate on overlays – I’d suggest checking out flake.parts. Takes a bit of reading to get started, but saves a lot of time in the long run.

The only remaining problem has something to do with unfree licenses, but I think I’ll be able to fix this on my own.

If my guess is correct, this might actually be a tough one for actually a very similar reason – see 1000 instances of nixpkgs for more info.

You were indeed right, enable unfree packages seems to be impossible and a much bigger issue…

Thanks for the pointers, incidentally the related post https://zimbatm.com/notes/nixpkgs-unfree, seems to tackle the issue I experience