about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/config
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/config')
-rw-r--r--nixpkgs/nixos/modules/config/appstream.nix25
-rw-r--r--nixpkgs/nixos/modules/config/console.nix251
-rw-r--r--nixpkgs/nixos/modules/config/debug-info.nix44
-rw-r--r--nixpkgs/nixos/modules/config/fanout.nix49
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fontconfig.nix528
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fontdir.nix67
-rw-r--r--nixpkgs/nixos/modules/config/fonts/ghostscript.nix23
-rw-r--r--nixpkgs/nixos/modules/config/fonts/packages.nix43
-rw-r--r--nixpkgs/nixos/modules/config/gtk/gtk-icon-cache.nix85
-rw-r--r--nixpkgs/nixos/modules/config/i18n.nix113
-rw-r--r--nixpkgs/nixos/modules/config/iproute2.nix26
-rw-r--r--nixpkgs/nixos/modules/config/ldap.nix312
-rw-r--r--nixpkgs/nixos/modules/config/ldso.nix58
-rw-r--r--nixpkgs/nixos/modules/config/locale.nix93
-rw-r--r--nixpkgs/nixos/modules/config/malloc.nix114
-rw-r--r--nixpkgs/nixos/modules/config/mysql.nix469
-rw-r--r--nixpkgs/nixos/modules/config/networking.nix237
-rw-r--r--nixpkgs/nixos/modules/config/nix-channel.nix103
-rw-r--r--nixpkgs/nixos/modules/config/nix-flakes.nix95
-rw-r--r--nixpkgs/nixos/modules/config/nix-remote-build.nix226
-rw-r--r--nixpkgs/nixos/modules/config/nix.nix383
-rw-r--r--nixpkgs/nixos/modules/config/no-x-libs.nix87
-rw-r--r--nixpkgs/nixos/modules/config/nsswitch.nix136
-rw-r--r--nixpkgs/nixos/modules/config/power-management.nix106
-rw-r--r--nixpkgs/nixos/modules/config/pulseaudio.nix327
-rw-r--r--nixpkgs/nixos/modules/config/qt.nix154
-rw-r--r--nixpkgs/nixos/modules/config/resolvconf.nix160
-rw-r--r--nixpkgs/nixos/modules/config/shells-environment.nix224
-rw-r--r--nixpkgs/nixos/modules/config/stevenblack.nix34
-rw-r--r--nixpkgs/nixos/modules/config/stub-ld.nix56
-rw-r--r--nixpkgs/nixos/modules/config/swap.nix305
-rw-r--r--nixpkgs/nixos/modules/config/sysctl.nix86
-rw-r--r--nixpkgs/nixos/modules/config/system-environment.nix100
-rw-r--r--nixpkgs/nixos/modules/config/system-path.nix189
-rw-r--r--nixpkgs/nixos/modules/config/terminfo.nix76
-rw-r--r--nixpkgs/nixos/modules/config/unix-odbc-drivers.nix38
-rw-r--r--nixpkgs/nixos/modules/config/update-users-groups.pl381
-rw-r--r--nixpkgs/nixos/modules/config/users-groups.nix935
-rw-r--r--nixpkgs/nixos/modules/config/vte.nix56
-rw-r--r--nixpkgs/nixos/modules/config/xdg/autostart.nix26
-rw-r--r--nixpkgs/nixos/modules/config/xdg/icons.nix48
-rw-r--r--nixpkgs/nixos/modules/config/xdg/menus.nix29
-rw-r--r--nixpkgs/nixos/modules/config/xdg/mime.nix102
-rw-r--r--nixpkgs/nixos/modules/config/xdg/portal.nix181
-rw-r--r--nixpkgs/nixos/modules/config/xdg/portals/lxqt.nix49
-rw-r--r--nixpkgs/nixos/modules/config/xdg/portals/wlr.nix67
-rw-r--r--nixpkgs/nixos/modules/config/xdg/sounds.nix30
-rw-r--r--nixpkgs/nixos/modules/config/zram.nix130
48 files changed, 7456 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/config/appstream.nix b/nixpkgs/nixos/modules/config/appstream.nix
new file mode 100644
index 000000000000..5b48f6e1705d
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/appstream.nix
@@ -0,0 +1,25 @@
+{ config, lib, ... }:
+
+with lib;
+{
+  options = {
+    appstream.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to install files to support the
+        [AppStream metadata specification](https://www.freedesktop.org/software/appstream/docs/index.html).
+      '';
+    };
+  };
+
+  config = mkIf config.appstream.enable {
+    environment.pathsToLink = [
+      # per component metadata
+      "/share/metainfo"
+      # legacy path for above
+      "/share/appdata"
+    ];
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/console.nix b/nixpkgs/nixos/modules/config/console.nix
new file mode 100644
index 000000000000..442cfe9292ca
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/console.nix
@@ -0,0 +1,251 @@
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.console;
+
+  makeColor = i: concatMapStringsSep "," (x: "0x" + substring (2*i) 2 x);
+
+  isUnicode = '' \
+    LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive \
+    LANG=${config.i18n.defaultLocale} \
+    LC_IDENTIFICATION=${config.i18n.defaultLocale} \
+    locale -k identification-codeset | grep -i UTF-8 \
+  '';
+
+  optimizedKeymap = pkgs.runCommand "keymap" {
+    nativeBuildInputs = with pkgs.buildPackages; [ kbd locale ];
+    LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
+    preferLocalBuild = true;
+  } ''
+    if ${isUnicode} ; then
+      loadkeys -b -u "${cfg.keyMap}" > $out
+    else
+      loadkeys -b "${cfg.keyMap}" > $out
+    fi
+  '';
+
+  # Sadly, systemd-vconsole-setup doesn't support binary keymaps.
+  vconsoleConf = pkgs.writeText "vconsole.conf" ''
+    KEYMAP=${cfg.keyMap}
+    ${optionalString (cfg.font != null) "FONT=${cfg.font}"}
+  '';
+
+  consoleEnv = kbd: pkgs.buildEnv {
+    name = "console-env";
+    paths = [ kbd ] ++ cfg.packages;
+    pathsToLink = [
+      "/share/consolefonts"
+      "/share/consoletrans"
+      "/share/keymaps"
+      "/share/unimaps"
+    ];
+  };
+in
+
+{
+  ###### interface
+
+  options.console  = {
+    enable = mkEnableOption (lib.mdDoc "virtual console") // {
+      default = true;
+    };
+
+    font = mkOption {
+      type = with types; nullOr (either str path);
+      default = null;
+      example = "LatArCyrHeb-16";
+      description = mdDoc ''
+        The font used for the virtual consoles.
+        Can be `null`, a font name, or a path to a PSF font file.
+
+        Use `null` to let the kernel choose a built-in font.
+        The default is 8x16, and, as of Linux 5.3, Terminus 32 bold for display
+        resolutions of 2560x1080 and higher.
+        These fonts cover the [IBM437][] character set.
+
+        [IBM437]: https://en.wikipedia.org/wiki/Code_page_437
+      '';
+    };
+
+    keyMap = mkOption {
+      type = with types; either str path;
+      default = "us";
+      example = "fr";
+      description = lib.mdDoc ''
+        The keyboard mapping table for the virtual consoles.
+      '';
+    };
+
+    colors = mkOption {
+      type = with types; listOf (strMatching "[[:xdigit:]]{6}");
+      default = [ ];
+      example = [
+        "002b36" "dc322f" "859900" "b58900"
+        "268bd2" "d33682" "2aa198" "eee8d5"
+        "002b36" "cb4b16" "586e75" "657b83"
+        "839496" "6c71c4" "93a1a1" "fdf6e3"
+      ];
+      description = lib.mdDoc ''
+        The 16 colors palette used by the virtual consoles.
+        Leave empty to use the default colors.
+        Colors must be in hexadecimal format and listed in
+        order from color 0 to color 15.
+      '';
+
+    };
+
+    packages = mkOption {
+      type = types.listOf types.package;
+      default = [ ];
+      description = lib.mdDoc ''
+        List of additional packages that provide console fonts, keymaps and
+        other resources for virtual consoles use.
+      '';
+    };
+
+    useXkbConfig = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        If set, configure the virtual console keymap from the xserver
+        keyboard settings.
+      '';
+    };
+
+    earlySetup = mkOption {
+      default = false;
+      type = types.bool;
+      description = lib.mdDoc ''
+        Enable setting virtual console options as early as possible (in initrd).
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkMerge [
+    { console.keyMap = with config.services.xserver;
+        mkIf cfg.useXkbConfig
+          (pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
+            '${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
+              ${optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
+                "-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
+              } \
+              -model '${xkb.model}' -layout '${xkb.layout}' \
+              -option '${xkb.options}' -variant '${xkb.variant}' > "$out"
+          '');
+    }
+
+    (mkIf (!cfg.enable) {
+      systemd.services = {
+        "serial-getty@ttyS0".enable = false;
+        "serial-getty@hvc0".enable = false;
+        "getty@tty1".enable = false;
+        "autovt@".enable = false;
+        systemd-vconsole-setup.enable = false;
+      };
+    })
+
+    (mkIf cfg.enable (mkMerge [
+      { environment.systemPackages = with pkgs; [ kbd locale ];
+
+        # Let systemd-vconsole-setup.service do the work of setting up the
+        # virtual consoles.
+        environment.etc."vconsole.conf".source = vconsoleConf;
+        # Provide kbd with additional packages.
+        environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
+
+        boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (mkBefore ''
+          if ${isUnicode} ; then
+            kbd_mode -u -C /dev/console
+            printf "\033%%G" >> /dev/console
+          else
+            kbd_mode -a -C /dev/console
+            printf "\033%%@" >> /dev/console
+          fi
+          loadkmap < ${optimizedKeymap}
+
+          ${optionalString (cfg.earlySetup && cfg.font != null) ''
+            setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
+          ''}
+        '');
+
+        boot.initrd.systemd.contents = {
+          "/etc/vconsole.conf".source = vconsoleConf;
+          # Add everything if we want full console setup...
+          "/etc/kbd" = lib.mkIf cfg.earlySetup { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share"; };
+          # ...but only the keymaps if we don't
+          "/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps"; };
+        };
+        boot.initrd.systemd.additionalUpstreamUnits = [
+          "systemd-vconsole-setup.service"
+        ];
+        boot.initrd.systemd.storePaths = [
+          "${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
+          "${config.boot.initrd.systemd.package.kbd}/bin/setfont"
+          "${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
+          "${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
+        ] ++ optionals (cfg.font != null && hasPrefix builtins.storeDir cfg.font) [
+          "${cfg.font}"
+        ] ++ optionals (hasPrefix builtins.storeDir cfg.keyMap) [
+          "${cfg.keyMap}"
+        ];
+
+        systemd.services.reload-systemd-vconsole-setup =
+          { description = "Reset console on configuration changes";
+            wantedBy = [ "multi-user.target" ];
+            restartTriggers = [ vconsoleConf (consoleEnv pkgs.kbd) ];
+            reloadIfChanged = true;
+            serviceConfig =
+              { RemainAfterExit = true;
+                ExecStart = "${pkgs.coreutils}/bin/true";
+                ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
+              };
+          };
+      }
+
+      (mkIf (cfg.colors != []) {
+        boot.kernelParams = [
+          "vt.default_red=${makeColor 0 cfg.colors}"
+          "vt.default_grn=${makeColor 1 cfg.colors}"
+          "vt.default_blu=${makeColor 2 cfg.colors}"
+        ];
+      })
+
+      (mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
+        boot.initrd.extraUtilsCommands = ''
+          mkdir -p $out/share/consolefonts
+          ${if substring 0 1 cfg.font == "/" then ''
+            font="${cfg.font}"
+          '' else ''
+            font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
+          ''}
+          if [[ $font == *.gz ]]; then
+            gzip -cd $font > $out/share/consolefonts/font.psf
+          else
+            cp -L $font $out/share/consolefonts/font.psf
+          fi
+        '';
+      })
+    ]))
+  ];
+
+  imports = [
+    (mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ])
+    (mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ])
+    (mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ])
+    (mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ])
+    (mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ])
+    (mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ])
+    (mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ])
+    (mkRemovedOptionModule [ "console" "extraTTYs" ] ''
+      Since NixOS switched to systemd (circa 2012), TTYs have been spawned on
+      demand, so there is no need to configure them manually.
+    '')
+  ];
+}
diff --git a/nixpkgs/nixos/modules/config/debug-info.nix b/nixpkgs/nixos/modules/config/debug-info.nix
new file mode 100644
index 000000000000..78de26fda440
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/debug-info.nix
@@ -0,0 +1,44 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+  options = {
+
+    environment.enableDebugInfo = mkOption {
+      type = types.bool;
+      default = false;
+      description = mdDoc ''
+        Some NixOS packages provide debug symbols. However, these are
+        not included in the system closure by default to save disk
+        space. Enabling this option causes the debug symbols to appear
+        in {file}`/run/current-system/sw/lib/debug/.build-id`,
+        where tools such as {command}`gdb` can find them.
+        If you need debug symbols for a package that doesn't
+        provide them by default, you can enable them as follows:
+
+            nixpkgs.config.packageOverrides = pkgs: {
+              hello = pkgs.hello.overrideAttrs (oldAttrs: {
+                separateDebugInfo = true;
+              });
+            };
+      '';
+    };
+
+  };
+
+
+  config = mkIf config.environment.enableDebugInfo {
+
+    # FIXME: currently disabled because /lib is already in
+    # environment.pathsToLink, and we can't have both.
+    #environment.pathsToLink = [ "/lib/debug/.build-id" ];
+
+    environment.extraOutputsToInstall = [ "debug" ];
+
+    environment.variables.NIX_DEBUG_INFO_DIRS = [ "/run/current-system/sw/lib/debug" ];
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/fanout.nix b/nixpkgs/nixos/modules/config/fanout.nix
new file mode 100644
index 000000000000..60ee145f19af
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/fanout.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.fanout;
+  mknodCmds = n: lib.lists.imap0 (i: s:
+    "mknod /dev/fanout${builtins.toString i} c $MAJOR ${builtins.toString i}"
+  ) (lib.lists.replicate n "");
+in
+{
+  options.services.fanout = {
+    enable = lib.mkEnableOption (lib.mdDoc "fanout");
+    fanoutDevices = lib.mkOption {
+      type = lib.types.int;
+      default = 1;
+      description = "Number of /dev/fanout devices";
+    };
+    bufferSize = lib.mkOption {
+      type = lib.types.int;
+      default = 16384;
+      description = "Size of /dev/fanout buffer in bytes";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    boot.extraModulePackages = [ config.boot.kernelPackages.fanout.out ];
+
+    boot.kernelModules = [ "fanout" ];
+
+    boot.extraModprobeConfig = ''
+      options fanout buffersize=${builtins.toString cfg.bufferSize}
+    '';
+
+    systemd.services.fanout = {
+      description = "Bring up /dev/fanout devices";
+      script = ''
+        MAJOR=$(${pkgs.gnugrep}/bin/grep fanout /proc/devices | ${pkgs.gawk}/bin/awk '{print $1}')
+        ${lib.strings.concatLines (mknodCmds cfg.fanoutDevices)}
+      '';
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "root";
+        RemainAfterExit = "yes";
+        Restart = "no";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/fonts/fontconfig.nix b/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
new file mode 100644
index 000000000000..5e2e054f7c4e
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
@@ -0,0 +1,528 @@
+/*
+
+Configuration files are linked to /etc/fonts/conf.d/
+
+This module generates a package containing configuration files and link it in /etc/fonts.
+
+Fontconfig reads files in folder name / file name order, so the number prepended to the configuration file name decide the order of parsing.
+Low number means high priority.
+
+NOTE: Please take extreme care when adjusting the default settings of this module.
+People care a lot, and I mean A LOT, about their font rendering, and you will be
+The Person That Broke It if it changes in a way people don't like.
+
+See prior art:
+- https://github.com/NixOS/nixpkgs/pull/194594
+- https://github.com/NixOS/nixpkgs/pull/222236
+- https://github.com/NixOS/nixpkgs/pull/222689
+
+And do not repeat our mistakes.
+
+- @K900, March 2023
+
+*/
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.fonts.fontconfig;
+
+  fcBool = x: "<bool>" + (boolToString x) + "</bool>";
+  pkg = pkgs.fontconfig;
+
+  # configuration file to read fontconfig cache
+  # priority 0
+  cacheConf  = makeCacheConf {};
+
+  # generate the font cache setting file
+  # When cross-compiling, we can’t generate the cache, so we skip the
+  # <cachedir> part. fontconfig still works but is a little slower in
+  # looking things up.
+  makeCacheConf = { }:
+    let
+      makeCache = fontconfig: pkgs.makeFontsCache { inherit fontconfig; fontDirectories = config.fonts.packages; };
+      cache     = makeCache pkgs.fontconfig;
+      cache32   = makeCache pkgs.pkgsi686Linux.fontconfig;
+    in
+    pkgs.writeText "fc-00-nixos-cache.conf" ''
+      <?xml version='1.0'?>
+      <!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
+      <fontconfig>
+        <!-- Font directories -->
+        ${concatStringsSep "\n" (map (font: "<dir>${font}</dir>") config.fonts.packages)}
+        ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
+        <!-- Pre-generated font caches -->
+        <cachedir>${cache}</cachedir>
+        ${optionalString (pkgs.stdenv.isx86_64 && cfg.cache32Bit) ''
+          <cachedir>${cache32}</cachedir>
+        ''}
+        ''}
+      </fontconfig>
+    '';
+
+  # rendering settings configuration file
+  # priority 10
+  renderConf = pkgs.writeText "fc-10-nixos-rendering.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default rendering settings -->
+      <match target="pattern">
+        <edit mode="append" name="hinting">
+          ${fcBool cfg.hinting.enable}
+        </edit>
+        <edit mode="append" name="autohint">
+          ${fcBool cfg.hinting.autohint}
+        </edit>
+      </match>
+
+    </fontconfig>
+  '';
+
+  # local configuration file
+  localConf = pkgs.writeText "fc-local.conf" cfg.localConf;
+
+  # default fonts configuration file
+  # priority 52
+  defaultFontsConf =
+    let genDefault = fonts: name:
+      optionalString (fonts != []) ''
+        <alias binding="same">
+          <family>${name}</family>
+          <prefer>
+          ${concatStringsSep ""
+          (map (font: ''
+            <family>${font}</family>
+          '') fonts)}
+          </prefer>
+        </alias>
+      '';
+    in
+    pkgs.writeText "fc-52-nixos-default-fonts.conf" ''
+    <?xml version='1.0'?>
+    <!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
+    <fontconfig>
+
+      <!-- Default fonts -->
+      ${genDefault cfg.defaultFonts.sansSerif "sans-serif"}
+
+      ${genDefault cfg.defaultFonts.serif     "serif"}
+
+      ${genDefault cfg.defaultFonts.monospace "monospace"}
+
+      ${genDefault cfg.defaultFonts.emoji "emoji"}
+
+    </fontconfig>
+  '';
+
+  # bitmap font options
+  # priority 53
+  rejectBitmaps = pkgs.writeText "fc-53-no-bitmaps.conf" ''
+    <?xml version="1.0"?>
+    <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
+    <fontconfig>
+
+    ${optionalString (!cfg.allowBitmaps) ''
+    <!-- Reject bitmap fonts -->
+    <selectfont>
+      <rejectfont>
+        <pattern>
+          <patelt name="scalable"><bool>false</bool></patelt>
+        </pattern>
+      </rejectfont>
+    </selectfont>
+    ''}
+
+    <!-- Use embedded bitmaps in fonts like Calibri? -->
+    <match target="font">
+      <edit name="embeddedbitmap" mode="assign">
+        ${fcBool cfg.useEmbeddedBitmaps}
+      </edit>
+    </match>
+
+    </fontconfig>
+  '';
+
+  # reject Type 1 fonts
+  # priority 53
+  rejectType1 = pkgs.writeText "fc-53-nixos-reject-type1.conf" ''
+    <?xml version="1.0"?>
+    <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
+    <fontconfig>
+
+    <!-- Reject Type 1 fonts -->
+    <selectfont>
+      <rejectfont>
+        <pattern>
+          <patelt name="fontformat"><string>Type 1</string></patelt>
+        </pattern>
+      </rejectfont>
+    </selectfont>
+
+    </fontconfig>
+  '';
+
+  # Replace default linked config with a different variant
+  replaceDefaultConfig = defaultConfig: newConfig: ''
+    rm $dst/${defaultConfig}
+    ln -s ${pkg.out}/share/fontconfig/conf.avail/${newConfig} \
+          $dst/
+  '';
+
+  # fontconfig configuration package
+  confPkg = pkgs.runCommand "fontconfig-conf" {
+    preferLocalBuild = true;
+  } ''
+    dst=$out/etc/fonts/conf.d
+    mkdir -p $dst
+
+    # fonts.conf
+    ln -s ${pkg.out}/etc/fonts/fonts.conf \
+          $dst/../fonts.conf
+    # TODO: remove this legacy symlink once people stop using packages built before #95358 was merged
+    mkdir -p $out/etc/fonts/2.11
+    ln -s /etc/fonts/fonts.conf \
+          $out/etc/fonts/2.11/fonts.conf
+
+    # fontconfig default config files
+    ln -s ${pkg.out}/etc/fonts/conf.d/*.conf \
+          $dst/
+
+    ${optionalString (!cfg.antialias)
+      (replaceDefaultConfig "10-yes-antialias.conf"
+        "10-no-antialias.conf")
+    }
+
+    ${optionalString (cfg.hinting.style != "slight")
+      (replaceDefaultConfig "10-hinting-slight.conf"
+        "10-hinting-${cfg.hinting.style}.conf")
+    }
+
+    ${optionalString (cfg.subpixel.rgba != "none")
+      (replaceDefaultConfig "10-sub-pixel-none.conf"
+        "10-sub-pixel-${cfg.subpixel.rgba}.conf")
+    }
+
+    ${optionalString (cfg.subpixel.lcdfilter != "default")
+      (replaceDefaultConfig "11-lcdfilter-default.conf"
+        "11-lcdfilter-${cfg.subpixel.lcdfilter}.conf")
+    }
+
+    # 00-nixos-cache.conf
+    ln -s ${cacheConf}  $dst/00-nixos-cache.conf
+
+    # 10-nixos-rendering.conf
+    ln -s ${renderConf}       $dst/10-nixos-rendering.conf
+
+    # 50-user.conf
+    ${optionalString (!cfg.includeUserConf) ''
+    rm $dst/50-user.conf
+    ''}
+
+    # local.conf (indirect priority 51)
+    ${optionalString (cfg.localConf != "") ''
+    ln -s ${localConf}        $dst/../local.conf
+    ''}
+
+    # 52-nixos-default-fonts.conf
+    ln -s ${defaultFontsConf} $dst/52-nixos-default-fonts.conf
+
+    # 53-no-bitmaps.conf
+    ln -s ${rejectBitmaps} $dst/53-no-bitmaps.conf
+
+    ${optionalString (!cfg.allowType1) ''
+    # 53-nixos-reject-type1.conf
+    ln -s ${rejectType1} $dst/53-nixos-reject-type1.conf
+    ''}
+  '';
+
+  # Package with configuration files
+  # this merge all the packages in the fonts.fontconfig.confPackages list
+  fontconfigEtc = pkgs.buildEnv {
+    name  = "fontconfig-etc";
+    paths = cfg.confPackages;
+    ignoreCollisions = true;
+  };
+
+  fontconfigNote = "Consider manually configuring fonts.fontconfig according to personal preference.";
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowBitmaps" ] [ "fonts" "fontconfig" "allowBitmaps" ])
+    (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "allowType1" ] [ "fonts" "fontconfig" "allowType1" ])
+    (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "fonts" "fontconfig" "useEmbeddedBitmaps" ])
+    (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "fonts" "fontconfig" "forceAutohint" ])
+    (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ])
+    (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
+    (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
+    (mkRemovedOptionModule [ "fonts" "fontconfig" "dpi" ] "Use display server-specific options")
+    (mkRemovedOptionModule [ "hardware" "video" "hidpi" "enable" ] fontconfigNote)
+    (mkRemovedOptionModule [ "fonts" "optimizeForVeryHighDPI" ] fontconfigNote)
+  ] ++ lib.forEach [ "enable" "substitutions" "preset" ]
+     (opt: lib.mkRemovedOptionModule [ "fonts" "fontconfig" "ultimate" "${opt}" ] ''
+       The fonts.fontconfig.ultimate module and configuration is obsolete.
+       The repository has since been archived and activity has ceased.
+       https://github.com/bohoomil/fontconfig-ultimate/issues/171.
+       No action should be needed for font configuration, as the fonts.fontconfig
+       module is already used by default.
+     '');
+
+  options = {
+
+    fonts = {
+
+      fontconfig = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            If enabled, a Fontconfig configuration file will be built
+            pointing to a set of default fonts.  If you don't care about
+            running X11 applications or any other program that uses
+            Fontconfig, you can turn this option off and prevent a
+            dependency on all those fonts.
+          '';
+        };
+
+        confPackages = mkOption {
+          internal = true;
+          type     = with types; listOf path;
+          default  = [ ];
+          description = lib.mdDoc ''
+            Fontconfig configuration packages.
+          '';
+        };
+
+        antialias = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Enable font antialiasing. At high resolution (> 200 DPI),
+            antialiasing has no visible effect; users of such displays may want
+            to disable this option.
+          '';
+        };
+
+        localConf = mkOption {
+          type = types.lines;
+          default = "";
+          description = lib.mdDoc ''
+            System-wide customization file contents, has higher priority than
+            `defaultFonts` settings.
+          '';
+        };
+
+        defaultFonts = {
+          monospace = mkOption {
+            type = types.listOf types.str;
+            default = ["DejaVu Sans Mono"];
+            description = lib.mdDoc ''
+              System-wide default monospace font(s). Multiple fonts may be
+              listed in case multiple languages must be supported.
+            '';
+          };
+
+          sansSerif = mkOption {
+            type = types.listOf types.str;
+            default = ["DejaVu Sans"];
+            description = lib.mdDoc ''
+              System-wide default sans serif font(s). Multiple fonts may be
+              listed in case multiple languages must be supported.
+            '';
+          };
+
+          serif = mkOption {
+            type = types.listOf types.str;
+            default = ["DejaVu Serif"];
+            description = lib.mdDoc ''
+              System-wide default serif font(s). Multiple fonts may be listed
+              in case multiple languages must be supported.
+            '';
+          };
+
+          emoji = mkOption {
+            type = types.listOf types.str;
+            default = ["Noto Color Emoji"];
+            description = lib.mdDoc ''
+              System-wide default emoji font(s). Multiple fonts may be listed
+              in case a font does not support all emoji.
+
+              Note that fontconfig matches color emoji fonts preferentially,
+              so if you want to use a black and white font while having
+              a color font installed (eg. Noto Color Emoji installed alongside
+              Noto Emoji), fontconfig will still choose the color font even
+              when it is later in the list.
+            '';
+          };
+        };
+
+        hinting = {
+          enable = mkOption {
+            type = types.bool;
+            default = true;
+            description = lib.mdDoc ''
+              Enable font hinting. Hinting aligns glyphs to pixel boundaries to
+              improve rendering sharpness at low resolution. At high resolution
+              (> 200 dpi) hinting will do nothing (at best); users of such
+              displays may want to disable this option.
+            '';
+          };
+
+          autohint = mkOption {
+            type = types.bool;
+            default = false;
+            description = lib.mdDoc ''
+              Enable the autohinter in place of the default interpreter.
+              The results are usually lower quality than correctly-hinted
+              fonts, but better than unhinted fonts.
+            '';
+          };
+
+          style = mkOption {
+            type = types.enum ["none" "slight" "medium" "full"];
+            default = "slight";
+            description = lib.mdDoc ''
+              Hintstyle is the amount of font reshaping done to line up
+              to the grid.
+
+              slight will make the font more fuzzy to line up to the grid but
+              will be better in retaining font shape, while full will be a
+              crisp font that aligns well to the pixel grid but will lose a
+              greater amount of font shape.
+            '';
+            apply =
+              val:
+              let
+                from = "fonts.fontconfig.hinting.style";
+                val' = lib.removePrefix "hint" val;
+                warning = "The option `${from}` contains a deprecated value `${val}`. Use `${val'}` instead.";
+              in
+              lib.warnIf (lib.hasPrefix "hint" val) warning val';
+          };
+        };
+
+        includeUserConf = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Include the user configuration from
+            {file}`~/.config/fontconfig/fonts.conf` or
+            {file}`~/.config/fontconfig/conf.d`.
+          '';
+        };
+
+        subpixel = {
+
+          rgba = mkOption {
+            default = "none";
+            type = types.enum ["rgb" "bgr" "vrgb" "vbgr" "none"];
+            description = lib.mdDoc ''
+              Subpixel order. The overwhelming majority of displays are
+              `rgb` in their normal orientation. Select
+              `vrgb` for mounting such a display 90 degrees
+              clockwise from its normal orientation or `vbgr`
+              for mounting 90 degrees counter-clockwise. Select
+              `bgr` in the unlikely event of mounting 180
+              degrees from the normal orientation. Reverse these directions in
+              the improbable event that the display's native subpixel order is
+              `bgr`.
+            '';
+          };
+
+          lcdfilter = mkOption {
+            default = "default";
+            type = types.enum ["none" "default" "light" "legacy"];
+            description = lib.mdDoc ''
+              FreeType LCD filter. At high resolution (> 200 DPI), LCD filtering
+              has no visible effect; users of such displays may want to select
+              `none`.
+            '';
+          };
+
+        };
+
+        cache32Bit = mkOption {
+          default = false;
+          type = types.bool;
+          description = lib.mdDoc ''
+            Generate system fonts cache for 32-bit applications.
+          '';
+        };
+
+        allowBitmaps = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Allow bitmap fonts. Set to `false` to ban all
+            bitmap fonts.
+          '';
+        };
+
+        allowType1 = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Allow Type-1 fonts. Default is `false` because of
+            poor rendering.
+          '';
+        };
+
+        useEmbeddedBitmaps = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc "Use embedded bitmaps in fonts like Calibri.";
+        };
+
+      };
+
+    };
+
+  };
+  config = mkMerge [
+    (mkIf cfg.enable {
+      environment.systemPackages    = [ pkgs.fontconfig ];
+      environment.etc.fonts.source  = "${fontconfigEtc}/etc/fonts/";
+      security.apparmor.includes."abstractions/fonts" = ''
+        # fonts.conf
+        r ${pkg.out}/etc/fonts/fonts.conf,
+
+        # fontconfig default config files
+        r ${pkg.out}/etc/fonts/conf.d/*.conf,
+
+        # 00-nixos-cache.conf
+        r ${cacheConf},
+
+        # 10-nixos-rendering.conf
+        r ${renderConf},
+
+        # 50-user.conf
+        ${optionalString cfg.includeUserConf ''
+        r ${pkg.out}/etc/fonts/conf.d.bak/50-user.conf,
+        ''}
+
+        # local.conf (indirect priority 51)
+        ${optionalString (cfg.localConf != "") ''
+        r ${localConf},
+        ''}
+
+        # 52-nixos-default-fonts.conf
+        r ${defaultFontsConf},
+
+        # 53-no-bitmaps.conf
+        r ${rejectBitmaps},
+
+        ${optionalString (!cfg.allowType1) ''
+        # 53-nixos-reject-type1.conf
+        r ${rejectType1},
+        ''}
+      '';
+    })
+    (mkIf cfg.enable {
+      fonts.fontconfig.confPackages = [ confPkg ];
+    })
+  ];
+
+}
diff --git a/nixpkgs/nixos/modules/config/fonts/fontdir.nix b/nixpkgs/nixos/modules/config/fonts/fontdir.nix
new file mode 100644
index 000000000000..3b5eaf5b2d7f
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/fonts/fontdir.nix
@@ -0,0 +1,67 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.fonts.fontDir;
+
+  x11Fonts = pkgs.runCommand "X11-fonts" { preferLocalBuild = true; } ''
+    mkdir -p "$out/share/X11/fonts"
+    font_regexp='.*\.\(ttf\|ttc\|otb\|otf\|pcf\|pfa\|pfb\|bdf\)\(\.gz\)?'
+    find ${toString config.fonts.packages} -regex "$font_regexp" \
+      -exec ln -sf -t "$out/share/X11/fonts" '{}' \;
+    cd "$out/share/X11/fonts"
+    ${optionalString cfg.decompressFonts ''
+      ${pkgs.gzip}/bin/gunzip -f *.gz
+    ''}
+    ${pkgs.xorg.mkfontscale}/bin/mkfontscale
+    ${pkgs.xorg.mkfontdir}/bin/mkfontdir
+    cat $(find ${pkgs.xorg.fontalias}/ -name fonts.alias) >fonts.alias
+  '';
+
+in
+
+{
+
+  options = {
+    fonts.fontDir = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to create a directory with links to all fonts in
+          {file}`/run/current-system/sw/share/X11/fonts`.
+        '';
+      };
+
+      decompressFonts = mkOption {
+        type = types.bool;
+        default = config.programs.xwayland.enable;
+        defaultText = literalExpression "config.programs.xwayland.enable";
+        description = lib.mdDoc ''
+          Whether to decompress fonts in
+          {file}`/run/current-system/sw/share/X11/fonts`.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ x11Fonts ];
+    environment.pathsToLink = [ "/share/X11/fonts" ];
+
+    services.xserver.filesSection = ''
+      FontPath "${x11Fonts}/share/X11/fonts"
+    '';
+
+  };
+
+  imports = [
+    (mkRenamedOptionModule [ "fonts" "enableFontDir" ] [ "fonts" "fontDir" "enable" ])
+  ];
+
+}
diff --git a/nixpkgs/nixos/modules/config/fonts/ghostscript.nix b/nixpkgs/nixos/modules/config/fonts/ghostscript.nix
new file mode 100644
index 000000000000..c41fcdaaa329
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/fonts/ghostscript.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  options = {
+    fonts.enableGhostscriptFonts = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Whether to add the fonts provided by Ghostscript (such as
+        various URW fonts and the “Base-14” Postscript fonts) to the
+        list of system fonts, making them available to X11
+        applications.
+      '';
+    };
+
+  };
+
+  config = mkIf config.fonts.enableGhostscriptFonts {
+    fonts.packages = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/fonts/packages.nix b/nixpkgs/nixos/modules/config/fonts/packages.nix
new file mode 100644
index 000000000000..37b705ecb345
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/fonts/packages.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.fonts;
+in
+{
+  imports = [
+    (lib.mkRemovedOptionModule [ "fonts" "enableCoreFonts" ] "Use fonts.packages = [ pkgs.corefonts ]; instead.")
+    (lib.mkRenamedOptionModule [ "fonts" "enableDefaultFonts" ] [ "fonts" "enableDefaultPackages" ])
+    (lib.mkRenamedOptionModule [ "fonts" "fonts" ] [ "fonts" "packages" ])
+  ];
+
+  options = {
+    fonts = {
+      packages = lib.mkOption {
+        type = with lib.types; listOf path;
+        default = [];
+        example = lib.literalExpression "[ pkgs.dejavu_fonts ]";
+        description = lib.mdDoc "List of primary font packages.";
+      };
+
+      enableDefaultPackages = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Enable a basic set of fonts providing several styles
+          and families and reasonable coverage of Unicode.
+        '';
+      };
+    };
+  };
+
+  config = {
+    fonts.packages = lib.mkIf cfg.enableDefaultPackages (with pkgs; [
+      dejavu_fonts
+      freefont_ttf
+      gyre-fonts # TrueType substitutes for standard PostScript fonts
+      liberation_ttf
+      unifont
+      noto-fonts-color-emoji
+    ]);
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/gtk/gtk-icon-cache.nix b/nixpkgs/nixos/modules/config/gtk/gtk-icon-cache.nix
new file mode 100644
index 000000000000..62f0cc3f090f
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/gtk/gtk-icon-cache.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  options = {
+    gtk.iconCache.enable = mkOption {
+      type = types.bool;
+      default = config.services.xserver.enable;
+      defaultText = literalExpression "config.services.xserver.enable";
+      description = lib.mdDoc ''
+        Whether to build icon theme caches for GTK applications.
+      '';
+    };
+  };
+
+  config = mkIf config.gtk.iconCache.enable {
+
+    # (Re)build icon theme caches
+    # ---------------------------
+    # Each icon theme has its own cache. The difficult is that many
+    # packages may contribute with icons to the same theme by installing
+    # some icons.
+    #
+    # For instance, on my current NixOS system, the following packages
+    # (among many others) have icons installed into the hicolor icon
+    # theme: hicolor-icon-theme, psensor, wpa_gui, caja, etc.
+    #
+    # As another example, the mate icon theme has icons installed by the
+    # packages mate-icon-theme, mate-settings-daemon, and libmateweather.
+    #
+    # The HighContrast icon theme also has icons from different packages,
+    # like gnome-theme-extras and meld.
+
+    # When the cache is built all of its icons has to be known. How to
+    # implement this?
+    #
+    # I think that most themes have all icons installed by only one
+    # package. On my system there are 71 themes installed. Only 3 of them
+    # have icons installed from more than one package.
+    #
+    # If the main package of the theme provides a cache, presumably most
+    # of its icons will be available to applications without running this
+    # module. But additional icons offered by other packages will not be
+    # available. Therefore I think that it is good that the main theme
+    # package installs a cache (although it does not completely fixes the
+    # situation for packages installed with nix-env).
+    #
+    # The module solution presented here keeps the cache when there is
+    # only one package contributing with icons to the theme. Otherwise it
+    # rebuilds the cache taking into account the icons provided all
+    # packages.
+
+    environment.extraSetup = ''
+      # For each icon theme directory ...
+      find $out/share/icons -exec test -d {} ';' -mindepth 1 -maxdepth 1 -print0 | while read -d $'\0' themedir
+      do
+        # In order to build the cache, the theme dir should be
+        # writable. When the theme dir is a symbolic link to somewhere
+        # in the nix store it is not writable and it means that only
+        # one package is contributing to the theme. If it already has
+        # a cache, no rebuild is needed. Otherwise a cache has to be
+        # built, and to be able to do that we first remove the
+        # symbolic link and make a directory, and then make symbolic
+        # links from the original directory into the new one.
+
+        if [ ! -w "$themedir" -a -L "$themedir" -a ! -r "$themedir"/icon-theme.cache ]; then
+          name=$(basename "$themedir")
+          path=$(readlink -f "$themedir")
+          rm "$themedir"
+          mkdir -p "$themedir"
+          ln -s "$path"/* "$themedir"/
+        fi
+
+        # (Re)build the cache if the theme dir is writable, replacing any
+        # existing cache for the theme
+
+        if [ -w "$themedir" ]; then
+          rm -f "$themedir"/icon-theme.cache
+          ${pkgs.buildPackages.gtk3.out}/bin/gtk-update-icon-cache --ignore-theme-index "$themedir"
+        fi
+      done
+    '';
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/i18n.nix b/nixpkgs/nixos/modules/config/i18n.nix
new file mode 100644
index 000000000000..b19d38091e75
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/i18n.nix
@@ -0,0 +1,113 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+
+    i18n = {
+      glibcLocales = mkOption {
+        type = types.path;
+        default = pkgs.glibcLocales.override {
+          allLocales = any (x: x == "all") config.i18n.supportedLocales;
+          locales = config.i18n.supportedLocales;
+        };
+        defaultText = literalExpression ''
+          pkgs.glibcLocales.override {
+            allLocales = any (x: x == "all") config.i18n.supportedLocales;
+            locales = config.i18n.supportedLocales;
+          }
+        '';
+        example = literalExpression "pkgs.glibcLocales";
+        description = lib.mdDoc ''
+          Customized pkg.glibcLocales package.
+
+          Changing this option can disable handling of i18n.defaultLocale
+          and supportedLocale.
+        '';
+      };
+
+      defaultLocale = mkOption {
+        type = types.str;
+        default = "en_US.UTF-8";
+        example = "nl_NL.UTF-8";
+        description = lib.mdDoc ''
+          The default locale.  It determines the language for program
+          messages, the format for dates and times, sort order, and so on.
+          It also determines the character set, such as UTF-8.
+        '';
+      };
+
+      extraLocaleSettings = mkOption {
+        type = types.attrsOf types.str;
+        default = {};
+        example = { LC_MESSAGES = "en_US.UTF-8"; LC_TIME = "de_DE.UTF-8"; };
+        description = lib.mdDoc ''
+          A set of additional system-wide locale settings other than
+          `LANG` which can be configured with
+          {option}`i18n.defaultLocale`.
+        '';
+      };
+
+      supportedLocales = mkOption {
+        type = types.listOf types.str;
+        default = unique
+          (builtins.map (l: (replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
+            [
+              "C.UTF-8"
+              "en_US.UTF-8"
+              config.i18n.defaultLocale
+            ] ++ (attrValues (filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
+          ));
+        defaultText = literalExpression ''
+          unique
+            (builtins.map (l: (replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
+              [
+                "C.UTF-8"
+                "en_US.UTF-8"
+                config.i18n.defaultLocale
+              ] ++ (attrValues (filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
+            ))
+        '';
+        example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
+        description = lib.mdDoc ''
+          List of locales that the system should support.  The value
+          `"all"` means that all locales supported by
+          Glibc will be installed.  A full list of supported locales
+          can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>.
+        '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages =
+      # We increase the priority a little, so that plain glibc in systemPackages can't win.
+      optional (config.i18n.supportedLocales != []) (lib.setPrio (-1) config.i18n.glibcLocales);
+
+    environment.sessionVariables =
+      { LANG = config.i18n.defaultLocale;
+        LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
+      } // config.i18n.extraLocaleSettings;
+
+    systemd.globalEnvironment = mkIf (config.i18n.supportedLocales != []) {
+      LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
+    };
+
+    # ‘/etc/locale.conf’ is used by systemd.
+    environment.etc."locale.conf".source = pkgs.writeText "locale.conf"
+      ''
+        LANG=${config.i18n.defaultLocale}
+        ${concatStringsSep "\n" (mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings)}
+      '';
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/iproute2.nix b/nixpkgs/nixos/modules/config/iproute2.nix
new file mode 100644
index 000000000000..0cde57b759be
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/iproute2.nix
@@ -0,0 +1,26 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.networking.iproute2;
+in
+{
+  options.networking.iproute2 = {
+    enable = mkEnableOption (lib.mdDoc "copying IP route configuration files");
+    rttablesExtraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = lib.mdDoc ''
+        Verbatim lines to add to /etc/iproute2/rt_tables
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."iproute2/rt_tables.d/nixos.conf" = {
+      mode = "0644";
+      text = cfg.rttablesExtraConfig;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/ldap.nix b/nixpkgs/nixos/modules/config/ldap.nix
new file mode 100644
index 000000000000..e374e4a7a27e
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/ldap.nix
@@ -0,0 +1,312 @@
+{ config, lib, pkgs, ... }:
+
+with pkgs;
+with lib;
+
+let
+
+  cfg = config.users.ldap;
+
+  # Careful: OpenLDAP seems to be very picky about the indentation of
+  # this file.  Directives HAVE to start in the first column!
+  ldapConfig = {
+    target = "ldap.conf";
+    source = writeText "ldap.conf" ''
+      uri ${config.users.ldap.server}
+      base ${config.users.ldap.base}
+      timelimit ${toString config.users.ldap.timeLimit}
+      bind_timelimit ${toString config.users.ldap.bind.timeLimit}
+      bind_policy ${config.users.ldap.bind.policy}
+      ${optionalString config.users.ldap.useTLS ''
+        ssl start_tls
+      ''}
+      ${optionalString (config.users.ldap.bind.distinguishedName != "") ''
+        binddn ${config.users.ldap.bind.distinguishedName}
+      ''}
+      ${optionalString (cfg.extraConfig != "") cfg.extraConfig }
+    '';
+  };
+
+  nslcdConfig = writeText "nslcd.conf" ''
+    uri ${cfg.server}
+    base ${cfg.base}
+    timelimit ${toString cfg.timeLimit}
+    bind_timelimit ${toString cfg.bind.timeLimit}
+    ${optionalString (cfg.bind.distinguishedName != "")
+      "binddn ${cfg.bind.distinguishedName}" }
+    ${optionalString (cfg.daemon.rootpwmoddn != "")
+      "rootpwmoddn ${cfg.daemon.rootpwmoddn}" }
+    ${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
+  '';
+
+  # nslcd normally reads configuration from /etc/nslcd.conf.
+  # this file might contain secrets. We append those at runtime,
+  # so redirect its location to something more temporary.
+  nslcdWrapped = runCommand "nslcd-wrapped" { nativeBuildInputs = [ makeWrapper ]; } ''
+    mkdir -p $out/bin
+    makeWrapper ${nss_pam_ldapd}/sbin/nslcd $out/bin/nslcd \
+      --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
+      --set NIX_REDIRECTS "/etc/nslcd.conf=/run/nslcd/nslcd.conf"
+  '';
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    users.ldap = {
+
+      enable = mkEnableOption (lib.mdDoc "authentication against an LDAP server");
+
+      loginPam = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc "Whether to include authentication against LDAP in login PAM.";
+      };
+
+      nsswitch = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc "Whether to include lookup against LDAP in NSS.";
+      };
+
+      server = mkOption {
+        type = types.str;
+        example = "ldap://ldap.example.org/";
+        description = lib.mdDoc "The URL of the LDAP server.";
+      };
+
+      base = mkOption {
+        type = types.str;
+        example = "dc=example,dc=org";
+        description = lib.mdDoc "The distinguished name of the search base.";
+      };
+
+      useTLS = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          If enabled, use TLS (encryption) over an LDAP (port 389)
+          connection.  The alternative is to specify an LDAPS server (port
+          636) in {option}`users.ldap.server` or to forego
+          security.
+        '';
+      };
+
+      timeLimit = mkOption {
+        default = 0;
+        type = types.int;
+        description = lib.mdDoc ''
+          Specifies the time limit (in seconds) to use when performing
+          searches. A value of zero (0), which is the default, is to
+          wait indefinitely for searches to be completed.
+        '';
+      };
+
+      daemon = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Whether to let the nslcd daemon (nss-pam-ldapd) handle the
+            LDAP lookups for NSS and PAM. This can improve performance,
+            and if you need to bind to the LDAP server with a password,
+            it increases security, since only the nslcd user needs to
+            have access to the bindpw file, not everyone that uses NSS
+            and/or PAM. If this option is enabled, a local nscd user is
+            created automatically, and the nslcd service is started
+            automatically when the network get up.
+          '';
+        };
+
+        extraConfig = mkOption {
+          default =  "";
+          type = types.lines;
+          description = lib.mdDoc ''
+            Extra configuration options that will be added verbatim at
+            the end of the nslcd configuration file (`nslcd.conf(5)`).
+          '' ;
+        } ;
+
+        rootpwmoddn = mkOption {
+          default = "";
+          example = "cn=admin,dc=example,dc=com";
+          type = types.str;
+          description = lib.mdDoc ''
+            The distinguished name to use to bind to the LDAP server
+            when the root user tries to modify a user's password.
+          '';
+        };
+
+        rootpwmodpwFile = mkOption {
+          default = "";
+          example = "/run/keys/nslcd.rootpwmodpw";
+          type = types.str;
+          description = lib.mdDoc ''
+            The path to a file containing the credentials with which to bind to
+            the LDAP server if the root user tries to change a user's password.
+          '';
+        };
+      };
+
+      bind = {
+        distinguishedName = mkOption {
+          default = "";
+          example = "cn=admin,dc=example,dc=com";
+          type = types.str;
+          description = lib.mdDoc ''
+            The distinguished name to bind to the LDAP server with. If this
+            is not specified, an anonymous bind will be done.
+          '';
+        };
+
+        passwordFile = mkOption {
+          default = "/etc/ldap/bind.password";
+          type = types.str;
+          description = lib.mdDoc ''
+            The path to a file containing the credentials to use when binding
+            to the LDAP server (if not binding anonymously).
+          '';
+        };
+
+        timeLimit = mkOption {
+          default = 30;
+          type = types.int;
+          description = lib.mdDoc ''
+            Specifies the time limit (in seconds) to use when connecting
+            to the directory server. This is distinct from the time limit
+            specified in {option}`users.ldap.timeLimit` and affects
+            the initial server connection only.
+          '';
+        };
+
+        policy = mkOption {
+          default = "hard_open";
+          type = types.enum [ "hard_open" "hard_init" "soft" ];
+          description = lib.mdDoc ''
+            Specifies the policy to use for reconnecting to an unavailable
+            LDAP server. The default is `hard_open`, which
+            reconnects if opening the connection to the directory server
+            failed. By contrast, `hard_init` reconnects if
+            initializing the connection failed. Initializing may not
+            actually contact the directory server, and it is possible that
+            a malformed configuration file will trigger reconnection. If
+            `soft` is specified, then
+            `nss_ldap` will return immediately on server
+            failure. All hard reconnect policies block with exponential
+            backoff before retrying.
+          '';
+        };
+      };
+
+      extraConfig = mkOption {
+        default = "";
+        type = types.lines;
+        description = lib.mdDoc ''
+          Extra configuration options that will be added verbatim at
+          the end of the ldap configuration file (`ldap.conf(5)`).
+          If {option}`users.ldap.daemon` is enabled, this
+          configuration will not be used. In that case, use
+          {option}`users.ldap.daemon.extraConfig` instead.
+        '' ;
+      };
+
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.etc = optionalAttrs (!cfg.daemon.enable) {
+      "ldap.conf" = ldapConfig;
+    };
+
+    system.nssModules = mkIf cfg.nsswitch (singleton (
+      if cfg.daemon.enable then nss_pam_ldapd else nss_ldap
+    ));
+
+    system.nssDatabases.group = optional cfg.nsswitch "ldap";
+    system.nssDatabases.passwd = optional cfg.nsswitch "ldap";
+    system.nssDatabases.shadow = optional cfg.nsswitch "ldap";
+
+    users = mkIf cfg.daemon.enable {
+      groups.nslcd = {
+        gid = config.ids.gids.nslcd;
+      };
+
+      users.nslcd = {
+        uid = config.ids.uids.nslcd;
+        description = "nslcd user.";
+        group = "nslcd";
+      };
+    };
+
+    systemd.services = mkMerge [
+      (mkIf (!cfg.daemon.enable) {
+        ldap-password = {
+          wantedBy = [ "sysinit.target" ];
+          before = [ "sysinit.target" "shutdown.target" ];
+          conflicts = [ "shutdown.target" ];
+          unitConfig.DefaultDependencies = false;
+          serviceConfig.Type = "oneshot";
+          serviceConfig.RemainAfterExit = true;
+          script = ''
+            if test -f "${cfg.bind.passwordFile}" ; then
+              umask 0077
+              conf="$(mktemp)"
+              printf 'bindpw %s\n' "$(cat ${cfg.bind.passwordFile})" |
+              cat ${ldapConfig.source} - >"$conf"
+              mv -fT "$conf" /etc/ldap.conf
+            fi
+          '';
+        };
+      })
+
+      (mkIf cfg.daemon.enable {
+        nslcd = {
+          wantedBy = [ "multi-user.target" ];
+
+          preStart = ''
+            umask 0077
+            conf="$(mktemp)"
+            {
+              cat ${nslcdConfig}
+              test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.passwordFile}' ||
+              printf 'bindpw %s\n' "$(cat '${cfg.bind.passwordFile}')"
+              test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpwFile}' ||
+              printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpwFile}')"
+            } >"$conf"
+            mv -fT "$conf" /run/nslcd/nslcd.conf
+          '';
+
+          restartTriggers = [
+            nslcdConfig
+            cfg.bind.passwordFile
+            cfg.daemon.rootpwmodpwFile
+          ];
+
+          serviceConfig = {
+            ExecStart = "${nslcdWrapped}/bin/nslcd";
+            Type = "forking";
+            Restart = "always";
+            User = "nslcd";
+            Group = "nslcd";
+            RuntimeDirectory = [ "nslcd" ];
+            PIDFile = "/run/nslcd/nslcd.pid";
+            AmbientCapabilities = "CAP_SYS_RESOURCE";
+          };
+        };
+      })
+    ];
+
+  };
+
+  imports =
+    [ (mkRenamedOptionModule [ "users" "ldap" "bind" "password"] [ "users" "ldap" "bind" "passwordFile"])
+    ];
+}
diff --git a/nixpkgs/nixos/modules/config/ldso.nix b/nixpkgs/nixos/modules/config/ldso.nix
new file mode 100644
index 000000000000..72ae3958d886
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/ldso.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) last splitString mkOption types mdDoc optionals;
+
+  libDir = pkgs.stdenv.hostPlatform.libDir;
+  ldsoBasename = builtins.unsafeDiscardStringContext (last (splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker));
+
+  pkgs32 = pkgs.pkgsi686Linux;
+  libDir32 = pkgs32.stdenv.hostPlatform.libDir;
+  ldsoBasename32 = builtins.unsafeDiscardStringContext (last (splitString "/" pkgs32.stdenv.cc.bintools.dynamicLinker));
+in {
+  options = {
+    environment.ldso = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = mdDoc ''
+        The executable to link into the normal FHS location of the ELF loader.
+      '';
+    };
+
+    environment.ldso32 = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = mdDoc ''
+        The executable to link into the normal FHS location of the 32-bit ELF loader.
+
+        This currently only works on x86_64 architectures.
+      '';
+    };
+  };
+
+  config = {
+    assertions = [
+      { assertion = isNull config.environment.ldso32 || pkgs.stdenv.isx86_64;
+        message = "Option environment.ldso32 currently only works on x86_64.";
+      }
+    ];
+
+    systemd.tmpfiles.rules = (
+      if isNull config.environment.ldso then [
+        "r /${libDir}/${ldsoBasename} - - - - -"
+      ] else [
+        "d /${libDir} 0755 root root - -"
+        "L+ /${libDir}/${ldsoBasename} - - - - ${config.environment.ldso}"
+      ]
+    ) ++ optionals pkgs.stdenv.isx86_64 (
+      if isNull config.environment.ldso32 then [
+        "r /${libDir32}/${ldsoBasename32} - - - - -"
+      ] else [
+        "d /${libDir32} 0755 root root - -"
+        "L+ /${libDir32}/${ldsoBasename32} - - - - ${config.environment.ldso32}"
+      ]
+    );
+  };
+
+  meta.maintainers = with lib.maintainers; [ tejing ];
+}
diff --git a/nixpkgs/nixos/modules/config/locale.nix b/nixpkgs/nixos/modules/config/locale.nix
new file mode 100644
index 000000000000..7716e121c712
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/locale.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  tzdir = "${pkgs.tzdata}/share/zoneinfo";
+  nospace  = str: filter (c: c == " ") (stringToCharacters str) == [];
+  timezone = types.nullOr (types.addCheck types.str nospace)
+    // { description = "null or string without spaces"; };
+
+  lcfg = config.location;
+
+in
+
+{
+  options = {
+
+    time = {
+
+      timeZone = mkOption {
+        default = null;
+        type = timezone;
+        example = "America/New_York";
+        description = lib.mdDoc ''
+          The time zone used when displaying times and dates. See <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
+          for a comprehensive list of possible values for this setting.
+
+          If null, the timezone will default to UTC and can be set imperatively
+          using timedatectl.
+        '';
+      };
+
+      hardwareClockInLocalTime = mkOption {
+        default = false;
+        type = types.bool;
+        description = lib.mdDoc "If set, keep the hardware clock in local time instead of UTC.";
+      };
+
+    };
+
+    location = {
+
+      latitude = mkOption {
+        type = types.float;
+        description = lib.mdDoc ''
+          Your current latitude, between
+          `-90.0` and `90.0`. Must be provided
+          along with longitude.
+        '';
+      };
+
+      longitude = mkOption {
+        type = types.float;
+        description = lib.mdDoc ''
+          Your current longitude, between
+          between `-180.0` and `180.0`. Must be
+          provided along with latitude.
+        '';
+      };
+
+      provider = mkOption {
+        type = types.enum [ "manual" "geoclue2" ];
+        default = "manual";
+        description = lib.mdDoc ''
+          The location provider to use for determining your location. If set to
+          `manual` you must also provide latitude/longitude.
+        '';
+      };
+
+    };
+  };
+
+  config = {
+
+    environment.sessionVariables.TZDIR = "/etc/zoneinfo";
+
+    services.geoclue2.enable = mkIf (lcfg.provider == "geoclue2") true;
+
+    # This way services are restarted when tzdata changes.
+    systemd.globalEnvironment.TZDIR = tzdir;
+
+    systemd.services.systemd-timedated.environment = lib.optionalAttrs (config.time.timeZone != null) { NIXOS_STATIC_TIMEZONE = "1"; };
+
+    environment.etc = {
+      zoneinfo.source = tzdir;
+    } // lib.optionalAttrs (config.time.timeZone != null) {
+        localtime.source = "/etc/zoneinfo/${config.time.timeZone}";
+        localtime.mode = "direct-symlink";
+      };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/malloc.nix b/nixpkgs/nixos/modules/config/malloc.nix
new file mode 100644
index 000000000000..043f78c8214e
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/malloc.nix
@@ -0,0 +1,114 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+  cfg = config.environment.memoryAllocator;
+
+  # The set of alternative malloc(3) providers.
+  providers = {
+    graphene-hardened = {
+      libPath = "${pkgs.graphene-hardened-malloc}/lib/libhardened_malloc.so";
+      description = ''
+        An allocator designed to mitigate memory corruption attacks, such as
+        those caused by use-after-free bugs.
+      '';
+    };
+
+    jemalloc = {
+      libPath = "${pkgs.jemalloc}/lib/libjemalloc.so";
+      description = ''
+        A general purpose allocator that emphasizes fragmentation avoidance
+        and scalable concurrency support.
+      '';
+    };
+
+    scudo = let
+      platformMap = {
+        aarch64-linux = "aarch64";
+        x86_64-linux  = "x86_64";
+      };
+
+      systemPlatform = platformMap.${pkgs.stdenv.hostPlatform.system} or (throw "scudo not supported on ${pkgs.stdenv.hostPlatform.system}");
+    in {
+      libPath = "${pkgs.llvmPackages_14.compiler-rt}/lib/linux/libclang_rt.scudo-${systemPlatform}.so";
+      description = ''
+        A user-mode allocator based on LLVM Sanitizer’s CombinedAllocator,
+        which aims at providing additional mitigations against heap based
+        vulnerabilities, while maintaining good performance.
+      '';
+    };
+
+    mimalloc = {
+      libPath = "${pkgs.mimalloc}/lib/libmimalloc.so";
+      description = ''
+        A compact and fast general purpose allocator, which may
+        optionally be built with mitigations against various heap
+        vulnerabilities.
+      '';
+    };
+  };
+
+  providerConf = providers.${cfg.provider};
+
+  # An output that contains only the shared library, to avoid
+  # needlessly bloating the system closure
+  mallocLib = pkgs.runCommand "malloc-provider-${cfg.provider}"
+    rec {
+      preferLocalBuild = true;
+      allowSubstitutes = false;
+      origLibPath = providerConf.libPath;
+      libName = baseNameOf origLibPath;
+    }
+    ''
+      mkdir -p $out/lib
+      cp -L $origLibPath $out/lib/$libName
+    '';
+
+  # The full path to the selected provider shlib.
+  providerLibPath = "${mallocLib}/lib/${mallocLib.libName}";
+in
+
+{
+  meta = {
+    maintainers = [ maintainers.joachifm ];
+  };
+
+  options = {
+    environment.memoryAllocator.provider = mkOption {
+      type = types.enum ([ "libc" ] ++ attrNames providers);
+      default = "libc";
+      description = lib.mdDoc ''
+        The system-wide memory allocator.
+
+        Briefly, the system-wide memory allocator providers are:
+
+        - `libc`: the standard allocator provided by libc
+        ${concatStringsSep "\n" (mapAttrsToList
+            (name: value: "- `${name}`: ${replaceStrings [ "\n" ] [ " " ] value.description}")
+            providers)}
+
+        ::: {.warning}
+        Selecting an alternative allocator (i.e., anything other than
+        `libc`) may result in instability, data loss,
+        and/or service failure.
+        :::
+      '';
+    };
+  };
+
+  config = mkIf (cfg.provider != "libc") {
+    environment.etc."ld-nix.so.preload".text = ''
+      ${providerLibPath}
+    '';
+    security.apparmor.includes = {
+      "abstractions/base" = ''
+        r /etc/ld-nix.so.preload,
+        r ${config.environment.etc."ld-nix.so.preload".source},
+        include "${pkgs.apparmorRulesFromClosure {
+            name = "mallocLib";
+            baseRules = ["mr $path/lib/**.so*"];
+          } [ mallocLib ] }"
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/mysql.nix b/nixpkgs/nixos/modules/config/mysql.nix
new file mode 100644
index 000000000000..4f72d22c4f0e
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/mysql.nix
@@ -0,0 +1,469 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.users.mysql;
+in
+{
+  meta.maintainers = [ maintainers.netali ];
+
+  options = {
+    users.mysql = {
+      enable = mkEnableOption (lib.mdDoc "Authentication against a MySQL/MariaDB database");
+      host = mkOption {
+        type = types.str;
+        example = "localhost";
+        description = lib.mdDoc "The hostname of the MySQL/MariaDB server";
+      };
+      database = mkOption {
+        type = types.str;
+        example = "auth";
+        description = lib.mdDoc "The name of the database containing the users";
+      };
+      user = mkOption {
+        type = types.str;
+        example = "nss-user";
+        description = lib.mdDoc "The username to use when connecting to the database";
+      };
+      passwordFile = mkOption {
+        type = types.path;
+        example = "/run/secrets/mysql-auth-db-passwd";
+        description = lib.mdDoc "The path to the file containing the password for the user";
+      };
+      pam = mkOption {
+        description = lib.mdDoc "Settings for `pam_mysql`";
+        type = types.submodule {
+          options = {
+            table = mkOption {
+              type = types.str;
+              example = "users";
+              description = lib.mdDoc "The name of table that maps unique login names to the passwords.";
+            };
+            updateTable = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "users_updates";
+              description = lib.mdDoc ''
+                The name of the table used for password alteration. If not defined, the value
+                of the `table` option will be used instead.
+              '';
+            };
+            userColumn = mkOption {
+              type = types.str;
+              example = "username";
+              description = lib.mdDoc "The name of the column that contains a unix login name.";
+            };
+            passwordColumn = mkOption {
+              type = types.str;
+              example = "password";
+              description = lib.mdDoc "The name of the column that contains a (encrypted) password string.";
+            };
+            statusColumn = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "status";
+              description = lib.mdDoc ''
+                The name of the column or an SQL expression that indicates the status of
+                the user. The status is expressed by the combination of two bitfields
+                shown below:
+
+                - `bit 0 (0x01)`:
+                   if flagged, `pam_mysql` deems the account to be expired and
+                   returns `PAM_ACCT_EXPIRED`. That is, the account is supposed
+                   to no longer be available. Note this doesn't mean that `pam_mysql`
+                   rejects further authentication operations.
+                -  `bit 1 (0x02)`:
+                   if flagged, `pam_mysql` deems the authentication token
+                   (password) to be expired and returns `PAM_NEW_AUTHTOK_REQD`.
+                   This ends up requiring that the user enter a new password.
+              '';
+            };
+            passwordCrypt = mkOption {
+              example = "2";
+              type = types.enum [
+                "0" "plain"
+                "1" "Y"
+                "2" "mysql"
+                "3" "md5"
+                "4" "sha1"
+                "5" "drupal7"
+                "6" "joomla15"
+                "7" "ssha"
+                "8" "sha512"
+                "9" "sha256"
+              ];
+              description = lib.mdDoc ''
+                The method to encrypt the user's password:
+
+                - `0` (or `"plain"`):
+                  No encryption. Passwords are stored in plaintext. HIGHLY DISCOURAGED.
+                - `1` (or `"Y"`):
+                  Use crypt(3) function.
+                - `2` (or `"mysql"`):
+                  Use the MySQL PASSWORD() function. It is possible that the encryption function used
+                  by `pam_mysql` is different from that of the MySQL server, as
+                  `pam_mysql` uses the function defined in MySQL's C-client API
+                  instead of using PASSWORD() SQL function in the query.
+                - `3` (or `"md5"`):
+                  Use plain hex MD5.
+                - `4` (or `"sha1"`):
+                  Use plain hex SHA1.
+                - `5` (or `"drupal7"`):
+                  Use Drupal7 salted passwords.
+                - `6` (or `"joomla15"`):
+                  Use Joomla15 salted passwords.
+                - `7` (or `"ssha"`):
+                  Use ssha hashed passwords.
+                - `8` (or `"sha512"`):
+                  Use sha512 hashed passwords.
+                - `9` (or `"sha256"`):
+                  Use sha256 hashed passwords.
+              '';
+            };
+            cryptDefault = mkOption {
+              type = types.nullOr (types.enum [ "md5" "sha256" "sha512" "blowfish" ]);
+              default = null;
+              example = "blowfish";
+              description = lib.mdDoc "The default encryption method to use for `passwordCrypt = 1`.";
+            };
+            where = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "host.name='web' AND user.active=1";
+              description = lib.mdDoc "Additional criteria for the query.";
+            };
+            verbose = mkOption {
+              type = types.bool;
+              default = false;
+              description = lib.mdDoc ''
+                If enabled, produces logs with detailed messages that describes what
+                `pam_mysql` is doing. May be useful for debugging.
+              '';
+            };
+            disconnectEveryOperation = mkOption {
+              type = types.bool;
+              default = false;
+              description = lib.mdDoc ''
+                By default, `pam_mysql` keeps the connection to the MySQL
+                database until the session is closed. If this option is set to true it
+                disconnects every time the PAM operation has finished. This option may
+                be useful in case the session lasts quite long.
+              '';
+            };
+            logging = {
+              enable = mkOption {
+                type = types.bool;
+                default = false;
+                description = lib.mdDoc "Enables logging of authentication attempts in the MySQL database.";
+              };
+              table = mkOption {
+                type = types.str;
+                example = "logs";
+                description = lib.mdDoc "The name of the table to which logs are written.";
+              };
+              msgColumn = mkOption {
+                type = types.str;
+                example = "msg";
+                description = lib.mdDoc ''
+                  The name of the column in the log table to which the description
+                  of the performed operation is stored.
+                '';
+              };
+              userColumn = mkOption {
+                type = types.str;
+                example = "user";
+                description = lib.mdDoc ''
+                  The name of the column in the log table to which the name of the
+                  user being authenticated is stored.
+                '';
+              };
+              pidColumn = mkOption {
+                type = types.str;
+                example = "pid";
+                description = lib.mdDoc ''
+                  The name of the column in the log table to which the pid of the
+                  process utilising the `pam_mysql` authentication
+                  service is stored.
+                '';
+              };
+              hostColumn = mkOption {
+                type = types.str;
+                example = "host";
+                description = lib.mdDoc ''
+                  The name of the column in the log table to which the name of the user
+                  being authenticated is stored.
+                '';
+              };
+              rHostColumn = mkOption {
+                type = types.str;
+                example = "rhost";
+                description = lib.mdDoc ''
+                  The name of the column in the log table to which the name of the remote
+                  host that initiates the session is stored. The value is supposed to be
+                  set by the PAM-aware application with `pam_set_item(PAM_RHOST)`.
+                '';
+              };
+              timeColumn = mkOption {
+                type = types.str;
+                example = "timestamp";
+                description = lib.mdDoc ''
+                  The name of the column in the log table to which the timestamp of the
+                  log entry is stored.
+                '';
+              };
+            };
+          };
+        };
+      };
+      nss = mkOption {
+        description = lib.mdDoc ''
+          Settings for `libnss-mysql`.
+
+          All examples are from the [minimal example](https://github.com/saknopper/libnss-mysql/tree/master/sample/minimal)
+          of `libnss-mysql`, but they are modified with NixOS paths for bash.
+        '';
+        type = types.submodule {
+          options = {
+            getpwnam = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT username,'x',uid,'5000','MySQL User', CONCAT('/home/',username),'/run/sw/current-system/bin/bash' \
+                FROM users \
+                WHERE username='%1$s' \
+                LIMIT 1
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getpwnam](https://man7.org/linux/man-pages/man3/getpwnam.3.html)
+                syscall.
+              '';
+            };
+            getpwuid = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT username,'x',uid,'5000','MySQL User', CONCAT('/home/',username),'/run/sw/current-system/bin/bash' \
+                FROM users \
+                WHERE uid='%1$u' \
+                LIMIT 1
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getpwuid](https://man7.org/linux/man-pages/man3/getpwuid.3.html)
+                syscall.
+              '';
+            };
+            getspnam = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT username,password,'1','0','99999','0','0','-1','0' \
+                FROM users \
+                WHERE username='%1$s' \
+                LIMIT 1
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getspnam](https://man7.org/linux/man-pages/man3/getspnam.3.html)
+                syscall.
+              '';
+            };
+            getpwent = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT username,'x',uid,'5000','MySQL User', CONCAT('/home/',username),'/run/sw/current-system/bin/bash' FROM users
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getpwent](https://man7.org/linux/man-pages/man3/getpwent.3.html)
+                syscall.
+              '';
+            };
+            getspent = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT username,password,'1','0','99999','0','0','-1','0' FROM users
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getspent](https://man7.org/linux/man-pages/man3/getspent.3.html)
+                syscall.
+              '';
+            };
+            getgrnam = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT name,password,gid FROM groups WHERE name='%1$s' LIMIT 1
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getgrnam](https://man7.org/linux/man-pages/man3/getgrnam.3.html)
+                syscall.
+              '';
+            };
+            getgrgid = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT name,password,gid FROM groups WHERE gid='%1$u' LIMIT 1
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getgrgid](https://man7.org/linux/man-pages/man3/getgrgid.3.html)
+                syscall.
+              '';
+            };
+            getgrent = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT name,password,gid FROM groups
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [getgrent](https://man7.org/linux/man-pages/man3/getgrent.3.html)
+                syscall.
+              '';
+            };
+            memsbygid = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT username FROM grouplist WHERE gid='%1$u'
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [memsbygid](https://man7.org/linux/man-pages/man3/memsbygid.3.html)
+                syscall.
+              '';
+            };
+            gidsbymem = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = literalExpression ''
+                SELECT gid FROM grouplist WHERE username='%1$s'
+              '';
+              description = lib.mdDoc ''
+                SQL query for the [gidsbymem](https://man7.org/linux/man-pages/man3/gidsbymem.3.html)
+                syscall.
+              '';
+            };
+          };
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    system.nssModules = [ pkgs.libnss-mysql ];
+    system.nssDatabases.shadow = [ "mysql" ];
+    system.nssDatabases.group = [ "mysql" ];
+    system.nssDatabases.passwd = [ "mysql" ];
+
+    environment.etc."security/pam_mysql.conf" = {
+      user = "root";
+      group = "root";
+      mode = "0600";
+      # password will be added from password file in systemd oneshot
+      text = ''
+        users.host=${cfg.host}
+        users.db_user=${cfg.user}
+        users.database=${cfg.database}
+        users.table=${cfg.pam.table}
+        users.user_column=${cfg.pam.userColumn}
+        users.password_column=${cfg.pam.passwordColumn}
+        users.password_crypt=${cfg.pam.passwordCrypt}
+        users.disconnect_every_operation=${if cfg.pam.disconnectEveryOperation then "1" else "0"}
+        verbose=${if cfg.pam.verbose then "1" else "0"}
+      '' + optionalString (cfg.pam.cryptDefault != null) ''
+        users.use_${cfg.pam.cryptDefault}=1
+      '' + optionalString (cfg.pam.where != null) ''
+        users.where_clause=${cfg.pam.where}
+      '' + optionalString (cfg.pam.statusColumn != null) ''
+        users.status_column=${cfg.pam.statusColumn}
+      '' + optionalString (cfg.pam.updateTable != null) ''
+        users.update_table=${cfg.pam.updateTable}
+      '' + optionalString cfg.pam.logging.enable ''
+        log.enabled=true
+        log.table=${cfg.pam.logging.table}
+        log.message_column=${cfg.pam.logging.msgColumn}
+        log.pid_column=${cfg.pam.logging.pidColumn}
+        log.user_column=${cfg.pam.logging.userColumn}
+        log.host_column=${cfg.pam.logging.hostColumn}
+        log.rhost_column=${cfg.pam.logging.rHostColumn}
+        log.time_column=${cfg.pam.logging.timeColumn}
+      '';
+    };
+
+    environment.etc."libnss-mysql.cfg" = {
+      mode = "0600";
+      user = config.services.nscd.user;
+      group = config.services.nscd.group;
+      text = optionalString (cfg.nss.getpwnam != null) ''
+        getpwnam ${cfg.nss.getpwnam}
+      '' + optionalString (cfg.nss.getpwuid != null) ''
+        getpwuid ${cfg.nss.getpwuid}
+      '' + optionalString (cfg.nss.getspnam != null) ''
+        getspnam ${cfg.nss.getspnam}
+      '' + optionalString (cfg.nss.getpwent != null) ''
+        getpwent ${cfg.nss.getpwent}
+      '' + optionalString (cfg.nss.getspent != null) ''
+        getspent ${cfg.nss.getspent}
+      '' + optionalString (cfg.nss.getgrnam != null) ''
+        getgrnam ${cfg.nss.getgrnam}
+      '' + optionalString (cfg.nss.getgrgid != null) ''
+        getgrgid ${cfg.nss.getgrgid}
+      '' + optionalString (cfg.nss.getgrent != null) ''
+        getgrent ${cfg.nss.getgrent}
+      '' + optionalString (cfg.nss.memsbygid != null) ''
+        memsbygid ${cfg.nss.memsbygid}
+      '' + optionalString (cfg.nss.gidsbymem != null) ''
+        gidsbymem ${cfg.nss.gidsbymem}
+      '' + ''
+        host ${cfg.host}
+        database ${cfg.database}
+      '';
+    };
+
+    environment.etc."libnss-mysql-root.cfg" = {
+      mode = "0600";
+      user = config.services.nscd.user;
+      group = config.services.nscd.group;
+      # password will be added from password file in systemd oneshot
+      text = ''
+        username ${cfg.user}
+      '';
+    };
+
+    systemd.services.mysql-auth-pw-init = {
+      description = "Adds the mysql password to the mysql auth config files";
+
+      before = [ "nscd.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "root";
+        Group = "root";
+      };
+
+      restartTriggers = [
+        config.environment.etc."security/pam_mysql.conf".source
+        config.environment.etc."libnss-mysql.cfg".source
+        config.environment.etc."libnss-mysql-root.cfg".source
+      ];
+
+      script = ''
+        if [[ -r ${cfg.passwordFile} ]]; then
+          umask 0077
+          conf_nss="$(mktemp)"
+          cp /etc/libnss-mysql-root.cfg $conf_nss
+          printf 'password %s\n' "$(cat ${cfg.passwordFile})" >> $conf_nss
+          mv -fT "$conf_nss" /etc/libnss-mysql-root.cfg
+          chown ${config.services.nscd.user}:${config.services.nscd.group} /etc/libnss-mysql-root.cfg
+
+          conf_pam="$(mktemp)"
+          cp /etc/security/pam_mysql.conf $conf_pam
+          printf 'users.db_passwd=%s\n' "$(cat ${cfg.passwordFile})" >> $conf_pam
+          mv -fT "$conf_pam" /etc/security/pam_mysql.conf
+        fi
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/networking.nix b/nixpkgs/nixos/modules/config/networking.nix
new file mode 100644
index 000000000000..fc910fee94bf
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/networking.nix
@@ -0,0 +1,237 @@
+# /etc files related to networking, such as /etc/services.
+
+{ config, lib, options, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking;
+  opt = options.networking;
+
+  localhostMultiple = any (elem "localhost") (attrValues (removeAttrs cfg.hosts [ "127.0.0.1" "::1" ]));
+
+in
+
+{
+  imports = [
+    (mkRemovedOptionModule [ "networking" "hostConf" ] "Use environment.etc.\"host.conf\" instead.")
+  ];
+
+  options = {
+
+    networking.hosts = lib.mkOption {
+      type = types.attrsOf (types.listOf types.str);
+      example = literalExpression ''
+        {
+          "127.0.0.1" = [ "foo.bar.baz" ];
+          "192.168.0.2" = [ "fileserver.local" "nameserver.local" ];
+        };
+      '';
+      description = lib.mdDoc ''
+        Locally defined maps of hostnames to IP addresses.
+      '';
+    };
+
+    networking.hostFiles = lib.mkOption {
+      type = types.listOf types.path;
+      defaultText = literalMD "Hosts from {option}`networking.hosts` and {option}`networking.extraHosts`";
+      example = literalExpression ''[ "''${pkgs.my-blocklist-package}/share/my-blocklist/hosts" ]'';
+      description = lib.mdDoc ''
+        Files that should be concatenated together to form {file}`/etc/hosts`.
+      '';
+    };
+
+    networking.extraHosts = lib.mkOption {
+      type = types.lines;
+      default = "";
+      example = "192.168.0.1 lanlocalhost";
+      description = lib.mdDoc ''
+        Additional verbatim entries to be appended to {file}`/etc/hosts`.
+        For adding hosts from derivation results, use {option}`networking.hostFiles` instead.
+      '';
+    };
+
+    networking.timeServers = mkOption {
+      default = [
+        "0.nixos.pool.ntp.org"
+        "1.nixos.pool.ntp.org"
+        "2.nixos.pool.ntp.org"
+        "3.nixos.pool.ntp.org"
+      ];
+      type = types.listOf types.str;
+      description = lib.mdDoc ''
+        The set of NTP servers from which to synchronise.
+      '';
+    };
+
+    networking.proxy = {
+
+      default = lib.mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = lib.mdDoc ''
+          This option specifies the default value for httpProxy, httpsProxy, ftpProxy and rsyncProxy.
+        '';
+        example = "http://127.0.0.1:3128";
+      };
+
+      httpProxy = lib.mkOption {
+        type = types.nullOr types.str;
+        default = cfg.proxy.default;
+        defaultText = literalExpression "config.${opt.proxy.default}";
+        description = lib.mdDoc ''
+          This option specifies the http_proxy environment variable.
+        '';
+        example = "http://127.0.0.1:3128";
+      };
+
+      httpsProxy = lib.mkOption {
+        type = types.nullOr types.str;
+        default = cfg.proxy.default;
+        defaultText = literalExpression "config.${opt.proxy.default}";
+        description = lib.mdDoc ''
+          This option specifies the https_proxy environment variable.
+        '';
+        example = "http://127.0.0.1:3128";
+      };
+
+      ftpProxy = lib.mkOption {
+        type = types.nullOr types.str;
+        default = cfg.proxy.default;
+        defaultText = literalExpression "config.${opt.proxy.default}";
+        description = lib.mdDoc ''
+          This option specifies the ftp_proxy environment variable.
+        '';
+        example = "http://127.0.0.1:3128";
+      };
+
+      rsyncProxy = lib.mkOption {
+        type = types.nullOr types.str;
+        default = cfg.proxy.default;
+        defaultText = literalExpression "config.${opt.proxy.default}";
+        description = lib.mdDoc ''
+          This option specifies the rsync_proxy environment variable.
+        '';
+        example = "http://127.0.0.1:3128";
+      };
+
+      allProxy = lib.mkOption {
+        type = types.nullOr types.str;
+        default = cfg.proxy.default;
+        defaultText = literalExpression "config.${opt.proxy.default}";
+        description = lib.mdDoc ''
+          This option specifies the all_proxy environment variable.
+        '';
+        example = "http://127.0.0.1:3128";
+      };
+
+      noProxy = lib.mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = lib.mdDoc ''
+          This option specifies the no_proxy environment variable.
+          If a default proxy is used and noProxy is null,
+          then noProxy will be set to 127.0.0.1,localhost.
+        '';
+        example = "127.0.0.1,localhost,.localdomain";
+      };
+
+      envVars = lib.mkOption {
+        type = types.attrs;
+        internal = true;
+        default = {};
+        description = lib.mdDoc ''
+          Environment variables used for the network proxy.
+        '';
+      };
+    };
+  };
+
+  config = {
+
+    assertions = [{
+      assertion = !localhostMultiple;
+      message = ''
+        `networking.hosts` maps "localhost" to something other than "127.0.0.1"
+        or "::1". This will break some applications. Please use
+        `networking.extraHosts` if you really want to add such a mapping.
+      '';
+    }];
+
+    # These entries are required for "hostname -f" and to resolve both the
+    # hostname and FQDN correctly:
+    networking.hosts = let
+      hostnames = # Note: The FQDN (canonical hostname) has to come first:
+        optional (cfg.hostName != "" && cfg.domain != null) "${cfg.hostName}.${cfg.domain}"
+        ++ optional (cfg.hostName != "") cfg.hostName; # Then the hostname (without the domain)
+    in {
+      "127.0.0.2" = hostnames;
+    } // optionalAttrs cfg.enableIPv6 {
+      "::1" = hostnames;
+    };
+
+    networking.hostFiles = let
+      # Note: localhostHosts has to appear first in /etc/hosts so that 127.0.0.1
+      # resolves back to "localhost" (as some applications assume) instead of
+      # the FQDN! By default "networking.hosts" also contains entries for the
+      # FQDN so that e.g. "hostname -f" works correctly.
+      hosts = foldAttrs (a: e: a ++ e) [] ([ { "127.0.0.1" = [ "localhost" ]; } ]
+        ++ optional cfg.enableIPv6 { "::1" = [ "localhost" ]; }
+        ++ [ cfg.hosts ]);
+
+      stringHosts =
+        let
+          oneToString = set: ip: ip + " " + concatStringsSep " " set.${ip} + "\n";
+          allToString = set: concatMapStrings (oneToString set) (attrNames set);
+        in pkgs.writeText "string-hosts" (allToString (filterAttrs (_: v: v != []) hosts));
+      extraHosts = pkgs.writeText "extra-hosts" cfg.extraHosts;
+    in mkBefore [ stringHosts extraHosts ];
+
+    environment.etc =
+      { # /etc/services: TCP/UDP port assignments.
+        services.source = pkgs.iana-etc + "/etc/services";
+
+        # /etc/protocols: IP protocol numbers.
+        protocols.source  = pkgs.iana-etc + "/etc/protocols";
+
+        # /etc/hosts: Hostname-to-IP mappings.
+        hosts.source = pkgs.concatText "hosts" cfg.hostFiles;
+
+        # /etc/netgroup: Network-wide groups.
+        netgroup.text = mkDefault "";
+
+        # /etc/host.conf: resolver configuration file
+        "host.conf".text = ''
+          multi on
+        '';
+
+      } // optionalAttrs (pkgs.stdenv.hostPlatform.libc == "glibc") {
+        # /etc/rpc: RPC program numbers.
+        rpc.source = pkgs.stdenv.cc.libc.out + "/etc/rpc";
+      };
+
+      networking.proxy.envVars =
+        optionalAttrs (cfg.proxy.default != null) {
+          # other options already fallback to proxy.default
+          no_proxy = "127.0.0.1,localhost";
+        } // optionalAttrs (cfg.proxy.httpProxy != null) {
+          http_proxy  = cfg.proxy.httpProxy;
+        } // optionalAttrs (cfg.proxy.httpsProxy != null) {
+          https_proxy = cfg.proxy.httpsProxy;
+        } // optionalAttrs (cfg.proxy.rsyncProxy != null) {
+          rsync_proxy = cfg.proxy.rsyncProxy;
+        } // optionalAttrs (cfg.proxy.ftpProxy != null) {
+          ftp_proxy   = cfg.proxy.ftpProxy;
+        } // optionalAttrs (cfg.proxy.allProxy != null) {
+          all_proxy   = cfg.proxy.allProxy;
+        } // optionalAttrs (cfg.proxy.noProxy != null) {
+          no_proxy    = cfg.proxy.noProxy;
+        };
+
+    # Install the proxy environment variables
+    environment.sessionVariables = cfg.proxy.envVars;
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/nix-channel.nix b/nixpkgs/nixos/modules/config/nix-channel.nix
new file mode 100644
index 000000000000..dd97cb730ae4
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix-channel.nix
@@ -0,0 +1,103 @@
+/*
+  Manages the things that are needed for a traditional nix-channel based
+  configuration to work.
+
+  See also
+  - ./nix.nix
+  - ./nix-flakes.nix
+ */
+{ config, lib, ... }:
+let
+  inherit (lib)
+    mkDefault
+    mkIf
+    mkOption
+    types
+    ;
+
+  cfg = config.nix;
+
+in
+{
+  options = {
+    nix = {
+      channel = {
+        enable = mkOption {
+          description = lib.mdDoc ''
+            Whether the `nix-channel` command and state files are made available on the machine.
+
+            The following files are initialized when enabled:
+              - `/nix/var/nix/profiles/per-user/root/channels`
+              - `/root/.nix-channels`
+              - `$HOME/.nix-defexpr/channels` (on login)
+
+            Disabling this option will not remove the state files from the system.
+          '';
+          type = types.bool;
+          default = true;
+        };
+      };
+
+      nixPath = mkOption {
+        type = types.listOf types.str;
+        default =
+          if cfg.channel.enable
+          then [
+            "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
+            "nixos-config=/etc/nixos/configuration.nix"
+            "/nix/var/nix/profiles/per-user/root/channels"
+          ]
+          else [ ];
+        defaultText = ''
+          if nix.channel.enable
+          then [
+            "nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos"
+            "nixos-config=/etc/nixos/configuration.nix"
+            "/nix/var/nix/profiles/per-user/root/channels"
+          ]
+          else [];
+        '';
+        description = lib.mdDoc ''
+          The default Nix expression search path, used by the Nix
+          evaluator to look up paths enclosed in angle brackets
+          (e.g. `<nixpkgs>`).
+        '';
+      };
+    };
+
+    system = {
+      defaultChannel = mkOption {
+        internal = true;
+        type = types.str;
+        default = "https://nixos.org/channels/nixos-unstable";
+        description = lib.mdDoc "Default NixOS channel to which the root user is subscribed.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    environment.extraInit =
+      mkIf cfg.channel.enable ''
+        if [ -e "$HOME/.nix-defexpr/channels" ]; then
+          export NIX_PATH="$HOME/.nix-defexpr/channels''${NIX_PATH:+:$NIX_PATH}"
+        fi
+      '';
+
+    environment.extraSetup = mkIf (!cfg.channel.enable) ''
+      rm --force $out/bin/nix-channel
+    '';
+
+    # NIX_PATH has a non-empty default according to Nix docs, so we don't unset
+    # it when empty.
+    environment.sessionVariables = {
+      NIX_PATH = cfg.nixPath;
+    };
+
+    nix.settings.nix-path = mkIf (! cfg.channel.enable) (mkDefault "");
+
+    systemd.tmpfiles.rules = lib.mkIf cfg.channel.enable [
+      ''f /root/.nix-channels - - - - ${config.system.defaultChannel} nixos\n''
+    ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nix-flakes.nix b/nixpkgs/nixos/modules/config/nix-flakes.nix
new file mode 100644
index 000000000000..242d8d3b82b7
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix-flakes.nix
@@ -0,0 +1,95 @@
+/*
+  Manages the flake registry.
+
+  See also
+   - ./nix.nix
+   - ./nix-channel.nix
+ */
+{ config, lib, ... }:
+let
+  inherit (lib)
+    filterAttrs
+    literalExpression
+    mapAttrsToList
+    mkDefault
+    mkIf
+    mkOption
+    types
+    ;
+
+  cfg = config.nix;
+
+in
+{
+  options = {
+    nix = {
+      registry = mkOption {
+        type = types.attrsOf (types.submodule (
+          let
+            referenceAttrs = with types; attrsOf (oneOf [
+              str
+              int
+              bool
+              path
+              package
+            ]);
+          in
+          { config, name, ... }:
+          {
+            options = {
+              from = mkOption {
+                type = referenceAttrs;
+                example = { type = "indirect"; id = "nixpkgs"; };
+                description = lib.mdDoc "The flake reference to be rewritten.";
+              };
+              to = mkOption {
+                type = referenceAttrs;
+                example = { type = "github"; owner = "my-org"; repo = "my-nixpkgs"; };
+                description = lib.mdDoc "The flake reference {option}`from` is rewritten to.";
+              };
+              flake = mkOption {
+                type = types.nullOr types.attrs;
+                default = null;
+                example = literalExpression "nixpkgs";
+                description = lib.mdDoc ''
+                  The flake input {option}`from` is rewritten to.
+                '';
+              };
+              exact = mkOption {
+                type = types.bool;
+                default = true;
+                description = lib.mdDoc ''
+                  Whether the {option}`from` reference needs to match exactly. If set,
+                  a {option}`from` reference like `nixpkgs` does not
+                  match with a reference like `nixpkgs/nixos-20.03`.
+                '';
+              };
+            };
+            config = {
+              from = mkDefault { type = "indirect"; id = name; };
+              to = mkIf (config.flake != null) (mkDefault (
+                {
+                  type = "path";
+                  path = config.flake.outPath;
+                } // filterAttrs
+                  (n: _: n == "lastModified" || n == "rev" || n == "revCount" || n == "narHash")
+                  config.flake
+              ));
+            };
+          }
+        ));
+        default = { };
+        description = lib.mdDoc ''
+          A system-wide flake registry.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."nix/registry.json".text = builtins.toJSON {
+      version = 2;
+      flakes = mapAttrsToList (n: v: { inherit (v) from to exact; }) cfg.registry;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nix-remote-build.nix b/nixpkgs/nixos/modules/config/nix-remote-build.nix
new file mode 100644
index 000000000000..98c8fc06d2ee
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix-remote-build.nix
@@ -0,0 +1,226 @@
+/*
+  Manages the remote build configuration, /etc/nix/machines
+
+  See also
+   - ./nix.nix
+   - nixos/modules/services/system/nix-daemon.nix
+ */
+{ config, lib, ... }:
+
+let
+  inherit (lib)
+    any
+    concatMapStrings
+    concatStringsSep
+    filter
+    getVersion
+    mkIf
+    mkMerge
+    mkOption
+    optional
+    optionalString
+    types
+    versionAtLeast
+    ;
+
+  cfg = config.nix;
+
+  nixPackage = cfg.package.out;
+
+  isNixAtLeast = versionAtLeast (getVersion nixPackage);
+
+  buildMachinesText =
+    concatMapStrings
+      (machine:
+        (concatStringsSep " " ([
+          "${optionalString (machine.protocol != null) "${machine.protocol}://"}${optionalString (machine.sshUser != null) "${machine.sshUser}@"}${machine.hostName}"
+          (if machine.system != null then machine.system else if machine.systems != [ ] then concatStringsSep "," machine.systems else "-")
+          (if machine.sshKey != null then machine.sshKey else "-")
+          (toString machine.maxJobs)
+          (toString machine.speedFactor)
+          (let res = (machine.supportedFeatures ++ machine.mandatoryFeatures);
+            in if (res == []) then "-" else (concatStringsSep "," res))
+          (let res = machine.mandatoryFeatures;
+            in if (res == []) then "-" else (concatStringsSep "," machine.mandatoryFeatures))
+        ]
+        ++ optional (isNixAtLeast "2.4pre") (if machine.publicHostKey != null then machine.publicHostKey else "-")))
+        + "\n"
+      )
+      cfg.buildMachines;
+
+in
+{
+  options = {
+    nix = {
+      buildMachines = mkOption {
+        type = types.listOf (types.submodule {
+          options = {
+            hostName = mkOption {
+              type = types.str;
+              example = "nixbuilder.example.org";
+              description = lib.mdDoc ''
+                The hostname of the build machine.
+              '';
+            };
+            protocol = mkOption {
+              type = types.enum [ null "ssh" "ssh-ng" ];
+              default = "ssh";
+              example = "ssh-ng";
+              description = lib.mdDoc ''
+                The protocol used for communicating with the build machine.
+                Use `ssh-ng` if your remote builder and your
+                local Nix version support that improved protocol.
+
+                Use `null` when trying to change the special localhost builder
+                without a protocol which is for example used by hydra.
+              '';
+            };
+            system = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "x86_64-linux";
+              description = lib.mdDoc ''
+                The system type the build machine can execute derivations on.
+                Either this attribute or {var}`systems` must be
+                present, where {var}`system` takes precedence if
+                both are set.
+              '';
+            };
+            systems = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "x86_64-linux" "aarch64-linux" ];
+              description = lib.mdDoc ''
+                The system types the build machine can execute derivations on.
+                Either this attribute or {var}`system` must be
+                present, where {var}`system` takes precedence if
+                both are set.
+              '';
+            };
+            sshUser = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "builder";
+              description = lib.mdDoc ''
+                The username to log in as on the remote host. This user must be
+                able to log in and run nix commands non-interactively. It must
+                also be privileged to build derivations, so must be included in
+                {option}`nix.settings.trusted-users`.
+              '';
+            };
+            sshKey = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "/root/.ssh/id_buildhost_builduser";
+              description = lib.mdDoc ''
+                The path to the SSH private key with which to authenticate on
+                the build machine. The private key must not have a passphrase.
+                If null, the building user (root on NixOS machines) must have an
+                appropriate ssh configuration to log in non-interactively.
+
+                Note that for security reasons, this path must point to a file
+                in the local filesystem, *not* to the nix store.
+              '';
+            };
+            maxJobs = mkOption {
+              type = types.int;
+              default = 1;
+              description = lib.mdDoc ''
+                The number of concurrent jobs the build machine supports. The
+                build machine will enforce its own limits, but this allows hydra
+                to schedule better since there is no work-stealing between build
+                machines.
+              '';
+            };
+            speedFactor = mkOption {
+              type = types.int;
+              default = 1;
+              description = lib.mdDoc ''
+                The relative speed of this builder. This is an arbitrary integer
+                that indicates the speed of this builder, relative to other
+                builders. Higher is faster.
+              '';
+            };
+            mandatoryFeatures = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "big-parallel" ];
+              description = lib.mdDoc ''
+                A list of features mandatory for this builder. The builder will
+                be ignored for derivations that don't require all features in
+                this list. All mandatory features are automatically included in
+                {var}`supportedFeatures`.
+              '';
+            };
+            supportedFeatures = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "kvm" "big-parallel" ];
+              description = lib.mdDoc ''
+                A list of features supported by this builder. The builder will
+                be ignored for derivations that require features not in this
+                list.
+              '';
+            };
+            publicHostKey = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = lib.mdDoc ''
+                The (base64-encoded) public host key of this builder. The field
+                is calculated via {command}`base64 -w0 /etc/ssh/ssh_host_type_key.pub`.
+                If null, SSH will use its regular known-hosts file when connecting.
+              '';
+            };
+          };
+        });
+        default = [ ];
+        description = lib.mdDoc ''
+          This option lists the machines to be used if distributed builds are
+          enabled (see {option}`nix.distributedBuilds`).
+          Nix will perform derivations on those machines via SSH by copying the
+          inputs to the Nix store on the remote machine, starting the build,
+          then copying the output back to the local Nix store.
+        '';
+      };
+
+      distributedBuilds = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to distribute builds to the machines listed in
+          {option}`nix.buildMachines`.
+        '';
+      };
+    };
+  };
+
+  # distributedBuilds does *not* inhibit /etc/machines generation; caller may
+  # override that nix option.
+  config = mkIf cfg.enable {
+    assertions =
+      let badMachine = m: m.system == null && m.systems == [ ];
+      in
+      [
+        {
+          assertion = !(any badMachine cfg.buildMachines);
+          message = ''
+            At least one system type (via <varname>system</varname> or
+              <varname>systems</varname>) must be set for every build machine.
+              Invalid machine specifications:
+          '' + "      " +
+          (concatStringsSep "\n      "
+            (map (m: m.hostName)
+              (filter (badMachine) cfg.buildMachines)));
+        }
+      ];
+
+    # List of machines for distributed Nix builds
+    environment.etc."nix/machines" =
+      mkIf (cfg.buildMachines != [ ]) {
+        text = buildMachinesText;
+      };
+
+    # Legacy configuration conversion.
+    nix.settings = mkIf (!cfg.distributedBuilds) { builders = null; };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nix.nix b/nixpkgs/nixos/modules/config/nix.nix
new file mode 100644
index 000000000000..2769d8b25ef6
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nix.nix
@@ -0,0 +1,383 @@
+/*
+  Manages /etc/nix.conf.
+
+  See also
+   - ./nix-channel.nix
+   - ./nix-flakes.nix
+   - ./nix-remote-build.nix
+   - nixos/modules/services/system/nix-daemon.nix
+ */
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib)
+    concatStringsSep
+    boolToString
+    escape
+    floatToString
+    getVersion
+    isBool
+    isDerivation
+    isFloat
+    isInt
+    isList
+    isString
+    literalExpression
+    mapAttrsToList
+    mkAfter
+    mkDefault
+    mkIf
+    mkOption
+    mkRenamedOptionModuleWith
+    optionalString
+    optionals
+    strings
+    systems
+    toPretty
+    types
+    versionAtLeast
+    ;
+
+  cfg = config.nix;
+
+  nixPackage = cfg.package.out;
+
+  isNixAtLeast = versionAtLeast (getVersion nixPackage);
+
+  legacyConfMappings = {
+    useSandbox = "sandbox";
+    buildCores = "cores";
+    maxJobs = "max-jobs";
+    sandboxPaths = "extra-sandbox-paths";
+    binaryCaches = "substituters";
+    trustedBinaryCaches = "trusted-substituters";
+    binaryCachePublicKeys = "trusted-public-keys";
+    autoOptimiseStore = "auto-optimise-store";
+    requireSignedBinaryCaches = "require-sigs";
+    trustedUsers = "trusted-users";
+    allowedUsers = "allowed-users";
+    systemFeatures = "system-features";
+  };
+
+  semanticConfType = with types;
+    let
+      confAtom = nullOr
+        (oneOf [
+          bool
+          int
+          float
+          str
+          path
+          package
+        ]) // {
+        description = "Nix config atom (null, bool, int, float, str, path or package)";
+      };
+    in
+    attrsOf (either confAtom (listOf confAtom));
+
+  nixConf =
+    assert isNixAtLeast "2.2";
+    let
+
+      mkValueString = v:
+        if v == null then ""
+        else if isInt v then toString v
+        else if isBool v then boolToString v
+        else if isFloat v then floatToString v
+        else if isList v then toString v
+        else if isDerivation v then toString v
+        else if builtins.isPath v then toString v
+        else if isString v then v
+        else if strings.isConvertibleWithToString v then toString v
+        else abort "The nix conf value: ${toPretty {} v} can not be encoded";
+
+      mkKeyValue = k: v: "${escape [ "=" ] k} = ${mkValueString v}";
+
+      mkKeyValuePairs = attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
+
+    in
+    pkgs.writeTextFile {
+      name = "nix.conf";
+      text = ''
+        # WARNING: this file is generated from the nix.* options in
+        # your NixOS configuration, typically
+        # /etc/nixos/configuration.nix.  Do not edit it!
+        ${mkKeyValuePairs cfg.settings}
+        ${cfg.extraOptions}
+      '';
+      checkPhase = lib.optionalString cfg.checkConfig (
+        if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
+          echo "Ignoring validation for cross-compilation"
+        ''
+        else
+        let
+          showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
+        in
+        ''
+          echo "Validating generated nix.conf"
+          ln -s $out ./nix.conf
+          set -e
+          set +o pipefail
+          NIX_CONF_DIR=$PWD \
+            ${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
+              ${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
+            |& sed -e 's/^warning:/error:/' \
+            | (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
+          set -o pipefail
+        '');
+    };
+
+in
+{
+  imports = [
+    (mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "useChroot" ]; to = [ "nix" "useSandbox" ]; })
+    (mkRenamedOptionModuleWith { sinceRelease = 2003; from = [ "nix" "chrootDirs" ]; to = [ "nix" "sandboxPaths" ]; })
+  ] ++
+    mapAttrsToList
+      (oldConf: newConf:
+        mkRenamedOptionModuleWith {
+          sinceRelease = 2205;
+          from = [ "nix" oldConf ];
+          to = [ "nix" "settings" newConf ];
+      })
+      legacyConfMappings;
+
+  options = {
+    nix = {
+      checkConfig = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          If enabled, checks that Nix can parse the generated nix.conf.
+        '';
+      };
+
+      checkAllErrors = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          If enabled, checks the nix.conf parsing for any kind of error. When disabled, checks only for unknown settings.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          keep-outputs = true
+          keep-derivations = true
+        '';
+        description = lib.mdDoc "Additional text appended to {file}`nix.conf`.";
+      };
+
+      settings = mkOption {
+        type = types.submodule {
+          freeformType = semanticConfType;
+
+          options = {
+            max-jobs = mkOption {
+              type = types.either types.int (types.enum [ "auto" ]);
+              default = "auto";
+              example = 64;
+              description = lib.mdDoc ''
+                This option defines the maximum number of jobs that Nix will try to
+                build in parallel. The default is auto, which means it will use all
+                available logical cores. It is recommend to set it to the total
+                number of logical cores in your system (e.g., 16 for two CPUs with 4
+                cores each and hyper-threading).
+              '';
+            };
+
+            auto-optimise-store = mkOption {
+              type = types.bool;
+              default = false;
+              example = true;
+              description = lib.mdDoc ''
+                If set to true, Nix automatically detects files in the store that have
+                identical contents, and replaces them with hard links to a single copy.
+                This saves disk space. If set to false (the default), you can still run
+                nix-store --optimise to get rid of duplicate files.
+              '';
+            };
+
+            cores = mkOption {
+              type = types.int;
+              default = 0;
+              example = 64;
+              description = lib.mdDoc ''
+                This option defines the maximum number of concurrent tasks during
+                one build. It affects, e.g., -j option for make.
+                The special value 0 means that the builder should use all
+                available CPU cores in the system. Some builds may become
+                non-deterministic with this option; use with care! Packages will
+                only be affected if enableParallelBuilding is set for them.
+              '';
+            };
+
+            sandbox = mkOption {
+              type = types.either types.bool (types.enum [ "relaxed" ]);
+              default = true;
+              description = lib.mdDoc ''
+                If set, Nix will perform builds in a sandboxed environment that it
+                will set up automatically for each build. This prevents impurities
+                in builds by disallowing access to dependencies outside of the Nix
+                store by using network and mount namespaces in a chroot environment.
+
+                This is enabled by default even though it has a possible performance
+                impact due to the initial setup time of a sandbox for each build. It
+                doesn't affect derivation hashes, so changing this option will not
+                trigger a rebuild of packages.
+
+                When set to "relaxed", this option permits derivations that set
+                `__noChroot = true;` to run outside of the sandboxed environment.
+                Exercise caution when using this mode of operation! It is intended to
+                be a quick hack when building with packages that are not easily setup
+                to be built reproducibly.
+              '';
+            };
+
+            extra-sandbox-paths = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "/dev" "/proc" ];
+              description = lib.mdDoc ''
+                Directories from the host filesystem to be included
+                in the sandbox.
+              '';
+            };
+
+            substituters = mkOption {
+              type = types.listOf types.str;
+              description = lib.mdDoc ''
+                List of binary cache URLs used to obtain pre-built binaries
+                of Nix packages.
+
+                By default https://cache.nixos.org/ is added.
+              '';
+            };
+
+            trusted-substituters = mkOption {
+              type = types.listOf types.str;
+              default = [ ];
+              example = [ "https://hydra.nixos.org/" ];
+              description = lib.mdDoc ''
+                List of binary cache URLs that non-root users can use (in
+                addition to those specified using
+                {option}`nix.settings.substituters`) by passing
+                `--option binary-caches` to Nix commands.
+              '';
+            };
+
+            require-sigs = mkOption {
+              type = types.bool;
+              default = true;
+              description = lib.mdDoc ''
+                If enabled (the default), Nix will only download binaries from binary caches if
+                they are cryptographically signed with any of the keys listed in
+                {option}`nix.settings.trusted-public-keys`. If disabled, signatures are neither
+                required nor checked, so it's strongly recommended that you use only
+                trustworthy caches and https to prevent man-in-the-middle attacks.
+              '';
+            };
+
+            trusted-public-keys = mkOption {
+              type = types.listOf types.str;
+              example = [ "hydra.nixos.org-1:CNHJZBh9K4tP3EKF6FkkgeVYsS3ohTl+oS0Qa8bezVs=" ];
+              description = lib.mdDoc ''
+                List of public keys used to sign binary caches. If
+                {option}`nix.settings.trusted-public-keys` is enabled,
+                then Nix will use a binary from a binary cache if and only
+                if it is signed by *any* of the keys
+                listed here. By default, only the key for
+                `cache.nixos.org` is included.
+              '';
+            };
+
+            trusted-users = mkOption {
+              type = types.listOf types.str;
+              default = [ "root" ];
+              example = [ "root" "alice" "@wheel" ];
+              description = lib.mdDoc ''
+                A list of names of users that have additional rights when
+                connecting to the Nix daemon, such as the ability to specify
+                additional binary caches, or to import unsigned NARs. You
+                can also specify groups by prefixing them with
+                `@`; for instance,
+                `@wheel` means all users in the wheel
+                group.
+              '';
+            };
+
+            system-features = mkOption {
+              type = types.listOf types.str;
+              example = [ "kvm" "big-parallel" "gccarch-skylake" ];
+              description = lib.mdDoc ''
+                The set of features supported by the machine. Derivations
+                can express dependencies on system features through the
+                `requiredSystemFeatures` attribute.
+
+                By default, pseudo-features `nixos-test`, `benchmark`,
+                and `big-parallel` used in Nixpkgs are set, `kvm`
+                is also included if it is available.
+              '';
+            };
+
+            allowed-users = mkOption {
+              type = types.listOf types.str;
+              default = [ "*" ];
+              example = [ "@wheel" "@builders" "alice" "bob" ];
+              description = lib.mdDoc ''
+                A list of names of users (separated by whitespace) that are
+                allowed to connect to the Nix daemon. As with
+                {option}`nix.settings.trusted-users`, you can specify groups by
+                prefixing them with `@`. Also, you can
+                allow all users by specifying `*`. The
+                default is `*`. Note that trusted users are
+                always allowed to connect.
+              '';
+            };
+          };
+        };
+        default = { };
+        example = literalExpression ''
+          {
+            use-sandbox = true;
+            show-trace = true;
+
+            system-features = [ "big-parallel" "kvm" "recursive-nix" ];
+            sandbox-paths = { "/bin/sh" = "''${pkgs.busybox-sandbox-shell.out}/bin/busybox"; };
+          }
+        '';
+        description = lib.mdDoc ''
+          Configuration for Nix, see
+          <https://nixos.org/manual/nix/stable/command-ref/conf-file.html> or
+          {manpage}`nix.conf(5)` for available options.
+          The value declared here will be translated directly to the key-value pairs Nix expects.
+
+          You can use {command}`nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.nix.settings`
+          to view the current value. By default it is empty.
+
+          Nix configurations defined under {option}`nix.*` will be translated and applied to this
+          option. In addition, configuration specified in {option}`nix.extraOptions` will be appended
+          verbatim to the resulting config file.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."nix/nix.conf".source = nixConf;
+    nix.settings = {
+      trusted-public-keys = [ "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" ];
+      substituters = mkAfter [ "https://cache.nixos.org/" ];
+      system-features = mkDefault (
+        [ "nixos-test" "benchmark" "big-parallel" "kvm" ] ++
+        optionals (pkgs.stdenv.hostPlatform ? gcc.arch) (
+          # a builder can run code for `gcc.arch` and inferior architectures
+          [ "gccarch-${pkgs.stdenv.hostPlatform.gcc.arch}" ] ++
+          map (x: "gccarch-${x}") (systems.architectures.inferiors.${pkgs.stdenv.hostPlatform.gcc.arch} or [])
+        )
+      );
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/no-x-libs.nix b/nixpkgs/nixos/modules/config/no-x-libs.nix
new file mode 100644
index 000000000000..4727e5b85ef2
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/no-x-libs.nix
@@ -0,0 +1,87 @@
+# This module gets rid of all dependencies on X11 client libraries
+# (including fontconfig).
+
+{ config, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    environment.noXlibs = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Switch off the options in the default configuration that
+        require X11 libraries. This includes client-side font
+        configuration and SSH forwarding of X11 authentication
+        in. Thus, you probably do not want to enable this option if
+        you want to run X11 programs on this machine via SSH.
+      '';
+    };
+  };
+
+  config = mkIf config.environment.noXlibs {
+    programs.ssh.setXAuthLocation = false;
+    security.pam.services.su.forwardXAuth = lib.mkForce false;
+
+    fonts.fontconfig.enable = false;
+
+    nixpkgs.overlays = singleton (const (super: {
+      beam = super.beam_nox;
+      cairo = super.cairo.override { x11Support = false; };
+      dbus = super.dbus.override { x11Support = false; };
+      ffmpeg_4 = super.ffmpeg_4.override { ffmpegVariant = "headless"; };
+      ffmpeg_5 = super.ffmpeg_5.override { ffmpegVariant = "headless"; };
+      # dep of graphviz, libXpm is optional for Xpm support
+      gd = super.gd.override { withXorg = false; };
+      ghostscript = super.ghostscript.override { cupsSupport = false; x11Support = false; };
+      gjs = super.gjs.overrideAttrs { doCheck = false; installTests = false; }; # avoid test dependency on gtk3
+      gobject-introspection = super.gobject-introspection.override { x11Support = false; };
+      gpsd = super.gpsd.override { guiSupport = false; };
+      graphviz = super.graphviz-nox;
+      gst_all_1 = super.gst_all_1 // {
+        gst-plugins-bad = super.gst_all_1.gst-plugins-bad.override { guiSupport = false; };
+        gst-plugins-base = super.gst_all_1.gst-plugins-base.override { enableWayland = false; enableX11 = false; };
+        gst-plugins-good = super.gst_all_1.gst-plugins-good.override { enableX11 = false; };
+      };
+      imagemagick = super.imagemagick.override { libX11Support = false; libXtSupport = false; };
+      imagemagickBig = super.imagemagickBig.override { libX11Support = false; libXtSupport = false; };
+      intel-vaapi-driver = super.intel-vaapi-driver.override { enableGui = false; };
+      libdevil = super.libdevil-nox;
+      libextractor = super.libextractor.override { gtkSupport = false; };
+      libva = super.libva-minimal;
+      limesuite = super.limesuite.override { withGui = false; };
+      mc = super.mc.override { x11Support = false; };
+      mpv-unwrapped = super.mpv-unwrapped.override { sdl2Support = false; x11Support = false; waylandSupport = false; };
+      msmtp = super.msmtp.override { withKeyring = false; };
+      mupdf = super.mupdf.override { enableGL = false; enableX11 = false; };
+      neofetch = super.neofetch.override { x11Support = false; };
+      networkmanager-fortisslvpn = super.networkmanager-fortisslvpn.override { withGnome = false; };
+      networkmanager-iodine = super.networkmanager-iodine.override { withGnome = false; };
+      networkmanager-l2tp = super.networkmanager-l2tp.override { withGnome = false; };
+      networkmanager-openconnect = super.networkmanager-openconnect.override { withGnome = false; };
+      networkmanager-openvpn = super.networkmanager-openvpn.override { withGnome = false; };
+      networkmanager-sstp = super.networkmanager-vpnc.override { withGnome = false; };
+      networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; };
+      pango = super.pango.override { x11Support = false; };
+      pinentry = super.pinentry.override { enabledFlavors = [ "curses" "tty" "emacs" ]; withLibsecret = false; };
+      pipewire = super.pipewire.override { x11Support = false; };
+      pythonPackagesExtensions = super.pythonPackagesExtensions ++ [
+        (python-final: python-prev: {
+          # tk feature requires wayland which fails to compile
+          matplotlib = python-prev.matplotlib.override { enableTk = false; };
+        })
+      ];
+      qemu = super.qemu.override { gtkSupport = false; spiceSupport = false; sdlSupport = false; };
+      qrencode = super.qrencode.overrideAttrs (_: { doCheck = false; });
+      qt5 = super.qt5.overrideScope (const (super': {
+        qtbase = super'.qtbase.override { withGtk3 = false; withQttranslation = false; };
+      }));
+      stoken = super.stoken.override { withGTK3 = false; };
+      # translateManpages -> perlPackages.po4a -> texlive-combined-basic -> texlive-core-big -> libX11
+      util-linux = super.util-linux.override { translateManpages = false; };
+      vim-full = super.vim-full.override { guiSupport = false; };
+      zbar = super.zbar.override { enableVideo = false; withXorg = false; };
+    }));
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/nsswitch.nix b/nixpkgs/nixos/modules/config/nsswitch.nix
new file mode 100644
index 000000000000..b004072813bd
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/nsswitch.nix
@@ -0,0 +1,136 @@
+# Configuration for the Name Service Switch (/etc/nsswitch.conf).
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  options = {
+
+    # NSS modules.  Hacky!
+    # Only works with nscd!
+    system.nssModules = mkOption {
+      type = types.listOf types.path;
+      internal = true;
+      default = [];
+      description = lib.mdDoc ''
+        Search path for NSS (Name Service Switch) modules.  This allows
+        several DNS resolution methods to be specified via
+        {file}`/etc/nsswitch.conf`.
+      '';
+      apply = list:
+        {
+          inherit list;
+          path = makeLibraryPath list;
+        };
+    };
+
+    system.nssDatabases = {
+      passwd = mkOption {
+        type = types.listOf types.str;
+        description = lib.mdDoc ''
+          List of passwd entries to configure in {file}`/etc/nsswitch.conf`.
+
+          Note that "files" is always prepended while "systemd" is appended if nscd is enabled.
+
+          This option only takes effect if nscd is enabled.
+        '';
+        default = [];
+      };
+
+      group = mkOption {
+        type = types.listOf types.str;
+        description = lib.mdDoc ''
+          List of group entries to configure in {file}`/etc/nsswitch.conf`.
+
+          Note that "files" is always prepended while "systemd" is appended if nscd is enabled.
+
+          This option only takes effect if nscd is enabled.
+        '';
+        default = [];
+      };
+
+      shadow = mkOption {
+        type = types.listOf types.str;
+        description = lib.mdDoc ''
+          List of shadow entries to configure in {file}`/etc/nsswitch.conf`.
+
+          Note that "files" is always prepended.
+
+          This option only takes effect if nscd is enabled.
+        '';
+        default = [];
+      };
+
+      hosts = mkOption {
+        type = types.listOf types.str;
+        description = lib.mdDoc ''
+          List of hosts entries to configure in {file}`/etc/nsswitch.conf`.
+
+          Note that "files" is always prepended, and "dns" and "myhostname" are always appended.
+
+          This option only takes effect if nscd is enabled.
+        '';
+        default = [];
+      };
+
+      services = mkOption {
+        type = types.listOf types.str;
+        description = lib.mdDoc ''
+          List of services entries to configure in {file}`/etc/nsswitch.conf`.
+
+          Note that "files" is always prepended.
+
+          This option only takes effect if nscd is enabled.
+        '';
+        default = [];
+      };
+    };
+  };
+
+  imports = [
+    (mkRenamedOptionModule [ "system" "nssHosts" ] [ "system" "nssDatabases" "hosts" ])
+  ];
+
+  config = {
+    assertions = [
+      {
+        assertion = config.system.nssModules.path != "" -> config.services.nscd.enable;
+        message = ''
+          Loading NSS modules from system.nssModules (${config.system.nssModules.path}),
+          requires services.nscd.enable being set to true.
+
+          If disabling nscd is really necessary, it is possible to disable loading NSS modules
+          by setting `system.nssModules = lib.mkForce [];` in your configuration.nix.
+        '';
+      }
+    ];
+
+    # Name Service Switch configuration file.  Required by the C
+    # library.
+    environment.etc."nsswitch.conf".text = ''
+      passwd:    ${concatStringsSep " " config.system.nssDatabases.passwd}
+      group:     ${concatStringsSep " " config.system.nssDatabases.group}
+      shadow:    ${concatStringsSep " " config.system.nssDatabases.shadow}
+
+      hosts:     ${concatStringsSep " " config.system.nssDatabases.hosts}
+      networks:  files
+
+      ethers:    files
+      services:  ${concatStringsSep " " config.system.nssDatabases.services}
+      protocols: files
+      rpc:       files
+    '';
+
+    system.nssDatabases = {
+      passwd = mkBefore [ "files" ];
+      group = mkBefore [ "files" ];
+      shadow = mkBefore [ "files" ];
+      hosts = mkMerge [
+        (mkOrder 998 [ "files" ])
+        (mkOrder 1499 [ "dns" ])
+      ];
+      services = mkBefore [ "files" ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/power-management.nix b/nixpkgs/nixos/modules/config/power-management.nix
new file mode 100644
index 000000000000..e7fd02920e0d
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/power-management.nix
@@ -0,0 +1,106 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.powerManagement;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    powerManagement = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          lib.mdDoc ''
+            Whether to enable power management.  This includes support
+            for suspend-to-RAM and powersave features on laptops.
+          '';
+      };
+
+      resumeCommands = mkOption {
+        type = types.lines;
+        default = "";
+        description = lib.mdDoc "Commands executed after the system resumes from suspend-to-RAM.";
+      };
+
+      powerUpCommands = mkOption {
+        type = types.lines;
+        default = "";
+        example = literalExpression ''
+          "''${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda"
+        '';
+        description =
+          lib.mdDoc ''
+            Commands executed when the machine powers up.  That is,
+            they're executed both when the system first boots and when
+            it resumes from suspend or hibernation.
+          '';
+      };
+
+      powerDownCommands = mkOption {
+        type = types.lines;
+        default = "";
+        example = literalExpression ''
+          "''${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda"
+        '';
+        description =
+          lib.mdDoc ''
+            Commands executed when the machine powers down.  That is,
+            they're executed both when the system shuts down and when
+            it goes to suspend or hibernation.
+          '';
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    systemd.targets.post-resume = {
+      description = "Post-Resume Actions";
+      requires = [ "post-resume.service" ];
+      after = [ "post-resume.service" ];
+      wantedBy = [ "sleep.target" ];
+      unitConfig.StopWhenUnneeded = true;
+    };
+
+    # Service executed before suspending/hibernating.
+    systemd.services.pre-sleep =
+      { description = "Pre-Sleep Actions";
+        wantedBy = [ "sleep.target" ];
+        before = [ "sleep.target" ];
+        script =
+          ''
+            ${cfg.powerDownCommands}
+          '';
+        serviceConfig.Type = "oneshot";
+      };
+
+    systemd.services.post-resume =
+      { description = "Post-Resume Actions";
+        after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" "suspend-then-hibernate.target" ];
+        script =
+          ''
+            /run/current-system/systemd/bin/systemctl try-restart --no-block post-resume.target
+            ${cfg.resumeCommands}
+            ${cfg.powerUpCommands}
+          '';
+        serviceConfig.Type = "oneshot";
+      };
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/pulseaudio.nix b/nixpkgs/nixos/modules/config/pulseaudio.nix
new file mode 100644
index 000000000000..662959bf0071
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/pulseaudio.nix
@@ -0,0 +1,327 @@
+{ config, lib, pkgs, ... }:
+
+with pkgs;
+with lib;
+
+let
+
+  cfg = config.hardware.pulseaudio;
+  alsaCfg = config.sound;
+
+  hasZeroconf = let z = cfg.zeroconf; in z.publish.enable || z.discovery.enable;
+
+  overriddenPackage = cfg.package.override
+    (optionalAttrs hasZeroconf { zeroconfSupport = true; });
+  binary = "${getBin overriddenPackage}/bin/pulseaudio";
+  binaryNoDaemon = "${binary} --daemonize=no";
+
+  # Forces 32bit pulseaudio and alsa-plugins to be built/supported for apps
+  # using 32bit alsa on 64bit linux.
+  enable32BitAlsaPlugins = cfg.support32Bit && stdenv.isx86_64 && (pkgs.pkgsi686Linux.alsa-lib != null && pkgs.pkgsi686Linux.libpulseaudio != null);
+
+
+  myConfigFile =
+    let
+      addModuleIf = cond: mod: optionalString cond "load-module ${mod}";
+      allAnon = optional cfg.tcp.anonymousClients.allowAll "auth-anonymous=1";
+      ipAnon =  let a = cfg.tcp.anonymousClients.allowedIpRanges;
+                in optional (a != []) ''auth-ip-acl=${concatStringsSep ";" a}'';
+    in writeTextFile {
+      name = "default.pa";
+        text = ''
+        .include ${cfg.configFile}
+        ${addModuleIf cfg.zeroconf.publish.enable "module-zeroconf-publish"}
+        ${addModuleIf cfg.zeroconf.discovery.enable "module-zeroconf-discover"}
+        ${addModuleIf cfg.tcp.enable (concatStringsSep " "
+           ([ "module-native-protocol-tcp" ] ++ allAnon ++ ipAnon))}
+        ${addModuleIf config.services.jack.jackd.enable "module-jack-sink"}
+        ${addModuleIf config.services.jack.jackd.enable "module-jack-source"}
+        ${cfg.extraConfig}
+      '';
+    };
+
+  ids = config.ids;
+
+  uid = ids.uids.pulseaudio;
+  gid = ids.gids.pulseaudio;
+
+  stateDir = "/run/pulse";
+
+  # Create pulse/client.conf even if PulseAudio is disabled so
+  # that we can disable the autospawn feature in programs that
+  # are built with PulseAudio support (like KDE).
+  clientConf = writeText "client.conf" ''
+    autospawn=no
+    ${cfg.extraClientConf}
+  '';
+
+  # Write an /etc/asound.conf that causes all ALSA applications to
+  # be re-routed to the PulseAudio server through ALSA's Pulse
+  # plugin.
+  alsaConf = writeText "asound.conf" (''
+    pcm_type.pulse {
+      libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;
+      ${lib.optionalString enable32BitAlsaPlugins
+     "libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_pcm_pulse.so ;"}
+    }
+    pcm.!default {
+      type pulse
+      hint.description "Default Audio Device (via PulseAudio)"
+    }
+    ctl_type.pulse {
+      libs.native = ${pkgs.alsa-plugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;
+      ${lib.optionalString enable32BitAlsaPlugins
+     "libs.32Bit = ${pkgs.pkgsi686Linux.alsa-plugins}/lib/alsa-lib/libasound_module_ctl_pulse.so ;"}
+    }
+    ctl.!default {
+      type pulse
+    }
+    ${alsaCfg.extraConfig}
+  '');
+
+in {
+
+  options = {
+
+    hardware.pulseaudio = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to enable the PulseAudio sound server.
+        '';
+      };
+
+      systemWide = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          If false, a PulseAudio server is launched automatically for
+          each user that tries to use the sound system. The server runs
+          with user privileges. If true, one system-wide PulseAudio
+          server is launched on boot, running as the user "pulse", and
+          only users in the "pulse-access" group will have access to the server.
+          Please read the PulseAudio documentation for more details.
+
+          Don't enable this option unless you know what you are doing.
+        '';
+      };
+
+      support32Bit = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to include the 32-bit pulseaudio libraries in the system or not.
+          This is only useful on 64-bit systems and currently limited to x86_64-linux.
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.nullOr types.path;
+        description = lib.mdDoc ''
+          The path to the default configuration options the PulseAudio server
+          should use. By default, the "default.pa" configuration
+          from the PulseAudio distribution is used.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = lib.mdDoc ''
+          Literal string to append to `configFile`
+          and the config file generated by the pulseaudio module.
+        '';
+      };
+
+      extraClientConf = mkOption {
+        type = types.lines;
+        default = "";
+        description = lib.mdDoc ''
+          Extra configuration appended to pulse/client.conf file.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = if config.services.jack.jackd.enable
+                  then pkgs.pulseaudioFull
+                  else pkgs.pulseaudio;
+        defaultText = literalExpression "pkgs.pulseaudio";
+        example = literalExpression "pkgs.pulseaudioFull";
+        description = lib.mdDoc ''
+          The PulseAudio derivation to use.  This can be used to enable
+          features (such as JACK support, Bluetooth) via the
+          `pulseaudioFull` package.
+        '';
+      };
+
+      extraModules = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExpression "[ pkgs.pulseaudio-modules-bt ]";
+        description = lib.mdDoc ''
+          Extra pulseaudio modules to use. This is intended for out-of-tree
+          pulseaudio modules like extra bluetooth codecs.
+
+          Extra modules take precedence over built-in pulseaudio modules.
+        '';
+      };
+
+      daemon = {
+        logLevel = mkOption {
+          type = types.str;
+          default = "notice";
+          description = lib.mdDoc ''
+            The log level that the system-wide pulseaudio daemon should use,
+            if activated.
+          '';
+        };
+
+        config = mkOption {
+          type = types.attrsOf types.unspecified;
+          default = {};
+          description = lib.mdDoc "Config of the pulse daemon. See `man pulse-daemon.conf`.";
+          example = literalExpression ''{ realtime-scheduling = "yes"; }'';
+        };
+      };
+
+      zeroconf = {
+        discovery.enable =
+          mkEnableOption (lib.mdDoc "discovery of pulseaudio sinks in the local network");
+        publish.enable =
+          mkEnableOption (lib.mdDoc "publishing the pulseaudio sink in the local network");
+      };
+
+      # TODO: enable by default?
+      tcp = {
+        enable = mkEnableOption (lib.mdDoc "tcp streaming support");
+
+        anonymousClients = {
+          allowAll = mkEnableOption (lib.mdDoc "all anonymous clients to stream to the server");
+          allowedIpRanges = mkOption {
+            type = types.listOf types.str;
+            default = [];
+            example = literalExpression ''[ "127.0.0.1" "192.168.1.0/24" ]'';
+            description = lib.mdDoc ''
+              A list of IP subnets that are allowed to stream to the server.
+            '';
+          };
+        };
+      };
+
+    };
+
+  };
+
+
+  config = lib.mkIf cfg.enable (mkMerge [
+    {
+      environment.etc."pulse/client.conf".source = clientConf;
+
+      environment.systemPackages = [ overriddenPackage ];
+
+      sound.enable = true;
+
+      environment.etc = {
+        "asound.conf".source = alsaConf;
+
+        "pulse/daemon.conf".source = writeText "daemon.conf"
+          (lib.generators.toKeyValue {} cfg.daemon.config);
+
+        "openal/alsoft.conf".source = writeText "alsoft.conf" "drivers=pulse";
+
+        "libao.conf".source = writeText "libao.conf" "default_driver=pulse";
+      };
+
+      hardware.pulseaudio.configFile = mkDefault "${getBin overriddenPackage}/etc/pulse/default.pa";
+
+      # Disable flat volumes to enable relative ones
+      hardware.pulseaudio.daemon.config.flat-volumes = mkDefault "no";
+
+      # Upstream defaults to speex-float-1 which results in audible artifacts
+      hardware.pulseaudio.daemon.config.resample-method = mkDefault "speex-float-5";
+
+      # Allow PulseAudio to get realtime priority using rtkit.
+      security.rtkit.enable = true;
+
+      systemd.packages = [ overriddenPackage ];
+
+      # PulseAudio is packaged with udev rules to handle various audio device quirks
+      services.udev.packages = [ overriddenPackage ];
+    }
+
+    (mkIf (cfg.extraModules != []) {
+      hardware.pulseaudio.daemon.config.dl-search-path = let
+        overriddenModules = builtins.map
+          (drv: drv.override { pulseaudio = overriddenPackage; })
+          cfg.extraModules;
+        modulePaths = builtins.map
+          (drv: "${drv}/lib/pulseaudio/modules")
+          # User-provided extra modules take precedence
+          (overriddenModules ++ [ overriddenPackage ]);
+      in lib.concatStringsSep ":" modulePaths;
+    })
+
+    (mkIf hasZeroconf {
+      services.avahi.enable = true;
+    })
+    (mkIf cfg.zeroconf.publish.enable {
+      services.avahi.publish.enable = true;
+      services.avahi.publish.userServices = true;
+    })
+
+    (mkIf (!cfg.systemWide) {
+      environment.etc = {
+        "pulse/default.pa".source = myConfigFile;
+      };
+      systemd.user = {
+        services.pulseaudio = {
+          restartIfChanged = true;
+          serviceConfig = {
+            RestartSec = "500ms";
+            PassEnvironment = "DISPLAY";
+          };
+        } // optionalAttrs config.services.jack.jackd.enable {
+          environment.JACK_PROMISCUOUS_SERVER = "jackaudio";
+        };
+        sockets.pulseaudio = {
+          wantedBy = [ "sockets.target" ];
+        };
+      };
+    })
+
+    (mkIf cfg.systemWide {
+      users.users.pulse = {
+        # For some reason, PulseAudio wants UID == GID.
+        uid = assert uid == gid; uid;
+        group = "pulse";
+        extraGroups = [ "audio" ];
+        description = "PulseAudio system service user";
+        home = stateDir;
+        homeMode = "755";
+        createHome = true;
+        isSystemUser = true;
+      };
+
+      users.groups.pulse.gid = gid;
+      users.groups.pulse-access = {};
+
+      systemd.services.pulseaudio = {
+        description = "PulseAudio System-Wide Server";
+        wantedBy = [ "sound.target" ];
+        before = [ "sound.target" ];
+        environment.PULSE_RUNTIME_PATH = stateDir;
+        serviceConfig = {
+          Type = "notify";
+          ExecStart = "${binaryNoDaemon} --log-level=${cfg.daemon.logLevel} --system -n --file=${myConfigFile}";
+          Restart = "on-failure";
+          RestartSec = "500ms";
+        };
+      };
+
+      environment.variables.PULSE_COOKIE = "${stateDir}/.config/pulse/cookie";
+    })
+  ]);
+
+}
diff --git a/nixpkgs/nixos/modules/config/qt.nix b/nixpkgs/nixos/modules/config/qt.nix
new file mode 100644
index 000000000000..f82b7ab85a8c
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/qt.nix
@@ -0,0 +1,154 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.qt;
+
+  platformPackages = with pkgs; {
+    gnome = [ qgnomeplatform qgnomeplatform-qt6 ];
+    gtk2 = [ libsForQt5.qtstyleplugins qt6Packages.qt6gtk2 ];
+    kde = [ libsForQt5.plasma-integration libsForQt5.systemsettings ];
+    lxqt = [ lxqt.lxqt-qtplugin lxqt.lxqt-config ];
+    qt5ct = [ libsForQt5.qt5ct qt6Packages.qt6ct ];
+  };
+
+  stylePackages = with pkgs; {
+    bb10bright = [ libsForQt5.qtstyleplugins ];
+    bb10dark = [ libsForQt5.qtstyleplugins ];
+    cde = [ libsForQt5.qtstyleplugins ];
+    cleanlooks = [ libsForQt5.qtstyleplugins ];
+    gtk2 = [ libsForQt5.qtstyleplugins qt6Packages.qt6gtk2 ];
+    motif = [ libsForQt5.qtstyleplugins ];
+    plastique = [ libsForQt5.qtstyleplugins ];
+
+    adwaita = [ adwaita-qt adwaita-qt6 ];
+    adwaita-dark = [ adwaita-qt adwaita-qt6 ];
+    adwaita-highcontrast = [ adwaita-qt adwaita-qt6 ];
+    adwaita-highcontrastinverse = [ adwaita-qt adwaita-qt6 ];
+
+    breeze = [ libsForQt5.breeze-qt5 ];
+
+    kvantum = [ libsForQt5.qtstyleplugin-kvantum qt6Packages.qtstyleplugin-kvantum ];
+  };
+in
+{
+  meta.maintainers = with lib.maintainers; [ romildo thiagokokada ];
+
+  imports = [
+    (lib.mkRenamedOptionModule [ "qt5" "enable" ] [ "qt" "enable" ])
+    (lib.mkRenamedOptionModule [ "qt5" "platformTheme" ] [ "qt" "platformTheme" ])
+    (lib.mkRenamedOptionModule [ "qt5" "style" ] [ "qt" "style" ])
+  ];
+
+  options = {
+    qt = {
+      enable = lib.mkEnableOption "" // {
+        description = lib.mdDoc ''
+          Whether to enable Qt configuration, including theming.
+
+          Enabling this option is necessary for Qt plugins to work in the
+          installed profiles (e.g.: `nix-env -i` or `environment.systemPackages`).
+        '';
+      };
+
+      platformTheme = lib.mkOption {
+        type = with lib.types; nullOr (enum (lib.attrNames platformPackages));
+        default = null;
+        example = "gnome";
+        relatedPackages = [
+          "qgnomeplatform"
+          "qgnomeplatform-qt6"
+          [ "libsForQt5" "plasma-integration" ]
+          [ "libsForQt5" "qt5ct" ]
+          [ "libsForQt5" "qtstyleplugins" ]
+          [ "libsForQt5" "systemsettings" ]
+          [ "lxqt" "lxqt-config" ]
+          [ "lxqt" "lxqt-qtplugin" ]
+          [ "qt6Packages" "qt6ct" ]
+          [ "qt6Packages" "qt6gtk2" ]
+        ];
+        description = lib.mdDoc ''
+          Selects the platform theme to use for Qt applications.
+
+          The options are
+          - `gnome`: Use GNOME theme with [qgnomeplatform](https://github.com/FedoraQt/QGnomePlatform)
+          - `gtk2`: Use GTK theme with [qtstyleplugins](https://github.com/qt/qtstyleplugins)
+          - `kde`: Use Qt settings from Plasma.
+          - `lxqt`: Use LXQt style set using the [lxqt-config-appearance](https://github.com/lxqt/lxqt-config)
+             application.
+          - `qt5ct`: Use Qt style set using the [qt5ct](https://sourceforge.net/projects/qt5ct/)
+             and [qt6ct](https://github.com/trialuser02/qt6ct) applications.
+        '';
+      };
+
+      style = lib.mkOption {
+        type = with lib.types; nullOr (enum (lib.attrNames stylePackages));
+        default = null;
+        example = "adwaita";
+        relatedPackages = [
+          "adwaita-qt"
+          "adwaita-qt6"
+          [ "libsForQt5" "breeze-qt5" ]
+          [ "libsForQt5" "qtstyleplugin-kvantum" ]
+          [ "libsForQt5" "qtstyleplugins" ]
+          [ "qt6Packages" "qt6gtk2" ]
+          [ "qt6Packages" "qtstyleplugin-kvantum" ]
+        ];
+        description = lib.mdDoc ''
+          Selects the style to use for Qt applications.
+
+          The options are
+          - `adwaita`, `adwaita-dark`, `adwaita-highcontrast`, `adawaita-highcontrastinverse`:
+            Use Adwaita Qt style with
+            [adwaita](https://github.com/FedoraQt/adwaita-qt)
+          - `breeze`: Use the Breeze style from
+            [breeze](https://github.com/KDE/breeze)
+          - `bb10bright`, `bb10dark`, `cleanlooks`, `gtk2`, `motif`, `plastique`:
+            Use styles from
+            [qtstyleplugins](https://github.com/qt/qtstyleplugins)
+          - `kvantum`: Use styles from
+            [kvantum](https://github.com/tsujan/Kvantum)
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions =
+      let
+        gnomeStyles = [
+          "adwaita"
+          "adwaita-dark"
+          "adwaita-highcontrast"
+          "adwaita-highcontrastinverse"
+          "breeze"
+        ];
+      in
+      [
+        {
+          assertion = cfg.platformTheme == "gnome" -> (builtins.elem cfg.style gnomeStyles);
+          message = ''
+            `qt.platformTheme` "gnome" must have `qt.style` set to a theme that supports both Qt and Gtk,
+            for example: ${lib.concatStringsSep ", " gnomeStyles}.
+          '';
+        }
+      ];
+
+    environment.variables = {
+      QT_QPA_PLATFORMTHEME = lib.mkIf (cfg.platformTheme != null) cfg.platformTheme;
+      QT_STYLE_OVERRIDE = lib.mkIf (cfg.style != null) cfg.style;
+    };
+
+    environment.profileRelativeSessionVariables =
+      let
+        qtVersions = with pkgs; [ qt5 qt6 ];
+      in
+      {
+        QT_PLUGIN_PATH = map (qt: "/${qt.qtbase.qtPluginPrefix}") qtVersions;
+        QML2_IMPORT_PATH = map (qt: "/${qt.qtbase.qtQmlPrefix}") qtVersions;
+      };
+
+    environment.systemPackages =
+      lib.optionals (cfg.platformTheme != null) (platformPackages.${cfg.platformTheme})
+      ++ lib.optionals (cfg.style != null) (stylePackages.${cfg.style});
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/resolvconf.nix b/nixpkgs/nixos/modules/config/resolvconf.nix
new file mode 100644
index 000000000000..e9ae4d651d26
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/resolvconf.nix
@@ -0,0 +1,160 @@
+# /etc files related to networking, such as /etc/services.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.networking.resolvconf;
+
+  resolvconfOptions = cfg.extraOptions
+    ++ optional cfg.dnsSingleRequest "single-request"
+    ++ optional cfg.dnsExtensionMechanism "edns0";
+
+  configText =
+    ''
+      # This is the default, but we must set it here to prevent
+      # a collision with an apparently unrelated environment
+      # variable with the same name exported by dhcpcd.
+      interface_order='lo lo[0-9]*'
+    '' + optionalString config.services.nscd.enable ''
+      # Invalidate the nscd cache whenever resolv.conf is
+      # regenerated.
+      libc_restart='/run/current-system/systemd/bin/systemctl try-restart --no-block nscd.service 2> /dev/null'
+    '' + optionalString (length resolvconfOptions > 0) ''
+      # Options as described in resolv.conf(5)
+      resolv_conf_options='${concatStringsSep " " resolvconfOptions}'
+    '' + optionalString cfg.useLocalResolver ''
+      # This hosts runs a full-blown DNS resolver.
+      name_servers='127.0.0.1'
+    '' + cfg.extraConfig;
+
+in
+
+{
+  imports = [
+    (mkRenamedOptionModule [ "networking" "dnsSingleRequest" ] [ "networking" "resolvconf" "dnsSingleRequest" ])
+    (mkRenamedOptionModule [ "networking" "dnsExtensionMechanism" ] [ "networking" "resolvconf" "dnsExtensionMechanism" ])
+    (mkRenamedOptionModule [ "networking" "extraResolvconfConf" ] [ "networking" "resolvconf" "extraConfig" ])
+    (mkRenamedOptionModule [ "networking" "resolvconfOptions" ] [ "networking" "resolvconf" "extraOptions" ])
+    (mkRemovedOptionModule [ "networking" "resolvconf" "useHostResolvConf" ] "This option was never used for anything anyways")
+  ];
+
+  options = {
+
+    networking.resolvconf = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = !(config.environment.etc ? "resolv.conf");
+        defaultText = literalExpression ''!(config.environment.etc ? "resolv.conf")'';
+        description = lib.mdDoc ''
+          Whether DNS configuration is managed by resolvconf.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.openresolv;
+        defaultText = literalExpression "pkgs.openresolv";
+        description = lib.mdDoc ''
+          The package that provides the system-wide resolvconf command. Defaults to `openresolv`
+          if this module is enabled. Otherwise, can be used by other modules (for example {option}`services.resolved`) to
+          provide a compatibility layer.
+
+          This option generally shouldn't be set by the user.
+        '';
+      };
+
+      dnsSingleRequest = lib.mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
+          address queries at the same time, from the same port. Sometimes upstream
+          routers will systemically drop the ipv4 queries. The symptom of this problem is
+          that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
+          workaround for this is to specify the option 'single-request' in
+          /etc/resolv.conf. This option enables that.
+        '';
+      };
+
+      dnsExtensionMechanism = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Enable the `edns0` option in {file}`resolv.conf`. With
+          that option set, `glibc` supports use of the extension mechanisms for
+          DNS (EDNS) specified in RFC 2671. The most popular user of that feature is DNSSEC,
+          which does not work without it.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = "libc=NO";
+        description = lib.mdDoc ''
+          Extra configuration to append to {file}`resolvconf.conf`.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "ndots:1" "rotate" ];
+        description = lib.mdDoc ''
+          Set the options in {file}`/etc/resolv.conf`.
+        '';
+      };
+
+      useLocalResolver = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Use local DNS server for resolving.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkMerge [
+    {
+      environment.etc."resolvconf.conf".text =
+        if !cfg.enable then
+          # Force-stop any attempts to use resolvconf
+          ''
+            echo "resolvconf is disabled on this system but was used anyway:" >&2
+            echo "$0 $*" >&2
+            exit 1
+          ''
+        else configText;
+    }
+
+    (mkIf cfg.enable {
+      networking.resolvconf.package = pkgs.openresolv;
+
+      environment.systemPackages = [ cfg.package ];
+
+      systemd.services.resolvconf = {
+        description = "resolvconf update";
+
+        before = [ "network-pre.target" ];
+        wants = [ "network-pre.target" ];
+        wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ config.environment.etc."resolvconf.conf".source ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${cfg.package}/bin/resolvconf -u";
+          RemainAfterExit = true;
+        };
+      };
+
+    })
+  ];
+
+}
diff --git a/nixpkgs/nixos/modules/config/shells-environment.nix b/nixpkgs/nixos/modules/config/shells-environment.nix
new file mode 100644
index 000000000000..bc6583442edf
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/shells-environment.nix
@@ -0,0 +1,224 @@
+# This module defines a global environment configuration and
+# a common configuration for all shells.
+
+{ config, lib, utils, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.environment;
+
+  exportedEnvVars =
+    let
+      absoluteVariables =
+        mapAttrs (n: toList) cfg.variables;
+
+      suffixedVariables =
+        flip mapAttrs cfg.profileRelativeEnvVars (envVar: listSuffixes:
+          concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
+        );
+
+      allVariables =
+        zipAttrsWith (n: concatLists) [ absoluteVariables suffixedVariables ];
+
+      exportVariables =
+        mapAttrsToList (n: v: ''export ${n}="${concatStringsSep ":" v}"'') allVariables;
+    in
+      concatStringsSep "\n" exportVariables;
+in
+
+{
+
+  options = {
+
+    environment.variables = mkOption {
+      default = {};
+      example = { EDITOR = "nvim"; VISUAL = "nvim"; };
+      description = lib.mdDoc ''
+        A set of environment variables used in the global environment.
+        These variables will be set on shell initialisation (e.g. in /etc/profile).
+        The value of each variable can be either a string or a list of
+        strings.  The latter is concatenated, interspersed with colon
+        characters.
+      '';
+      type = with types; attrsOf (oneOf [ (listOf str) str path ]);
+      apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else "${v}");
+    };
+
+    environment.profiles = mkOption {
+      default = [];
+      description = lib.mdDoc ''
+        A list of profiles used to setup the global environment.
+      '';
+      type = types.listOf types.str;
+    };
+
+    environment.profileRelativeEnvVars = mkOption {
+      type = types.attrsOf (types.listOf types.str);
+      example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
+      description = lib.mdDoc ''
+        Attribute set of environment variable.  Each attribute maps to a list
+        of relative paths.  Each relative path is appended to the each profile
+        of {option}`environment.profiles` to form the content of the
+        corresponding environment variable.
+      '';
+    };
+
+    # !!! isn't there a better way?
+    environment.extraInit = mkOption {
+      default = "";
+      description = lib.mdDoc ''
+        Shell script code called during global environment initialisation
+        after all variables and profileVariables have been set.
+        This code is assumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.shellInit = mkOption {
+      default = "";
+      description = lib.mdDoc ''
+        Shell script code called during shell initialisation.
+        This code is assumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.loginShellInit = mkOption {
+      default = "";
+      description = lib.mdDoc ''
+        Shell script code called during login shell initialisation.
+        This code is assumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.interactiveShellInit = mkOption {
+      default = "";
+      description = lib.mdDoc ''
+        Shell script code called during interactive shell initialisation.
+        This code is assumed to be shell-independent, which means you should
+        stick to pure sh without sh word split.
+      '';
+      type = types.lines;
+    };
+
+    environment.shellAliases = mkOption {
+      example = { l = null; ll = "ls -l"; };
+      description = lib.mdDoc ''
+        An attribute set that maps aliases (the top level attribute names in
+        this option) to command strings or directly to build outputs. The
+        aliases are added to all users' shells.
+        Aliases mapped to `null` are ignored.
+      '';
+      type = with types; attrsOf (nullOr (either str path));
+    };
+
+    environment.homeBinInPath = mkOption {
+      description = lib.mdDoc ''
+        Include ~/bin/ in $PATH.
+      '';
+      default = false;
+      type = types.bool;
+    };
+
+    environment.localBinInPath = mkOption {
+      description = lib.mdDoc ''
+        Add ~/.local/bin/ to $PATH
+      '';
+      default = false;
+      type = types.bool;
+    };
+
+    environment.binsh = mkOption {
+      default = "${config.system.build.binsh}/bin/sh";
+      defaultText = literalExpression ''"''${config.system.build.binsh}/bin/sh"'';
+      example = literalExpression ''"''${pkgs.dash}/bin/dash"'';
+      type = types.path;
+      visible = false;
+      description = lib.mdDoc ''
+        The shell executable that is linked system-wide to
+        `/bin/sh`. Please note that NixOS assumes all
+        over the place that shell to be Bash, so override the default
+        setting only if you know exactly what you're doing.
+      '';
+    };
+
+    environment.shells = mkOption {
+      default = [];
+      example = literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
+      description = lib.mdDoc ''
+        A list of permissible login shells for user accounts.
+        No need to mention `/bin/sh`
+        here, it is placed into this list implicitly.
+      '';
+      type = types.listOf (types.either types.shellPackage types.path);
+    };
+
+  };
+
+  config = {
+
+    system.build.binsh = pkgs.bashInteractive;
+
+    # Set session variables in the shell as well. This is usually
+    # unnecessary, but it allows changes to session variables to take
+    # effect without restarting the session (e.g. by opening a new
+    # terminal instead of logging out of X11).
+    environment.variables = config.environment.sessionVariables;
+
+    environment.profileRelativeEnvVars = config.environment.profileRelativeSessionVariables;
+
+    environment.shellAliases = mapAttrs (name: mkDefault) {
+      ls = "ls --color=tty";
+      ll = "ls -l";
+      l  = "ls -alh";
+    };
+
+    environment.etc.shells.text =
+      ''
+        ${concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
+        /bin/sh
+      '';
+
+    # For resetting environment with `. /etc/set-environment` when needed
+    # and discoverability (see motivation of #30418).
+    environment.etc.set-environment.source = config.system.build.setEnvironment;
+
+    system.build.setEnvironment = pkgs.writeText "set-environment"
+      ''
+        # DO NOT EDIT -- this file has been generated automatically.
+
+        # Prevent this file from being sourced by child shells.
+        export __NIXOS_SET_ENVIRONMENT_DONE=1
+
+        ${exportedEnvVars}
+
+        ${cfg.extraInit}
+
+        ${optionalString cfg.homeBinInPath ''
+          # ~/bin if it exists overrides other bin directories.
+          export PATH="$HOME/bin:$PATH"
+        ''}
+
+        ${optionalString cfg.localBinInPath ''
+          export PATH="$HOME/.local/bin:$PATH"
+        ''}
+      '';
+
+    system.activationScripts.binsh = stringAfter [ "stdio" ]
+      ''
+        # Create the required /bin/sh symlink; otherwise lots of things
+        # (notably the system() function) won't work.
+        mkdir -m 0755 -p /bin
+        ln -sfn "${cfg.binsh}" /bin/.sh.tmp
+        mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
+      '';
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/stevenblack.nix b/nixpkgs/nixos/modules/config/stevenblack.nix
new file mode 100644
index 000000000000..7e6235169847
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/stevenblack.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) optionals mkOption mkEnableOption types mkIf elem concatStringsSep maintainers mdDoc;
+  cfg = config.networking.stevenblack;
+
+  # needs to be in a specific order
+  activatedHosts = with cfg; [ ]
+    ++ optionals (elem "fakenews" block) [ "fakenews" ]
+    ++ optionals (elem "gambling" block) [ "gambling" ]
+    ++ optionals (elem "porn" block) [ "porn" ]
+    ++ optionals (elem "social" block) [ "social" ];
+
+  hostsPath = "${pkgs.stevenblack-blocklist}/alternates/" + concatStringsSep "-" activatedHosts + "/hosts";
+in
+{
+  options.networking.stevenblack = {
+    enable = mkEnableOption (mdDoc "the stevenblack hosts file blocklist");
+
+    block = mkOption {
+      type = types.listOf (types.enum [ "fakenews" "gambling" "porn" "social" ]);
+      default = [ ];
+      description = mdDoc "Additional blocklist extensions.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.hostFiles = [ ]
+      ++ optionals (activatedHosts != [ ]) [ hostsPath ]
+      ++ optionals (activatedHosts == [ ]) [ "${pkgs.stevenblack-blocklist}/hosts" ];
+  };
+
+  meta.maintainers = [ maintainers.moni maintainers.artturin ];
+}
diff --git a/nixpkgs/nixos/modules/config/stub-ld.nix b/nixpkgs/nixos/modules/config/stub-ld.nix
new file mode 100644
index 000000000000..14c07466d061
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/stub-ld.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) optionalString mkOption types mdDoc mkIf mkDefault;
+
+  cfg = config.environment.stub-ld;
+
+  message = ''
+    NixOS cannot run dynamically linked executables intended for generic
+    linux environments out of the box. For more information, see:
+    https://nix.dev/permalink/stub-ld
+  '';
+
+  stub-ld-for = pkgsArg: messageArg: pkgsArg.pkgsStatic.runCommandCC "stub-ld" {
+    nativeBuildInputs = [ pkgsArg.unixtools.xxd ];
+    inherit messageArg;
+  } ''
+    printf "%s" "$messageArg" | xxd -i -n message >main.c
+    cat <<EOF >>main.c
+    #include <stdio.h>
+    int main(int argc, char * argv[]) {
+      fprintf(stderr, "Could not start dynamically linked executable: %s\n", argv[0]);
+      fwrite(message, sizeof(unsigned char), message_len, stderr);
+      return 127; // matches behavior of bash and zsh without a loader. fish uses 139
+    }
+    EOF
+    $CC -Os main.c -o $out
+  '';
+
+  pkgs32 = pkgs.pkgsi686Linux;
+
+  stub-ld = stub-ld-for pkgs message;
+  stub-ld32 = stub-ld-for pkgs32 message;
+in {
+  options = {
+    environment.stub-ld = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        example = false;
+        description = mdDoc ''
+          Install a stub ELF loader to print an informative error message
+          in the event that a user attempts to run an ELF binary not
+          compiled for NixOS.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.ldso = mkDefault stub-ld;
+    environment.ldso32 = mkIf pkgs.stdenv.isx86_64 (mkDefault stub-ld32);
+  };
+
+  meta.maintainers = with lib.maintainers; [ tejing ];
+}
diff --git a/nixpkgs/nixos/modules/config/swap.nix b/nixpkgs/nixos/modules/config/swap.nix
new file mode 100644
index 000000000000..21046d6f1697
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/swap.nix
@@ -0,0 +1,305 @@
+{ config, lib, pkgs, utils, ... }:
+
+with utils;
+with lib;
+
+let
+
+  randomEncryptionCoerce = enable: { inherit enable; };
+
+  randomEncryptionOpts = { ... }: {
+
+    options = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = lib.mdDoc ''
+          Encrypt swap device with a random key. This way you won't have a persistent swap device.
+
+          WARNING: Don't try to hibernate when you have at least one swap partition with
+          this option enabled! We have no way to set the partition into which hibernation image
+          is saved, so if your image ends up on an encrypted one you would lose it!
+
+          WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
+          when using randomEncryption as the UUIDs and labels will get erased on every boot when
+          the partition is encrypted. Best to use /dev/disk/by-partuuid/…
+        '';
+      };
+
+      cipher = mkOption {
+        default = "aes-xts-plain64";
+        example = "serpent-xts-plain64";
+        type = types.str;
+        description = lib.mdDoc ''
+          Use specified cipher for randomEncryption.
+
+          Hint: Run "cryptsetup benchmark" to see which one is fastest on your machine.
+        '';
+      };
+
+      keySize = mkOption {
+        default = null;
+        example = "512";
+        type = types.nullOr types.int;
+        description = lib.mdDoc ''
+          Set the encryption key size for the plain device.
+
+          If not specified, the amount of data to read from `source` will be
+          determined by cryptsetup.
+
+          See `cryptsetup-open(8)` for details.
+        '';
+      };
+
+      sectorSize = mkOption {
+        default = null;
+        example = "4096";
+        type = types.nullOr types.int;
+        description = lib.mdDoc ''
+          Set the sector size for the plain encrypted device type.
+
+          If not specified, the default sector size is determined from the
+          underlying block device.
+
+          See `cryptsetup-open(8)` for details.
+        '';
+      };
+
+      source = mkOption {
+        default = "/dev/urandom";
+        example = "/dev/random";
+        type = types.str;
+        description = lib.mdDoc ''
+          Define the source of randomness to obtain a random key for encryption.
+        '';
+      };
+
+      allowDiscards = mkOption {
+        default = false;
+        type = types.bool;
+        description = lib.mdDoc ''
+          Whether to allow TRIM requests to the underlying device. This option
+          has security implications; please read the LUKS documentation before
+          activating it.
+        '';
+      };
+    };
+
+  };
+
+  swapCfg = {config, options, ...}: {
+
+    options = {
+
+      device = mkOption {
+        example = "/dev/sda3";
+        type = types.nonEmptyStr;
+        description = lib.mdDoc "Path of the device or swap file.";
+      };
+
+      label = mkOption {
+        example = "swap";
+        type = types.str;
+        description = lib.mdDoc ''
+          Label of the device.  Can be used instead of {var}`device`.
+        '';
+      };
+
+      size = mkOption {
+        default = null;
+        example = 2048;
+        type = types.nullOr types.int;
+        description = lib.mdDoc ''
+          If this option is set, ‘device’ is interpreted as the
+          path of a swapfile that will be created automatically
+          with the indicated size (in megabytes).
+        '';
+      };
+
+      priority = mkOption {
+        default = null;
+        example = 2048;
+        type = types.nullOr types.int;
+        description = lib.mdDoc ''
+          Specify the priority of the swap device. Priority is a value between 0 and 32767.
+          Higher numbers indicate higher priority.
+          null lets the kernel choose a priority, which will show up as a negative value.
+        '';
+      };
+
+      randomEncryption = mkOption {
+        default = false;
+        example = {
+          enable = true;
+          cipher = "serpent-xts-plain64";
+          source = "/dev/random";
+        };
+        type = types.coercedTo types.bool randomEncryptionCoerce (types.submodule randomEncryptionOpts);
+        description = lib.mdDoc ''
+          Encrypt swap device with a random key. This way you won't have a persistent swap device.
+
+          HINT: run "cryptsetup benchmark" to test cipher performance on your machine.
+
+          WARNING: Don't try to hibernate when you have at least one swap partition with
+          this option enabled! We have no way to set the partition into which hibernation image
+          is saved, so if your image ends up on an encrypted one you would lose it!
+
+          WARNING #2: Do not use /dev/disk/by-uuid/… or /dev/disk/by-label/… as your swap device
+          when using randomEncryption as the UUIDs and labels will get erased on every boot when
+          the partition is encrypted. Best to use /dev/disk/by-partuuid/…
+        '';
+      };
+
+      discardPolicy = mkOption {
+        default = null;
+        example = "once";
+        type = types.nullOr (types.enum ["once" "pages" "both" ]);
+        description = lib.mdDoc ''
+          Specify the discard policy for the swap device. If "once", then the
+          whole swap space is discarded at swapon invocation. If "pages",
+          asynchronous discard on freed pages is performed, before returning to
+          the available pages pool. With "both", both policies are activated.
+          See swapon(8) for more information.
+        '';
+      };
+
+      options = mkOption {
+        default = [ "defaults" ];
+        example = [ "nofail" ];
+        type = types.listOf types.nonEmptyStr;
+        description = lib.mdDoc ''
+          Options used to mount the swap.
+        '';
+      };
+
+      deviceName = mkOption {
+        type = types.str;
+        internal = true;
+      };
+
+      realDevice = mkOption {
+        type = types.path;
+        internal = true;
+      };
+
+    };
+
+    config = {
+      device = mkIf options.label.isDefined
+        "/dev/disk/by-label/${config.label}";
+      deviceName = lib.replaceStrings ["\\"] [""] (escapeSystemdPath config.device);
+      realDevice = if config.randomEncryption.enable then "/dev/mapper/${config.deviceName}" else config.device;
+    };
+
+  };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    swapDevices = mkOption {
+      default = [];
+      example = [
+        { device = "/dev/hda7"; }
+        { device = "/var/swapfile"; }
+        { label = "bigswap"; }
+      ];
+      description = lib.mdDoc ''
+        The swap devices and swap files.  These must have been
+        initialised using {command}`mkswap`.  Each element
+        should be an attribute set specifying either the path of the
+        swap device or file (`device`) or the label
+        of the swap device (`label`, see
+        {command}`mkswap -L`).  Using a label is
+        recommended.
+      '';
+
+      type = types.listOf (types.submodule swapCfg);
+    };
+
+  };
+
+  config = mkIf ((length config.swapDevices) != 0) {
+    assertions = map (sw: {
+      assertion = sw.randomEncryption.enable -> builtins.match "/dev/disk/by-(uuid|label)/.*" sw.device == null;
+      message = ''
+        You cannot use swap device "${sw.device}" with randomEncryption enabled.
+        The UUIDs and labels will get erased on every boot when the partition is encrypted.
+        Use /dev/disk/by-partuuid/… instead.
+      '';
+    }) config.swapDevices;
+
+    warnings =
+      concatMap (sw:
+        if sw.size != null && hasPrefix "/dev/" sw.device
+        then [ "Setting the swap size of block device ${sw.device} has no effect" ]
+        else [ ])
+      config.swapDevices;
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "SWAP")
+    ];
+
+    # Create missing swapfiles.
+    systemd.services =
+      let
+        createSwapDevice = sw:
+          let realDevice' = escapeSystemdPath sw.realDevice;
+          in nameValuePair "mkswap-${sw.deviceName}"
+          { description = "Initialisation of swap device ${sw.device}";
+            # The mkswap service fails for file-backed swap devices if the
+            # loop module has not been loaded before the service runs.
+            # We add an ordering constraint to run after systemd-modules-load to
+            # avoid this race condition.
+            after = [ "systemd-modules-load.service" ];
+            wantedBy = [ "${realDevice'}.swap" ];
+            before = [ "${realDevice'}.swap" "shutdown.target"];
+            conflicts = [ "shutdown.target" ];
+            path = [ pkgs.util-linux pkgs.e2fsprogs ]
+              ++ optional sw.randomEncryption.enable pkgs.cryptsetup;
+
+            environment.DEVICE = sw.device;
+
+            script =
+              ''
+                ${optionalString (sw.size != null) ''
+                  currentSize=$(( $(stat -c "%s" "$DEVICE" 2>/dev/null || echo 0) / 1024 / 1024 ))
+                  if [[ ! -b "$DEVICE" && "${toString sw.size}" != "$currentSize" ]]; then
+                    # Disable CoW for CoW based filesystems like BTRFS.
+                    truncate --size 0 "$DEVICE"
+                    chattr +C "$DEVICE" 2>/dev/null || true
+
+                    dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size}
+                    chmod 0600 ${sw.device}
+                    ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
+                  fi
+                ''}
+                ${optionalString sw.randomEncryption.enable ''
+                  cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
+                  ${concatStringsSep " \\\n" (flatten [
+                    (optional (sw.randomEncryption.sectorSize != null) "--sector-size=${toString sw.randomEncryption.sectorSize}")
+                    (optional (sw.randomEncryption.keySize != null) "--key-size=${toString sw.randomEncryption.keySize}")
+                    (optional sw.randomEncryption.allowDiscards "--allow-discards")
+                  ])} ${sw.device} ${sw.deviceName}
+                  mkswap ${sw.realDevice}
+                ''}
+              '';
+
+            unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
+            unitConfig.DefaultDependencies = false; # needed to prevent a cycle
+            serviceConfig.Type = "oneshot";
+            serviceConfig.RemainAfterExit = sw.randomEncryption.enable;
+            serviceConfig.ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
+            restartIfChanged = false;
+          };
+
+      in listToAttrs (map createSwapDevice (filter (sw: sw.size != null || sw.randomEncryption.enable) config.swapDevices));
+
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/sysctl.nix b/nixpkgs/nixos/modules/config/sysctl.nix
new file mode 100644
index 000000000000..bedba984a3c2
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/sysctl.nix
@@ -0,0 +1,86 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+
+  sysctlOption = mkOptionType {
+    name = "sysctl option value";
+    check = val:
+      let
+        checkType = x: isBool x || isString x || isInt x || x == null;
+      in
+        checkType val || (val._type or "" == "override" && checkType val.content);
+    merge = loc: defs: mergeOneOption loc (filterOverrides defs);
+  };
+
+in
+
+{
+
+  options = {
+
+    boot.kernel.sysctl = mkOption {
+      type = let
+        highestValueType = types.ints.unsigned // {
+          merge = loc: defs:
+            foldl
+              (a: b: if b.value == null then null else lib.max a b.value)
+              0
+              (filterOverrides defs);
+        };
+      in types.submodule {
+        freeformType = types.attrsOf sysctlOption;
+        options = {
+          "net.core.rmem_max" = mkOption {
+            type = types.nullOr highestValueType;
+            default = null;
+            description = lib.mdDoc "The maximum receive socket buffer size in bytes. In case of conflicting values, the highest will be used.";
+          };
+
+          "net.core.wmem_max" = mkOption {
+            type = types.nullOr highestValueType;
+            default = null;
+            description = lib.mdDoc "The maximum send socket buffer size in bytes. In case of conflicting values, the highest will be used.";
+          };
+        };
+      };
+      default = {};
+      example = literalExpression ''
+        { "net.ipv4.tcp_syncookies" = false; "vm.swappiness" = 60; }
+      '';
+      description = lib.mdDoc ''
+        Runtime parameters of the Linux kernel, as set by
+        {manpage}`sysctl(8)`.  Note that sysctl
+        parameters names must be enclosed in quotes
+        (e.g. `"vm.swappiness"` instead of
+        `vm.swappiness`).  The value of each
+        parameter may be a string, integer, boolean, or null
+        (signifying the option will not appear at all).
+      '';
+
+    };
+
+  };
+
+  config = {
+
+    environment.etc."sysctl.d/60-nixos.conf".text =
+      concatStrings (mapAttrsToList (n: v:
+        optionalString (v != null) "${n}=${if v == false then "0" else toString v}\n"
+      ) config.boot.kernel.sysctl);
+
+    systemd.services.systemd-sysctl =
+      { wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ config.environment.etc."sysctl.d/60-nixos.conf".source ];
+      };
+
+    # Hide kernel pointers (e.g. in /proc/modules) for unprivileged
+    # users as these make it easier to exploit kernel vulnerabilities.
+    boot.kernel.sysctl."kernel.kptr_restrict" = mkDefault 1;
+
+    # Improve compatibility with applications that allocate
+    # a lot of memory, like modern games
+    boot.kernel.sysctl."vm.max_map_count" = mkDefault 1048576;
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/system-environment.nix b/nixpkgs/nixos/modules/config/system-environment.nix
new file mode 100644
index 000000000000..399304185223
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/system-environment.nix
@@ -0,0 +1,100 @@
+# This module defines a system-wide environment that will be
+# initialised by pam_env (that is, not only in shells).
+{ config, lib, options, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.environment;
+
+in
+
+{
+
+  options = {
+
+    environment.sessionVariables = mkOption {
+      default = {};
+      description = lib.mdDoc ''
+        A set of environment variables used in the global environment.
+        These variables will be set by PAM early in the login process.
+
+        The value of each session variable can be either a string or a
+        list of strings. The latter is concatenated, interspersed with
+        colon characters.
+
+        Note, due to limitations in the PAM format values may not
+        contain the `"` character.
+
+        Also, these variables are merged into
+        [](#opt-environment.variables) and it is
+        therefore not possible to use PAM style variables such as
+        `@{HOME}`.
+      '';
+      inherit (options.environment.variables) type apply;
+    };
+
+    environment.profileRelativeSessionVariables = mkOption {
+      type = types.attrsOf (types.listOf types.str);
+      example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
+      description = lib.mdDoc ''
+        Attribute set of environment variable used in the global
+        environment. These variables will be set by PAM early in the
+        login process.
+
+        Variable substitution is available as described in
+        {manpage}`pam_env.conf(5)`.
+
+        Each attribute maps to a list of relative paths. Each relative
+        path is appended to the each profile of
+        {option}`environment.profiles` to form the content of
+        the corresponding environment variable.
+
+        Also, these variables are merged into
+        [](#opt-environment.profileRelativeEnvVars) and it is
+        therefore not possible to use PAM style variables such as
+        `@{HOME}`.
+      '';
+    };
+
+  };
+
+  config = {
+    environment.etc."pam/environment".text = let
+      suffixedVariables =
+        flip mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
+          flip concatMap cfg.profiles (profile:
+            map (suffix: "${profile}${suffix}") suffixes
+          )
+        );
+
+      # We're trying to use the same syntax for PAM variables and env variables.
+      # That means we need to map the env variables that people might use to their
+      # equivalent PAM variable.
+      replaceEnvVars = replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
+
+      pamVariable = n: v:
+        ''${n}   DEFAULT="${concatStringsSep ":" (map replaceEnvVars (toList v))}"'';
+
+      pamVariables =
+        concatStringsSep "\n"
+        (mapAttrsToList pamVariable
+        (zipAttrsWith (n: concatLists)
+          [
+            # Make sure security wrappers are prioritized without polluting
+            # shell environments with an extra entry. Sessions which depend on
+            # pam for its environment will otherwise have eg. broken sudo. In
+            # particular Gnome Shell sometimes fails to source a proper
+            # environment from a shell.
+            { PATH = [ config.security.wrapperDir ]; }
+
+            (mapAttrs (n: toList) cfg.sessionVariables)
+            suffixedVariables
+          ]));
+    in ''
+      ${pamVariables}
+    '';
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/system-path.nix b/nixpkgs/nixos/modules/config/system-path.nix
new file mode 100644
index 000000000000..71274ea8999f
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/system-path.nix
@@ -0,0 +1,189 @@
+# This module defines the packages that appear in
+# /run/current-system/sw.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  requiredPackages = map (pkg: setPrio ((pkg.meta.priority or 5) + 3) pkg)
+    [ pkgs.acl
+      pkgs.attr
+      pkgs.bashInteractive # bash with ncurses support
+      pkgs.bzip2
+      pkgs.coreutils-full
+      pkgs.cpio
+      pkgs.curl
+      pkgs.diffutils
+      pkgs.findutils
+      pkgs.gawk
+      pkgs.stdenv.cc.libc
+      pkgs.getent
+      pkgs.getconf
+      pkgs.gnugrep
+      pkgs.gnupatch
+      pkgs.gnused
+      pkgs.gnutar
+      pkgs.gzip
+      pkgs.xz
+      pkgs.less
+      pkgs.libcap
+      pkgs.ncurses
+      pkgs.netcat
+      config.programs.ssh.package
+      pkgs.mkpasswd
+      pkgs.procps
+      pkgs.su
+      pkgs.time
+      pkgs.util-linux
+      pkgs.which
+      pkgs.zstd
+    ];
+
+  defaultPackageNames =
+    [ "perl"
+      "rsync"
+      "strace"
+    ];
+  defaultPackages =
+    map
+      (n: let pkg = pkgs.${n}; in setPrio ((pkg.meta.priority or 5) + 3) pkg)
+      defaultPackageNames;
+  defaultPackagesText = "[ ${concatMapStringsSep " " (n: "pkgs.${n}") defaultPackageNames } ]";
+
+in
+
+{
+  options = {
+
+    environment = {
+
+      systemPackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExpression "[ pkgs.firefox pkgs.thunderbird ]";
+        description = lib.mdDoc ''
+          The set of packages that appear in
+          /run/current-system/sw.  These packages are
+          automatically available to all users, and are
+          automatically updated every time you rebuild the system
+          configuration.  (The latter is the main difference with
+          installing them in the default profile,
+          {file}`/nix/var/nix/profiles/default`.
+        '';
+      };
+
+      defaultPackages = mkOption {
+        type = types.listOf types.package;
+        default = defaultPackages;
+        defaultText = literalMD ''
+          these packages, with their `meta.priority` numerically increased
+          (thus lowering their installation priority):
+
+              ${defaultPackagesText}
+        '';
+        example = [];
+        description = lib.mdDoc ''
+          Set of default packages that aren't strictly necessary
+          for a running system, entries can be removed for a more
+          minimal NixOS installation.
+
+          Like with systemPackages, packages are installed to
+          {file}`/run/current-system/sw`. They are
+          automatically available to all users, and are
+          automatically updated every time you rebuild the system
+          configuration.
+        '';
+      };
+
+      pathsToLink = mkOption {
+        type = types.listOf types.str;
+        # Note: We need `/lib' to be among `pathsToLink' for NSS modules
+        # to work.
+        default = [];
+        example = ["/"];
+        description = lib.mdDoc "List of directories to be symlinked in {file}`/run/current-system/sw`.";
+      };
+
+      extraOutputsToInstall = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "dev" "info" ];
+        description = lib.mdDoc ''
+          Entries listed here will be appended to the `meta.outputsToInstall` attribute for each package in `environment.systemPackages`, and the files from the corresponding derivation outputs symlinked into {file}`/run/current-system/sw`.
+
+          For example, this can be used to install the `dev` and `info` outputs for all packages in the system environment, if they are available.
+
+          To use specific outputs instead of configuring them globally, select the corresponding attribute on the package derivation, e.g. `libxml2.dev` or `coreutils.info`.
+        '';
+      };
+
+      extraSetup = mkOption {
+        type = types.lines;
+        default = "";
+        description = lib.mdDoc "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out.";
+      };
+
+    };
+
+    system = {
+
+      path = mkOption {
+        internal = true;
+        description = lib.mdDoc ''
+          The packages you want in the boot environment.
+        '';
+      };
+
+    };
+
+  };
+
+  config = {
+
+    environment.systemPackages = requiredPackages ++ config.environment.defaultPackages;
+
+    environment.pathsToLink =
+      [ "/bin"
+        "/etc/xdg"
+        "/etc/gtk-2.0"
+        "/etc/gtk-3.0"
+        "/lib" # FIXME: remove and update debug-info.nix
+        "/sbin"
+        "/share/emacs"
+        "/share/hunspell"
+        "/share/nano"
+        "/share/org"
+        "/share/themes"
+        "/share/vim-plugins"
+        "/share/vulkan"
+        "/share/kservices5"
+        "/share/kservicetypes5"
+        "/share/kxmlgui5"
+        "/share/systemd"
+        "/share/thumbnailers"
+      ];
+
+    system.path = pkgs.buildEnv {
+      name = "system-path";
+      paths = config.environment.systemPackages;
+      inherit (config.environment) pathsToLink extraOutputsToInstall;
+      ignoreCollisions = true;
+      # !!! Hacky, should modularise.
+      # outputs TODO: note that the tools will often not be linked by default
+      postBuild =
+        ''
+          # Remove wrapped binaries, they shouldn't be accessible via PATH.
+          find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete
+
+          if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
+              $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
+          fi
+
+          ${config.environment.extraSetup}
+        '';
+    };
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/terminfo.nix b/nixpkgs/nixos/modules/config/terminfo.nix
new file mode 100644
index 000000000000..ebd1aaea8f04
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/terminfo.nix
@@ -0,0 +1,76 @@
+# This module manages the terminfo database
+# and its integration in the system.
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  options = with lib; {
+    environment.enableAllTerminfo = mkOption {
+      default = false;
+      type = types.bool;
+      description = lib.mdDoc ''
+        Whether to install all terminfo outputs
+      '';
+    };
+
+    security.sudo.keepTerminfo = mkOption {
+      default = true;
+      type = types.bool;
+      description = lib.mdDoc ''
+        Whether to preserve the `TERMINFO` and `TERMINFO_DIRS`
+        environment variables, for `root` and the `wheel` group.
+      '';
+    };
+  };
+
+  config = {
+
+    # can be generated with:
+    # attrNames (filterAttrs
+    #  (_: drv: (builtins.tryEval (isDerivation drv && drv ? terminfo)).value)
+    #  pkgs)
+    environment.systemPackages = mkIf config.environment.enableAllTerminfo (map (x: x.terminfo) (with pkgs; [
+      alacritty
+      contour
+      foot
+      kitty
+      mtm
+      rio
+      rxvt-unicode-unwrapped
+      rxvt-unicode-unwrapped-emoji
+      st
+      termite
+      tmux
+      wezterm
+      yaft
+    ]));
+
+    environment.pathsToLink = [
+      "/share/terminfo"
+    ];
+
+    environment.etc.terminfo = {
+      source = "${config.system.path}/share/terminfo";
+    };
+
+    environment.profileRelativeSessionVariables = {
+      TERMINFO_DIRS = [ "/share/terminfo" ];
+    };
+
+    environment.extraInit = ''
+
+      # reset TERM with new TERMINFO available (if any)
+      export TERM=$TERM
+    '';
+
+    security.sudo.extraConfig = mkIf config.security.sudo.keepTerminfo ''
+
+      # Keep terminfo database for root and %wheel.
+      Defaults:root,%wheel env_keep+=TERMINFO_DIRS
+      Defaults:root,%wheel env_keep+=TERMINFO
+    '';
+
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/unix-odbc-drivers.nix b/nixpkgs/nixos/modules/config/unix-odbc-drivers.nix
new file mode 100644
index 000000000000..7bd3fa1600b0
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/unix-odbc-drivers.nix
@@ -0,0 +1,38 @@
+{ config, lib, ... }:
+
+with lib;
+
+# unixODBC drivers (this solution is not perfect.. Because the user has to
+# ask the admin to add a driver.. but it's simple and works
+
+let
+  iniDescription = pkg: ''
+    [${pkg.fancyName}]
+    Description = ${pkg.meta.description}
+    Driver = ${pkg}/${pkg.driver}
+  '';
+
+in {
+  ###### interface
+
+  options = {
+    environment.unixODBCDrivers = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression "with pkgs.unixODBCDrivers; [ sqlite psql ]";
+      description = lib.mdDoc ''
+        Specifies Unix ODBC drivers to be registered in
+        {file}`/etc/odbcinst.ini`.  You may also want to
+        add `pkgs.unixODBC` to the system path to get
+        a command line client to connect to ODBC databases.
+      '';
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf (config.environment.unixODBCDrivers != []) {
+    environment.etc."odbcinst.ini".text = concatMapStringsSep "\n" iniDescription config.environment.unixODBCDrivers;
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/update-users-groups.pl b/nixpkgs/nixos/modules/config/update-users-groups.pl
new file mode 100644
index 000000000000..7aee58e697de
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/update-users-groups.pl
@@ -0,0 +1,381 @@
+use strict;
+use warnings;
+use File::Basename;
+use File::Path qw(make_path);
+use File::Slurp;
+use Getopt::Long;
+use JSON;
+use Time::Piece;
+
+# Keep track of deleted uids and gids.
+my $uidMapFile = "/var/lib/nixos/uid-map";
+my $uidMap = -e $uidMapFile ? decode_json(read_file($uidMapFile)) : {};
+
+my $gidMapFile = "/var/lib/nixos/gid-map";
+my $gidMap = -e $gidMapFile ? decode_json(read_file($gidMapFile)) : {};
+
+my $is_dry = ($ENV{'NIXOS_ACTION'} // "") eq "dry-activate";
+GetOptions("dry-activate" => \$is_dry);
+make_path("/var/lib/nixos", { mode => 0755 }) unless $is_dry;
+
+sub updateFile {
+    my ($path, $contents, $perms) = @_;
+    return if $is_dry;
+    write_file($path, { atomic => 1, binmode => ':utf8', perms => $perms // 0644 }, $contents) or die;
+}
+
+# Converts an ISO date to number of days since 1970-01-01
+sub dateToDays {
+    my ($date) = @_;
+    my $time = Time::Piece->strptime($date, "%Y-%m-%d");
+    return $time->epoch / 60 / 60 / 24;
+}
+
+sub nscdInvalidate {
+    system("nscd", "--invalidate", $_[0]) unless $is_dry;
+}
+
+sub hashPassword {
+    my ($password) = @_;
+    my $salt = "";
+    my @chars = ('.', '/', 0..9, 'A'..'Z', 'a'..'z');
+    $salt .= $chars[rand 64] for (1..8);
+    return crypt($password, '$6$' . $salt . '$');
+}
+
+sub dry_print {
+    if ($is_dry) {
+        print STDERR ("$_[1] $_[2]\n")
+    } else {
+        print STDERR ("$_[0] $_[2]\n")
+    }
+}
+
+
+# Functions for allocating free GIDs/UIDs. FIXME: respect ID ranges in
+# /etc/login.defs.
+sub allocId {
+    my ($used, $prevUsed, $idMin, $idMax, $up, $getid) = @_;
+    my $id = $up ? $idMin : $idMax;
+    while ($id >= $idMin && $id <= $idMax) {
+        if (!$used->{$id} && !$prevUsed->{$id} && !defined &$getid($id)) {
+            $used->{$id} = 1;
+            return $id;
+        }
+        $used->{$id} = 1;
+        if ($up) { $id++; } else { $id--; }
+    }
+    die "$0: out of free UIDs or GIDs\n";
+}
+
+my (%gidsUsed, %uidsUsed, %gidsPrevUsed, %uidsPrevUsed);
+
+sub allocGid {
+    my ($name) = @_;
+    my $prevGid = $gidMap->{$name};
+    if (defined $prevGid && !defined $gidsUsed{$prevGid}) {
+        dry_print("reviving", "would revive", "group '$name' with GID $prevGid");
+        $gidsUsed{$prevGid} = 1;
+        return $prevGid;
+    }
+    return allocId(\%gidsUsed, \%gidsPrevUsed, 400, 999, 0, sub { my ($gid) = @_; getgrgid($gid) });
+}
+
+sub allocUid {
+    my ($name, $isSystemUser) = @_;
+    my ($min, $max, $up) = $isSystemUser ? (400, 999, 0) : (1000, 29999, 1);
+    my $prevUid = $uidMap->{$name};
+    if (defined $prevUid && $prevUid >= $min && $prevUid <= $max && !defined $uidsUsed{$prevUid}) {
+        dry_print("reviving", "would revive", "user '$name' with UID $prevUid");
+        $uidsUsed{$prevUid} = 1;
+        return $prevUid;
+    }
+    return allocId(\%uidsUsed, \%uidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
+}
+
+# Read the declared users/groups
+my $spec = decode_json(read_file($ARGV[0]));
+
+# Don't allocate UIDs/GIDs that are manually assigned.
+foreach my $g (@{$spec->{groups}}) {
+    $gidsUsed{$g->{gid}} = 1 if defined $g->{gid};
+}
+
+foreach my $u (@{$spec->{users}}) {
+    $uidsUsed{$u->{uid}} = 1 if defined $u->{uid};
+}
+
+# Likewise for previously used but deleted UIDs/GIDs.
+$uidsPrevUsed{$_} = 1 foreach values %{$uidMap};
+$gidsPrevUsed{$_} = 1 foreach values %{$gidMap};
+
+
+# Read the current /etc/group.
+sub parseGroup {
+    chomp;
+    my @f = split(':', $_, -4);
+    my $gid = $f[2] eq "" ? undef : int($f[2]);
+    $gidsUsed{$gid} = 1 if defined $gid;
+    return ($f[0], { name => $f[0], password => $f[1], gid => $gid, members => $f[3] });
+}
+
+my %groupsCur = -f "/etc/group" ? map { parseGroup } read_file("/etc/group", { binmode => ":utf8" }) : ();
+
+# Read the current /etc/passwd.
+sub parseUser {
+    chomp;
+    my @f = split(':', $_, -7);
+    my $uid = $f[2] eq "" ? undef : int($f[2]);
+    $uidsUsed{$uid} = 1 if defined $uid;
+    return ($f[0], { name => $f[0], fakePassword => $f[1], uid => $uid,
+        gid => $f[3], description => $f[4], home => $f[5], shell => $f[6] });
+}
+my %usersCur = -f "/etc/passwd" ? map { parseUser } read_file("/etc/passwd", { binmode => ":utf8" }) : ();
+
+# Read the groups that were created declaratively (i.e. not by groups)
+# in the past. These must be removed if they are no longer in the
+# current spec.
+my $declGroupsFile = "/var/lib/nixos/declarative-groups";
+my %declGroups;
+$declGroups{$_} = 1 foreach split / /, -e $declGroupsFile ? read_file($declGroupsFile, { binmode => ":utf8" }) : "";
+
+# Idem for the users.
+my $declUsersFile = "/var/lib/nixos/declarative-users";
+my %declUsers;
+$declUsers{$_} = 1 foreach split / /, -e $declUsersFile ? read_file($declUsersFile, { binmode => ":utf8" }) : "";
+
+
+# Generate a new /etc/group containing the declared groups.
+my %groupsOut;
+foreach my $g (@{$spec->{groups}}) {
+    my $name = $g->{name};
+    my $existing = $groupsCur{$name};
+
+    my %members = map { ($_, 1) } @{$g->{members}};
+
+    if (defined $existing) {
+        $g->{gid} = $existing->{gid} if !defined $g->{gid};
+        if ($g->{gid} != $existing->{gid}) {
+            dry_print("warning: not applying", "warning: would not apply", "GID change of group ‘$name’ ($existing->{gid} -> $g->{gid}) in /etc/group");
+            $g->{gid} = $existing->{gid};
+        }
+        $g->{password} = $existing->{password}; # do we want this?
+        if ($spec->{mutableUsers}) {
+            # Merge in non-declarative group members.
+            foreach my $uname (split /,/, $existing->{members} // "") {
+                $members{$uname} = 1 if !defined $declUsers{$uname};
+            }
+        }
+    } else {
+        $g->{gid} = allocGid($name) if !defined $g->{gid};
+        $g->{password} = "x";
+    }
+
+    $g->{members} = join ",", sort(keys(%members));
+    $groupsOut{$name} = $g;
+
+    $gidMap->{$name} = $g->{gid};
+}
+
+# Update the persistent list of declarative groups.
+updateFile($declGroupsFile, join(" ", sort(keys %groupsOut)));
+
+# Merge in the existing /etc/group.
+foreach my $name (keys %groupsCur) {
+    my $g = $groupsCur{$name};
+    next if defined $groupsOut{$name};
+    if (!$spec->{mutableUsers} || defined $declGroups{$name}) {
+        dry_print("removing group", "would remove group", "‘$name’");
+    } else {
+        $groupsOut{$name} = $g;
+    }
+}
+
+
+# Rewrite /etc/group. FIXME: acquire lock.
+my @lines = map { join(":", $_->{name}, $_->{password}, $_->{gid}, $_->{members}) . "\n" }
+    (sort { $a->{gid} <=> $b->{gid} } values(%groupsOut));
+updateFile($gidMapFile, to_json($gidMap, {canonical => 1}));
+updateFile("/etc/group", \@lines);
+nscdInvalidate("group");
+
+# Generate a new /etc/passwd containing the declared users.
+my %usersOut;
+foreach my $u (@{$spec->{users}}) {
+    my $name = $u->{name};
+
+    # Resolve the gid of the user.
+    if ($u->{group} =~ /^[0-9]$/) {
+        $u->{gid} = $u->{group};
+    } elsif (defined $groupsOut{$u->{group}}) {
+        $u->{gid} = $groupsOut{$u->{group}}->{gid} // die;
+    } else {
+        warn "warning: user ‘$name’ has unknown group ‘$u->{group}’\n";
+        $u->{gid} = 65534;
+    }
+
+    my $existing = $usersCur{$name};
+    if (defined $existing) {
+        $u->{uid} = $existing->{uid} if !defined $u->{uid};
+        if ($u->{uid} != $existing->{uid}) {
+            dry_print("warning: not applying", "warning: would not apply", "UID change of user ‘$name’ ($existing->{uid} -> $u->{uid}) in /etc/passwd");
+            $u->{uid} = $existing->{uid};
+        }
+    } else {
+        $u->{uid} = allocUid($name, $u->{isSystemUser}) if !defined $u->{uid};
+
+        if (!defined $u->{hashedPassword}) {
+            if (defined $u->{initialPassword}) {
+                $u->{hashedPassword} = hashPassword($u->{initialPassword});
+            } elsif (defined $u->{initialHashedPassword}) {
+                $u->{hashedPassword} = $u->{initialHashedPassword};
+            }
+        }
+    }
+
+    # Ensure home directory incl. ownership and permissions.
+    if ($u->{createHome} and !$is_dry) {
+        make_path(dirname($u->{home}), { mode => 0755 });
+        mkdir $u->{home}, oct($u->{homeMode}) if ! -e $u->{home};
+        chown $u->{uid}, $u->{gid}, $u->{home};
+        chmod oct($u->{homeMode}), $u->{home};
+    }
+
+    if (defined $u->{hashedPasswordFile}) {
+        if (-e $u->{hashedPasswordFile}) {
+            $u->{hashedPassword} = read_file($u->{hashedPasswordFile});
+            chomp $u->{hashedPassword};
+        } else {
+            warn "warning: password file ‘$u->{hashedPasswordFile}’ does not exist\n";
+        }
+    } elsif (defined $u->{password}) {
+        $u->{hashedPassword} = hashPassword($u->{password});
+    }
+
+    if (!defined $u->{shell}) {
+        if (defined $existing) {
+            $u->{shell} = $existing->{shell};
+        } else {
+            warn "warning: no declarative or previous shell for ‘$name’, setting shell to nologin\n";
+            $u->{shell} = "/run/current-system/sw/bin/nologin";
+        }
+    }
+
+    $u->{fakePassword} = $existing->{fakePassword} // "x";
+    $usersOut{$name} = $u;
+
+    $uidMap->{$name} = $u->{uid};
+}
+
+# Update the persistent list of declarative users.
+updateFile($declUsersFile, join(" ", sort(keys %usersOut)));
+
+# Merge in the existing /etc/passwd.
+foreach my $name (keys %usersCur) {
+    my $u = $usersCur{$name};
+    next if defined $usersOut{$name};
+    if (!$spec->{mutableUsers} || defined $declUsers{$name}) {
+        dry_print("removing user", "would remove user", "‘$name’");
+    } else {
+        $usersOut{$name} = $u;
+    }
+}
+
+# Rewrite /etc/passwd. FIXME: acquire lock.
+@lines = map { join(":", $_->{name}, $_->{fakePassword}, $_->{uid}, $_->{gid}, $_->{description}, $_->{home}, $_->{shell}) . "\n" }
+    (sort { $a->{uid} <=> $b->{uid} } (values %usersOut));
+updateFile($uidMapFile, to_json($uidMap, {canonical => 1}));
+updateFile("/etc/passwd", \@lines);
+nscdInvalidate("passwd");
+
+
+# Rewrite /etc/shadow to add new accounts or remove dead ones.
+my @shadowNew;
+my %shadowSeen;
+
+foreach my $line (-f "/etc/shadow" ? read_file("/etc/shadow", { binmode => ":utf8" }) : ()) {
+    chomp $line;
+    # struct name copied from `man 3 shadow`
+    my ($sp_namp, $sp_pwdp, $sp_lstch, $sp_min, $sp_max, $sp_warn, $sp_inact, $sp_expire, $sp_flag) = split(':', $line, -9);
+    my $u = $usersOut{$sp_namp};;
+    next if !defined $u;
+    $sp_pwdp = "!" if !$spec->{mutableUsers};
+    $sp_pwdp = $u->{hashedPassword} if defined $u->{hashedPassword} && !$spec->{mutableUsers}; # FIXME
+    $sp_expire = dateToDays($u->{expires}) if defined $u->{expires};
+    chomp $sp_pwdp;
+    push @shadowNew, join(":", $sp_namp, $sp_pwdp, $sp_lstch, $sp_min, $sp_max, $sp_warn, $sp_inact, $sp_expire, $sp_flag) . "\n";
+    $shadowSeen{$sp_namp} = 1;
+}
+
+foreach my $u (values %usersOut) {
+    next if defined $shadowSeen{$u->{name}};
+    my $hashedPassword = "!";
+    $hashedPassword = $u->{hashedPassword} if defined $u->{hashedPassword};
+    my $expires = "";
+    $expires = dateToDays($u->{expires}) if defined $u->{expires};
+    # FIXME: set correct value for sp_lstchg.
+    push @shadowNew, join(":", $u->{name}, $hashedPassword, "1::::", $expires, "") . "\n";
+}
+
+updateFile("/etc/shadow", \@shadowNew, 0640);
+{
+    my $uid = getpwnam "root";
+    my $gid = getgrnam "shadow";
+    my $path = "/etc/shadow";
+    (chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!") unless $is_dry;
+}
+
+# Rewrite /etc/subuid & /etc/subgid to include default container mappings
+
+my $subUidMapFile = "/var/lib/nixos/auto-subuid-map";
+my $subUidMap = -e $subUidMapFile ? decode_json(read_file($subUidMapFile)) : {};
+
+my (%subUidsUsed, %subUidsPrevUsed);
+
+$subUidsPrevUsed{$_} = 1 foreach values %{$subUidMap};
+
+sub allocSubUid {
+    my ($name, @rest) = @_;
+
+    # TODO: No upper bounds?
+    my ($min, $max, $up) = (100000, 100000 * 100, 1);
+    my $prevId = $subUidMap->{$name};
+    if (defined $prevId && !defined $subUidsUsed{$prevId}) {
+        $subUidsUsed{$prevId} = 1;
+        return $prevId;
+    }
+
+    my $id = allocId(\%subUidsUsed, \%subUidsPrevUsed, $min, $max, $up, sub { my ($uid) = @_; getpwuid($uid) });
+    my $offset = $id - 100000;
+    my $count = $offset * 65536;
+    my $subordinate = 100000 + $count;
+    return $subordinate;
+}
+
+my @subGids;
+my @subUids;
+foreach my $u (values %usersOut) {
+    my $name = $u->{name};
+
+    foreach my $range (@{$u->{subUidRanges}}) {
+        my $value = join(":", ($name, $range->{startUid}, $range->{count}));
+        push @subUids, $value;
+    }
+
+    foreach my $range (@{$u->{subGidRanges}}) {
+        my $value = join(":", ($name, $range->{startGid}, $range->{count}));
+        push @subGids, $value;
+    }
+
+    if($u->{autoSubUidGidRange}) {
+        my $subordinate = allocSubUid($name);
+        $subUidMap->{$name} = $subordinate;
+        my $value = join(":", ($name, $subordinate, 65536));
+        push @subUids, $value;
+        push @subGids, $value;
+    }
+}
+
+updateFile("/etc/subuid", join("\n", @subUids) . "\n");
+updateFile("/etc/subgid", join("\n", @subGids) . "\n");
+updateFile($subUidMapFile, encode_json($subUidMap) . "\n");
diff --git a/nixpkgs/nixos/modules/config/users-groups.nix b/nixpkgs/nixos/modules/config/users-groups.nix
new file mode 100644
index 000000000000..2aed620eb154
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/users-groups.nix
@@ -0,0 +1,935 @@
+{ config, lib, utils, pkgs, ... }:
+
+with lib;
+
+let
+  ids = config.ids;
+  cfg = config.users;
+
+  # Check whether a password hash will allow login.
+  allowsLogin = hash:
+    hash == "" # login without password
+    || !(lib.elem hash
+      [ null   # password login disabled
+        "!"    # password login disabled
+        "!!"   # a variant of "!"
+        "*"    # password unset
+      ]);
+
+  passwordDescription = ''
+    The options {option}`hashedPassword`,
+    {option}`password` and {option}`hashedPasswordFile`
+    controls what password is set for the user.
+    {option}`hashedPassword` overrides both
+    {option}`password` and {option}`hashedPasswordFile`.
+    {option}`password` overrides {option}`hashedPasswordFile`.
+    If none of these three options are set, no password is assigned to
+    the user, and the user will not be able to do password logins.
+    If the option {option}`users.mutableUsers` is true, the
+    password defined in one of the three options will only be set when
+    the user is created for the first time. After that, you are free to
+    change the password with the ordinary user management commands. If
+    {option}`users.mutableUsers` is false, you cannot change
+    user passwords, they will always be set according to the password
+    options.
+  '';
+
+  hashedPasswordDescription = ''
+    To generate a hashed password run `mkpasswd`.
+
+    If set to an empty string (`""`), this user will
+    be able to log in without being asked for a password (but not via remote
+    services such as SSH, or indirectly via {command}`su` or
+    {command}`sudo`). This should only be used for e.g. bootable
+    live systems. Note: this is different from setting an empty password,
+    which can be achieved using {option}`users.users.<name?>.password`.
+
+    If set to `null` (default) this user will not
+    be able to log in using a password (i.e. via {command}`login`
+    command).
+  '';
+
+  userOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = types.passwdEntry types.str;
+        apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
+        description = lib.mdDoc ''
+          The name of the user account. If undefined, the name of the
+          attribute set will be used.
+        '';
+      };
+
+      description = mkOption {
+        type = types.passwdEntry types.str;
+        default = "";
+        example = "Alice Q. User";
+        description = lib.mdDoc ''
+          A short description of the user account, typically the
+          user's full name.  This is actually the “GECOS” or “comment”
+          field in {file}`/etc/passwd`.
+        '';
+      };
+
+      uid = mkOption {
+        type = with types; nullOr int;
+        default = null;
+        description = lib.mdDoc ''
+          The account UID. If the UID is null, a free UID is picked on
+          activation.
+        '';
+      };
+
+      isSystemUser = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Indicates if the user is a system user or not. This option
+          only has an effect if {option}`uid` is
+          {option}`null`, in which case it determines whether
+          the user's UID is allocated in the range for system users
+          (below 1000) or in the range for normal users (starting at
+          1000).
+          Exactly one of `isNormalUser` and
+          `isSystemUser` must be true.
+        '';
+      };
+
+      isNormalUser = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Indicates whether this is an account for a “real” user.
+          This automatically sets {option}`group` to `users`,
+          {option}`createHome` to `true`,
+          {option}`home` to {file}`/home/«username»`,
+          {option}`useDefaultShell` to `true`,
+          and {option}`isSystemUser` to `false`.
+          Exactly one of `isNormalUser` and `isSystemUser` must be true.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        apply = x: assert (builtins.stringLength x < 32 || abort "Group name '${x}' is longer than 31 characters which is not allowed!"); x;
+        default = "";
+        description = lib.mdDoc "The user's primary group.";
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = lib.mdDoc "The user's auxiliary groups.";
+      };
+
+      home = mkOption {
+        type = types.passwdEntry types.path;
+        default = "/var/empty";
+        description = lib.mdDoc "The user's home directory.";
+      };
+
+      homeMode = mkOption {
+        type = types.strMatching "[0-7]{1,5}";
+        default = "700";
+        description = lib.mdDoc "The user's home directory mode in numeric format. See chmod(1). The mode is only applied if {option}`users.users.<name>.createHome` is true.";
+      };
+
+      cryptHomeLuks = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = lib.mdDoc ''
+          Path to encrypted luks device that contains
+          the user's home directory.
+        '';
+      };
+
+      pamMount = mkOption {
+        type = with types; attrsOf str;
+        default = {};
+        description = lib.mdDoc ''
+          Attributes for user's entry in
+          {file}`pam_mount.conf.xml`.
+          Useful attributes might include `path`,
+          `options`, `fstype`, and `server`.
+          See <https://pam-mount.sourceforge.net/pam_mount.conf.5.html>
+          for more information.
+        '';
+      };
+
+      shell = mkOption {
+        type = types.nullOr (types.either types.shellPackage (types.passwdEntry types.path));
+        default = pkgs.shadow;
+        defaultText = literalExpression "pkgs.shadow";
+        example = literalExpression "pkgs.bashInteractive";
+        description = lib.mdDoc ''
+          The path to the user's shell. Can use shell derivations,
+          like `pkgs.bashInteractive`. Don’t
+          forget to enable your shell in
+          `programs` if necessary,
+          like `programs.zsh.enable = true;`.
+        '';
+      };
+
+      ignoreShellProgramCheck = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          By default, nixos will check that programs.SHELL.enable is set to
+          true if the user has a custom shell specified. If that behavior isn't
+          required and there are custom overrides in place to make sure that the
+          shell is functional, set this to true.
+        '';
+      };
+
+      subUidRanges = mkOption {
+        type = with types; listOf (submodule subordinateUidRange);
+        default = [];
+        example = [
+          { startUid = 1000; count = 1; }
+          { startUid = 100001; count = 65534; }
+        ];
+        description = lib.mdDoc ''
+          Subordinate user ids that user is allowed to use.
+          They are set into {file}`/etc/subuid` and are used
+          by `newuidmap` for user namespaces.
+        '';
+      };
+
+      subGidRanges = mkOption {
+        type = with types; listOf (submodule subordinateGidRange);
+        default = [];
+        example = [
+          { startGid = 100; count = 1; }
+          { startGid = 1001; count = 999; }
+        ];
+        description = lib.mdDoc ''
+          Subordinate group ids that user is allowed to use.
+          They are set into {file}`/etc/subgid` and are used
+          by `newgidmap` for user namespaces.
+        '';
+      };
+
+      autoSubUidGidRange = mkOption {
+        type = types.bool;
+        default = false;
+        example = true;
+        description = lib.mdDoc ''
+          Automatically allocate subordinate user and group ids for this user.
+          Allocated range is currently always of size 65536.
+        '';
+      };
+
+      createHome = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to create the home directory and ensure ownership as well as
+          permissions to match the user.
+        '';
+      };
+
+      useDefaultShell = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          If true, the user's shell will be set to
+          {option}`users.defaultUserShell`.
+        '';
+      };
+
+      hashedPassword = mkOption {
+        type = with types; nullOr (passwdEntry str);
+        default = null;
+        description = lib.mdDoc ''
+          Specifies the hashed password for the user.
+          ${passwordDescription}
+          ${hashedPasswordDescription}
+        '';
+      };
+
+      password = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = lib.mdDoc ''
+          Specifies the (clear text) password for the user.
+          Warning: do not set confidential information here
+          because it is world-readable in the Nix store. This option
+          should only be used for public accounts.
+          ${passwordDescription}
+        '';
+      };
+
+      hashedPasswordFile = mkOption {
+        type = with types; nullOr str;
+        default = cfg.users.${name}.passwordFile;
+        defaultText = literalExpression "null";
+        description = lib.mdDoc ''
+          The full path to a file that contains the hash of the user's
+          password. The password file is read on each system activation. The
+          file should contain exactly one line, which should be the password in
+          an encrypted form that is suitable for the `chpasswd -e` command.
+          ${passwordDescription}
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        visible = false;
+        description = lib.mdDoc "Deprecated alias of hashedPasswordFile";
+      };
+
+      initialHashedPassword = mkOption {
+        type = with types; nullOr (passwdEntry str);
+        default = null;
+        description = lib.mdDoc ''
+          Specifies the initial hashed password for the user, i.e. the
+          hashed password assigned if the user does not already
+          exist. If {option}`users.mutableUsers` is true, the
+          password can be changed subsequently using the
+          {command}`passwd` command. Otherwise, it's
+          equivalent to setting the {option}`hashedPassword` option.
+
+          Note that the {option}`hashedPassword` option will override
+          this option if both are set.
+
+          ${hashedPasswordDescription}
+        '';
+      };
+
+      initialPassword = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = lib.mdDoc ''
+          Specifies the initial password for the user, i.e. the
+          password assigned if the user does not already exist. If
+          {option}`users.mutableUsers` is true, the password
+          can be changed subsequently using the
+          {command}`passwd` command. Otherwise, it's
+          equivalent to setting the {option}`password`
+          option. The same caveat applies: the password specified here
+          is world-readable in the Nix store, so it should only be
+          used for guest accounts or passwords that will be changed
+          promptly.
+
+          Note that the {option}`password` option will override this
+          option if both are set.
+        '';
+      };
+
+      packages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExpression "[ pkgs.firefox pkgs.thunderbird ]";
+        description = lib.mdDoc ''
+          The set of packages that should be made available to the user.
+          This is in contrast to {option}`environment.systemPackages`,
+          which adds packages to all users.
+        '';
+      };
+
+      expires = mkOption {
+        type = types.nullOr (types.strMatching "[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}");
+        default = null;
+        description = lib.mdDoc ''
+          Set the date on which the user's account will no longer be
+          accessible. The date is expressed in the format YYYY-MM-DD, or null
+          to disable the expiry.
+          A user whose account is locked must contact the system
+          administrator before being able to use the system again.
+        '';
+      };
+
+      linger = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether to enable lingering for this user. If true, systemd user
+          units will start at boot, rather than starting at login and stopping
+          at logout. This is the declarative equivalent of running
+          `loginctl enable-linger` for this user.
+
+          If false, user units will not be started until the user logs in, and
+          may be stopped on logout depending on the settings in `logind.conf`.
+        '';
+      };
+    };
+
+    config = mkMerge
+      [ { name = mkDefault name;
+          shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
+        }
+        (mkIf config.isNormalUser {
+          group = mkDefault "users";
+          createHome = mkDefault true;
+          home = mkDefault "/home/${config.name}";
+          homeMode = mkDefault "700";
+          useDefaultShell = mkDefault true;
+          isSystemUser = mkDefault false;
+        })
+        # If !mutableUsers, setting ‘initialPassword’ is equivalent to
+        # setting ‘password’ (and similarly for hashed passwords).
+        (mkIf (!cfg.mutableUsers && config.initialPassword != null) {
+          password = mkDefault config.initialPassword;
+        })
+        (mkIf (!cfg.mutableUsers && config.initialHashedPassword != null) {
+          hashedPassword = mkDefault config.initialHashedPassword;
+        })
+        (mkIf (config.isNormalUser && config.subUidRanges == [] && config.subGidRanges == []) {
+          autoSubUidGidRange = mkDefault true;
+        })
+      ];
+
+  };
+
+  groupOpts = { name, config, ... }: {
+
+    options = {
+
+      name = mkOption {
+        type = types.passwdEntry types.str;
+        description = lib.mdDoc ''
+          The name of the group. If undefined, the name of the attribute set
+          will be used.
+        '';
+      };
+
+      gid = mkOption {
+        type = with types; nullOr int;
+        default = null;
+        description = lib.mdDoc ''
+          The group GID. If the GID is null, a free GID is picked on
+          activation.
+        '';
+      };
+
+      members = mkOption {
+        type = with types; listOf (passwdEntry str);
+        default = [];
+        description = lib.mdDoc ''
+          The user names of the group members, added to the
+          `/etc/group` file.
+        '';
+      };
+
+    };
+
+    config = {
+      name = mkDefault name;
+
+      members = mapAttrsToList (n: u: u.name) (
+        filterAttrs (n: u: elem config.name u.extraGroups) cfg.users
+      );
+    };
+
+  };
+
+  subordinateUidRange = {
+    options = {
+      startUid = mkOption {
+        type = types.int;
+        description = lib.mdDoc ''
+          Start of the range of subordinate user ids that user is
+          allowed to use.
+        '';
+      };
+      count = mkOption {
+        type = types.int;
+        default = 1;
+        description = lib.mdDoc "Count of subordinate user ids";
+      };
+    };
+  };
+
+  subordinateGidRange = {
+    options = {
+      startGid = mkOption {
+        type = types.int;
+        description = lib.mdDoc ''
+          Start of the range of subordinate group ids that user is
+          allowed to use.
+        '';
+      };
+      count = mkOption {
+        type = types.int;
+        default = 1;
+        description = lib.mdDoc "Count of subordinate group ids";
+      };
+    };
+  };
+
+  idsAreUnique = set: idAttr: !(foldr (name: args@{ dup, acc }:
+    let
+      id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
+      exists = builtins.hasAttr id acc;
+      newAcc = acc // (builtins.listToAttrs [ { name = id; value = true; } ]);
+    in if dup then args else if exists
+      then builtins.trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; }
+      else { dup = false; acc = newAcc; }
+    ) { dup = false; acc = {}; } (builtins.attrNames set)).dup;
+
+  uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.users) "uid";
+  gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.groups) "gid";
+  sdInitrdUidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) config.boot.initrd.systemd.users) "uid";
+  sdInitrdGidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) config.boot.initrd.systemd.groups) "gid";
+  groupNames = lib.mapAttrsToList (n: g: g.name) cfg.groups;
+  usersWithoutExistingGroup = lib.filterAttrs (n: u: u.group != "" && !lib.elem u.group groupNames) cfg.users;
+
+  spec = pkgs.writeText "users-groups.json" (builtins.toJSON {
+    inherit (cfg) mutableUsers;
+    users = mapAttrsToList (_: u:
+      { inherit (u)
+          name uid group description home homeMode createHome isSystemUser
+          password hashedPasswordFile hashedPassword
+          autoSubUidGidRange subUidRanges subGidRanges
+          initialPassword initialHashedPassword expires;
+        shell = utils.toShellPath u.shell;
+      }) cfg.users;
+    groups = attrValues cfg.groups;
+  });
+
+  systemShells =
+    let
+      shells = mapAttrsToList (_: u: u.shell) cfg.users;
+    in
+      filter types.shellPackage.check shells;
+
+in {
+  imports = [
+    (mkAliasOptionModuleMD [ "users" "extraUsers" ] [ "users" "users" ])
+    (mkAliasOptionModuleMD [ "users" "extraGroups" ] [ "users" "groups" ])
+    (mkRenamedOptionModule ["security" "initialRootPassword"] ["users" "users" "root" "initialHashedPassword"])
+  ];
+
+  ###### interface
+  options = {
+
+    users.mutableUsers = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        If set to `true`, you are free to add new users and groups to the system
+        with the ordinary `useradd` and
+        `groupadd` commands. On system activation, the
+        existing contents of the `/etc/passwd` and
+        `/etc/group` files will be merged with the
+        contents generated from the `users.users` and
+        `users.groups` options.
+        The initial password for a user will be set
+        according to `users.users`, but existing passwords
+        will not be changed.
+
+        ::: {.warning}
+        If set to `false`, the contents of the user and
+        group files will simply be replaced on system activation. This also
+        holds for the user passwords; all changed
+        passwords will be reset according to the
+        `users.users` configuration on activation.
+        :::
+      '';
+    };
+
+    users.enforceIdUniqueness = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to require that no two users/groups share the same uid/gid.
+      '';
+    };
+
+    users.users = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule userOpts);
+      example = {
+        alice = {
+          uid = 1234;
+          description = "Alice Q. User";
+          home = "/home/alice";
+          createHome = true;
+          group = "users";
+          extraGroups = ["wheel"];
+          shell = "/bin/sh";
+        };
+      };
+      description = lib.mdDoc ''
+        Additional user accounts to be created automatically by the system.
+        This can also be used to set options for root.
+      '';
+    };
+
+    users.groups = mkOption {
+      default = {};
+      example =
+        { students.gid = 1001;
+          hackers = { };
+        };
+      type = with types; attrsOf (submodule groupOpts);
+      description = lib.mdDoc ''
+        Additional groups to be created automatically by the system.
+      '';
+    };
+
+
+    users.allowNoPasswordLogin = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Disable checking that at least the `root` user or a user in the `wheel` group can log in using
+        a password or an SSH key.
+
+        WARNING: enabling this can lock you out of your system. Enable this only if you know what are you doing.
+      '';
+    };
+
+    # systemd initrd
+    boot.initrd.systemd.users = mkOption {
+      description = ''
+        Users to include in initrd.
+      '';
+      default = {};
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options.uid = mkOption {
+          type = types.int;
+          description = ''
+            ID of the user in initrd.
+          '';
+          defaultText = literalExpression "config.users.users.\${name}.uid";
+          default = cfg.users.${name}.uid;
+        };
+        options.group = mkOption {
+          type = types.singleLineStr;
+          description = ''
+            Group the user belongs to in initrd.
+          '';
+          defaultText = literalExpression "config.users.users.\${name}.group";
+          default = cfg.users.${name}.group;
+        };
+        options.shell = mkOption {
+          type = types.passwdEntry types.path;
+          description = ''
+            The path to the user's shell in initrd.
+          '';
+          default = "${pkgs.shadow}/bin/nologin";
+          defaultText = literalExpression "\${pkgs.shadow}/bin/nologin";
+        };
+      }));
+    };
+
+    boot.initrd.systemd.groups = mkOption {
+      description = ''
+        Groups to include in initrd.
+      '';
+      default = {};
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options.gid = mkOption {
+          type = types.int;
+          description = ''
+            ID of the group in initrd.
+          '';
+          defaultText = literalExpression "config.users.groups.\${name}.gid";
+          default = cfg.groups.${name}.gid;
+        };
+      }));
+    };
+  };
+
+
+  ###### implementation
+
+  config = let
+    cryptSchemeIdPatternGroup = "(${lib.concatStringsSep "|" pkgs.libxcrypt.enabledCryptSchemeIds})";
+  in {
+
+    users.users = {
+      root = {
+        uid = ids.uids.root;
+        description = "System administrator";
+        home = "/root";
+        shell = mkDefault cfg.defaultUserShell;
+        group = "root";
+        initialHashedPassword = mkDefault "!";
+      };
+      nobody = {
+        uid = ids.uids.nobody;
+        isSystemUser = true;
+        description = "Unprivileged account (don't use!)";
+        group = "nogroup";
+      };
+    };
+
+    users.groups = {
+      root.gid = ids.gids.root;
+      wheel.gid = ids.gids.wheel;
+      disk.gid = ids.gids.disk;
+      kmem.gid = ids.gids.kmem;
+      tty.gid = ids.gids.tty;
+      floppy.gid = ids.gids.floppy;
+      uucp.gid = ids.gids.uucp;
+      lp.gid = ids.gids.lp;
+      cdrom.gid = ids.gids.cdrom;
+      tape.gid = ids.gids.tape;
+      audio.gid = ids.gids.audio;
+      video.gid = ids.gids.video;
+      dialout.gid = ids.gids.dialout;
+      nogroup.gid = ids.gids.nogroup;
+      users.gid = ids.gids.users;
+      nixbld.gid = ids.gids.nixbld;
+      utmp.gid = ids.gids.utmp;
+      adm.gid = ids.gids.adm;
+      input.gid = ids.gids.input;
+      kvm.gid = ids.gids.kvm;
+      render.gid = ids.gids.render;
+      sgx.gid = ids.gids.sgx;
+      shadow.gid = ids.gids.shadow;
+    };
+
+    system.activationScripts.users = {
+      supportsDryActivation = true;
+      text = ''
+        install -m 0700 -d /root
+        install -m 0755 -d /home
+
+        ${pkgs.perl.withPackages (p: [ p.FileSlurp p.JSON ])}/bin/perl \
+        -w ${./update-users-groups.pl} ${spec}
+      '';
+    };
+
+    system.activationScripts.update-lingering = let
+      lingerDir = "/var/lib/systemd/linger";
+      lingeringUsers = map (u: u.name) (attrValues (flip filterAttrs cfg.users (n: u: u.linger)));
+      lingeringUsersFile = builtins.toFile "lingering-users"
+        (concatStrings (map (s: "${s}\n")
+          (sort (a: b: a < b) lingeringUsers)));  # this sorting is important for `comm` to work correctly
+    in stringAfter [ "users" ] ''
+      if [ -e ${lingerDir} ] ; then
+        cd ${lingerDir}
+        ls ${lingerDir} | sort | comm -3 -1 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl disable-linger
+        ls ${lingerDir} | sort | comm -3 -2 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl  enable-linger
+      fi
+    '';
+
+    # Warn about user accounts with deprecated password hashing schemes
+    system.activationScripts.hashes = {
+      deps = [ "users" ];
+      text = ''
+        users=()
+        while IFS=: read -r user hash _; do
+          if [[ "$hash" = "$"* && ! "$hash" =~ ^\''$${cryptSchemeIdPatternGroup}\$ ]]; then
+            users+=("$user")
+          fi
+        done </etc/shadow
+
+        if (( "''${#users[@]}" )); then
+          echo "
+        WARNING: The following user accounts rely on password hashing algorithms
+        that have been removed. They need to be renewed as soon as possible, as
+        they do prevent their users from logging in."
+          printf ' - %s\n' "''${users[@]}"
+        fi
+      '';
+    };
+
+    # for backwards compatibility
+    system.activationScripts.groups = stringAfter [ "users" ] "";
+
+    # Install all the user shells
+    environment.systemPackages = systemShells;
+
+    environment.etc = mapAttrs' (_: { packages, name, ... }: {
+      name = "profiles/per-user/${name}";
+      value.source = pkgs.buildEnv {
+        name = "user-environment";
+        paths = packages;
+        inherit (config.environment) pathsToLink extraOutputsToInstall;
+        inherit (config.system.path) ignoreCollisions postBuild;
+      };
+    }) (filterAttrs (_: u: u.packages != []) cfg.users);
+
+    environment.profiles = [
+      "$HOME/.nix-profile"
+      "\${XDG_STATE_HOME}/nix/profile"
+      "$HOME/.local/state/nix/profile"
+      "/etc/profiles/per-user/$USER"
+    ];
+
+    # systemd initrd
+    boot.initrd.systemd = lib.mkIf config.boot.initrd.systemd.enable {
+      contents = {
+        "/etc/passwd".text = ''
+          ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { uid, group, shell }: let
+            g = config.boot.initrd.systemd.groups.${group};
+          in "${n}:x:${toString uid}:${toString g.gid}::/var/empty:${shell}") config.boot.initrd.systemd.users)}
+        '';
+        "/etc/group".text = ''
+          ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { gid }: "${n}:x:${toString gid}:") config.boot.initrd.systemd.groups)}
+        '';
+        "/etc/shells".text = lib.concatStringsSep "\n" (lib.unique (lib.mapAttrsToList (_: u: u.shell) config.boot.initrd.systemd.users)) + "\n";
+      };
+
+      storePaths = [ "${pkgs.shadow}/bin/nologin" ];
+
+      users = {
+        root = { shell = lib.mkDefault "/bin/bash"; };
+        nobody = {};
+      };
+
+      groups = {
+        root = {};
+        nogroup = {};
+        systemd-journal = {};
+        tty = {};
+        dialout = {};
+        kmem = {};
+        input = {};
+        video = {};
+        render = {};
+        sgx = {};
+        audio = {};
+        video = {};
+        lp = {};
+        disk = {};
+        cdrom = {};
+        tape = {};
+        kvm = {};
+      };
+    };
+
+    assertions = [
+      { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
+        message = "UIDs and GIDs must be unique!";
+      }
+      { assertion = !cfg.enforceIdUniqueness || (sdInitrdUidsAreUnique && sdInitrdGidsAreUnique);
+        message = "systemd initrd UIDs and GIDs must be unique!";
+      }
+      { assertion = usersWithoutExistingGroup == {};
+        message =
+          let
+            errUsers = lib.attrNames usersWithoutExistingGroup;
+            missingGroups = lib.unique (lib.mapAttrsToList (n: u: u.group) usersWithoutExistingGroup);
+            mkConfigHint = group: "users.groups.${group} = {};";
+          in ''
+            The following users have a primary group that is undefined: ${lib.concatStringsSep " " errUsers}
+            Hint: Add this to your NixOS configuration:
+              ${lib.concatStringsSep "\n  " (map mkConfigHint missingGroups)}
+          '';
+      }
+      { # If mutableUsers is false, to prevent users creating a
+        # configuration that locks them out of the system, ensure that
+        # there is at least one "privileged" account that has a
+        # password or an SSH authorized key. Privileged accounts are
+        # root and users in the wheel group.
+        # The check does not apply when users.disableLoginPossibilityAssertion
+        # The check does not apply when users.mutableUsers
+        assertion = !cfg.mutableUsers -> !cfg.allowNoPasswordLogin ->
+          any id (mapAttrsToList (name: cfg:
+            (name == "root"
+             || cfg.group == "wheel"
+             || elem "wheel" cfg.extraGroups)
+            &&
+            (allowsLogin cfg.hashedPassword
+             || cfg.password != null
+             || cfg.hashedPasswordFile != null
+             || cfg.openssh.authorizedKeys.keys != []
+             || cfg.openssh.authorizedKeys.keyFiles != [])
+          ) cfg.users ++ [
+            config.security.googleOsLogin.enable
+          ]);
+        message = ''
+          Neither the root account nor any wheel user has a password or SSH authorized key.
+          You must set one to prevent being locked out of your system.
+          If you really want to be locked out of your system, set users.allowNoPasswordLogin = true;
+          However you are most probably better off by setting users.mutableUsers = true; and
+          manually running passwd root to set the root password.
+          '';
+      }
+    ] ++ flatten (flip mapAttrsToList cfg.users (name: user:
+      [
+        {
+        assertion = (user.hashedPassword != null)
+        -> (builtins.match ".*:.*" user.hashedPassword == null);
+        message = ''
+            The password hash of user "${user.name}" contains a ":" character.
+            This is invalid and would break the login system because the fields
+            of /etc/shadow (file where hashes are stored) are colon-separated.
+            Please check the value of option `users.users."${user.name}".hashedPassword`.'';
+          }
+          {
+            assertion = let
+              xor = a: b: a && !b || b && !a;
+              isEffectivelySystemUser = user.isSystemUser || (user.uid != null && user.uid < 1000);
+            in xor isEffectivelySystemUser user.isNormalUser;
+            message = ''
+              Exactly one of users.users.${user.name}.isSystemUser and users.users.${user.name}.isNormalUser must be set.
+            '';
+          }
+          {
+            assertion = user.group != "";
+            message = ''
+              users.users.${user.name}.group is unset. This used to default to
+              nogroup, but this is unsafe. For example you can create a group
+              for this user with:
+              users.users.${user.name}.group = "${user.name}";
+              users.groups.${user.name} = {};
+            '';
+          }
+        ] ++ (map (shell: {
+            assertion = !user.ignoreShellProgramCheck -> (user.shell == pkgs.${shell}) -> (config.programs.${shell}.enable == true);
+            message = ''
+              users.users.${user.name}.shell is set to ${shell}, but
+              programs.${shell}.enable is not true. This will cause the ${shell}
+              shell to lack the basic nix directories in its PATH and might make
+              logging in as that user impossible. You can fix it with:
+              programs.${shell}.enable = true;
+
+              If you know what you're doing and you are fine with the behavior,
+              set users.users.${user.name}.ignoreShellProgramCheck = true;
+              instead.
+            '';
+          }) [
+          "fish"
+          "xonsh"
+          "zsh"
+        ])
+    ));
+
+    warnings =
+      builtins.filter (x: x != null) (
+        flip mapAttrsToList cfg.users (_: user:
+        # This regex matches a subset of the Modular Crypto Format (MCF)[1]
+        # informal standard. Since this depends largely on the OS or the
+        # specific implementation of crypt(3) we only support the (sane)
+        # schemes implemented by glibc and BSDs. In particular the original
+        # DES hash is excluded since, having no structure, it would validate
+        # common mistakes like typing the plaintext password.
+        #
+        # [1]: https://en.wikipedia.org/wiki/Crypt_(C)
+        let
+          sep = "\\$";
+          base64 = "[a-zA-Z0-9./]+";
+          id = cryptSchemeIdPatternGroup;
+          name = "[a-z0-9-]+";
+          value = "[a-zA-Z0-9/+.-]+";
+          options = "${name}(=${value})?(,${name}=${value})*";
+          scheme  = "${id}(${sep}${options})?";
+          content = "${base64}${sep}${base64}(${sep}${base64})?";
+          mcf = "^${sep}${scheme}${sep}${content}$";
+        in
+        if (allowsLogin user.hashedPassword
+            && user.hashedPassword != ""  # login without password
+            && builtins.match mcf user.hashedPassword == null)
+        then ''
+          The password hash of user "${user.name}" may be invalid. You must set a
+          valid hash or the user will be locked out of their account. Please
+          check the value of option `users.users."${user.name}".hashedPassword`.''
+        else null)
+        ++ flip mapAttrsToList cfg.users (name: user:
+          if user.passwordFile != null then
+            ''The option `users.users."${name}".passwordFile' has been renamed '' +
+            ''to `users.users."${name}".hashedPasswordFile'.''
+          else null)
+      );
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/vte.nix b/nixpkgs/nixos/modules/config/vte.nix
new file mode 100644
index 000000000000..a969607f6e0b
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/vte.nix
@@ -0,0 +1,56 @@
+# VTE
+
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+
+  vteInitSnippet = ''
+    # Show current working directory in VTE terminals window title.
+    # Supports both bash and zsh, requires interactive shell.
+    . ${pkgs.vte}/etc/profile.d/vte.sh
+  '';
+
+in
+
+{
+
+  meta = {
+    maintainers = teams.gnome.members;
+  };
+
+  options = {
+
+    programs.bash.vteIntegration = mkOption {
+      default = false;
+      type = types.bool;
+      description = lib.mdDoc ''
+        Whether to enable Bash integration for VTE terminals.
+        This allows it to preserve the current directory of the shell
+        across terminals.
+      '';
+    };
+
+    programs.zsh.vteIntegration = mkOption {
+      default = false;
+      type = types.bool;
+      description = lib.mdDoc ''
+        Whether to enable Zsh integration for VTE terminals.
+        This allows it to preserve the current directory of the shell
+        across terminals.
+      '';
+    };
+
+  };
+
+  config = mkMerge [
+    (mkIf config.programs.bash.vteIntegration {
+      programs.bash.interactiveShellInit = mkBefore vteInitSnippet;
+    })
+
+    (mkIf config.programs.zsh.vteIntegration {
+      programs.zsh.interactiveShellInit = vteInitSnippet;
+    })
+  ];
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/autostart.nix b/nixpkgs/nixos/modules/config/xdg/autostart.nix
new file mode 100644
index 000000000000..a4fdbda911a2
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/autostart.nix
@@ -0,0 +1,26 @@
+{ config, lib, ... }:
+
+with lib;
+{
+  meta = {
+    maintainers = teams.freedesktop.members;
+  };
+
+  options = {
+    xdg.autostart.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to install files to support the
+        [XDG Autostart specification](https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html).
+      '';
+    };
+  };
+
+  config = mkIf config.xdg.autostart.enable {
+    environment.pathsToLink = [
+      "/etc/xdg/autostart"
+    ];
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/icons.nix b/nixpkgs/nixos/modules/config/xdg/icons.nix
new file mode 100644
index 000000000000..8d44a431445b
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/icons.nix
@@ -0,0 +1,48 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  meta = {
+    maintainers = teams.freedesktop.members;
+  };
+
+  options = {
+    xdg.icons.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to install files to support the
+        [XDG Icon Theme specification](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html).
+      '';
+    };
+  };
+
+  config = mkIf config.xdg.icons.enable {
+    environment.pathsToLink = [
+      "/share/icons"
+      "/share/pixmaps"
+    ];
+
+    environment.systemPackages = [
+      # Empty icon theme that contains index.theme file describing directories
+      # where toolkits should look for icons installed by apps.
+      pkgs.hicolor-icon-theme
+    ];
+
+    # libXcursor looks for cursors in XCURSOR_PATH
+    # it mostly follows the spec for icons
+    # See: https://www.x.org/releases/current/doc/man/man3/Xcursor.3.xhtml Themes
+
+    # These are preferred so they come first in the list
+    environment.sessionVariables.XCURSOR_PATH = [
+      "$HOME/.icons"
+      "$HOME/.local/share/icons"
+    ];
+
+    environment.profileRelativeSessionVariables.XCURSOR_PATH = [
+      "/share/icons"
+      "/share/pixmaps"
+    ];
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/menus.nix b/nixpkgs/nixos/modules/config/xdg/menus.nix
new file mode 100644
index 000000000000..b8f829e81547
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/menus.nix
@@ -0,0 +1,29 @@
+{ config, lib, ... }:
+
+with lib;
+{
+  meta = {
+    maintainers = teams.freedesktop.members;
+  };
+
+  options = {
+    xdg.menus.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to install files to support the
+        [XDG Desktop Menu specification](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html).
+      '';
+    };
+  };
+
+  config = mkIf config.xdg.menus.enable {
+    environment.pathsToLink = [
+      "/share/applications"
+      "/share/desktop-directories"
+      "/etc/xdg/menus"
+      "/etc/xdg/menus/applications-merged"
+    ];
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/mime.nix b/nixpkgs/nixos/modules/config/xdg/mime.nix
new file mode 100644
index 000000000000..3aa863083219
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/mime.nix
@@ -0,0 +1,102 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.xdg.mime;
+  associationOptions = with types; attrsOf (
+    coercedTo (either (listOf str) str) (x: concatStringsSep ";" (toList x)) str
+  );
+in
+
+{
+  meta = {
+    maintainers = teams.freedesktop.members ++ (with maintainers; [ figsoda ]);
+  };
+
+  options = {
+    xdg.mime.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to install files to support the
+        [XDG Shared MIME-info specification](https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html) and the
+        [XDG MIME Applications specification](https://specifications.freedesktop.org/mime-apps-spec/mime-apps-spec-latest.html).
+      '';
+    };
+
+    xdg.mime.addedAssociations = mkOption {
+      type = associationOptions;
+      default = {};
+      example = {
+        "application/pdf" = "firefox.desktop";
+        "text/xml" = [ "nvim.desktop" "codium.desktop" ];
+      };
+      description = lib.mdDoc ''
+        Adds associations between mimetypes and applications. See the
+        [
+        specifications](https://specifications.freedesktop.org/mime-apps-spec/mime-apps-spec-latest.html#associations) for more information.
+      '';
+    };
+
+    xdg.mime.defaultApplications = mkOption {
+      type = associationOptions;
+      default = {};
+      example = {
+        "application/pdf" = "firefox.desktop";
+        "image/png" = [ "sxiv.desktop" "gimp.desktop" ];
+      };
+      description = lib.mdDoc ''
+        Sets the default applications for given mimetypes. See the
+        [
+        specifications](https://specifications.freedesktop.org/mime-apps-spec/mime-apps-spec-latest.html#default) for more information.
+      '';
+    };
+
+    xdg.mime.removedAssociations = mkOption {
+      type = associationOptions;
+      default = {};
+      example = {
+        "audio/mp3" = [ "mpv.desktop" "umpv.desktop" ];
+        "inode/directory" = "codium.desktop";
+      };
+      description = lib.mdDoc ''
+        Removes associations between mimetypes and applications. See the
+        [
+        specifications](https://specifications.freedesktop.org/mime-apps-spec/mime-apps-spec-latest.html#associations) for more information.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."xdg/mimeapps.list" = mkIf (
+      cfg.addedAssociations != {}
+      || cfg.defaultApplications != {}
+      || cfg.removedAssociations != {}
+    ) {
+      text = generators.toINI { } {
+        "Added Associations" = cfg.addedAssociations;
+        "Default Applications" = cfg.defaultApplications;
+        "Removed Associations" = cfg.removedAssociations;
+      };
+    };
+
+    environment.pathsToLink = [ "/share/mime" ];
+
+    environment.systemPackages = [
+      # this package also installs some useful data, as well as its utilities
+      pkgs.shared-mime-info
+    ];
+
+    environment.extraSetup = ''
+      if [ -w $out/share/mime ] && [ -d $out/share/mime/packages ]; then
+          XDG_DATA_DIRS=$out/share PKGSYSTEM_ENABLE_FSYNC=0 ${pkgs.buildPackages.shared-mime-info}/bin/update-mime-database -V $out/share/mime > /dev/null
+      fi
+
+      if [ -w $out/share/applications ]; then
+          ${pkgs.buildPackages.desktop-file-utils}/bin/update-desktop-database $out/share/applications
+      fi
+    '';
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/portal.nix b/nixpkgs/nixos/modules/config/xdg/portal.nix
new file mode 100644
index 000000000000..07d4fa76c2e8
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/portal.nix
@@ -0,0 +1,181 @@
+{ config, pkgs, lib, ... }:
+
+let
+  inherit (lib)
+    mkEnableOption
+    mkIf
+    mkOption
+    mkRenamedOptionModule
+    teams
+    types;
+
+  associationOptions = with types; attrsOf (
+    coercedTo (either (listOf str) str) (x: lib.concatStringsSep ";" (lib.toList x)) str
+  );
+in
+
+{
+  imports = [
+    (mkRenamedOptionModule [ "services" "flatpak" "extraPortals" ] [ "xdg" "portal" "extraPortals" ])
+
+    ({ config, lib, options, ... }:
+      let
+        from = [ "xdg" "portal" "gtkUsePortal" ];
+        fromOpt = lib.getAttrFromPath from options;
+      in
+      {
+        warnings = lib.mkIf config.xdg.portal.gtkUsePortal [
+          "The option `${lib.showOption from}' defined in ${lib.showFiles fromOpt.files} has been deprecated. Setting the variable globally with `environment.sessionVariables' NixOS option can have unforeseen side-effects."
+        ];
+      }
+    )
+  ];
+
+  meta = {
+    maintainers = teams.freedesktop.members;
+  };
+
+  options.xdg.portal = {
+    enable =
+      mkEnableOption (lib.mdDoc ''[xdg desktop integration](https://github.com/flatpak/xdg-desktop-portal)'') // {
+        default = false;
+      };
+
+    extraPortals = mkOption {
+      type = types.listOf types.package;
+      default = [ ];
+      description = lib.mdDoc ''
+        List of additional portals to add to path. Portals allow interaction
+        with system, like choosing files or taking screenshots. At minimum,
+        a desktop portal implementation should be listed. GNOME and KDE already
+        adds `xdg-desktop-portal-gtk`; and
+        `xdg-desktop-portal-kde` respectively. On other desktop
+        environments you probably want to add them yourself.
+      '';
+    };
+
+    gtkUsePortal = mkOption {
+      type = types.bool;
+      visible = false;
+      default = false;
+      description = lib.mdDoc ''
+        Sets environment variable `GTK_USE_PORTAL` to `1`.
+        This will force GTK-based programs ran outside Flatpak to respect and use XDG Desktop Portals
+        for features like file chooser but it is an unsupported hack that can easily break things.
+        Defaults to `false` to respect its opt-in nature.
+      '';
+    };
+
+    xdgOpenUsePortal = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Sets environment variable `NIXOS_XDG_OPEN_USE_PORTAL` to `1`
+        This will make `xdg-open` use the portal to open programs, which resolves bugs involving
+        programs opening inside FHS envs or with unexpected env vars set from wrappers.
+        See [#160923](https://github.com/NixOS/nixpkgs/issues/160923) for more info.
+      '';
+    };
+
+    config = mkOption {
+      type = types.attrsOf associationOptions;
+      default = { };
+      example = {
+        x-cinnamon = {
+          default = [ "xapp" "gtk" ];
+        };
+        pantheon = {
+          default = [ "pantheon" "gtk" ];
+          "org.freedesktop.impl.portal.Secret" = [ "gnome-keyring" ];
+        };
+        common = {
+          default = [ "gtk" ];
+        };
+      };
+      description = lib.mdDoc ''
+        Sets which portal backend should be used to provide the implementation
+        for the requested interface. For details check {manpage}`portals.conf(5)`.
+
+        Configs will be linked to `/etx/xdg/xdg-desktop-portal/` with the name `$desktop-portals.conf`
+        for `xdg.portal.config.$desktop` and `portals.conf` for `xdg.portal.config.common`
+        as an exception.
+      '';
+    };
+
+    configPackages = mkOption {
+      type = types.listOf types.package;
+      default = [ ];
+      example = lib.literalExpression "[ pkgs.gnome.gnome-session ]";
+      description = lib.mdDoc ''
+        List of packages that provide XDG desktop portal configuration, usually in
+        the form of `share/xdg-desktop-portal/$desktop-portals.conf`.
+
+        Note that configs in `xdg.portal.config` will be preferred if set.
+      '';
+    };
+  };
+
+  config =
+    let
+      cfg = config.xdg.portal;
+      packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
+      configPackages = cfg.configPackages;
+
+      joinedPortals = pkgs.buildEnv {
+        name = "xdg-portals";
+        paths = packages;
+        pathsToLink = [ "/share/xdg-desktop-portal/portals" "/share/applications" ];
+      };
+
+      joinedPortalConfigs = pkgs.buildEnv {
+        name = "xdg-portal-configs";
+        paths = configPackages;
+        pathsToLink = [ "/share/xdg-desktop-portal" ];
+      };
+    in
+    mkIf cfg.enable {
+      warnings = lib.optional (cfg.configPackages == [ ] && cfg.config == { }) ''
+        xdg-desktop-portal 1.17 reworked how portal implementations are loaded, you
+        should either set `xdg.portal.config` or `xdg.portal.configPackages`
+        to specify which portal backend to use for the requested interface.
+
+        https://github.com/flatpak/xdg-desktop-portal/blob/1.18.1/doc/portals.conf.rst.in
+
+        If you simply want to keep the behaviour in < 1.17, which uses the first
+        portal implementation found in lexicographical order, use the following:
+
+        xdg.portal.config.common.default = "*";
+      '';
+
+      assertions = [
+        {
+          assertion = cfg.extraPortals != [ ];
+          message = "Setting xdg.portal.enable to true requires a portal implementation in xdg.portal.extraPortals such as xdg-desktop-portal-gtk or xdg-desktop-portal-kde.";
+        }
+      ];
+
+      services.dbus.packages = packages;
+      systemd.packages = packages;
+
+      environment = {
+        # fixes screen sharing on plasmawayland on non-chromium apps by linking
+        # share/applications/*.desktop files
+        # see https://github.com/NixOS/nixpkgs/issues/145174
+        systemPackages = [ joinedPortals ];
+        pathsToLink = [ "/share/applications" ];
+
+        sessionVariables = {
+          GTK_USE_PORTAL = mkIf cfg.gtkUsePortal "1";
+          NIXOS_XDG_OPEN_USE_PORTAL = mkIf cfg.xdgOpenUsePortal "1";
+          XDG_DESKTOP_PORTAL_DIR = "${joinedPortals}/share/xdg-desktop-portal/portals";
+          NIXOS_XDG_DESKTOP_PORTAL_CONFIG_DIR = mkIf (cfg.configPackages != [ ]) "${joinedPortalConfigs}/share/xdg-desktop-portal";
+        };
+
+        etc = lib.concatMapAttrs
+          (desktop: conf: lib.optionalAttrs (conf != { }) {
+            "xdg/xdg-desktop-portal/${lib.optionalString (desktop != "common") "${desktop}-"}portals.conf".text =
+              lib.generators.toINI { } { preferred = conf; };
+          }) cfg.config;
+      };
+    };
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/portals/lxqt.nix b/nixpkgs/nixos/modules/config/xdg/portals/lxqt.nix
new file mode 100644
index 000000000000..18fcf3d81c02
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/portals/lxqt.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.xdg.portal.lxqt;
+
+in
+{
+  meta = {
+    maintainers = teams.lxqt.members;
+  };
+
+  options.xdg.portal.lxqt = {
+    enable = mkEnableOption (lib.mdDoc ''
+      the desktop portal for the LXQt desktop environment.
+
+      This will add the `lxqt.xdg-desktop-portal-lxqt`
+      package (with the extra Qt styles) into the
+      {option}`xdg.portal.extraPortals` option
+    '');
+
+    styles = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression ''[
+        pkgs.libsForQt5.qtstyleplugin-kvantum
+        pkgs.breeze-qt5
+        pkgs.qtcurve
+      ];
+      '';
+      description = lib.mdDoc ''
+        Extra Qt styles that will be available to the
+        `lxqt.xdg-desktop-portal-lxqt`.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    xdg.portal = {
+      enable = true;
+      extraPortals = [
+        (pkgs.lxqt.xdg-desktop-portal-lxqt.override { extraQtStyles = cfg.styles; })
+      ];
+    };
+
+    environment.systemPackages = cfg.styles;
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/portals/wlr.nix b/nixpkgs/nixos/modules/config/xdg/portals/wlr.nix
new file mode 100644
index 000000000000..d84ae794e3bc
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/portals/wlr.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.xdg.portal.wlr;
+  package = pkgs.xdg-desktop-portal-wlr;
+  settingsFormat = pkgs.formats.ini { };
+  configFile = settingsFormat.generate "xdg-desktop-portal-wlr.ini" cfg.settings;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ minijackson ];
+  };
+
+  options.xdg.portal.wlr = {
+    enable = mkEnableOption (lib.mdDoc ''
+      desktop portal for wlroots-based desktops
+
+      This will add the `xdg-desktop-portal-wlr` package into
+      the {option}`xdg.portal.extraPortals` option, and provide the
+      configuration file
+    '');
+
+    settings = mkOption {
+      description = lib.mdDoc ''
+        Configuration for `xdg-desktop-portal-wlr`.
+
+        See `xdg-desktop-portal-wlr(5)` for supported
+        values.
+      '';
+
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+      };
+
+      default = { };
+
+      # Example taken from the manpage
+      example = literalExpression ''
+        {
+          screencast = {
+            output_name = "HDMI-A-1";
+            max_fps = 30;
+            exec_before = "disable_notifications.sh";
+            exec_after = "enable_notifications.sh";
+            chooser_type = "simple";
+            chooser_cmd = "''${pkgs.slurp}/bin/slurp -f %o -or";
+          };
+        }
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    xdg.portal = {
+      enable = true;
+      extraPortals = [ package ];
+    };
+
+    systemd.user.services.xdg-desktop-portal-wlr.serviceConfig.ExecStart = [
+      # Empty ExecStart value to override the field
+      ""
+      "${package}/libexec/xdg-desktop-portal-wlr --config=${configFile}"
+    ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/config/xdg/sounds.nix b/nixpkgs/nixos/modules/config/xdg/sounds.nix
new file mode 100644
index 000000000000..713d68131fc0
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/xdg/sounds.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  meta = {
+    maintainers = teams.freedesktop.members;
+  };
+
+  options = {
+    xdg.sounds.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to install files to support the
+        [XDG Sound Theme specification](https://www.freedesktop.org/wiki/Specifications/sound-theme-spec/).
+      '';
+    };
+  };
+
+  config = mkIf config.xdg.sounds.enable {
+    environment.systemPackages = [
+      pkgs.sound-theme-freedesktop
+    ];
+
+    environment.pathsToLink = [
+      "/share/sounds"
+    ];
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/config/zram.nix b/nixpkgs/nixos/modules/config/zram.nix
new file mode 100644
index 000000000000..ec8b4ed6e931
--- /dev/null
+++ b/nixpkgs/nixos/modules/config/zram.nix
@@ -0,0 +1,130 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  cfg = config.zramSwap;
+  devices = map (nr: "zram${toString nr}") (lib.range 0 (cfg.swapDevices - 1));
+
+in
+
+{
+
+  imports = [
+    (lib.mkRemovedOptionModule [ "zramSwap" "numDevices" ] "Using ZRAM devices as general purpose ephemeral block devices is no longer supported")
+  ];
+
+  ###### interface
+
+  options = {
+
+    zramSwap = {
+
+      enable = lib.mkOption {
+        default = false;
+        type = lib.types.bool;
+        description = lib.mdDoc ''
+          Enable in-memory compressed devices and swap space provided by the zram
+          kernel module.
+          See [
+            https://www.kernel.org/doc/Documentation/blockdev/zram.txt
+          ](https://www.kernel.org/doc/Documentation/blockdev/zram.txt).
+        '';
+      };
+
+      swapDevices = lib.mkOption {
+        default = 1;
+        type = lib.types.int;
+        description = lib.mdDoc ''
+          Number of zram devices to be used as swap, recommended is 1.
+        '';
+      };
+
+      memoryPercent = lib.mkOption {
+        default = 50;
+        type = lib.types.int;
+        description = lib.mdDoc ''
+          Maximum total amount of memory that can be stored in the zram swap devices
+          (as a percentage of your total memory). Defaults to 1/2 of your total
+          RAM. Run `zramctl` to check how good memory is compressed.
+          This doesn't define how much memory will be used by the zram swap devices.
+        '';
+      };
+
+      memoryMax = lib.mkOption {
+        default = null;
+        type = with lib.types; nullOr int;
+        description = lib.mdDoc ''
+          Maximum total amount of memory (in bytes) that can be stored in the zram
+          swap devices.
+          This doesn't define how much memory will be used by the zram swap devices.
+        '';
+      };
+
+      priority = lib.mkOption {
+        default = 5;
+        type = lib.types.int;
+        description = lib.mdDoc ''
+          Priority of the zram swap devices. It should be a number higher than
+          the priority of your disk-based swap devices (so that the system will
+          fill the zram swap devices before falling back to disk swap).
+        '';
+      };
+
+      algorithm = lib.mkOption {
+        default = "zstd";
+        example = "lz4";
+        type = with lib.types; either (enum [ "lzo" "lz4" "zstd" ]) str;
+        description = lib.mdDoc ''
+          Compression algorithm. `lzo` has good compression,
+          but is slow. `lz4` has bad compression, but is fast.
+          `zstd` is both good compression and fast, but requires newer kernel.
+          You can check what other algorithms are supported by your zram device with
+          {command}`cat /sys/class/block/zram*/comp_algorithm`
+        '';
+      };
+
+      writebackDevice = lib.mkOption {
+        default = null;
+        example = "/dev/zvol/tarta-zoot/swap-writeback";
+        type = lib.types.nullOr lib.types.path;
+        description = lib.mdDoc ''
+          Write incompressible pages to this device,
+          as there's no gain from keeping them in RAM.
+        '';
+      };
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    assertions = [
+      {
+        assertion = cfg.writebackDevice == null || cfg.swapDevices <= 1;
+        message = "A single writeback device cannot be shared among multiple zram devices";
+      }
+    ];
+
+    services.zram-generator.enable = true;
+
+    services.zram-generator.settings = lib.listToAttrs
+      (builtins.map
+        (dev: {
+          name = dev;
+          value =
+            let
+              size = "${toString cfg.memoryPercent} / 100 * ram";
+            in
+            {
+              zram-size = if cfg.memoryMax != null then "min(${size}, ${toString cfg.memoryMax} / 1024 / 1024)" else size;
+              compression-algorithm = cfg.algorithm;
+              swap-priority = cfg.priority;
+            } // lib.optionalAttrs (cfg.writebackDevice != null) {
+              writeback-device = cfg.writebackDevice;
+            };
+        })
+        devices);
+
+  };
+
+}