Homogenous Nix Modules: Why and How
In the creation of my nixos configuration, I came to a (possibly unique) issue: many services that I wanted to configure required a "system" piece, and a "home" piece.
For example, in configuring a shell, it makes sense to configure that shell as the default shell for the user in the same place. However, in standard nixos configuration, you configure the actual shell with home-manager, and the default-ness with nixos.
My solution to this was to create a new way of managing my nixos modules, which I coined "Homogenous Modules."
The idea is as follows: each module contains three parts:
- a shared
options.nixfile, - a nixos part, and
- a home-manager part (or Hjem, in my case).
The options.nix file contains only the options section of a module.
The nixos part contains the config section of the module, as if it was a nixos module.
The home-manager part contains the config section of the module, as if it was a home-manager module.
Now, {imports = [./options.nix ./nixos.nix];} is a valid nixos module, and
{imports = [./options.nix ./home.nix];} is a valid home-manager module.
Example
Let's implement that shell example from the start, from scratch.
We'll start with with the options.nix:
{lib, ...}: let
inherit (lib) mkOption mkEnableOption;
in {
options = {
collinux.shells = {
zsh = {
enable = mkEnableOption "the zsh shell and customizations to it";
default = mkEnableOption "make zsh the default shell";
};
bash = {
enable = mkEnableOption "the bash shell and customizations to it";
default = mkEnableOption "make bash the default shell";
};
};
};
config.assertions = [
{
assertion = with collinux.shells; !(zsh.default && bash.default);
message = "You can only have one default shell!";
}
];
}
Here, we define a simple configuration.
In the nixos part, we'll set the default shell:
{pkgs, config, lib, ...}: let
cfg = config.collinux.shells;
in {
users.users."collin".shell =
if config.zsh.enable
then pkgs.zsh
else if config.bash.enable
then pkgs.bash
else null;
}
And in the home-manager part, we'll set some customization options:
{pkgs, config, lib, ...}: let
cfg = config.collinux.shells;
shellAliases = {
ll = "ls -l";
update = "home-manager switch";
};
in {
programs.zsh = {
enable = cfg.zsh.enable;
syntaxHighlighting.enable = true;
inherit shellAliases;
};
programs.bash = {
enable = cfg.bash.enable;
inherit shellAliases;
};
}
