summary refs log tree commit diff
path: root/nixos/doc/manual/from_md/development/settings-options.section.xml
blob: 746011a2d075bf8ab8d98fdce58d7d732c9ed182 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-settings-options">
  <title>Options for Program Settings</title>
  <para>
    Many programs have configuration files where program-specific
    settings can be declared. File formats can be separated into two
    categories:
  </para>
  <itemizedlist>
    <listitem>
      <para>
        Nix-representable ones: These can trivially be mapped to a
        subset of Nix syntax. E.g. JSON is an example, since its values
        like <literal>{&quot;foo&quot;:{&quot;bar&quot;:10}}</literal>
        can be mapped directly to Nix:
        <literal>{ foo = { bar = 10; }; }</literal>. Other examples are
        INI, YAML and TOML. The following section explains the
        convention for these settings.
      </para>
    </listitem>
    <listitem>
      <para>
        Non-nix-representable ones: These can't be trivially mapped to a
        subset of Nix syntax. Most generic programming languages are in
        this group, e.g. bash, since the statement
        <literal>if true; then echo hi; fi</literal> doesn't have a
        trivial representation in Nix.
      </para>
      <para>
        Currently there are no fixed conventions for these, but it is
        common to have a <literal>configFile</literal> option for
        setting the configuration file path directly. The default value
        of <literal>configFile</literal> can be an auto-generated file,
        with convenient options for controlling the contents. For
        example an option of type <literal>attrsOf str</literal> can be
        used for representing environment variables which generates a
        section like <literal>export FOO=&quot;foo&quot;</literal>.
        Often it can also be useful to also include an
        <literal>extraConfig</literal> option of type
        <literal>lines</literal> to allow arbitrary text after the
        autogenerated part of the file.
      </para>
    </listitem>
  </itemizedlist>
  <section xml:id="sec-settings-nix-representable">
    <title>Nix-representable Formats (JSON, YAML, TOML, INI,
    ...)</title>
    <para>
      By convention, formats like this are handled with a generic
      <literal>settings</literal> option, representing the full program
      configuration as a Nix value. The type of this option should
      represent the format. The most common formats have a predefined
      type and string generator already declared under
      <literal>pkgs.formats</literal>:
    </para>
    <variablelist>
      <varlistentry>
        <term>
          <literal>pkgs.formats.json</literal> { }
        </term>
        <listitem>
          <para>
            A function taking an empty attribute set (for future
            extensibility) and returning a set with JSON-specific
            attributes <literal>type</literal> and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.yaml</literal> { }
        </term>
        <listitem>
          <para>
            A function taking an empty attribute set (for future
            extensibility) and returning a set with YAML-specific
            attributes <literal>type</literal> and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.ini</literal> {
          <emphasis><literal>listsAsDuplicateKeys</literal></emphasis> ?
          false, <emphasis><literal>listToValue</literal></emphasis> ?
          null, ... }
        </term>
        <listitem>
          <para>
            A function taking an attribute set with values
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>listsAsDuplicateKeys</literal>
              </term>
              <listitem>
                <para>
                  A boolean for controlling whether list values can be
                  used to represent duplicate INI keys
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>listToValue</literal>
              </term>
              <listitem>
                <para>
                  A function for turning a list of values into a single
                  value.
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
          <para>
            It returns a set with INI-specific attributes
            <literal>type</literal> and <literal>generate</literal> as
            specified <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.toml</literal> { }
        </term>
        <listitem>
          <para>
            A function taking an empty attribute set (for future
            extensibility) and returning a set with TOML-specific
            attributes <literal>type</literal> and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>pkgs.formats.elixirConf { elixir ? pkgs.elixir }</literal>
        </term>
        <listitem>
          <para>
            A function taking an attribute set with values
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>elixir</literal>
              </term>
              <listitem>
                <para>
                  The Elixir package which will be used to format the
                  generated output
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
          <para>
            It returns a set with Elixir-Config-specific attributes
            <literal>type</literal>, <literal>lib</literal>, and
            <literal>generate</literal> as specified
            <link linkend="pkgs-formats-result">below</link>.
          </para>
          <para>
            The <literal>lib</literal> attribute contains functions to
            be used in settings, for generating special Elixir values:
          </para>
          <variablelist>
            <varlistentry>
              <term>
                <literal>mkRaw elixirCode</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given string as raw Elixir code
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkGetEnv { envVariable, fallback ? null }</literal>
              </term>
              <listitem>
                <para>
                  Makes the configuration fetch an environment variable
                  at runtime
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkAtom atom</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given string as an Elixir atom, instead of
                  the default Elixir binary string. Note: lowercase
                  atoms still needs to be prefixed with
                  <literal>:</literal>
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkTuple array</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given array as an Elixir tuple, instead of
                  the default Elixir list
                </para>
              </listitem>
            </varlistentry>
            <varlistentry>
              <term>
                <literal>mkMap attrset</literal>
              </term>
              <listitem>
                <para>
                  Outputs the given attribute set as an Elixir map,
                  instead of the default Elixir keyword list
                </para>
              </listitem>
            </varlistentry>
          </variablelist>
        </listitem>
      </varlistentry>
    </variablelist>
    <para xml:id="pkgs-formats-result">
      These functions all return an attribute set with these values:
    </para>
    <variablelist>
      <varlistentry>
        <term>
          <literal>type</literal>
        </term>
        <listitem>
          <para>
            A module system type representing a value of the format
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>lib</literal>
        </term>
        <listitem>
          <para>
            Utility functions for convenience, or special interactions
            with the format. This attribute is optional. It may contain
            inside a <literal>types</literal> attribute containing types
            specific to this format.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <literal>generate</literal>
          <emphasis><literal>filename jsonValue</literal></emphasis>
        </term>
        <listitem>
          <para>
            A function that can render a value of the format to a file.
            Returns a file path.
          </para>
          <note>
            <para>
              This function puts the value contents in the Nix store. So
              this should be avoided for secrets.
            </para>
          </note>
        </listitem>
      </varlistentry>
    </variablelist>
    <anchor xml:id="ex-settings-nix-representable" />
    <para>
      <emphasis role="strong">Example: Module with conventional
      <literal>settings</literal> option</emphasis>
    </para>
    <para>
      The following shows a module for an example program that uses a
      JSON configuration file. It demonstrates how above values can be
      used, along with some other related best practices. See the
      comments for explanations.
    </para>
    <programlisting language="bash">
{ options, config, lib, pkgs, ... }:
let
  cfg = config.services.foo;
  # Define the settings format used for this program
  settingsFormat = pkgs.formats.json {};
in {

  options.services.foo = {
    enable = lib.mkEnableOption &quot;foo service&quot;;

    settings = lib.mkOption {
      # Setting this type allows for correct merging behavior
      type = settingsFormat.type;
      default = {};
      description = ''
        Configuration for foo, see
        &lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
        for supported settings.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    # We can assign some default settings here to make the service work by just
    # enabling it. We use `mkDefault` for values that can be changed without
    # problems
    services.foo.settings = {
      # Fails at runtime without any value set
      log_level = lib.mkDefault &quot;WARN&quot;;

      # We assume systemd's `StateDirectory` is used, so we require this value,
      # therefore no mkDefault
      data_path = &quot;/var/lib/foo&quot;;

      # Since we use this to create a user we need to know the default value at
      # eval time
      user = lib.mkDefault &quot;foo&quot;;
    };

    environment.etc.&quot;foo.json&quot;.source =
      # The formats generator function takes a filename and the Nix value
      # representing the format value and produces a filepath with that value
      # rendered in the format
      settingsFormat.generate &quot;foo-config.json&quot; cfg.settings;

    # We know that the `user` attribute exists because we set a default value
    # for it above, allowing us to use it without worries here
    users.users.${cfg.settings.user} = { isSystemUser = true; };

    # ...
  };
}
</programlisting>
    <section xml:id="sec-settings-attrs-options">
      <title>Option declarations for attributes</title>
      <para>
        Some <literal>settings</literal> attributes may deserve some
        extra care. They may need a different type, default or merging
        behavior, or they are essential options that should show their
        documentation in the manual. This can be done using
        <xref linkend="sec-freeform-modules" />.
      </para>
      <para>
        We extend above example using freeform modules to declare an
        option for the port, which will enforce it to be a valid integer
        and make it show up in the manual.
      </para>
      <anchor xml:id="ex-settings-typed-attrs" />
      <para>
        <emphasis role="strong">Example: Declaring a type-checked
        <literal>settings</literal> attribute</emphasis>
      </para>
      <programlisting language="bash">
settings = lib.mkOption {
  type = lib.types.submodule {

    freeformType = settingsFormat.type;

    # Declare an option for the port such that the type is checked and this option
    # is shown in the manual.
    options.port = lib.mkOption {
      type = lib.types.port;
      default = 8080;
      description = ''
        Which port this service should listen on.
      '';
    };

  };
  default = {};
  description = ''
    Configuration for Foo, see
    &lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
    for supported values.
  '';
};
</programlisting>
    </section>
  </section>
</section>