Nix Module
6 min read
A NixOS module is just a Nix expression that at most declares:
• Options — the configuration schema (types, defaults, docs).
• Config — how those options are turned into system state (systemd units, config files, package installs, etc.).
Modules are merged together at evaluation time into one big configuration.
Structure
nix
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.myapp;
in {
#### 1. Options ####
options.services.myapp = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable MyApp.";
};
package = mkOption {
type = types.package;
default = pkgs.myapp;
description = "The package to use for MyApp.";
};
settings = mkOption {
type = types.attrs;
default = {};
description = "Configuration written to /etc/myapp.conf.";
};
};
#### 2. Config ####
config = mkIf cfg.enable {
# Ensure package is available
environment.systemPackages = [ cfg.package ];
# Write configuration file
environment.etc."myapp.conf".text =
lib.generators.toKeyValue {} cfg.settings;
# Create a systemd service
systemd.services.myapp = {
description = "MyApp Service";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${cfg.package}/bin/myapp --config /etc/myapp.conf";
Restart = "always";
};
};
};
}
Assertions
You can add which have to evaluate to true in order to successfully build. Using this you can make sure there are no invariants of your configuration.
nix
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.myapp;
in {
options.services.myapp = {
...
};
config = mkIf cfg.enable {
...
# Assertions
assertions = [
{
assertion = (cfg.myoption != null);
message = "This option cant be null"
}
];
};
}
Options
Use the mkOption function to declare options:
nix
mkOption {
# Optional default value used when no definition is given in the configuration.
default ? null,
# Substitute for documenting the default, if evaluating the default value during documentation rendering is not possible.
defaultText ? null,
# Optional example value used in the manual.
example ? null,
# Optional string describing the option. This is required if option documentation is generated.
description ? null,
# Optional option type, providing type-checking and value merging.
type ? null,
# Optional function that converts the option value to something else.
apply ? null,
# Optional boolean indicating whether the option is for NixOS developers only.
internal ? null,
# Optional, whether the option and/or sub-options show up in the manual.
visible ? null,
# Optional boolean indicating whether the option can be set only once.
readOnly ? null,
}
For declaring a enable option there is a mkEnableOption helper.
Types
Option types are a way to put constraints on the values a module option can take. Types are also responsible of how values are merged in case of multiple value definitions. There are many more but here are some basic types:
Basic
• types.attrs: A free-form attribute set.
• types.bool: A boolean, its values can be true or false.
• types.path: A filesystem path, defined as anything that when coerced to a string starts with a slash. Even if derivations can be considered as path, the more specific types.package should be preferred.
• types.package: A derivation or a store path.
Numbers
• types.int: A signed integer.
• types.ints.{s8, s16, s32}: Signed integers with a fixed length (8, 16 or 32 bits). They go from −2n/2 to 2n/2−1 respectively (e.g. −128 to 127 for 8 bits).
• types.ints.unsigned: An unsigned integer (that is >= 0).
• types.ints.{u8, u16, u32}: Unsigned integers with a fixed length (8, 16 or 32 bits). They go from 0 to 2n−1 respectively (e.g. 0 to 255 for 8 bits).
• types.ints.positive: A positive integer (that is > 0).
• types.port: A port number. This type is an alias to types.ints.u16 <None>_.
Text
• types.str: A string. Multiple definitions cannot be merged.
• types.lines: A string. Multiple definitions are concatenated with a new line "\n".
• types.commas: A string. Multiple definitions are concatenated with a comma ",".
• types.envVar: A string. Multiple definitions are concatenated with a collon ":".
• types.strMatching: A string matching a specific regular expression. Multiple definitions cannot be merged. The regular expression is processed using builtins.match.
Variants
• types.enum l: One element of the list l, e.g. types.enum [ "left" "right" ]. Multiple definitions cannot be merged.
• types.separatedString sep: A string with a custom separator sep, e.g. types.separatedString "\|".
• types.ints.between lowest highest: An integer between lowest and highest (both inclusive). Useful for creating types like types.port.
• types.submodule o: A set of sub options o. o can be an attribute set, a function returning an attribute set, or a path to a file containing such a value. Submodules are used in composed types to create modular options. This is equivalent to types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }.
• types.submoduleWith { modules, specialArgs ? {}, shorthandOnlyDefinesConfig ? false }: Like types.submodule, but more flexible and with better defaults.
Composed Types
Composed types are types that take a type as parameter. listOf int and either int str are examples of composed types.
• types.listOf t: A list of t type, e.g. types.listOf int. Multiple definitions are merged with list concatenation.
• types.attrsOf t: An attribute set of where all the values are of t type. Multiple definitions result in the joined attribute set.
• types.lazyAttrsOf t: An attribute set of where all the values are of t type. Multiple definitions result in the joined attribute set. This is the lazy version of types.attrsOf, allowing attributes to depend on each other.
• types.loaOf t: An attribute set or a list of t type. Multiple definitions are merged according to the value.
• types.nullOr t: null or type t. Multiple definitions are merged according to type t.
• types.uniq t: Ensures that type t cannot be merged. It is used to ensure option definitions are declared only once.
• types.either t1 t2: Type t1 or type t2, e.g. with types; either int str. Multiple definitions cannot be merged.
• types.oneOf [ t1 t2 … ]: Type t1 or type t2 and so forth, e.g. with types; oneOf [ int str bool ]. Multiple definitions cannot be merged.
• types.coercedTo from f to: Type to or type from which will be coerced to type to using function f which takes an argument of type from and return a value of type to.