1<chapter xmlns="http://docbook.org/ns/docbook"
2 xmlns:xlink="http://www.w3.org/1999/xlink"
3 xmlns:xi="http://www.w3.org/2001/XInclude"
4 version="5.0"
5 xml:id="sec-writing-modules">
6
7<title>Writing NixOS Modules</title>
8
9<para>NixOS has a modular system for declarative configuration. This
10system combines multiple <emphasis>modules</emphasis> to produce the
11full system configuration. One of the modules that constitute the
12configuration is <filename>/etc/nixos/configuration.nix</filename>.
13Most of the others live in the <link
14xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><filename>nixos/modules</filename></link>
15subdirectory of the Nixpkgs tree.</para>
16
17<para>Each NixOS module is a file that handles one logical aspect of
18the configuration, such as a specific kind of hardware, a service, or
19network settings. A module configuration does not have to handle
20everything from scratch; it can use the functionality provided by
21other modules for its implementation. Thus a module can
22<emphasis>declare</emphasis> options that can be used by other
23modules, and conversely can <emphasis>define</emphasis> options
24provided by other modules in its own implementation. For example, the
25module <link
26xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix"><filename>pam.nix</filename></link>
27declares the option <option>security.pam.services</option> that allows
28other modules (e.g. <link
29xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><filename>sshd.nix</filename></link>)
30to define PAM services; and it defines the option
31<option>environment.etc</option> (declared by <link
32xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><filename>etc.nix</filename></link>)
33to cause files to be created in
34<filename>/etc/pam.d</filename>.</para>
35
36<para xml:id="para-module-syn">In <xref
37linkend="sec-configuration-syntax"/>, we saw the following structure
38of NixOS modules:
39
40<programlisting>
41{ config, pkgs, ... }:
42
43{ <replaceable>option definitions</replaceable>
44}
45</programlisting>
46
47This is actually an <emphasis>abbreviated</emphasis> form of module
48that only defines options, but does not declare any. The structure of
49full NixOS modules is shown in <xref linkend='ex-module-syntax' />.</para>
50
51<example xml:id='ex-module-syntax'><title>Structure of NixOS Modules</title>
52<programlisting>
53{ config, pkgs, ... }: <co xml:id='module-syntax-1' />
54
55{
56 imports =
57 [ <replaceable>paths of other modules</replaceable> <co xml:id='module-syntax-2' />
58 ];
59
60 options = {
61 <replaceable>option declarations</replaceable> <co xml:id='module-syntax-3' />
62 };
63
64 config = {
65 <replaceable>option definitions</replaceable> <co xml:id='module-syntax-4' />
66 };
67}</programlisting>
68</example>
69
70<para>The meaning of each part is as follows.
71
72<calloutlist>
73 <callout arearefs='module-syntax-1'>
74 <para>This line makes the current Nix expression a function. The
75 variable <varname>pkgs</varname> contains Nixpkgs, while
76 <varname>config</varname> contains the full system configuration.
77 This line can be omitted if there is no reference to
78 <varname>pkgs</varname> and <varname>config</varname> inside the
79 module.</para>
80 </callout>
81
82 <callout arearefs='module-syntax-2'>
83 <para>This list enumerates the paths to other NixOS modules that
84 should be included in the evaluation of the system configuration.
85 A default set of modules is defined in the file
86 <filename>modules/module-list.nix</filename>. These don't need to
87 be added in the import list.</para>
88 </callout>
89
90 <callout arearefs='module-syntax-3'>
91 <para>The attribute <varname>options</varname> is a nested set of
92 <emphasis>option declarations</emphasis> (described below).</para>
93 </callout>
94
95 <callout arearefs='module-syntax-4'>
96 <para>The attribute <varname>config</varname> is a nested set of
97 <emphasis>option definitions</emphasis> (also described
98 below).</para>
99 </callout>
100</calloutlist>
101
102</para>
103
104<para><xref linkend='locate-example' /> shows a module that handles
105the regular update of the “locate” database, an index of all files in
106the file system. This module declares two options that can be defined
107by other modules (typically the user’s
108<filename>configuration.nix</filename>):
109<option>services.locate.enable</option> (whether the database should
110be updated) and <option>services.locate.interval</option> (when the
111update should be done). It implements its functionality by defining
112two options declared by other modules:
113<option>systemd.services</option> (the set of all systemd services)
114and <option>systemd.timers</option> (the list of commands to be
115executed periodically by <command>systemd</command>).</para>
116
117<example xml:id='locate-example'><title>NixOS Module for the “locate” Service</title>
118<programlisting>
119{ config, lib, pkgs, ... }:
120
121with lib;
122
123let
124 cfg = config.services.locate;
125in {
126 options.services.locate = {
127 enable = mkOption {
128 type = types.bool;
129 default = false;
130 description = ''
131 If enabled, NixOS will periodically update the database of
132 files used by the <command>locate</command> command.
133 '';
134 };
135
136 interval = mkOption {
137 type = types.str;
138 default = "02:15";
139 example = "hourly";
140 description = ''
141 Update the locate database at this interval. Updates by
142 default at 2:15 AM every day.
143
144 The format is described in
145 <citerefentry><refentrytitle>systemd.time</refentrytitle>
146 <manvolnum>7</manvolnum></citerefentry>.
147 '';
148 };
149
150 # Other options omitted for documentation
151 };
152
153 config = {
154 systemd.services.update-locatedb =
155 { description = "Update Locate Database";
156 path = [ pkgs.su ];
157 script =
158 ''
159 mkdir -m 0755 -p $(dirname ${toString cfg.output})
160 exec updatedb \
161 --localuser=${cfg.localuser} \
162 ${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \
163 --output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
164 '';
165 };
166
167 systemd.timers.update-locatedb = mkIf cfg.enable
168 { description = "Update timer for locate database";
169 partOf = [ "update-locatedb.service" ];
170 wantedBy = [ "timers.target" ];
171 timerConfig.OnCalendar = cfg.interval;
172 };
173 };
174}
175</programlisting>
176</example>
177
178<xi:include href="option-declarations.xml" />
179<xi:include href="option-def.xml" />
180<xi:include href="meta-attributes.xml" />
181
182</chapter>