Nixos custom module configuration with flakes and home-manager

So I am trying to create my configuration using flakes and home-manager using modules.
I would like to manage my configuration so that my modules can create configurations for home-manager and the system transparently. So I could write module.zsh.enable and it would install my config using home-manager and define it as my default shell with the system configuration.

Here is my flake.nix

{
  description = "My system config";

  inputs = {
    # Core deps
    nixpkgs.url = "nixpkgs/nixpkgs-unstable";
    home-manager = {
      url = "github:nix-community/home-manager/master";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs@{ self, nixpkgs, home-manager, ... }: 
  let
    system = "x86_64-linux";
    pkgs = import nixpkgs{
      inherit system;
      config.allowUnfree = true;
    };
    lib = nixpkgs.lib;
  in {

    nixosConfigurations = {
        # Wip for hosts
        ideapad = lib.nixosSystem {
          inherit system;
          modules = [
            ./hosts/ideapad
            home-manager.nixosModules.home-manager
            {
              home-manager.users.<myusername> = import ./home/home.nix;
            }
          ];
        };
    };
  };
}

Here is ./hosts/ideapad

{ config, pkgs, ... }:

{
  # imports, hardware config and other stuff here

  # Modules configurations
  home-manager.users.<myusername>.modules.test = "myname";
}

And here is ./home/home.nix

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

{
  options = {
    modules.test = lib.mkOption {
      type = lib.types.bool;
      example = true;
    };
  };
  config = mkIf config.modules.test {
     #Home-Manager configuration
     programs.zsh = {
        enable = true;
        dotDir = ".config/zsh";
        # plugins
        enableAutosuggestions = true;
    }
    # System configuration (not using home-manager)
    # Doesn't work since I am nested in home-manager.users.<myusername>
    # users.extraUsers.<myusername>.shell = pkgs.zsh;
}

Is there anyway so that in ./hosts/ideapad i can write

mode.test = "myname";
instead of
home-manager.users.xgroleau.

and that both the system and home-manager configuration would work?
I understand that they are not in the same hierarchy of the config, but would there be anyway to do it?

If I understand correctly, you’re trying to set per-device home manager options? I think your example has a few typos.

If so, what you want here in particular isn’t possible. If you think about what it would mean to do that conceptually, it makes sense too - your NixOS configuration should configure what the system needs to function, and your home-manager configuration should configure what your user needs, regardless of the underlying system.

There is of course always some overlap, so what you want to do does make sense, I’d just suggest coming at it from a different conceptual angle:

Define a basic set of home manager modules, which specify everything you need on every machine. Let’s assume that’s home.nix in this case. Then create a kind of entrypoint for each machine that defines machine-specific options, and also imports the generic module; note that this is separate from hosts/ideapad.nix. Something like this:

# home/ideapad.nix
{ config, pkgs, ... }:

{
  imports = [ ./home.nix ];

  # home-manager config for ideapad
  modules.test = "myname";
}

Then you use one of these entrypoints instead of home.nix directly for home-manager.users.<myusername>. With a little tinkering, you can even write a function that automatically picks the correct entrypoint.

Personally, rather than making these entrypoints per-machine, I use a concept of “profiles” - NixOS modules usually need to be machine specific because of drivers and such, but for user config the difference is usually how I use the computer - whether it is headless, a laptop, or a work machine or such. So each one of those conceptual computer use cases has a different entrypoint, instead of each machine - this may help convince you that that’s a better conceptual approach than setting user config in your NixOS modules :slight_smile:

See also my dotfiles here: GitHub - TLATER/dotfiles: Dotfiles deployed with nix/home-manager. Feel free to take what you like! - sadly a bit difficult to grok because I split the machine configs from my dotfiles entirely.

1 Like

Thanks for the input! Yes you understood correctly and I see now why it’s not really possible. Though I looked through your dotfiles and it seems to be very close to what I want. Your dotfiles seem’s to only be home-manager related, which makes it possible to use it on non NixOS if I understand correctly? I really like this setup and will probably use it as inspiration. Thanks for sharing your dotfiles!

Not quite, you can do both in a single repository - you just need to specify a homeConfigurations output for that, like in the second snippet here: GitHub - nix-community/home-manager: Manage a user environment using Nix [maintainer=@rycee]. As a word of warning, this turns out to be a bit more complex than you might expect - I made these lib functions in my flake to deal with incompatibilities between homeConfigurations and the home-manager module for NixOS, but it can probably be done quite succinctly without those too.

Using my dotfiles on non-NixOS systems is very important to me indeed, though, it’s incredibly useful if I have to work in a ubuntu 18.04 VM (far too many software vendors only support outdated ubuntus), or on customer-provided hardware.

The split in my case is mostly for historical reasons - once upon a time, home-manager didn’t have support for flakes, so trying to combine the two repositories would have made using my dotfiles on other distros harder. Nowadays there are downsides to doing this, so I probably wouldn’t recommend it, and intend to combine them in the future.

1 Like

Actually you can access the NixOS system configuration from a Home Manager module — in addition to the usual config argument (which holds the Home Manager config in that case), Home Manager modules also get the nixosConfig argument, which is the same as config in a regular NixOS module. For some reason this is not mentioned in the Home Manager documentation.

I did not actually try to use that support with custom NixOS options like yours, but this code currently works for me to get some host-specific Home Manager configuration (as other comments here suggest, this might not be a proper long term solution):

home-manager.users.foo = { lib, pkgs, config, nixosConfig, ... }: {

  # Import the per-host configuration module if it is present.
  imports =
    let
      hostModules = {
        bar = ./host_bar.nix;
        baz = ./host_baz.nix;
      };
      thisHostName = nixosConfig.networking.hostName or null;
      thisHostModule = lib.mapNullable (n: hostModules.${n} or null) thisHostName;
    in
    lib.optional (thisHostModule != null) thisHostModule;

  # ... the rest ...
}

Also apparently there is a similar darwinConfig argument if you use Home Manager together with nix-darwin, and even a generic osConfig argument which holds either NixOS or nix-darwin config, so you may write a Home Manager module which works with both OS types if the OS-level config options that you want to use have the same names.

1 Like