Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Options

Options are the public interface of a module. They declare what can be configured, what type of value is expected, what the default is, and how the option is described in the documentation. A module that only sets config without declaring options is valid, but modules that expose options allow other modules and users to interact with them in a controlled way.

Declaring an option

Options are declared under the options key using lib.mkOption:

{ lib, ... }:

{
  options.services.myapp.enable = lib.mkOption {
    type    = lib.types.bool;
    default = false;
    description = "Whether to enable myapp.";
  };
}

The path under options becomes the path users set in their configuration:

services.myapp.enable = true;

mkOption arguments

ArgumentRequiredDescription
typeyesThe option type (see the Option Types chapter)
defaultnoValue used when the option is not set
defaultTextnoHuman-readable description of the default, for documentation
examplenoExample value shown in the manual
descriptionnoMarkdown description rendered in the manual
internalnoIf true, hide from generated documentation
visiblenoIf false, hide from generated documentation
readOnlynoIf true, disallow assignments from outside the declaring module
applynoFunction applied to the final value before it is exposed in config

mkEnableOption

A boolean enable option is so common that nixpkgs provides a shorthand:

options.services.myapp.enable = lib.mkEnableOption "myapp";

This is equivalent to:

options.services.myapp.enable = lib.mkOption {
  type    = lib.types.bool;
  default = false;
  description = "Whether to enable myapp.";
};

mkPackageOption

Similarly, mkPackageOption declares a package option with a sensible default drawn from pkgs:

options.services.myapp.package = lib.mkPackageOption pkgs "myapp" { };

This produces an option of type lib.types.package defaulting to pkgs.myapp. An optional default override can be provided:

options.services.myapp.package = lib.mkPackageOption pkgs "myapp" {
  default = [ "myapp" "full" ];  # pkgs.myapp.full
};

Grouping options with submodules

Related options are conventionally nested under a common prefix. The full path to an option is just the attribute path from the top of options:

options.services.myapp = {
  enable  = lib.mkEnableOption "myapp";
  package = lib.mkPackageOption pkgs "myapp" { };

  port = lib.mkOption {
    type    = lib.types.port;
    default = 8080;
    description = "Port myapp listens on.";
  };

  dataDir = lib.mkOption {
    type    = lib.types.path;
    default = "/var/lib/myapp";
    description = "Directory for myapp state.";
  };
};

Users then configure the service as a coherent group:

services.myapp = {
  enable  = true;
  port    = 9000;
  dataDir = "/srv/myapp";
};

The apply argument

apply transforms the final merged value before it is exposed in config. This is useful for normalisation or for converting a user-friendly type into an internal representation:

options.services.myapp.logLevel = lib.mkOption {
  type    = lib.types.enum [ "debug" "info" "warn" "error" ];
  default = "info";
  apply   = lib.toUpper;  # config.services.myapp.logLevel will be "INFO"
};

The transformation is invisible to callers — they set the option as usual and receive the transformed value when reading config.

readOnly options

Marking an option readOnly prevents assignments from anywhere other than the module that declared it. This is useful for computed values that should be observable but not overridden:

options.services.myapp.configFile = lib.mkOption {
  type     = lib.types.path;
  readOnly = true;
  description = "Path to the generated configuration file (read-only).";
};

config.services.myapp.configFile = pkgs.writeText "myapp.conf" "...";

Any attempt by another module to assign services.myapp.configFile will produce an evaluation error.