Modularity
The NixOS configuration mechanism is modular. If your
configuration.nix becomes too big, you can split it into
multiple files. Likewise, if you have multiple NixOS configurations (e.g. for
different computers) with some commonality, you can move the common
configuration into a shared file.
Modules have exactly the same syntax as
configuration.nix. In fact,
configuration.nix is itself a module. You can use other
modules by including them from configuration.nix, e.g.:
{ config, pkgs, ... }:
{ imports = [ ./vpn.nix ./kde.nix ];
= true;
= [ pkgs.emacs ];
...
}
Here, we include two modules from the same directory,
vpn.nix and kde.nix. The latter
might look like this:
{ config, pkgs, ... }:
{ = true;
= true;
= true;
= [ pkgs.vim ];
}
Note that both configuration.nix and
kde.nix define the option
. When multiple modules
define an option, NixOS will try to merge the
definitions. In the case of ,
that’s easy: the lists of packages can simply be concatenated. The value in
configuration.nix is merged last, so for list-type
options, it will appear at the end of the merged list. If you want it to
appear first, you can use mkBefore:
= mkBefore [ "kvm-intel" ];
This causes the kvm-intel kernel module to be loaded
before any other kernel modules.
For other types of options, a merge may not be possible. For instance, if two
modules define ,
nixos-rebuild will give an error:
The unique option `services.httpd.adminAddr' is defined multiple times, in `/etc/nixos/httpd.nix' and `/etc/nixos/configuration.nix'.
When that happens, it’s possible to force one definition take precedence
over the others:
= pkgs.lib.mkForce "bob@example.org";
When using multiple modules, you may need to access configuration values
defined in other modules. This is what the config function
argument is for: it contains the complete, merged system configuration. That
is, config is the result of combining the configurations
returned by every module
If you’re wondering how it’s possible that the (indirect)
result of a function is passed as an
input to that same function: that’s because Nix is a
“lazy” language — it only computes values when they are needed. This
works as long as no individual configuration value depends on itself.
. For example, here is a module that adds some packages to
only if
is set to
true somewhere else:
{ config, pkgs, ... }:
{ =
if config. then
[ pkgs.firefox
pkgs.thunderbird
]
else
[ ];
}
With multiple modules, it may not be obvious what the final value of a
configuration option is. The command allows you
to find out:
$ nixos-option
true
$ nixos-option
[ "tun" "ipv6" "loop" ... ]
Interactive exploration of the configuration is possible using nix
repl, a read-eval-print loop for Nix expressions. A typical use:
$ nix repl '<nixpkgs/nixos>'
nix-repl> config.
"mandark"
nix-repl> map (x: x.hostName) config.
[ "example.org" "example.gov" ]
While abstracting your configuration, you may find it useful to generate
modules using code, instead of writing files. The example below would have
the same effect as importing a file which sets those options.
{ config, pkgs, ... }:
let netConfig = { hostName }: {
networking.hostName = hostName;
networking.useDHCP = false;
};
in
{ imports = [ (netConfig "nixos.localdomain") ]; }