- Nix modules are attribute sets all the way down
- Module system uses some “magic keys”
- Modules are parsed, then merged into one big attrset
Putting this all together, let’s write our own module
{ config, pkgs, lib, ... }:
{
imports = [ ... ];
options = { ... };
config = { ... };
}
options
keys are merged for pre-evaluationconfig
keys are merged for post-evalutationimports
are not merged but instead queued for evaluation
# hamster-module.nix
{ config, lib, pkgs, ... }:
{
options = {
programs.hamster.enable =
lib.mkEnableOption "hamster, a time tracking program";
};
config = lib.mkIf config.programs.hamster.enable {
environment.systemPackages = [ pkgs.hamster ];
};
}
nixpkgs/lib
defines some types and utilities that we can use in the module systemlib.mkEnableOption
creates a boolean type optionlib.mkOption
creates an arbitrary typed option- Each option can be given a description, default value, example configuration, etc
{
options.services.cow-server = {
enable = lib.mkEnableOption "Enable the cow server";
message = lib.mkOption {
type = lib.types.str;
default = "Hello!";
description = ''
The kind of message that you will get from the cow-server
'';
};
};
config = { ... };
}
- The
config
key is evaluated after the options - Via the
config.services.cow-server
key we have access to user-selected options - Again,
lib
contains some utilities to make our life easier
{
options = { ... };
config = lib.mkIf config.services.cow-server.enable {
# ... our configuration from previously ...
};
}
When creating a module, alias the local option scope in a
let...in
.
{ config, lib, ... }:
let cfg = config.foo.bar.my-module;
in
with lib;
{
options.foo.bar.my-module = {
# ...
};
config = mkIf cfg.enable {
# ...
}
}
If your configuration block is too complex, consider moving it to a different file.
{ config, lib, ... } @ args:
with lib;
{
options.foo.bar.my-module = {
# ...
};
config = (import ./config.nix args);
}