summary refs log tree commit diff
path: root/nixos/doc/manual/development/settings-options.xml
blob: 7292cac62b7072ec76f3a170c44c52e2961feb60 (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
<section xmlns="http://docbook.org/ns/docbook"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        xmlns:xi="http://www.w3.org/2001/XInclude"
        version="5.0"
        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:
   <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>{"foo":{"bar":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="foo"</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>
 </para>
 <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>:
     <variablelist>
       <varlistentry>
         <term>
           <varname>pkgs.formats.json</varname> { }
         </term>
         <listitem>
           <para>
             A function taking an empty attribute set (for future extensibility) and returning a set with JSON-specific attributes <varname>type</varname> and <varname>generate</varname> as specified <link linkend='pkgs-formats-result'>below</link>.
           </para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term>
           <varname>pkgs.formats.yaml</varname> { }
         </term>
         <listitem>
           <para>
             A function taking an empty attribute set (for future extensibility) and returning a set with YAML-specific attributes <varname>type</varname> and <varname>generate</varname> as specified <link linkend='pkgs-formats-result'>below</link>.
           </para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term>
           <varname>pkgs.formats.ini</varname> { <replaceable>listsAsDuplicateKeys</replaceable> ? false, <replaceable>listToValue</replaceable> ? null, ... }
         </term>
         <listitem>
           <para>
             A function taking an attribute set with values
             <variablelist>
               <varlistentry>
                 <term>
                   <varname>listsAsDuplicateKeys</varname>
                 </term>
                 <listitem>
                   <para>
                     A boolean for controlling whether list values can be used to represent duplicate INI keys
                   </para>
                 </listitem>
               </varlistentry>
               <varlistentry>
                 <term>
                   <varname>listToValue</varname>
                 </term>
                 <listitem>
                   <para>
                     A function for turning a list of values into a single value.
                   </para>
                 </listitem>
               </varlistentry>
             </variablelist>
            It returns a set with INI-specific attributes <varname>type</varname> and <varname>generate</varname> as specified <link linkend='pkgs-formats-result'>below</link>.
           </para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term>
           <varname>pkgs.formats.toml</varname> { }
         </term>
         <listitem>
           <para>
             A function taking an empty attribute set (for future extensibility) and returning a set with TOML-specific attributes <varname>type</varname> and <varname>generate</varname> as specified <link linkend='pkgs-formats-result'>below</link>.
           </para>
         </listitem>
       </varlistentry>
     </variablelist>

   </para>
   <para xml:id="pkgs-formats-result">
     These functions all return an attribute set with these values:
    <variablelist>
      <varlistentry>
        <term>
          <varname>type</varname>
        </term>
        <listitem>
          <para>
            A module system type representing a value of the format
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term>
          <varname>generate</varname> <replaceable>filename</replaceable> <replaceable>jsonValue</replaceable>
        </term>
        <listitem>
          <para>
           A function that can render a value of the format to a file. Returns a file path.
           <note>
            <para>
             This function puts the value contents in the Nix store. So this should be avoided for secrets.
            </para>
           </note>
          </para>
        </listitem>
      </varlistentry>
    </variablelist>
   </para>
   <example xml:id="ex-settings-nix-representable">
     <title>Module with conventional <literal>settings</literal> option</title>
     <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>
{ 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 "foo service";

    settings = lib.mkOption {
      # Setting this type allows for correct merging behavior
      type = settingsFormat.type;
      default = {};
      description = ''
        Configuration for foo, see
        &lt;link xlink:href="https://example.com/docs/foo"/&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 "WARN";

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

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

    environment.etc."foo.json".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 "foo-config.json" 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>
   </example>
   <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'/>.
     <example xml:id="ex-settings-typed-attrs">
      <title>Declaring a type-checked <literal>settings</literal> attribute</title>
      <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>
<programlisting>
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="https://example.com/docs/foo"/&gt;
    for supported values.
  '';
};
</programlisting>
     </example>
    </para>
   </section>
 </section>

</section>