How do you structure your NixOS configs?

I’m seeing that is a common misconception. I’ll try to clarify how it all depends on you designing your modules to compose effectively for you.

Imagine I have many hosts (I do have a home-lab of 11 hosts). If I want a common feature (say my ssh-keys setup) across all of them, then I’d create a generic ssh feature - on nixos it would add openssh, and some firewall rules. on darwin it would enable macos ssh server. on homeManager it would setup .ssh/config or keys -.

This ssh feature is just an example of something I’d like to be present across all my home-lab hosts.

Then I would add another more global feature: home-lab

{inputs, ...}:
{
  # this base module will be included in each host of mine, having a single point
  # where to include common configs across all my 11 hosts.
  flake.modules.nixos.home-lab.imports = with inputs.self.modules.nixos [
     ssh # our example feature
     # ... other modules that will always be part of any host
     home-lab-fw # firewall rules to participate on home-lab network
     vic-user # include my user on all of them
     etc-etc
  ];
}

And you are right that this single nixos.home-lab module should be included in all hosts. How you do that, is not mandated by the pattern, I do it by having a single abstraction that instantiates my nixos hosts.

  mkNixos =
    system: cls: name:
    inputs.nixpkgs.lib.nixosSystem {
      inherit system;
      modules = [
        inputs.self.modules.nixos.${cls}
        inputs.self.modules.nixos.${name}
        inputs.self.modules.nixos.${system}
        {
          networking.hostName = lib.mkDefault name;
          nixpkgs.hostPlatform = lib.mkDefault system;
          system.stateVersion = "25.05";
        }
      ];
    };

The important bit there is cls, for me that is the top-level aspect (or feature) that I want included on all hosts of that class, in our example this could be home-lab since home-lab already includes all other modules. Notice my mkNixos function just includes three modules modules.nixos.${cls} (eg, our home-lab example), modules.nixos.${hostname} so that we also have a module that contributes specific features for that host, and modules.nixos.${system} currently empty but could be common configs per arch.

At the end of the day, you DO have common imports you pass into nixpkgs.lib.nixosSystem.modules, and that’s all.

The dendritic pattern does not mandate anything about how you instantiate your hosts or which modules you include or how many files you touch if adding a global feature. We (people already using the Dendritic pattern) are still exploring and finding out better ways to do things. I’m quite happy with my mkNixos function and has served well for me.

I’m currently trying to move common shareable features like my mkNixos function (and many other reusable modules) for other people to reuse on their dendritic setups.

Hope this has helped clarify the issue about having common imports. Having common imports is actually the point of using such dendritic setups. :slight_smile:

Will do :slight_smile: Maybe we should follows these Dendritic specific questions on another thread to avoid hijacking this thread.