NixOS Modules Explained

2025-05-05

NixOS Modules

TL;DR: In this post I break down the NixOS module system and explain how to define options. I take notes in markdown so it's written in markdown (sorry old reddit). I write about things to deepen understanding, you think you know until you try to explain it to someone. Anyways, I hope this is useful.

Refresher:

{
  string = "hello";
  int = 3;
}
{ a, b }: a + b
{ ... }:
{
}

NixOS produces a full system configuration by combining smaller, more isolated and reusable components: Modules. In my opinion modules are one of the first things you should understand when learning about NixOS.

Declaring Options

The following is nixpkgs/nixos/modules/programs/vim.nix:

{
  config,
  lib,
  pkgs,
  ...
}:

let
  cfg = config.programs.vim;
in
{
  options.programs.vim = {
    enable = lib.mkEnableOption "Vi IMproved, an advanced text";

    defaultEditor = lib.mkEnableOption "vim as the default editor";

    package = lib.mkPackageOption pkgs "vim" { example = "vim-full"; };
  };

  # TODO: convert it into assert after 24.11 release
  config = lib.mkIf (cfg.enable || cfg.defaultEditor) {
    warnings = lib.mkIf (cfg.defaultEditor && !cfg.enable) [
      "programs.vim.defaultEditor will only work if programs.vim.enable is enabled, which will be enforced after the 24.11 release"
    ];
    environment = {
      systemPackages = [ cfg.package ];
      variables.EDITOR = lib.mkIf cfg.defaultEditor (lib.mkOverride 900 "vim");
      pathsToLink = [ "/share/vim-plugins" ];
    };
  };
}
  1. Module Inputs and Structure:
{
  config,
  lib,
  pkgs,
  ...
}

Key Takeaways: A NixOS module is typically a function that can include config, lib, and pkgs, but it doesn’t require them. The ... argument ensures flexibility, allowing a module to accept extra inputs without breaking future compatibility. Using lib simplifies handling options (mkEnableOption, mkIf, mkOverride) and helps follow best practices. Modules define options, which users can set in their configuration, and config, which applies changes based on those options.

  1. Local Configuration Reference:
let
  cfg = config.programs.vim;
in
  1. Option Declaration
options.programs.vim = {
  enable = lib.mkEnableOption "Vi IMproved, an advanced text";
  defaultEditor = lib.mkEnableOption "vim as the default editor";
  package = lib.mkPackageOption pkgs "vim" { example = "vim-full"; };
};

This defines three user-configurable options:

mkPackageOption is a helper that defines a package-typed option with a default (pkgs.vim) and provides docs + example.

  1. Conditional Configuration
config = lib.mkIf (cfg.enable || cfg.defaultEditor) {
  1. Warnings
warnings = lib.mkIf (cfg.defaultEditor && !cfg.enable) [
  "programs.vim.defaultEditor will only work if programs.vim.enable is enabled, which will be enforced after the 24.11 release"
];
  1. Actual System Config Changes
environment = {
  systemPackages = [ cfg.package ];
  variables.EDITOR = lib.mkIf cfg.defaultEditor (lib.mkOverride 900 "vim");
  pathsToLink = [ "/share/vim-plugins" ];
};

The following is a bat home-manager module that I wrote:

# bat.nix
{
  pkgs,
  config,
  lib,
  ...
}: let
  cfg = config.custom.batModule;
in {
  options.custom.batModule.enable = lib.mkOption {
    type = lib.types.bool;
    default = false;
    description = "Enable bat module";
  };

  config = lib.mkIf cfg.enable {
    programs.bat = {
      enable = true;
      themes = {
        dracula = {
          src = pkgs.fetchFromGitHub {
            owner = "dracula";
            repo = "sublime"; # Bat uses sublime syntax for its themes
            rev = "26c57ec282abcaa76e57e055f38432bd827ac34e";
            sha256 = "019hfl4zbn4vm4154hh3bwk6hm7bdxbr1hdww83nabxwjn99ndhv";
          };
          file = "Dracula.tmTheme";
        };
      };
      extraPackages = with pkgs.bat-extras; [
        batdiff
        batman
        prettybat
        batgrep
      ];
    };
  };
}

Now I could add this to my home.nix to enable it:

# home.nix
custom = {
  batModule.enable = true;
}

Module Composition

Resources on Modules