Skip to content

What is a module

In this lesson, you will learn what a module is and how to define one.

Definition

A module is a function that returns an attrset. It declares options with types. It defines option values. When evaluated, it produces a configuration based on the declarations and definitions.

If that does not make a lot of sense, do not worry. Keep reading. The rest of this lesson will provide clarity.

Structure

An empty module

Modules have the following basic structure.

useless.nix
{...}: {
}

This, as the filename suggests, is completely useless. It takes no arguments and returns an empty attrset. However, it does evaluate. So when we look at the following examples, keep in mind that everything we add is optional.

A basic module

Let us look at a more practical setup.

default.nix
{lib, ...}: {
  imports = [
    ./relative/path/to/another/module.nix
    /absolute/path/to/another/module.nix
  ];
  options = {
    # declarations of options
    };
  };
  config = {
    # configuration of options
  };
}

Note

options and config and have formal names — that is option declarations and option definitions respectively. The rest of these lessons will use them interchangeably.

function argument

Now the module is a function which takes at least one argument, lib, and may accept other arguments (expressed by the ellipsis ...). This will make Nixpkgs library functions available within the function body.

Note

The ellipsis ... is necessary because arbitrary arguments can be passed to modules. Every module you create that is a function, should have this.

The lib argument is passed automatically by the module system. It is absolutely vital for modules that have option declarations, as you will need lib for defining options and their types. It is one of several arguments that are automatically provided by the module system. The full list of arguments is discussed later.

imports

This imports list enumerates the paths to other NixOS modules. This is a useful mechanism for breaking up modules into small components and importing what you need.

options

To set any values, the module system first has to know which ones are allowed.

This is done by declaring options that specify which values can be set and used elsewhere. Options are declared by adding an attribute under the top-level options attribute. The most general way to declare an option is using lib.mkOption.

options.nix
{lib, ...}: {
  options = {
    name = lib.mkOption {
      type = lib.types.str;
    };
  };
}

While many attributes for customizing options are available, the most important one is type, which specifies which values are valid for an option. There are several types available under lib.types in the Nixpkgs library.

Here we have declared an option name with the str type. This specifies that the only valid value is a string and can only be a single definition.

config

Option definitions are generally straight-forward bindings of values to option names.

config.nix
{
  config.name = "Boaty McBoatface";
}

Note

Modules do not have to be functions. They can be attrsets as well. The above config.nix file is a valid module. Generally, you can write modules that are only option definitions as attrsets.

Note that our option declarations and option definitions do not need to exist in the same file. When we evaluate our modules, we can simply include both files. As long as every definition has a declaration, we can successfully evaluate our modules. If there is an option definition that has not been declared, the module system will throw an error.