Nix Module

A NixOS module is just a Nix expression that at most declares:

  • Options — the configuration schema (types, defaults, docs).
  • Config — how those options are turned into system state (systemd units, config files, package installs, etc.).

Modules are merged together at evaluation time into one big configuration.

Structure

{ config, lib, pkgs, ... }:
 
with lib;
 
let
  cfg = config.services.myapp;
in {
  #### 1. Options ####
  options.services.myapp = {
 
    enable = mkOption {
      type = types.bool;
      default = false;
      description = "Whether to enable MyApp.";
    };
 
    package = mkOption {
      type = types.package;
      default = pkgs.myapp;
      description = "The package to use for MyApp.";
    };
 
    settings = mkOption {
      type = types.attrs;
      default = {};
      description = "Configuration written to /etc/myapp.conf.";
    };
 
  };
 
  #### 2. Config ####
  config = mkIf cfg.enable {
 
    # Ensure package is available
    environment.systemPackages = [ cfg.package ];
 
    # Write configuration file
    environment.etc."myapp.conf".text =
      lib.generators.toKeyValue {} cfg.settings;
 
    # Create a systemd service
    systemd.services.myapp = {
      description = "MyApp Service";
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = "${cfg.package}/bin/myapp --config /etc/myapp.conf";
        Restart = "always";
      };
    };
 
  };
}

Assertions

You can add which have to evaluate to true in order to successfully build. Using this you can make sure there are no invariants of your configuration.

{ config, lib, pkgs, ... }:
with lib;
let
  cfg = config.services.myapp;
in {
  options.services.myapp = {
    ...
  };
 
  config = mkIf cfg.enable {
    ...
    
    # Assertions
    assertions = [
      {
        assertion = (cfg.myoption != null);
        message = "This option cant be null"
      }
    ];
  };
}

Options

Use the mkOption function to declare options:

mkOption {
    # Optional default value used when no definition is given in the configuration.
    default ? null,
    # Substitute for documenting the default, if evaluating the default value during documentation rendering is not possible.
    defaultText ? null,
    # Optional example value used in the manual.
    example ? null,
    # Optional string describing the option. This is required if option documentation is generated.
    description ? null,
    # Optional option type, providing type-checking and value merging.
    type ? null,
    # Optional function that converts the option value to something else.
    apply ? null,
    # Optional boolean indicating whether the option is for NixOS developers only.
    internal ? null,
    # Optional, whether the option and/or sub-options show up in the manual.
    visible ? null,
    # Optional boolean indicating whether the option can be set only once.
    readOnly ? null,
    }

For declaring a enable option there is a mkEnableOption helper.

Types

Option types are a way to put constraints on the values a module option can take. Types are also responsible of how values are merged in case of multiple value definitions. There are many more but here are some basic types:

Basic

  • types.attrs: A free-form attribute set.
  • types.bool: A boolean, its values can be true or false.
  • types.path: A filesystem path, defined as anything that when coerced to a string starts with a slash. Even if derivations can be considered as path, the more specific types.package should be preferred.
  • types.package: A derivation or a store path.

Numbers

  • types.int: A signed integer.
  • types.ints.{s8, s16, s32}: Signed integers with a fixed length (8, 16 or 32 bits). They go from −2n/2 to 2n/2−1 respectively (e.g. −128 to 127 for 8 bits).
  • types.ints.unsigned: An unsigned integer (that is >= 0).
  • types.ints.{u8, u16, u32}: Unsigned integers with a fixed length (8, 16 or 32 bits). They go from 0 to 2n−1 respectively (e.g. 0 to 255 for 8 bits).
  • types.ints.positive: A positive integer (that is > 0).
  • types.port: A port number. This type is an alias to types.ints.u16 <None>_.

Text

  • types.str: A string. Multiple definitions cannot be merged.
  • types.lines: A string. Multiple definitions are concatenated with a new line “\n”.
  • types.commas: A string. Multiple definitions are concatenated with a comma ”,“.
  • types.envVar: A string. Multiple definitions are concatenated with a collon ”:“.
  • types.strMatching: A string matching a specific regular expression. Multiple definitions cannot be merged. The regular expression is processed using builtins.match.

Variants

  • types.enum l: One element of the list l, e.g. types.enum [ "left" "right" ]. Multiple definitions cannot be merged.
  • types.separatedString sep: A string with a custom separator sep, e.g. types.separatedString "\|".
  • types.ints.between lowest highest: An integer between lowest and highest (both inclusive). Useful for creating types like types.port.
  • types.submodule o: A set of sub options o. o can be an attribute set, a function returning an attribute set, or a path to a file containing such a value. Submodules are used in composed types to create modular options. This is equivalent to types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }.
  • types.submoduleWith { modules, specialArgs ? {}, shorthandOnlyDefinesConfig ? false }: Like types.submodule, but more flexible and with better defaults.

Composed Types

Composed types are types that take a type as parameter. listOf int and either int str are examples of composed types.

  • types.listOf t: A list of t type, e.g. types.listOf int. Multiple definitions are merged with list concatenation.
  • types.attrsOf t: An attribute set of where all the values are of t type. Multiple definitions result in the joined attribute set.
  • types.lazyAttrsOf t: An attribute set of where all the values are of t type. Multiple definitions result in the joined attribute set. This is the lazy version of types.attrsOf, allowing attributes to depend on each other.
  • types.loaOf t: An attribute set or a list of t type. Multiple definitions are merged according to the value.
  • types.nullOr t: null or type t. Multiple definitions are merged according to type t.
  • types.uniq t: Ensures that type t cannot be merged. It is used to ensure option definitions are declared only once.
  • types.either t1 t2: Type t1 or type t2, e.g. with types; either int str. Multiple definitions cannot be merged.
  • types.oneOf [ t1 t2 … ]: Type t1 or type t2 and so forth, e.g. with types; oneOf [ int str bool ]. Multiple definitions cannot be merged.
  • types.coercedTo from f to: Type to or type from which will be coerced to type to using function f which takes an argument of type from and return a value of type to.