<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, ... } </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> </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 <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} = {}; # ... }; } </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 <link xlink:href="https://example.com/docs/foo"/> for supported values. ''; }; </programlisting> </example> </para> </section> </section> </section>