NixOS modules: Difference between revisions

imported>Montchr
Replace example of obsolete `require` option with `imports`
Blokyk (talk | contribs)
Add a "Disabling modules" section in advanced usages, partially migrated from the unofficial wiki
 
(29 intermediate revisions by 15 users not shown)
Line 1: Line 1:
'''Modules''' are files combined by NixOS to produce the full system configuration. A module contains a Nix expression. It ''declares'' options for other modules to ''define'' (give a value). It processes them and defines options declared in other modules.<ref>{{manual:nixos|sec=#sec-writing-modules|chapter=Chapter 42. Writing NixOS Modules}}</ref>
NixOS produces a full system configuration by combining smaller, more isolated and reusable components: '''Modules'''. A module is a file containing a Nix expression with a specific structure. It ''declares'' options for other modules to ''define'' (give a value). It processes them and defines options declared in other modules.<ref>{{manual:nixos|sec=#sec-writing-modules|chapter=Chapter 42. Writing NixOS Modules}}</ref>


For example, {{ic|/etc/nixos/configuration.nix}} is a module. Most other modules are in {{Nixpkgs Link|nixos/modules}}.
For example, {{ic|/etc/nixos/configuration.nix}} is a module. Most other modules are in {{Nixpkgs Link|nixos/modules}}.
Line 10: Line 10:
{
{
   imports = [
   imports = [
     # paths to other modules
     # Paths to other modules.
    # Compose this module out of smaller ones.
   ];
   ];


   options = {
   options = {
     # option declarations
     # Option declarations.
    # Declare what settings a user of this module can set.
    # Usually this includes a global "enable" option which defaults to false.
   };
   };


   config = {
   config = {
     # option definitions
     # Option definitions.
    # Define what other settings, services and resources should be active.
    # Usually these depend on whether a user of this module chose to "enable" it
    # using the "option" above.
    # Options for modules imported in "imports" can be set here.
  };
 
  meta = {
    # Meta-attributes to provide extra information like documentation or maintainers.
   };
   };
}
}
</syntaxhighlight>
</syntaxhighlight>


There is a shorthand for modules without any declarations:
There is a shorthand for modules without any option declarations:


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
   imports = [
   imports = [
     # paths to other modules
     # Paths to other modules.
     ./module.nix
     ./module.nix
     /path/to/absolute/module.nix
     /path/to/absolute/module.nix
   ];
   ];


   # option definitions
   # Config definitions.
  services.othermodule.enable = true;
  # ...
  # Notice that you can leave out the "config { }" wrapper.
}
}
</syntaxhighlight>
</syntaxhighlight>


Note: <code>imports</code> provides the same behavior as the obsolete <code>require</code>. There is no reason to use <code>require</code> anymore, however it may still linger in some legacy code. .  
Note that despite the name, <code>imports = [./module.nix]</code> should not be confused with the Nix [https://nixos.org/manual/nix/stable/language/builtins.html#builtins-import builtins] function <code>import module.nix</code>.
 
<code>imports</code> expects a path to a file containing a NixOS module structured as described here. <code>import</code> can load arbitrary Nix expression from provided file with no expectation of structure. (no expected structure). See [https://discourse.nixos.org/t/import-list-in-configuration-nix-vs-import-function/11372/8 this post] for more details.
 
Note: <code>imports</code> provides the same behavior as the obsolete <code>require</code>. There is no reason to use <code>require</code> anymore, however it may still linger in some legacy code.


=== Function ===
=== Function ===


A module can be turned into a function accepting an attribute set.
A module may be a function accepting an attribute set.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{ config, pkgs, ... }:
{ config, pkgs, ... }:
{
{
  imports = [];
   # ...
   # ...
}
}
</syntaxhighlight>
</syntaxhighlight>


It may require the attribute set to contain:
Following arguments are available in NixOS modules by default:


<dl>
<dl>
Line 59: Line 78:
<dt><code>options</code></dt>
<dt><code>options</code></dt>
<dd>All option declarations refined with all definition and declaration references.</dd>
<dd>All option declarations refined with all definition and declaration references.</dd>
<dt><code>lib</code></dt>
<dd>An instance of the nixpkgs "standard library", providing what usually is in <code>pkgs.lib</code>.</dd>


<dt><code>pkgs</code></dt>
<dt><code>pkgs</code></dt>
Line 66: Line 88:
<dd>The location of the <code>module</code> directory of NixOS.</dd>
<dd>The location of the <code>module</code> directory of NixOS.</dd>


</dl>
</dl>The "<code>...</code>"  part of the argument attribute set indicates that this module does not depend on the rest of the arguments. When the module is defined as a function, this pattern is typically required, otherwise the evaluation will fail citing unexpected arguments.
 
==== Passing custom values to modules ====
The [[NixOS:config_argument|<code>config</code>]], <code>options</code>, <code>lib</code>, <code>pkgs</code>, and <code>modulesPath</code> arguments are passed automatically to modules, when the module is imported.
 
For example, in the following Nix flake, the `./configuration.nix` file will be provided with the default set of arguments listed above, plus `extraArg`, which was set in the `specialArgs` argument to the `nixosGenerate` function.<syntaxhighlight lang="nix">
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
    nixos-generators = {
      url = "github:nix-community/nixos-generators";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    xc = {
      url = "github:joerdav/xc";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };
 
  outputs = { nixpkgs, nixos-generators, xc, ... }:
    let
      pkgsForSystem = system: import nixpkgs {
        inherit system;
        overlays = [
          (final: prev: { xc = xc.packages.${system}.xc; })
        ];
      };
      allVMs = [ "x86_64-linux" "aarch64-linux" ];
      forAllVMs = f: nixpkgs.lib.genAttrs allVMs (system: f {
        inherit system;
        pkgs = pkgsForSystem system;
      });
    in
    {
      packages = forAllVMs ({ system, pkgs }: {
        vm = nixos-generators.nixosGenerate {
          system = system;
          specialArgs = {
            extraArg = "foobar";
          };
          modules = [
            ./configuration.nix
          ];
          format = "raw";
        };
      });
    };
}
</syntaxhighlight>


==== modulesPath ====
==== modulesPath ====
Line 104: Line 174:
Imports are paths to other NixOS modules that should be included in the evaluation of the system configuration. A default set of modules is defined in {{Nixpkgs Link|nixos/modules/module-list.nix}}. These don't need to be added in the import list.  
Imports are paths to other NixOS modules that should be included in the evaluation of the system configuration. A default set of modules is defined in {{Nixpkgs Link|nixos/modules/module-list.nix}}. These don't need to be added in the import list.  


=== Declarations ===
=== Option Declarations ===


Declarations specify a module's external interfaces.
Declarations specify a module's external interfaces.


<syntaxhighlight lang=nix>
<syntaxhighlight lang="nix">
optionName = mkOption {
options = {
  # ...
  optionName = lib.options.mkOption {
    # ...
  };
}
}
</syntaxhighlight>
</syntaxhighlight>


They are created with {{ic|mkOption}}, a function accepting a set with following attributes:<ref>{{Nixpkgs Link|lib/options.nix#L51-L84}}</ref><ref>{{manual:nixos|sec=#sec-option-declarations|chapter=42.1. Option Declarations}}</ref>
They are created with {{ic|mkOption}}, a function in <code>lib</code>.<ref>{{Nixpkgs Link|lib/options.nix#L66-L88}}</ref><ref>{{manual:nixos|sec=#sec-option-declarations|chapter=42.1. Option Declarations}}</ref>


<dl>
==== Examples ====
<dt><code>type</code></dt>
[[File:NixOS Search - Options.png|alt=A search query `programs.bat.` on the official NixOS Search - Options.|thumb|270x270px|For even more examples you can browse [https://github.com/NixOS/nixpkgs NixOS/nixpkgs]: Search a similar option on [https://search.nixos.org/options NixOS Search - Options] and click on the link beside "Declared in" and studding the option declarations.]]
<dd><p>
Some useful option examples:<syntaxhighlight lang="nixos">
      The type of the option. It may
{ lib, pkgs, ... }: {
      be omitted, but that’s not advisable since it may lead to errors that
  options.examples = {
      are hard to diagnose.
    enable = lib.options.mkEnableOption "showcasing of options";
</p></dd>
    # type = lib.types.bool;
<dt><code>default</code></dt>
 
<dd><p>
    package = lib.options.mkPackageOption pkgs "bash" { };
       The default value used if no value is defined by any module. A default is
    # type = lib.types.package;
      not required; but if a default is not given, then users of the module
 
      will have to define the value of the option, otherwise an error will be
    numberOfTheDay = lib.options.mkOption {
       thrown.
       type = lib.types.ints.between 50 100;
</p></dd>
      default = 61;
<dt><code class="varname">example</code></dt>
    };
<dd><p>
 
       An example value that will be shown in the NixOS manual.
    groceries = lib.options.mkOption {
</p></dd>
       type = lib.types.listOf lib.types.strMatching "^[a-z]*$";
<dt><code>description</code></dt>
      default = [];
<dd><p>
      description = "Showcase `listOf` and `strMatching`".
      A textual description of the option, in DocBook format, that will be
       example = [
       included in the NixOS manual.
        "bananas"
</p></dd>
        "cherrys"
</dl>
        "Throws an error at evaluation beause of regex not matching."
       ];
    };


    settings = lib.options.mkOption {
      type = lib.types.toml;
      default = {};
      description = ''
        Showcase the possible option for settings of a configuration
        file.
        Always document, where you can find possible options e.g.:
          Refer <https://example.com/> for possible options.
      '';
      example = {
        global = {
          log_format = "-";
          log_filter = "^$";
        };
      };
    };
 
    units = lib.options.mkOption {
      type = lib.types.attrsOf (lib.types.submodule (
        { name, ... }:
        {
          options = {
            name = lib.options.mkOption {
              type = lib.types.str;
              default = name;
              defaultText = "<name>";
              description = "Name of the unit";
            };
            unit = lib.options.mkOption {
              type = lib.types.str;
              defaultText = ''
                Default will be set in the `config` block
                of the `submodule` (see down below)
              '';
              description = "A unit of measurement";
            };
          };
          config = {
            unit = lib.mkDefault "kg";
          };
        }
      ));
      default = {};
      description = "Showcase the combination `attrsOf` and `submodule`";
      example = {
        "unit1" = { unit = "m" };
        "unit2".unit = "cm";
        "unit3" = {}; # kg
      };
    };
  };
}
</syntaxhighlight>
== Rationale ==
== Rationale ==


Line 147: Line 274:
== Example ==
== Example ==


put into <code>hello.nix</code> in the same folder as your <code>configuration.nix</code>.
To see how modules are setup and reuse other modules in practice put <code>hello.nix</code> in the same folder as your <code>configuration.nix</code>:
<syntaxhighlight lang="nix">
 
{ lib, pkgs, config, ... }:
<syntaxhighlight lang="nix">{ lib, pkgs, config, ... }:
with lib;                       
with lib;                       
let
let
  # Shorter name to access final settings a
  # user of hello.nix module HAS ACTUALLY SET.
  # cfg is a typical convention.
   cfg = config.services.hello;
   cfg = config.services.hello;
in {
in {
  meta.doc = "Says hello every time you login.";
  # Declare what settings a user of this "hello.nix" module CAN SET.
   options.services.hello = {
   options.services.hello = {
     enable = mkEnableOption "hello service";
     enable = mkEnableOption "hello service";
Line 162: Line 295:
   };
   };


  # Define what other settings, services and resources should be active IF
  # a user of this "hello.nix" module ENABLED this module
  # by setting "services.hello.enable = true;".
   config = mkIf cfg.enable {
   config = mkIf cfg.enable {
     systemd.services.hello = {
     systemd.services.hello = {
Line 168: Line 304:
     };
     };
   };
   };
}
}</syntaxhighlight>
</syntaxhighlight>


Add the following to your <code>configuration.nix</code>
The other <code>configuration.nix</code> module can then import this <code>hello.nix</code> module
and decide to enable it (and optionally set other allowed settings) as follows:
<syntaxhighlight lang=nix>
<syntaxhighlight lang=nix>
{
{
Line 228: Line 365:
}
}
</syntaxhighlight>
</syntaxhighlight>
=== Using external NixOS modules ===
Some external modules provide extra functionality to the NixOS module system. You can include these modules, after making them available as a file system path (e.g. through <code>builtins.fetchTarball</code>), by using <code>imports = [ `path to module`]</code> in your <code>configuration.nix</code>.
* [https://github.com/ip1981/nixsap Nixsap] - allows to run multiple instances of a service without containers.
* [https://github.com/musnix/musnix musnix] - real-time audio in NixOS.
* [https://gitlab.com/simple-nixos-mailserver/nixos-mailserver nixos-mailserver] - full-featured mail server module
* [https://github.com/xtruder/nix-profiles X-Truder Nix-profiles] - modules for Nix to quickly configure your system based on application profiles.
=== Disabling modules ===
In some cases, you may wish to override or disable modules previously imported, for example by NixOS. This can be achieved using the top-level attribute <code>disabledModules</code><ref>[https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/development/replace-modules.section.md "Replace Modules" section, NixOS manual]</ref>. It prevents that module, as well as any module it imports itself, from being imported into the current config scope. This means that neither the options it declares nor the configuration it might set are actually imported. This can be particularly useful to override or debug the implementation of a certain module, for example to use a module from an unstable NixOS channel while using the stable version of every other module in the system.
To disable a given module, you must refer to it based on either:
* the full, absolute path of the module: <code>/mnt/dev/foo/my-modules/services/thingy.nix</code><syntaxhighlight lang="nix">
{ ... }: {
  imports = [ <jade-nur/modules> ];
  disabledModules = [
    # make sure that the GL module is never imported
    "/home/xia/custom-modules/gl.nix"
    # prevent the tilix module from 'jade-nur' being imported
    # note that this doesn't disable the rest of 'jade-nur/modules'
    <jade-nur/modules/programs/tilix.nix>
  ];
}
</syntaxhighlight>
* the path relative to the value of <code>modulesPath</code>: <code>"programs/hello.nix"</code><syntaxhighlight lang="nix">
{ ... }: {
  disabledModules = [
    # prevent NixOS's immich module from being imported
    "services/web-apps/immich.nix"
  ];
  imports = [
    # ...and then import a replacement of it from somewhere else
    <hotnix/services/immich>
  ];
}
</syntaxhighlight>
* an attribute set containing a <code>key</code> attribute, which should be a unique identity declared by the module you're trying to disable: <code>{ key = "my-super-unique-module"; }</code>. This is most useful for modules you have control over, as modules from NixOS and home-manager do not generally have specific keys.<syntaxhighlight lang="nix">
# in file foo.nix
{ ... }: {
  disabledModules = [
    # disable the module with the exact
    # key "staple-battery-horse", as well
    # as any module it imports itself
    { key = "staple-battery-horse"; }
  ];
  ...
}
# in file horse.nix, which could be imported
# from somewhere else in the config
{ ... }: {
  # this key should be globally unique, it is
  # the unique ID by which this module is tracked
  # by the module system, and thus how it can
  # be precisely disabled
  key = "stable-battery-horse";
  imports = [ ./impl.nix ];
  options = { ... };
}
</syntaxhighlight>
Note that keys and paths (whether absolute or relative) must be exact. For example, <code>disabledModules = [ "services/web-apps" ]</code> won't have any effect, because the files inside that folder are imported individually; however, <code>disabledModules = [ "services/web-apps/akkoma.nix" "services/web-apps/immich.nix" ... ];</code> will disable every module specified.
More examples are available in the [https://nixos.org/manual/nixos/unstable/#sec-replace-modules "Replace modules" section of the NixOS manual] .


== Under the hood  ==
== Under the hood  ==
Line 275: Line 484:
The module system itself is rather complex, but here's a short overview. A module evaluation consists of a set of "modules", which can do three things:
The module system itself is rather complex, but here's a short overview. A module evaluation consists of a set of "modules", which can do three things:


* Import other modules (through imports = [ ./other-module.nix ])
* Import other modules (through <code>imports = [ ./other-module.nix ]</code>)
* Declare options (through options = { ... })
* Declare options (through <code>options = { ... }</code>)
* Define option values (through |config = { ... }, or without the config key as a shorthand if you don't have imports or options)
* Define option values (through <code>config = { ... }</code>, or without the config key as a shorthand if you don't have imports or options)


To do the actual evaluation, there's these rough steps:
To do the actual evaluation, there's these rough steps:
Line 313: Line 522:
</syntaxhighlight>
</syntaxhighlight>


If you're developing on top of master, this will potentially cause the compilation of lots of packages, since changes on master might not cached on cache.nixos.org yet. To avoid that, you can develop your module on top of the <code>nixos-unstable</code> [[Channels|channel]], tracked by the eponymous branch in https://github.com/NixOS/nixpkgs:
If you're developing on top of master, this will potentially cause the compilation of lots of packages, since changes on master might not cached on cache.nixos.org yet. To avoid that, you can develop your module on top of the <code>nixos-unstable</code> [[Channel branches|channel branch]], tracked by the eponymous branch in https://github.com/NixOS/nixpkgs:


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
git checkout -b mymodule upstream/nixos-unstable
git checkout -b mymodule upstream/nixos-unstable
</syntaxhighlight>
</syntaxhighlight>
=== With Flakes ===
If you're developing a module from nixpkgs, you can try and follow the directions here: https://github.com/Misterio77/nix-starter-configs/issues/28.
If you want to develop a module from a git repo, you can use `--override-input`. For example, if you have an input in your flake called {{ic|jovian}},, you can use
<syntaxhighlight lang="bash">
nixos-rebuild switch --override-input jovian <path-to-url> --flake <uri>
</syntaxhighlight>
Of course, it doesn't have to [[nixos-rebuild|<code>nixos-rebuild</code>]] in particular.


== References ==
== References ==