summary refs log tree commit diff
path: root/nixos/doc/manual/development/settings-options.section.md
blob: f9bb6ff9cc41f97b5b293af5b957657316d25332 (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
# Options for Program Settings {#sec-settings-options}

Many programs have configuration files where program-specific settings
can be declared. File formats can be separated into two categories:

-   Nix-representable ones: These can trivially be mapped to a subset of
    Nix syntax. E.g. JSON is an example, since its values like
    `{"foo":{"bar":10}}` can be mapped directly to Nix:
    `{ foo = { bar = 10; }; }`. Other examples are INI, YAML and TOML.
    The following section explains the convention for these settings.

-   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 `if true; then echo hi; fi`
    doesn\'t have a trivial representation in Nix.

    Currently there are no fixed conventions for these, but it is common
    to have a `configFile` option for setting the configuration file
    path directly. The default value of `configFile` can be an
    auto-generated file, with convenient options for controlling the
    contents. For example an option of type `attrsOf str` can be used
    for representing environment variables which generates a section
    like `export FOO="foo"`. Often it can also be useful to also include
    an `extraConfig` option of type `lines` to allow arbitrary text
    after the autogenerated part of the file.

## Nix-representable Formats (JSON, YAML, TOML, INI, \...) {#sec-settings-nix-representable}

By convention, formats like this are handled with a generic `settings`
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
`pkgs.formats`:

`pkgs.formats.json` { }

:   A function taking an empty attribute set (for future extensibility)
    and returning a set with JSON-specific attributes `type` and
    `generate` as specified [below](#pkgs-formats-result).

`pkgs.formats.yaml` { }

:   A function taking an empty attribute set (for future extensibility)
    and returning a set with YAML-specific attributes `type` and
    `generate` as specified [below](#pkgs-formats-result).

`pkgs.formats.ini` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \... }

:   A function taking an attribute set with values

    `listsAsDuplicateKeys`

    :   A boolean for controlling whether list values can be used to
        represent duplicate INI keys

    `listToValue`

    :   A function for turning a list of values into a single value.

    It returns a set with INI-specific attributes `type` and `generate`
    as specified [below](#pkgs-formats-result).

`pkgs.formats.toml` { }

:   A function taking an empty attribute set (for future extensibility)
    and returning a set with TOML-specific attributes `type` and
    `generate` as specified [below](#pkgs-formats-result).

`pkgs.formats.elixirConf { elixir ? pkgs.elixir }`

:   A function taking an attribute set with values

    `elixir`

    :   The Elixir package which will be used to format the generated output

    It returns a set with Elixir-Config-specific attributes `type`, `lib`, and
    `generate` as specified [below](#pkgs-formats-result).

    The `lib` attribute contains functions to be used in settings, for
    generating special Elixir values:

    `mkRaw elixirCode`

    :   Outputs the given string as raw Elixir code

    `mkGetEnv { envVariable, fallback ? null }`

    :   Makes the configuration fetch an environment variable at runtime

    `mkAtom atom`

    :   Outputs the given string as an Elixir atom, instead of the default
        Elixir binary string. Note: lowercase atoms still needs to be prefixed
        with `:`

    `mkTuple array`

    :   Outputs the given array as an Elixir tuple, instead of the default
        Elixir list

    `mkMap attrset`

    :   Outputs the given attribute set as an Elixir map, instead of the
        default Elixir keyword list


::: {#pkgs-formats-result}
These functions all return an attribute set with these values:
:::

`type`

:   A module system type representing a value of the format

`lib`

:   Utility functions for convenience, or special interactions with the format.
    This attribute is optional. It may contain inside a `types` attribute
    containing types specific to this format.

`generate` *`filename jsonValue`*

:   A function that can render a value of the format to a file. Returns
    a file path.

    ::: {.note}
    This function puts the value contents in the Nix store. So this
    should be avoided for secrets.
    :::

::: {#ex-settings-nix-representable .example}
::: {.title}
**Example: Module with conventional `settings` option**
:::
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.

```nix
{ 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
        <link xlink:href="https://example.com/docs/foo"/>
        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; };

    # ...
  };
}
```
:::

### Option declarations for attributes {#sec-settings-attrs-options}

Some `settings` 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 [](#sec-freeform-modules).

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.

::: {#ex-settings-typed-attrs .example}
::: {.title}
**Example: Declaring a type-checked `settings` attribute**
:::
```nix
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
    <link xlink:href="https://example.com/docs/foo"/>
    for supported values.
  '';
};
```
:::