let defaults = import defaults/linux.nix { inherit pkgs; };
in
{
imports = [ defaults.nixosDefaults ]
...
environment.systemPackages = with pkgs; [ foo bar ] ++ defaults.basePackages;
It seems usable, but that there may be room for improvement,.
For example, is there a way to use mkMerge to create a set of defaults that can then be modified and added to? I got started down this path, but it seemed like every single attribute in the set required a default, which made it clunkier than I had expected.
What other approaches is everyone using to DRY their NixOS configs?
Instead of making it file-based (each machine imports the files they need), I suggest making your own modules instead and load those and then simply assign roles and profiles to the various machines.
Then your hypothetical (and admitttedly contrived example) config ends up looking like this:
I swear I had tried that and it gave me a conflict instead of merging. Must have been before I tried importing module style – definitely works now. Thanks, this is what I was hoping for!
Proving it to myself, ripgrep is in the defaults, nix-index in the importer:
@peterhoeg still having a little trouble visualizing the approach you’re recommending. Are you aware of any more complete examples I could check out? (I realize you can’t share your customers’ data of course.)
Most of nixpkgs/nixos/modules/** fundamentally works the same way, though not in the “roles and profiles” sense. Still, they’re written to do nothing unless their activation conditions, usually an enable option, are met.
It’s just a set of custom modules. The roles and profiles are also just modules that switch things on/off and set their configuration in a consistent way.
I mentioned a hardware profile and then didn’t actually show it in my example - apologies. The reason for this is that it’s often set based on a customer profile (if customer X has standardized on a particular virtualization product, the hardware profile is set as part of the customer configuration).