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 107with lib; 108 109let 110 cfg = config.services.locate; 111in { 112 options.services.locate = { 113 enable = mkOption { 114 type = types.bool; 115 default = false; 116 description = '' 117 If enabled, NixOS will periodically update the database of 118 files used by the locate command. 119 ''; 120 }; 121 122 interval = mkOption { 123 type = types.str; 124 default = "02:15"; 125 example = "hourly"; 126 description = '' 127 Update the locate database at this interval. Updates by 128 default at 2:15 AM every day. 129 130 The format is described in 131 systemd.time(7). 132 ''; 133 }; 134 135 # Other options omitted for documentation 136 }; 137 138 config = { 139 systemd.services.update-locatedb = 140 { description = "Update Locate Database"; 141 path = [ pkgs.su ]; 142 script = 143 '' 144 mkdir -m 0755 -p $(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, lib, pkgs, utils, ... }: 167 168with lib; 169 170let 171 cfg = config.services.echo; 172 echoAll = pkgs.writeScript "echo-all" '' 173 #! ${pkgs.runtimeShell} 174 for s in "$@"; do 175 printf '%s\n' "$s" 176 done 177 ''; 178 args = [ "a%Nything" "lang=\${LANG}" ";" "/bin/sh -c date" ]; 179in { 180 systemd.services.echo = 181 { description = "Echo to the journal"; 182 wantedBy = [ "multi-user.target" ]; 183 serviceConfig.Type = "oneshot"; 184 serviceConfig.ExecStart = '' 185 ${echoAll} ${utils.escapeSystemdExecArgs args} 186 ''; 187 }; 188} 189``` 190::: 191 192```{=include=} sections 193option-declarations.section.md 194option-types.section.md 195option-def.section.md 196assertions.section.md 197meta-attributes.section.md 198importing-modules.section.md 199replace-modules.section.md 200freeform-modules.section.md 201settings-options.section.md 202```