# Options for Program Settings

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, ...)

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.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.ini` { listsAsDuplicateKeys ? false, ... }

  A function taking an attribute set with values

  `listsAsDuplicateKeys`

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

  It returns a set with INI-specific attributes `type` and `generate` as specified below.
`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.

These functions all return an attribute set with these values:

`type`

  A module system type representing a value of the format

`generate` filename jsonValue

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

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

### 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.

    { 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} = {};

        # ...
      };
    }

### Option declarations for attributes

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 .

#### Declaring a type-checked `settings` attribute

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.

    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.
      '';
    };