1# Writing NixOS Modules {#sec-writing-modules} 2 3NixOS has a modular system for declarative configuration. This system 4combines multiple *modules* to produce the full system configuration. 5One of the modules that constitute the configuration is 6`/etc/nixos/configuration.nix`. Most of the others live in the 7[`nixos/modules`](https://github.com/NixOS/nixpkgs/tree/master/nixos/modules) 8subdirectory of the Nixpkgs tree. 9 10Each NixOS module is a file that handles one logical aspect of the 11configuration, such as a specific kind of hardware, a service, or 12network settings. A module configuration does not have to handle 13everything from scratch; it can use the functionality provided by other 14modules for its implementation. Thus a module can *declare* options that 15can be used by other modules, and conversely can *define* options 16provided by other modules in its own implementation. For example, the 17module 18[`pam.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix) 19declares the option `security.pam.services` that allows other modules (e.g. 20[`sshd.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix)) 21to define PAM services; and it defines the option `environment.etc` (declared by 22[`etc.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix)) 23to cause files to be created in `/etc/pam.d`. 24 25In [](#sec-configuration-syntax), we saw the following structure of 26NixOS modules: 27 28```nix 29{ config, pkgs, ... }: 30 31{ # option definitions 32} 33``` 34 35This is actually an *abbreviated* form of module that only defines 36options, but does not declare any. The structure of full NixOS modules 37is shown in [Example: Structure of NixOS Modules](#ex-module-syntax). 38 39::: {#ex-module-syntax .example} 40### Structure of NixOS Modules 41```nix 42{ config, pkgs, ... }: 43 44{ 45 imports = 46 [ # paths of other modules 47 ]; 48 49 options = { 50 # option declarations 51 }; 52 53 config = { 54 # option definitions 55 }; 56} 57``` 58::: 59 60The meaning of each part is as follows. 61 62- The first line makes the current Nix expression a function. The variable 63 `pkgs` contains Nixpkgs (by default, it takes the `nixpkgs` entry of 64 `NIX_PATH`, see the [Nix manual](https://nixos.org/manual/nix/stable/#sec-common-env) 65 for further details), while `config` contains the full system 66 configuration. This line can be omitted if there is no reference to 67 `pkgs` and `config` inside the module. 68 69- This `imports` list enumerates the paths to other NixOS modules that 70 should be included in the evaluation of the system configuration. A 71 default set of modules is defined in the file `modules/module-list.nix`. 72 These don't need to be added in the import list. 73 74- The attribute `options` is a nested set of *option declarations* 75 (described below). 76 77- The attribute `config` is a nested set of *option definitions* (also 78 described below). 79 80[Example: NixOS Module for the "locate" Service](#locate-example) 81shows a module that handles the regular update of the "locate" database, 82an index of all files in the file system. This module declares two 83options that can be defined by other modules (typically the user's 84`configuration.nix`): `services.locate.enable` (whether the database should 85be updated) and `services.locate.interval` (when the update should be done). 86It implements its functionality by defining two options declared by other 87modules: `systemd.services` (the set of all systemd services) and 88`systemd.timers` (the list of commands to be executed periodically by 89`systemd`). 90 91Care must be taken when writing systemd services using `Exec*` directives. By 92default systemd performs substitution on `%<char>` specifiers in these 93directives, expands environment variables from `$FOO` and `${FOO}`, splits 94arguments on whitespace, and splits commands on `;`. All of these must be escaped 95to avoid unexpected substitution or splitting when interpolating into an `Exec*` 96directive, e.g. when using an `extraArgs` option to pass additional arguments to 97the service. The functions `utils.escapeSystemdExecArg` and 98`utils.escapeSystemdExecArgs` are provided for this, see [Example: Escaping in 99Exec directives](#exec-escaping-example) for an example. When using these 100functions system environment substitution should *not* be disabled explicitly. 101 102::: {#locate-example .example} 103### NixOS Module for the "locate" Service 104```nix 105{ config, lib, pkgs, ... }: 106 107let 108 inherit (lib) concatStringsSep mkIf mkOption optionalString types; 109 cfg = config.services.locate; 110in { 111 options.services.locate = { 112 enable = mkOption { 113 type = types.bool; 114 default = false; 115 description = '' 116 If enabled, NixOS will periodically update the database of 117 files used by the locate command. 118 ''; 119 }; 120 121 interval = mkOption { 122 type = types.str; 123 default = "02:15"; 124 example = "hourly"; 125 description = '' 126 Update the locate database at this interval. Updates by 127 default at 2:15 AM every day. 128 129 The format is described in 130 systemd.time(7). 131 ''; 132 }; 133 134 # Other options omitted for documentation 135 }; 136 137 config = { 138 systemd.services.update-locatedb = 139 { description = "Update Locate Database"; 140 path = [ pkgs.su ]; 141 script = 142 '' 143 mkdir -p $(dirname ${toString cfg.output}) 144 chmod 0755 $(dirname ${toString cfg.output}) 145 exec updatedb \ 146 --localuser=${cfg.localuser} \ 147 ${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \ 148 --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags} 149 ''; 150 }; 151 152 systemd.timers.update-locatedb = mkIf cfg.enable 153 { description = "Update timer for locate database"; 154 partOf = [ "update-locatedb.service" ]; 155 wantedBy = [ "timers.target" ]; 156 timerConfig.OnCalendar = cfg.interval; 157 }; 158 }; 159} 160``` 161::: 162 163::: {#exec-escaping-example .example} 164### Escaping in Exec directives 165```nix 166{ config, pkgs, utils, ... }: 167 168let 169 cfg = config.services.echo; 170 echoAll = pkgs.writeScript "echo-all" '' 171 #! ${pkgs.runtimeShell} 172 for s in "$@"; do 173 printf '%s\n' "$s" 174 done 175 ''; 176 args = [ "a%Nything" "lang=\${LANG}" ";" "/bin/sh -c date" ]; 177in { 178 systemd.services.echo = 179 { description = "Echo to the journal"; 180 wantedBy = [ "multi-user.target" ]; 181 serviceConfig.Type = "oneshot"; 182 serviceConfig.ExecStart = '' 183 ${echoAll} ${utils.escapeSystemdExecArgs args} 184 ''; 185 }; 186} 187``` 188::: 189 190```{=include=} sections 191option-declarations.section.md 192option-types.section.md 193option-def.section.md 194assertions.section.md 195meta-attributes.section.md 196importing-modules.section.md 197replace-modules.section.md 198freeform-modules.section.md 199settings-options.section.md 200```