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