about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/lib/systemd-lib.nix84
-rw-r--r--nixos/lib/systemd-types.nix37
-rw-r--r--nixos/lib/systemd-unit-options.nix415
-rw-r--r--nixos/lib/utils.nix1
-rw-r--r--nixos/modules/misc/version.nix40
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/system/boot/stage-1.nix8
-rw-r--r--nixos/modules/system/boot/systemd.nix43
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix417
-rw-r--r--nixos/modules/system/boot/systemd/nspawn.nix8
-rw-r--r--nixos/modules/system/boot/systemd/user.nix31
-rw-r--r--nixos/modules/tasks/filesystems.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix24
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/systemd-initrd-simple.nix27
-rw-r--r--pkgs/build-support/docker/default.nix21
-rw-r--r--pkgs/build-support/fake-nss/default.nix24
-rw-r--r--pkgs/build-support/kernel/make-initrd-ng-tool.nix9
-rw-r--r--pkgs/build-support/kernel/make-initrd-ng.nix79
-rw-r--r--pkgs/build-support/kernel/make-initrd-ng/Cargo.lock5
-rw-r--r--pkgs/build-support/kernel/make-initrd-ng/Cargo.toml9
-rw-r--r--pkgs/build-support/kernel/make-initrd-ng/README.md79
-rw-r--r--pkgs/build-support/kernel/make-initrd-ng/src/main.rs208
-rw-r--r--pkgs/os-specific/linux/systemd/default.nix2
-rw-r--r--pkgs/top-level/all-packages.nix6
25 files changed, 1309 insertions, 272 deletions
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index 37900b0b16f6a..16ec47df3a464 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -122,10 +122,15 @@ in rec {
         (if isList value then value else [value]))
         as));
 
-  generateUnits = generateUnits' true;
-
-  generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants:
-    pkgs.runCommand "${type}-units"
+  generateUnits = { allowCollisions ? true, type, units, upstreamUnits, upstreamWants, packages ? cfg.packages, package ? cfg.package }:
+    let
+      typeDir = ({
+        system = "system";
+        initrd = "system";
+        user = "user";
+        nspawn = "nspawn";
+      }).${type};
+    in pkgs.runCommand "${type}-units"
       { preferLocalBuild = true;
         allowSubstitutes = false;
       } ''
@@ -133,7 +138,7 @@ in rec {
 
       # Copy the upstream systemd units we're interested in.
       for i in ${toString upstreamUnits}; do
-        fn=${cfg.package}/example/systemd/${type}/$i
+        fn=${package}/example/systemd/${typeDir}/$i
         if ! [ -e $fn ]; then echo "missing $fn"; false; fi
         if [ -L $fn ]; then
           target="$(readlink "$fn")"
@@ -150,7 +155,7 @@ in rec {
       # Copy .wants links, but only those that point to units that
       # we're interested in.
       for i in ${toString upstreamWants}; do
-        fn=${cfg.package}/example/systemd/${type}/$i
+        fn=${package}/example/systemd/${typeDir}/$i
         if ! [ -e $fn ]; then echo "missing $fn"; false; fi
         x=$out/$(basename $fn)
         mkdir $x
@@ -162,14 +167,14 @@ in rec {
       done
 
       # Symlink all units provided listed in systemd.packages.
-      packages="${toString cfg.packages}"
+      packages="${toString packages}"
 
       # Filter duplicate directories
       declare -A unique_packages
       for k in $packages ; do unique_packages[$k]=1 ; done
 
       for i in ''${!unique_packages[@]}; do
-        for fn in $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*; do
+        for fn in $i/etc/systemd/${typeDir}/* $i/lib/systemd/${typeDir}/*; do
           if ! [[ "$fn" =~ .wants$ ]]; then
             if [[ -d "$fn" ]]; then
               targetDir="$out/$(basename "$fn")"
@@ -270,9 +275,9 @@ in rec {
           { Conflicts = toString config.conflicts; }
         // optionalAttrs (config.requisite != [])
           { Requisite = toString config.requisite; }
-        // optionalAttrs (config.restartTriggers != [])
+        // optionalAttrs (config ? restartTriggers && config.restartTriggers != [])
           { X-Restart-Triggers = toString config.restartTriggers; }
-        // optionalAttrs (config.reloadTriggers != [])
+        // optionalAttrs (config ? reloadTriggers && config.reloadTriggers != [])
           { X-Reload-Triggers = toString config.reloadTriggers; }
         // optionalAttrs (config.description != "") {
           Description = config.description; }
@@ -288,45 +293,24 @@ in rec {
     };
   };
 
-  serviceConfig = { name, config, ... }: {
-    config = mkMerge
-      [ { # Default path for systemd services.  Should be quite minimal.
-          path = mkAfter
-            [ pkgs.coreutils
-              pkgs.findutils
-              pkgs.gnugrep
-              pkgs.gnused
-              systemd
-            ];
-          environment.PATH = "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
-        }
-        (mkIf (config.preStart != "")
-          { serviceConfig.ExecStartPre =
-              [ (makeJobScript "${name}-pre-start" config.preStart) ];
-          })
-        (mkIf (config.script != "")
-          { serviceConfig.ExecStart =
-              makeJobScript "${name}-start" config.script + " " + config.scriptArgs;
-          })
-        (mkIf (config.postStart != "")
-          { serviceConfig.ExecStartPost =
-              [ (makeJobScript "${name}-post-start" config.postStart) ];
-          })
-        (mkIf (config.reload != "")
-          { serviceConfig.ExecReload =
-              makeJobScript "${name}-reload" config.reload;
-          })
-        (mkIf (config.preStop != "")
-          { serviceConfig.ExecStop =
-              makeJobScript "${name}-pre-stop" config.preStop;
-          })
-        (mkIf (config.postStop != "")
-          { serviceConfig.ExecStopPost =
-              makeJobScript "${name}-post-stop" config.postStop;
-          })
-      ];
+  serviceConfig = { config, ... }: {
+    config.environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
   };
 
+  stage2ServiceConfig = {
+    imports = [ serviceConfig ];
+    # Default path for systemd services. Should be quite minimal.
+    config.path = mkAfter [
+      pkgs.coreutils
+      pkgs.findutils
+      pkgs.gnugrep
+      pkgs.gnused
+      systemd
+    ];
+  };
+
+  stage1ServiceConfig = serviceConfig;
+
   mountConfig = { config, ... }: {
     config = {
       mountConfig =
@@ -374,12 +358,12 @@ in rec {
               # systemd max line length is now 1MiB
               # https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
               in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)}
-          ${if def.reloadIfChanged then ''
+          ${if def ? reloadIfChanged && def.reloadIfChanged then ''
             X-ReloadIfChanged=true
-          '' else if !def.restartIfChanged then ''
+          '' else if (def ? restartIfChanged && !def.restartIfChanged) then ''
             X-RestartIfChanged=false
           '' else ""}
-          ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
+          ${optionalString (def ? stopIfChanged && !def.stopIfChanged) "X-StopIfChanged=false"}
           ${attrsToSection def.serviceConfig}
         '';
     };
diff --git a/nixos/lib/systemd-types.nix b/nixos/lib/systemd-types.nix
new file mode 100644
index 0000000000000..71962fab2e177
--- /dev/null
+++ b/nixos/lib/systemd-types.nix
@@ -0,0 +1,37 @@
+{ lib, systemdUtils }:
+
+with systemdUtils.lib;
+with systemdUtils.unitOptions;
+with lib;
+
+rec {
+  units = with types;
+    attrsOf (submodule ({ name, config, ... }: {
+      options = concreteUnitOptions;
+      config = { unit = mkDefault (systemdUtils.lib.makeUnit name config); };
+    }));
+
+  services = with types; attrsOf (submodule [ stage2ServiceOptions unitConfig stage2ServiceConfig ]);
+  initrdServices = with types; attrsOf (submodule [ stage1ServiceOptions unitConfig stage1ServiceConfig ]);
+
+  targets = with types; attrsOf (submodule [ stage2CommonUnitOptions unitConfig ]);
+  initrdTargets = with types; attrsOf (submodule [ stage1CommonUnitOptions unitConfig ]);
+
+  sockets = with types; attrsOf (submodule [ stage2SocketOptions unitConfig ]);
+  initrdSockets = with types; attrsOf (submodule [ stage1SocketOptions unitConfig ]);
+
+  timers = with types; attrsOf (submodule [ stage2TimerOptions unitConfig ]);
+  initrdTimers = with types; attrsOf (submodule [ stage1TimerOptions unitConfig ]);
+
+  paths = with types; attrsOf (submodule [ stage2PathOptions unitConfig ]);
+  initrdPaths = with types; attrsOf (submodule [ stage1PathOptions unitConfig ]);
+
+  slices = with types; attrsOf (submodule [ stage2SliceOptions unitConfig ]);
+  initrdSlices = with types; attrsOf (submodule [ stage1SliceOptions unitConfig ]);
+
+  mounts = with types; listOf (submodule [ stage2MountOptions unitConfig mountConfig ]);
+  initrdMounts = with types; listOf (submodule [ stage1MountOptions unitConfig mountConfig ]);
+
+  automounts = with types; listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]);
+  initrdAutomounts = with types; attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]);
+}
diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix
index 8029ba0e3f6cf..c9d424d391196 100644
--- a/nixos/lib/systemd-unit-options.nix
+++ b/nixos/lib/systemd-unit-options.nix
@@ -94,7 +94,7 @@ in rec {
 
   };
 
-  commonUnitOptions = sharedOptions // {
+  commonUnitOptions = { options = (sharedOptions // {
 
     description = mkOption {
       default = "";
@@ -191,27 +191,6 @@ in rec {
       '';
     };
 
-    restartTriggers = mkOption {
-      default = [];
-      type = types.listOf types.unspecified;
-      description = ''
-        An arbitrary list of items such as derivations.  If any item
-        in the list changes between reconfigurations, the service will
-        be restarted.
-      '';
-    };
-
-    reloadTriggers = mkOption {
-      default = [];
-      type = types.listOf unitOption;
-      description = ''
-        An arbitrary list of items such as derivations.  If any item
-        in the list changes between reconfigurations, the service will
-        be reloaded.  If anything but a reload trigger changes in the
-        unit file, the unit will be restarted instead.
-      '';
-    };
-
     onFailure = mkOption {
       default = [];
       type = types.listOf unitNameType;
@@ -239,10 +218,39 @@ in rec {
        '';
     };
 
+  }); };
+
+  stage2CommonUnitOptions = {
+    imports = [
+      commonUnitOptions
+    ];
+
+    options = {
+      restartTriggers = mkOption {
+        default = [];
+        type = types.listOf types.unspecified;
+        description = ''
+          An arbitrary list of items such as derivations.  If any item
+          in the list changes between reconfigurations, the service will
+          be restarted.
+        '';
+      };
+
+      reloadTriggers = mkOption {
+        default = [];
+        type = types.listOf unitOption;
+        description = ''
+          An arbitrary list of items such as derivations.  If any item
+          in the list changes between reconfigurations, the service will
+          be reloaded.  If anything but a reload trigger changes in the
+          unit file, the unit will be restarted instead.
+        '';
+      };
+    };
   };
+  stage1CommonUnitOptions = commonUnitOptions;
 
-
-  serviceOptions = commonUnitOptions // {
+  serviceOptions = { options = {
 
     environment = mkOption {
       default = {};
@@ -276,121 +284,164 @@ in rec {
       '';
     };
 
-    script = mkOption {
-      type = types.lines;
-      default = "";
-      description = "Shell commands executed as the service's main process.";
-    };
-
-    scriptArgs = mkOption {
-      type = types.str;
-      default = "";
-      description = "Arguments passed to the main process script.";
-    };
-
-    preStart = mkOption {
-      type = types.lines;
-      default = "";
-      description = ''
-        Shell commands executed before the service's main process
-        is started.
-      '';
-    };
-
-    postStart = mkOption {
-      type = types.lines;
-      default = "";
-      description = ''
-        Shell commands executed after the service's main process
-        is started.
-      '';
-    };
-
-    reload = mkOption {
-      type = types.lines;
-      default = "";
-      description = ''
-        Shell commands executed when the service's main process
-        is reloaded.
-      '';
-    };
-
-    preStop = mkOption {
-      type = types.lines;
-      default = "";
-      description = ''
-        Shell commands executed to stop the service.
-      '';
-    };
-
-    postStop = mkOption {
-      type = types.lines;
-      default = "";
-      description = ''
-        Shell commands executed after the service's main process
-        has exited.
-      '';
-    };
-
-    restartIfChanged = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        Whether the service should be restarted during a NixOS
-        configuration switch if its definition has changed.
-      '';
-    };
-
-    reloadIfChanged = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Whether the service should be reloaded during a NixOS
-        configuration switch if its definition has changed.  If
-        enabled, the value of <option>restartIfChanged</option> is
-        ignored.
-
-        This option should not be used anymore in favor of
-        <option>reloadTriggers</option> which allows more granular
-        control of when a service is reloaded and when a service
-        is restarted.
-      '';
-    };
-
-    stopIfChanged = mkOption {
-      type = types.bool;
-      default = true;
-      description = ''
-        If set, a changed unit is restarted by calling
-        <command>systemctl stop</command> in the old configuration,
-        then <command>systemctl start</command> in the new one.
-        Otherwise, it is restarted in a single step using
-        <command>systemctl restart</command> in the new configuration.
-        The latter is less correct because it runs the
-        <literal>ExecStop</literal> commands from the new
-        configuration.
-      '';
-    };
-
-    startAt = mkOption {
-      type = with types; either str (listOf str);
-      default = [];
-      example = "Sun 14:00:00";
-      description = ''
-        Automatically start this unit at the given date/time, which
-        must be in the format described in
-        <citerefentry><refentrytitle>systemd.time</refentrytitle>
-        <manvolnum>7</manvolnum></citerefentry>.  This is equivalent
-        to adding a corresponding timer unit with
-        <option>OnCalendar</option> set to the value given here.
-      '';
-      apply = v: if isList v then v else [ v ];
-    };
+  }; };
+
+  stage2ServiceOptions = { name, config, ... }: {
+    imports = [
+      stage2CommonUnitOptions
+      serviceOptions
+    ];
+
+    options = {
+      script = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Shell commands executed as the service's main process.";
+      };
+
+      scriptArgs = mkOption {
+        type = types.str;
+        default = "";
+        description = "Arguments passed to the main process script.";
+      };
+
+      preStart = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Shell commands executed before the service's main process
+          is started.
+        '';
+      };
+
+      postStart = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Shell commands executed after the service's main process
+          is started.
+        '';
+      };
+
+      reload = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Shell commands executed when the service's main process
+          is reloaded.
+        '';
+      };
+
+      preStop = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Shell commands executed to stop the service.
+        '';
+      };
+
+      postStop = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Shell commands executed after the service's main process
+          has exited.
+        '';
+      };
+
+      restartIfChanged = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether the service should be restarted during a NixOS
+          configuration switch if its definition has changed.
+        '';
+      };
+
+      reloadIfChanged = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether the service should be reloaded during a NixOS
+          configuration switch if its definition has changed.  If
+          enabled, the value of <option>restartIfChanged</option> is
+          ignored.
+
+          This option should not be used anymore in favor of
+          <option>reloadTriggers</option> which allows more granular
+          control of when a service is reloaded and when a service
+          is restarted.
+        '';
+      };
+
+      stopIfChanged = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          If set, a changed unit is restarted by calling
+          <command>systemctl stop</command> in the old configuration,
+          then <command>systemctl start</command> in the new one.
+          Otherwise, it is restarted in a single step using
+          <command>systemctl restart</command> in the new configuration.
+          The latter is less correct because it runs the
+          <literal>ExecStop</literal> commands from the new
+          configuration.
+        '';
+      };
+
+      startAt = mkOption {
+        type = with types; either str (listOf str);
+        default = [];
+        example = "Sun 14:00:00";
+        description = ''
+          Automatically start this unit at the given date/time, which
+          must be in the format described in
+          <citerefentry><refentrytitle>systemd.time</refentrytitle>
+          <manvolnum>7</manvolnum></citerefentry>.  This is equivalent
+          to adding a corresponding timer unit with
+          <option>OnCalendar</option> set to the value given here.
+        '';
+        apply = v: if isList v then v else [ v ];
+      };
+    };
+
+    config = mkMerge
+      [ (mkIf (config.preStart != "")
+          { serviceConfig.ExecStartPre =
+              [ (makeJobScript "${name}-pre-start" config.preStart) ];
+          })
+        (mkIf (config.script != "")
+          { serviceConfig.ExecStart =
+              makeJobScript "${name}-start" config.script + " " + config.scriptArgs;
+          })
+        (mkIf (config.postStart != "")
+          { serviceConfig.ExecStartPost =
+              [ (makeJobScript "${name}-post-start" config.postStart) ];
+          })
+        (mkIf (config.reload != "")
+          { serviceConfig.ExecReload =
+              makeJobScript "${name}-reload" config.reload;
+          })
+        (mkIf (config.preStop != "")
+          { serviceConfig.ExecStop =
+              makeJobScript "${name}-pre-stop" config.preStop;
+          })
+        (mkIf (config.postStop != "")
+          { serviceConfig.ExecStopPost =
+              makeJobScript "${name}-post-stop" config.postStop;
+          })
+      ];
+  };
 
+  stage1ServiceOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      serviceOptions
+    ];
   };
 
 
-  socketOptions = commonUnitOptions // {
+  socketOptions = { options = {
 
     listenStreams = mkOption {
       default = [];
@@ -424,10 +475,24 @@ in rec {
       '';
     };
 
+  }; };
+
+  stage2SocketOptions = {
+    imports = [
+      stage2CommonUnitOptions
+      socketOptions
+    ];
   };
 
+  stage1SocketOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      socketOptions
+    ];
+  };
 
-  timerOptions = commonUnitOptions // {
+
+  timerOptions = { options = {
 
     timerConfig = mkOption {
       default = {};
@@ -443,10 +508,24 @@ in rec {
       '';
     };
 
+  }; };
+
+  stage2TimerOptions = {
+    imports = [
+      stage2CommonUnitOptions
+      timerOptions
+    ];
   };
 
+  stage1TimerOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      timerOptions
+    ];
+  };
 
-  pathOptions = commonUnitOptions // {
+
+  pathOptions = { options = {
 
     pathConfig = mkOption {
       default = {};
@@ -460,10 +539,24 @@ in rec {
       '';
     };
 
+  }; };
+
+  stage2PathOptions = {
+    imports = [
+      stage2CommonUnitOptions
+      pathOptions
+    ];
+  };
+
+  stage1PathOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      pathOptions
+    ];
   };
 
 
-  mountOptions = commonUnitOptions // {
+  mountOptions = { options = {
 
     what = mkOption {
       example = "/dev/sda1";
@@ -505,9 +598,23 @@ in rec {
         <manvolnum>5</manvolnum></citerefentry> for details.
       '';
     };
+  }; };
+
+  stage2MountOptions = {
+    imports = [
+      stage2CommonUnitOptions
+      mountOptions
+    ];
+  };
+
+  stage1MountOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      mountOptions
+    ];
   };
 
-  automountOptions = commonUnitOptions // {
+  automountOptions = { options = {
 
     where = mkOption {
       example = "/mnt";
@@ -529,11 +636,23 @@ in rec {
         <manvolnum>5</manvolnum></citerefentry> for details.
       '';
     };
+  }; };
+
+  stage2AutomountOptions = {
+    imports = [
+      stage2CommonUnitOptions
+      automountOptions
+    ];
   };
 
-  targetOptions = commonUnitOptions;
+  stage1AutomountOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      automountOptions
+    ];
+  };
 
-  sliceOptions = commonUnitOptions // {
+  sliceOptions = { options = {
 
     sliceConfig = mkOption {
       default = {};
@@ -547,6 +666,20 @@ in rec {
       '';
     };
 
+  }; };
+
+  stage2SliceOptions = {
+    imports = [
+      stage2CommonUnitOptions
+      sliceOptions
+    ];
+  };
+
+  stage1SliceOptions = {
+    imports = [
+      stage1CommonUnitOptions
+      sliceOptions
+    ];
   };
 
 }
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index ae68c3920c5bb..80341dd48fcd5 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -197,5 +197,6 @@ rec {
   systemdUtils = {
     lib = import ./systemd-lib.nix { inherit lib config pkgs; };
     unitOptions = import ./systemd-unit-options.nix { inherit lib systemdUtils; };
+    types = import ./systemd-types.nix { inherit lib systemdUtils; };
   };
 }
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index d825f4beb301c..931201ade2935 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -15,6 +15,26 @@ let
       mapAttrsToList (n: v: ''${n}=${escapeIfNeccessary (toString v)}'') attrs
     );
 
+  osReleaseContents = {
+    NAME = "NixOS";
+    ID = "nixos";
+    VERSION = "${cfg.release} (${cfg.codeName})";
+    VERSION_CODENAME = toLower cfg.codeName;
+    VERSION_ID = cfg.release;
+    BUILD_ID = cfg.version;
+    PRETTY_NAME = "NixOS ${cfg.release} (${cfg.codeName})";
+    LOGO = "nix-snowflake";
+    HOME_URL = "https://nixos.org/";
+    DOCUMENTATION_URL = "https://nixos.org/learn.html";
+    SUPPORT_URL = "https://nixos.org/community.html";
+    BUG_REPORT_URL = "https://github.com/NixOS/nixpkgs/issues";
+  };
+
+  initrdReleaseContents = osReleaseContents // {
+    PRETTY_NAME = "${osReleaseContents.PRETTY_NAME} (Initrd)";
+  };
+  initrdRelease = pkgs.writeText "initrd-release" (attrsToText initrdReleaseContents);
+
 in
 {
   imports = [
@@ -119,20 +139,12 @@ in
         DISTRIB_DESCRIPTION = "NixOS ${cfg.release} (${cfg.codeName})";
       };
 
-      "os-release".text = attrsToText {
-        NAME = "NixOS";
-        ID = "nixos";
-        VERSION = "${cfg.release} (${cfg.codeName})";
-        VERSION_CODENAME = toLower cfg.codeName;
-        VERSION_ID = cfg.release;
-        BUILD_ID = cfg.version;
-        PRETTY_NAME = "NixOS ${cfg.release} (${cfg.codeName})";
-        LOGO = "nix-snowflake";
-        HOME_URL = "https://nixos.org/";
-        DOCUMENTATION_URL = "https://nixos.org/learn.html";
-        SUPPORT_URL = "https://nixos.org/community.html";
-        BUG_REPORT_URL = "https://github.com/NixOS/nixpkgs/issues";
-      };
+      "os-release".text = attrsToText osReleaseContents;
+    };
+
+    boot.initrd.systemd.contents = {
+      "/etc/os-release".source = initrdRelease;
+      "/etc/initrd-release".source = initrdRelease;
     };
   };
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index c4958c36ea004..789faea91977c 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1181,6 +1181,7 @@
   ./system/boot/systemd/nspawn.nix
   ./system/boot/systemd/tmpfiles.nix
   ./system/boot/systemd/user.nix
+  ./system/boot/systemd/initrd.nix
   ./system/boot/timesyncd.nix
   ./system/boot/tmp.nix
   ./system/etc/etc-activation.nix
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 8b011d91563f0..1bafec30b53d4 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -703,8 +703,12 @@ in
       }
     ];
 
-    system.build =
-      { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
+    system.build = mkMerge [
+      { inherit bootStage1 initialRamdiskSecretAppender extraUtils; }
+
+      # generated in nixos/modules/system/boot/systemd/initrd.nix
+      (mkIf (!config.boot.initrd.systemd.enable) { inherit initialRamdisk; })
+    ];
 
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isYes "TMPFS")
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index f69c5d3d5a641..844a6793c1540 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -11,14 +11,7 @@ let
   systemd = cfg.package;
 
   inherit (systemdUtils.lib)
-    makeUnit
     generateUnits
-    makeJobScript
-    unitConfig
-    serviceConfig
-    mountConfig
-    automountConfig
-    commonUnitText
     targetToUnit
     serviceToUnit
     socketToUnit
@@ -185,13 +178,7 @@ in
     systemd.units = mkOption {
       description = "Definition of systemd units.";
       default = {};
-      type = with types; attrsOf (submodule (
-        { name, config, ... }:
-        { options = concreteUnitOptions;
-          config = {
-            unit = mkDefault (makeUnit name config);
-          };
-        }));
+      type = systemdUtils.types.units;
     };
 
     systemd.packages = mkOption {
@@ -203,37 +190,37 @@ in
 
     systemd.targets = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
+      type = systemdUtils.types.targets;
       description = "Definition of systemd target units.";
     };
 
     systemd.services = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
+      type = systemdUtils.types.services;
       description = "Definition of systemd service units.";
     };
 
     systemd.sockets = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]);
+      type = systemdUtils.types.sockets;
       description = "Definition of systemd socket units.";
     };
 
     systemd.timers = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]);
+      type = systemdUtils.types.timers;
       description = "Definition of systemd timer units.";
     };
 
     systemd.paths = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
+      type = systemdUtils.types.paths;
       description = "Definition of systemd path units.";
     };
 
     systemd.mounts = mkOption {
       default = [];
-      type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]);
+      type = systemdUtils.types.mounts;
       description = ''
         Definition of systemd mount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
@@ -243,7 +230,7 @@ in
 
     systemd.automounts = mkOption {
       default = [];
-      type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]);
+      type = systemdUtils.types.automounts;
       description = ''
         Definition of systemd automount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
@@ -253,7 +240,7 @@ in
 
     systemd.slices = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig] );
+      type = systemdUtils.types.slices;
       description = "Definition of slice configurations.";
     };
 
@@ -362,10 +349,11 @@ in
       type = types.listOf types.str;
       example = [ "systemd-backlight@.service" ];
       description = ''
-        A list of units to suppress when generating system systemd configuration directory. This has
+        A list of units to skip when generating system systemd configuration directory. This has
         priority over upstream units, <option>systemd.units</option>, and
         <option>systemd.additionalUpstreamSystemUnits</option>. The main purpose of this is to
-        suppress a upstream systemd unit with any modifications made to it by other NixOS modules.
+        prevent a upstream systemd unit from being added to the initrd with any modifications made to it
+        by other NixOS modules.
       '';
     };
 
@@ -482,7 +470,12 @@ in
       enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units;
 
     in ({
-      "systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
+      "systemd/system".source = generateUnits {
+        type = "system";
+        units = enabledUnits;
+        upstreamUnits = enabledUpstreamSystemUnits;
+        upstreamWants = upstreamSystemWants;
+      };
 
       "systemd/system.conf".text = ''
         [Manager]
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
new file mode 100644
index 0000000000000..30bdc9a3422c7
--- /dev/null
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -0,0 +1,417 @@
+{ lib, config, utils, pkgs, ... }:
+
+with lib;
+
+let
+  inherit (utils) systemdUtils escapeSystemdPath;
+  inherit (systemdUtils.lib)
+    generateUnits
+    pathToUnit
+    serviceToUnit
+    sliceToUnit
+    socketToUnit
+    targetToUnit
+    timerToUnit
+    mountToUnit
+    automountToUnit;
+
+
+  cfg = config.boot.initrd.systemd;
+
+  # Copied from fedora
+  upstreamUnits = [
+    "basic.target"
+    "ctrl-alt-del.target"
+    "emergency.service"
+    "emergency.target"
+    "final.target"
+    "halt.target"
+    "initrd-cleanup.service"
+    "initrd-fs.target"
+    "initrd-parse-etc.service"
+    "initrd-root-device.target"
+    "initrd-root-fs.target"
+    "initrd-switch-root.service"
+    "initrd-switch-root.target"
+    "initrd.target"
+    "initrd-udevadm-cleanup-db.service"
+    "kexec.target"
+    "kmod-static-nodes.service"
+    "local-fs-pre.target"
+    "local-fs.target"
+    "multi-user.target"
+    "paths.target"
+    "poweroff.target"
+    "reboot.target"
+    "rescue.service"
+    "rescue.target"
+    "rpcbind.target"
+    "shutdown.target"
+    "sigpwr.target"
+    "slices.target"
+    "sockets.target"
+    "swap.target"
+    "sysinit.target"
+    "sys-kernel-config.mount"
+    "syslog.socket"
+    "systemd-ask-password-console.path"
+    "systemd-ask-password-console.service"
+    "systemd-fsck@.service"
+    "systemd-halt.service"
+    "systemd-hibernate-resume@.service"
+    "systemd-journald-audit.socket"
+    "systemd-journald-dev-log.socket"
+    "systemd-journald.service"
+    "systemd-journald.socket"
+    "systemd-kexec.service"
+    "systemd-modules-load.service"
+    "systemd-poweroff.service"
+    "systemd-random-seed.service"
+    "systemd-reboot.service"
+    "systemd-sysctl.service"
+    "systemd-tmpfiles-setup-dev.service"
+    "systemd-tmpfiles-setup.service"
+    "systemd-udevd-control.socket"
+    "systemd-udevd-kernel.socket"
+    "systemd-udevd.service"
+    "systemd-udev-settle.service"
+    "systemd-udev-trigger.service"
+    "systemd-vconsole-setup.service"
+    "timers.target"
+    "umount.target"
+
+    # TODO: Networking
+    # "network-online.target"
+    # "network-pre.target"
+    # "network.target"
+    # "nss-lookup.target"
+    # "nss-user-lookup.target"
+    # "remote-fs-pre.target"
+    # "remote-fs.target"
+  ] ++ cfg.additionalUpstreamUnits;
+
+  upstreamWants = [
+    "sysinit.target.wants"
+  ];
+
+  enabledUpstreamUnits = filter (n: ! elem n cfg.suppressedUnits) upstreamUnits;
+  enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedUnits) cfg.units;
+
+  stage1Units = generateUnits {
+    type = "initrd";
+    units = enabledUnits;
+    upstreamUnits = enabledUpstreamUnits;
+    inherit upstreamWants;
+    inherit (cfg) packages package;
+  };
+
+  fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
+
+  fstab = pkgs.writeText "fstab" (lib.concatMapStringsSep "\n"
+    ({ fsType, mountPoint, device, options, autoFormat, autoResize, ... }@fs: let
+        opts = options ++ optional autoFormat "x-systemd.makefs" ++ optional autoResize "x-systemd.growfs";
+      in "${device} /sysroot${mountPoint} ${fsType} ${lib.concatStringsSep "," opts}") fileSystems);
+
+  kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
+  modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
+  firmware = config.hardware.firmware;
+  # Determine the set of modules that we need to mount the root FS.
+  modulesClosure = pkgs.makeModulesClosure {
+    rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
+    kernel = modulesTree;
+    firmware = firmware;
+    allowMissing = false;
+  };
+
+  initrdBinEnv = pkgs.buildEnv {
+    name = "initrd-emergency-env";
+    paths = map getBin cfg.initrdBin;
+    pathsToLink = ["/bin" "/sbin"];
+    # Make recovery easier
+    postBuild = ''
+      ln -s ${cfg.package.util-linux}/bin/mount $out/bin/
+      ln -s ${cfg.package.util-linux}/bin/umount $out/bin/
+    '';
+  };
+
+  initialRamdisk = pkgs.makeInitrdNG {
+    contents = map (path: { object = path; symlink = ""; }) (subtractLists cfg.suppressedStorePaths cfg.storePaths)
+      ++ mapAttrsToList (_: v: { object = v.source; symlink = v.target; }) (filterAttrs (_: v: v.enable) cfg.contents);
+  };
+
+in {
+  options.boot.initrd.systemd = {
+    enable = mkEnableOption ''systemd in initrd.
+
+      Note: This is in very early development and is highly
+      experimental. Most of the features NixOS supports in initrd are
+      not yet supported by the intrd generated with this option.
+    '';
+
+    package = (mkPackageOption pkgs "systemd" {
+      default = "systemdMinimal";
+    }) // {
+      visible = false;
+    };
+
+    contents = mkOption {
+      description = "Set of files that have to be linked into the initrd";
+      example = literalExpression ''
+        {
+          "/etc/hostname".text = "mymachine";
+        }
+      '';
+      visible = false;
+      default = {};
+      type = types.attrsOf (types.submodule ({ config, options, name, ... }: {
+        options = {
+          enable = mkEnableOption "copying of this file to initrd and symlinking it" // { default = true; };
+
+          target = mkOption {
+            type = types.path;
+            description = ''
+              Path of the symlink.
+            '';
+            default = name;
+          };
+
+          text = mkOption {
+            default = null;
+            type = types.nullOr types.lines;
+            description = "Text of the file.";
+          };
+
+          source = mkOption {
+            type = types.path;
+            description = "Path of the source file.";
+          };
+        };
+
+        config = {
+          source = mkIf (config.text != null) (
+            let name' = "initrd-" + baseNameOf name;
+            in mkDerivedConfig options.text (pkgs.writeText name')
+          );
+        };
+      }));
+    };
+
+    storePaths = mkOption {
+      description = ''
+        Store paths to copy into the initrd as well.
+      '';
+      type = types.listOf types.singleLineStr;
+      default = [];
+    };
+
+    suppressedStorePaths = mkOption {
+      description = ''
+        Store paths specified in the storePaths option that
+        should not be copied.
+      '';
+      type = types.listOf types.singleLineStr;
+      default = [];
+    };
+
+    emergencyAccess = mkOption {
+      type = with types; oneOf [ bool singleLineStr ];
+      visible = false;
+      description = ''
+        Set to true for unauthenticated emergency access, and false for
+        no emergency access.
+
+        Can also be set to a hashed super user password to allow
+        authenticated access to the emergency mode.
+      '';
+      default = false;
+    };
+
+    initrdBin = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      visible = false;
+      description = ''
+        Packages to include in /bin for the stage 1 emergency shell.
+      '';
+    };
+
+    additionalUpstreamUnits = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      visible = false;
+      example = [ "debug-shell.service" "systemd-quotacheck.service" ];
+      description = ''
+        Additional units shipped with systemd that shall be enabled.
+      '';
+    };
+
+    suppressedUnits = mkOption {
+      default = [ ];
+      type = types.listOf types.str;
+      example = [ "systemd-backlight@.service" ];
+      visible = false;
+      description = ''
+        A list of units to skip when generating system systemd configuration directory. This has
+        priority over upstream units, <option>boot.initrd.systemd.units</option>, and
+        <option>boot.initrd.systemd.additionalUpstreamUnits</option>. The main purpose of this is to
+        prevent a upstream systemd unit from being added to the initrd with any modifications made to it
+        by other NixOS modules.
+      '';
+    };
+
+    units = mkOption {
+      description = "Definition of systemd units.";
+      default = {};
+      visible = false;
+      type = systemdUtils.types.units;
+    };
+
+    packages = mkOption {
+      default = [];
+      visible = false;
+      type = types.listOf types.package;
+      example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
+      description = "Packages providing systemd units and hooks.";
+    };
+
+    targets = mkOption {
+      default = {};
+      visible = false;
+      type = systemdUtils.types.initrdTargets;
+      description = "Definition of systemd target units.";
+    };
+
+    services = mkOption {
+      default = {};
+      type = systemdUtils.types.initrdServices;
+      visible = false;
+      description = "Definition of systemd service units.";
+    };
+
+    sockets = mkOption {
+      default = {};
+      type = systemdUtils.types.initrdSockets;
+      visible = false;
+      description = "Definition of systemd socket units.";
+    };
+
+    timers = mkOption {
+      default = {};
+      type = systemdUtils.types.initrdTimers;
+      visible = false;
+      description = "Definition of systemd timer units.";
+    };
+
+    paths = mkOption {
+      default = {};
+      type = systemdUtils.types.initrdPaths;
+      visible = false;
+      description = "Definition of systemd path units.";
+    };
+
+    mounts = mkOption {
+      default = [];
+      type = systemdUtils.types.initrdMounts;
+      visible = false;
+      description = ''
+        Definition of systemd mount units.
+        This is a list instead of an attrSet, because systemd mandates the names to be derived from
+        the 'where' attribute.
+      '';
+    };
+
+    automounts = mkOption {
+      default = [];
+      type = systemdUtils.types.automounts;
+      visible = false;
+      description = ''
+        Definition of systemd automount units.
+        This is a list instead of an attrSet, because systemd mandates the names to be derived from
+        the 'where' attribute.
+      '';
+    };
+
+    slices = mkOption {
+      default = {};
+      type = systemdUtils.types.slices;
+      visible = false;
+      description = "Definition of slice configurations.";
+    };
+  };
+
+  config = mkIf (config.boot.initrd.enable && cfg.enable) {
+    system.build = { inherit initialRamdisk; };
+    boot.initrd.systemd = {
+      initrdBin = [pkgs.bash pkgs.coreutils pkgs.kmod cfg.package] ++ config.system.fsPackages;
+
+      contents = {
+        "/init".source = "${cfg.package}/lib/systemd/systemd";
+        "/etc/systemd/system".source = stage1Units;
+
+        "/etc/systemd/system.conf".text = ''
+          [Manager]
+          DefaultEnvironment=PATH=/bin:/sbin
+        '';
+
+        "/etc/fstab".source = fstab;
+
+        "/lib/modules".source = "${modulesClosure}/lib/modules";
+
+        "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules;
+
+        "/etc/passwd".source = "${pkgs.fakeNss}/etc/passwd";
+        "/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then "!" else cfg.emergencyAccess}:::::::";
+
+        "/bin".source = "${initrdBinEnv}/bin";
+        "/sbin".source = "${initrdBinEnv}/sbin";
+
+        "/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe";
+      };
+
+      storePaths = [
+        # TODO: Limit this to the bare necessities
+        "${cfg.package}/lib"
+
+        "${cfg.package.util-linux}/bin/mount"
+        "${cfg.package.util-linux}/bin/umount"
+        "${cfg.package.util-linux}/bin/sulogin"
+
+        # so NSS can look up usernames
+        "${pkgs.glibc}/lib/libnss_files.so"
+      ];
+
+      targets.initrd.aliases = ["default.target"];
+      units =
+           mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit    n v)) cfg.paths
+        // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
+        // mapAttrs' (n: v: nameValuePair "${n}.slice"   (sliceToUnit   n v)) cfg.slices
+        // mapAttrs' (n: v: nameValuePair "${n}.socket"  (socketToUnit  n v)) cfg.sockets
+        // mapAttrs' (n: v: nameValuePair "${n}.target"  (targetToUnit  n v)) cfg.targets
+        // mapAttrs' (n: v: nameValuePair "${n}.timer"   (timerToUnit   n v)) cfg.timers
+        // listToAttrs (map
+                     (v: let n = escapeSystemdPath v.where;
+                         in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
+        // listToAttrs (map
+                     (v: let n = escapeSystemdPath v.where;
+                         in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
+
+      services.emergency = mkIf (isBool cfg.emergencyAccess && cfg.emergencyAccess) {
+        environment.SYSTEMD_SULOGIN_FORCE = "1";
+      };
+      # The unit in /run/systemd/generator shadows the unit in
+      # /etc/systemd/system, but will still apply drop-ins from
+      # /etc/systemd/system/foo.service.d/
+      #
+      # We need IgnoreOnIsolate, otherwise the Requires dependency of
+      # a mount unit on its makefs unit causes it to be unmounted when
+      # we isolate for switch-root. Use a dummy package so that
+      # generateUnits will generate drop-ins instead of unit files.
+      packages = [(pkgs.runCommand "dummy" {} ''
+        mkdir -p $out/etc/systemd/system
+        touch $out/etc/systemd/system/systemd-{makefs,growfs}@.service
+      '')];
+      services."systemd-makefs@".unitConfig.IgnoreOnIsolate = true;
+      services."systemd-growfs@".unitConfig.IgnoreOnIsolate = true;
+    };
+  };
+}
diff --git a/nixos/modules/system/boot/systemd/nspawn.nix b/nixos/modules/system/boot/systemd/nspawn.nix
index 0c6822319a5b0..bf9995d03cc18 100644
--- a/nixos/modules/system/boot/systemd/nspawn.nix
+++ b/nixos/modules/system/boot/systemd/nspawn.nix
@@ -116,7 +116,13 @@ in {
     in
       mkMerge [
         (mkIf (cfg != {}) {
-          environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits' false "nspawn" units [] []);
+          environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits {
+            allowCollisions = false;
+            type = "nspawn";
+            inherit units;
+            upstreamUnits = [];
+            upstreamWants = [];
+          });
         })
         {
           systemd.targets.multi-user.wants = [ "machines.target" ];
diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix
index e30f83f3457f8..4951aef95584b 100644
--- a/nixos/modules/system/boot/systemd/user.nix
+++ b/nixos/modules/system/boot/systemd/user.nix
@@ -12,10 +12,6 @@ let
     (systemdUtils.lib)
     makeUnit
     generateUnits
-    makeJobScript
-    unitConfig
-    serviceConfig
-    commonUnitText
     targetToUnit
     serviceToUnit
     socketToUnit
@@ -57,48 +53,42 @@ in {
     systemd.user.units = mkOption {
       description = "Definition of systemd per-user units.";
       default = {};
-      type = with types; attrsOf (submodule (
-        { name, config, ... }:
-        { options = concreteUnitOptions;
-          config = {
-            unit = mkDefault (makeUnit name config);
-          };
-        }));
+      type = systemdUtils.types.units;
     };
 
     systemd.user.paths = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
+      type = systemdUtils.types.paths;
       description = "Definition of systemd per-user path units.";
     };
 
     systemd.user.services = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
+      type = systemdUtils.types.services;
       description = "Definition of systemd per-user service units.";
     };
 
     systemd.user.slices = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
+      type = systemdUtils.types.slices;
       description = "Definition of systemd per-user slice units.";
     };
 
     systemd.user.sockets = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
+      type = systemdUtils.types.sockets;
       description = "Definition of systemd per-user socket units.";
     };
 
     systemd.user.targets = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
+      type = systemdUtils.types.targets;
       description = "Definition of systemd per-user target units.";
     };
 
     systemd.user.timers = mkOption {
       default = {};
-      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
+      type = systemdUtils.types.timers;
       description = "Definition of systemd per-user timer units.";
     };
 
@@ -119,7 +109,12 @@ in {
     ];
 
     environment.etc = {
-      "systemd/user".source = generateUnits "user" cfg.units upstreamUserUnits [];
+      "systemd/user".source = generateUnits {
+        type = "user";
+        inherit (cfg) units;
+        upstreamUnits = upstreamUserUnits;
+        upstreamWants = [];
+      };
 
       "systemd/user.conf".text = ''
         [Manager]
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index d68edd8d7d394..b8afe231dd2e1 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -352,7 +352,7 @@ in
             unitConfig.DefaultDependencies = false; # needed to prevent a cycle
             serviceConfig.Type = "oneshot";
           };
-      in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems)) // {
+      in listToAttrs (map formatDevice (filter (fs: fs.autoFormat && !(utils.fsNeededForBoot fs)) fileSystems)) // {
     # Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
     # This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
         "mount-pstore" = {
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 760f69121612b..74f6521462b85 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -923,6 +923,8 @@ in
       mkVMOverride (cfg.fileSystems //
       {
         "/".device = cfg.bootDevice;
+        "/".fsType = "ext4";
+        "/".autoFormat = true;
 
         "/tmp" = mkIf config.boot.tmpOnTmpfs
           { device = "tmpfs";
@@ -953,6 +955,28 @@ in
           };
       } // lib.mapAttrs' mkSharedDir cfg.sharedDirectories);
 
+    boot.initrd.systemd = lib.mkIf (config.boot.initrd.systemd.enable && cfg.writableStore) {
+      mounts = [{
+        where = "/sysroot/nix/store";
+        what = "overlay";
+        type = "overlay";
+        options = "lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/store,workdir=/sysroot/nix/.rw-store/work";
+        wantedBy = ["local-fs.target"];
+        before = ["local-fs.target"];
+        requires = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"];
+        after = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"];
+        unitConfig.IgnoreOnIsolate = true;
+      }];
+      services.rw-store = {
+        after = ["sysroot-nix-.rw\\x2dstore.mount"];
+        unitConfig.DefaultDependencies = false;
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "/bin/mkdir -p 0755 /sysroot/nix/.rw-store/store /sysroot/nix/.rw-store/work /sysroot/nix/store";
+        };
+      };
+    };
+
     swapDevices = mkVMOverride [ ];
     boot.initrd.luks.devices = mkVMOverride {};
 
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index cf4bfecf6f193..dcbdf34e9441c 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -514,6 +514,7 @@ in
   systemd-confinement = handleTest ./systemd-confinement.nix {};
   systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
   systemd-escaping = handleTest ./systemd-escaping.nix {};
+  systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {};
   systemd-journal = handleTest ./systemd-journal.nix {};
   systemd-machinectl = handleTest ./systemd-machinectl.nix {};
   systemd-networkd = handleTest ./systemd-networkd.nix {};
diff --git a/nixos/tests/systemd-initrd-simple.nix b/nixos/tests/systemd-initrd-simple.nix
new file mode 100644
index 0000000000000..ba62cdf3bbc77
--- /dev/null
+++ b/nixos/tests/systemd-initrd-simple.nix
@@ -0,0 +1,27 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "systemd-initrd-simple";
+
+  machine = { pkgs, ... }: {
+    boot.initrd.systemd = {
+      enable = true;
+      emergencyAccess = true;
+    };
+    fileSystems = lib.mkVMOverride {
+      "/".autoResize = true;
+    };
+  };
+
+  testScript = ''
+    import subprocess
+
+    oldAvail = machine.succeed("df --output=avail / | sed 1d")
+    machine.shutdown()
+
+    subprocess.check_call(["qemu-img", "resize", "vm-state-machine/machine.qcow2", "+1G"])
+
+    machine.start()
+    newAvail = machine.succeed("df --output=avail / | sed 1d")
+
+    assert int(oldAvail) < int(newAvail), "File system did not grow"
+  '';
+})
diff --git a/pkgs/build-support/docker/default.nix b/pkgs/build-support/docker/default.nix
index 5718cadd4ffa4..96ea363c811e0 100644
--- a/pkgs/build-support/docker/default.nix
+++ b/pkgs/build-support/docker/default.nix
@@ -6,6 +6,7 @@
 , coreutils
 , e2fsprogs
 , fakechroot
+, fakeNss
 , fakeroot
 , findutils
 , go
@@ -747,25 +748,7 @@ rec {
   # Useful when packaging binaries that insist on using nss to look up
   # username/groups (like nginx).
   # /bin/sh is fine to not exist, and provided by another shim.
-  fakeNss = symlinkJoin {
-    name = "fake-nss";
-    paths = [
-      (writeTextDir "etc/passwd" ''
-        root:x:0:0:root user:/var/empty:/bin/sh
-        nobody:x:65534:65534:nobody:/var/empty:/bin/sh
-      '')
-      (writeTextDir "etc/group" ''
-        root:x:0:
-        nobody:x:65534:
-      '')
-      (writeTextDir "etc/nsswitch.conf" ''
-        hosts: files dns
-      '')
-      (runCommand "var-empty" { } ''
-        mkdir -p $out/var/empty
-      '')
-    ];
-  };
+  inherit fakeNss; # alias
 
   # This provides a /usr/bin/env, for shell scripts using the
   # "#!/usr/bin/env executable" shebang.
diff --git a/pkgs/build-support/fake-nss/default.nix b/pkgs/build-support/fake-nss/default.nix
new file mode 100644
index 0000000000000..9e0b60133e00f
--- /dev/null
+++ b/pkgs/build-support/fake-nss/default.nix
@@ -0,0 +1,24 @@
+# Provide a /etc/passwd and /etc/group that contain root and nobody.
+# Useful when packaging binaries that insist on using nss to look up
+# username/groups (like nginx).
+# /bin/sh is fine to not exist, and provided by another shim.
+{ symlinkJoin, writeTextDir, runCommand }:
+symlinkJoin {
+  name = "fake-nss";
+  paths = [
+    (writeTextDir "etc/passwd" ''
+      root:x:0:0:root user:/var/empty:/bin/sh
+      nobody:x:65534:65534:nobody:/var/empty:/bin/sh
+    '')
+    (writeTextDir "etc/group" ''
+      root:x:0:
+      nobody:x:65534:
+    '')
+    (writeTextDir "etc/nsswitch.conf" ''
+      hosts: files dns
+    '')
+    (runCommand "var-empty" { } ''
+      mkdir -p $out/var/empty
+    '')
+  ];
+}
diff --git a/pkgs/build-support/kernel/make-initrd-ng-tool.nix b/pkgs/build-support/kernel/make-initrd-ng-tool.nix
new file mode 100644
index 0000000000000..66ffc09d43cf8
--- /dev/null
+++ b/pkgs/build-support/kernel/make-initrd-ng-tool.nix
@@ -0,0 +1,9 @@
+{ rustPlatform }:
+
+rustPlatform.buildRustPackage {
+  pname = "make-initrd-ng";
+  version = "0.1.0";
+
+  src = ./make-initrd-ng;
+  cargoLock.lockFile = ./make-initrd-ng/Cargo.lock;
+}
diff --git a/pkgs/build-support/kernel/make-initrd-ng.nix b/pkgs/build-support/kernel/make-initrd-ng.nix
new file mode 100644
index 0000000000000..9fd202c44847a
--- /dev/null
+++ b/pkgs/build-support/kernel/make-initrd-ng.nix
@@ -0,0 +1,79 @@
+let
+  # Some metadata on various compression programs, relevant to naming
+  # the initramfs file and, if applicable, generating a u-boot image
+  # from it.
+  compressors = import ./initrd-compressor-meta.nix;
+  # Get the basename of the actual compression program from the whole
+  # compression command, for the purpose of guessing the u-boot
+  # compression type and filename extension.
+  compressorName = fullCommand: builtins.elemAt (builtins.match "([^ ]*/)?([^ ]+).*" fullCommand) 1;
+in
+{ stdenvNoCC, perl, cpio, ubootTools, lib, pkgsBuildHost, makeInitrdNGTool, patchelf, runCommand, glibc
+# Name of the derivation (not of the resulting file!)
+, name ? "initrd"
+
+# Program used to compress the cpio archive; use "cat" for no compression.
+# This can also be a function which takes a package set and returns the path to the compressor,
+# such as `pkgs: "${pkgs.lzop}/bin/lzop"`.
+, compressor ? "gzip"
+, _compressorFunction ?
+  if lib.isFunction compressor then compressor
+  else if ! builtins.hasContext compressor && builtins.hasAttr compressor compressors then compressors.${compressor}.executable
+  else _: compressor
+, _compressorExecutable ? _compressorFunction pkgsBuildHost
+, _compressorName ? compressorName _compressorExecutable
+, _compressorMeta ? compressors.${_compressorName} or {}
+
+# List of arguments to pass to the compressor program, or null to use its defaults
+, compressorArgs ? null
+, _compressorArgsReal ? if compressorArgs == null then _compressorMeta.defaultArgs or [] else compressorArgs
+
+# Filename extension to use for the compressed initramfs. This is
+# included for clarity, but $out/initrd will always be a symlink to
+# the final image.
+# If this isn't guessed, you may want to complete the metadata above and send a PR :)
+, extension ? _compressorMeta.extension or
+    (throw "Unrecognised compressor ${_compressorName}, please specify filename extension")
+
+# List of { object = path_or_derivation; symlink = "/path"; }
+# The paths are copied into the initramfs in their nix store path
+# form, then linked at the root according to `symlink`.
+, contents
+
+# List of uncompressed cpio files to prepend to the initramfs. This
+# can be used to add files in specified paths without them becoming
+# symlinks to store paths.
+, prepend ? []
+
+# Whether to wrap the initramfs in a u-boot image.
+, makeUInitrd ? stdenvNoCC.hostPlatform.linux-kernel.target == "uImage"
+
+# If generating a u-boot image, the architecture to use. The default
+# guess may not align with u-boot's nomenclature correctly, so it can
+# be overridden.
+# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 for a list.
+, uInitrdArch ? stdenvNoCC.hostPlatform.linuxArch
+
+# The name of the compression, as recognised by u-boot.
+# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L195-204 for a list.
+# If this isn't guessed, you may want to complete the metadata above and send a PR :)
+, uInitrdCompression ? _compressorMeta.ubootName or
+    (throw "Unrecognised compressor ${_compressorName}, please specify uInitrdCompression")
+}: runCommand name {
+  compress = "${_compressorExecutable} ${lib.escapeShellArgs _compressorArgsReal}";
+  passthru = {
+    compressorExecutableFunction = _compressorFunction;
+    compressorArgs = _compressorArgsReal;
+  };
+
+  passAsFile = ["contents"];
+  contents = lib.concatMapStringsSep "\n" ({ object, symlink, ... }: "${object}\n${if symlink == null then "" else symlink}") contents + "\n";
+
+  nativeBuildInputs = [makeInitrdNGTool patchelf glibc cpio];
+} ''
+  mkdir ./root
+  make-initrd-ng "$contentsPath" ./root
+  mkdir "$out"
+  (cd root && find * .[^.*] -exec touch -h -d '@1' '{}' +)
+  (cd root && find * .[^.*] -print0 | sort -z | cpio -o -H newc -R +0:+0 --reproducible --null | eval -- $compress >> "$out/initrd")
+''
diff --git a/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock b/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
new file mode 100644
index 0000000000000..75e732029b511
--- /dev/null
+++ b/pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
@@ -0,0 +1,5 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "make-initrd-ng"
+version = "0.1.0"
diff --git a/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml b/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml
new file mode 100644
index 0000000000000..9076f6b156176
--- /dev/null
+++ b/pkgs/build-support/kernel/make-initrd-ng/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "make-initrd-ng"
+version = "0.1.0"
+authors = ["Will Fancher <elvishjerricco@gmail.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/pkgs/build-support/kernel/make-initrd-ng/README.md b/pkgs/build-support/kernel/make-initrd-ng/README.md
new file mode 100644
index 0000000000000..741eba67e43f0
--- /dev/null
+++ b/pkgs/build-support/kernel/make-initrd-ng/README.md
@@ -0,0 +1,79 @@
+# What is this for?
+
+NixOS's traditional initrd is generated by listing the paths that
+should be included in initrd and copying the full runtime closure of
+those paths into the archive. For most things, like almost any
+executable, this involves copying the entirety of huge packages like
+glibc, when only things like the shared library files are needed. To
+solve this, NixOS does a variety of patchwork to edit the files being
+copied in so they only refer to small, patched up paths. For instance,
+executables and their shared library dependencies are copied into an
+`extraUtils` derivation, and every ELF file is patched to refer to
+files in that output.
+
+The problem with this is that it is often difficult to correctly patch
+some things. For instance, systemd bakes the path to the `mount`
+command into the binary, so patchelf is no help. Instead, it's very
+often easier to simply copy the desired files to their original store
+locations in initrd and not copy their entire runtime closure. This
+does mean that it is the burden of the developer to ensure that all
+necessary dependencies are copied in, as closures won't be
+consulted. However, it is rare that full closures are actually
+desirable, so in the traditional initrd, the developer was likely to
+do manual work on patching the dependencies explicitly anyway.
+
+# How it works
+
+This program is similar to its inspiration (`find-libs` from the
+traditional initrd), except that it also handles symlinks and
+directories according to certain rules. As input, it receives a
+sequence of pairs of paths. The first path is an object to copy into
+initrd. The second path (if not empty) is the path to a symlink that
+should be placed in the initrd, pointing to that object. How that
+object is copied depends on its type.
+
+1. A regular file is copied directly to the same absolute path in the
+   initrd.
+
+   - If it is *also* an ELF file, then all of its direct shared
+     library dependencies are also listed as objects to be copied.
+
+2. A directory's direct children are listed as objects to be copied,
+   and a directory at the same absolute path in the initrd is created.
+
+3. A symlink's target is listed as an object to be copied.
+
+There are a couple of quirks to mention here. First, the term "object"
+refers to the final file path that the developer intends to have
+copied into initrd. This means any parent directory is not considered
+an object just because its child was listed as an object in the
+program input; instead those intermediate directories are simply
+created in support of the target object. Second, shared libraries,
+directory children, and symlink targets aren't immediately recursed,
+because they simply get listed as objects themselves, and are
+therefore traversed when they themselves are processed. Finally,
+symlinks in the intermediate directories leading to an object are
+preserved, meaning an input object `/a/symlink/b` will just result in
+initrd containing `/a/symlink -> /target/b` and `/target/b`, even if
+`/target` has other children. Preserving symlinks in this manner is
+important for things like systemd.
+
+These rules automate the most important and obviously necessary
+copying that needs to be done in most cases, allowing programs and
+configuration files to go unpatched, while keeping the content of the
+initrd to a minimum.
+
+# Why Rust?
+
+- A prototype of this logic was written in Bash, in an attempt to keep
+  with its `find-libs` ancestor, but that program was difficult to
+  write, and ended up taking several minutes to run. This program runs
+  in less than a second, and the code is substantially easier to work
+  with.
+
+- This will not require end users to install a rust toolchain to use
+  NixOS, as long as this tool is cached by Hydra. And if you're
+  bootstrapping NixOS from source, rustc is already required anyway.
+
+- Rust was favored over Python for its type system, and because if you
+  want to go fast, why not go *really fast*?
diff --git a/pkgs/build-support/kernel/make-initrd-ng/src/main.rs b/pkgs/build-support/kernel/make-initrd-ng/src/main.rs
new file mode 100644
index 0000000000000..1342734590f72
--- /dev/null
+++ b/pkgs/build-support/kernel/make-initrd-ng/src/main.rs
@@ -0,0 +1,208 @@
+use std::collections::{HashSet, VecDeque};
+use std::env;
+use std::ffi::OsStr;
+use std::fs;
+use std::hash::Hash;
+use std::io::{BufReader, BufRead, Error, ErrorKind};
+use std::os::unix;
+use std::path::{Component, Path, PathBuf};
+use std::process::{Command, Stdio};
+
+struct NonRepeatingQueue<T> {
+    queue: VecDeque<T>,
+    seen: HashSet<T>,
+}
+
+impl<T> NonRepeatingQueue<T> {
+    fn new() -> NonRepeatingQueue<T> {
+        NonRepeatingQueue {
+            queue: VecDeque::new(),
+            seen: HashSet::new(),
+        }
+    }
+}
+
+impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> {
+    fn push_back(&mut self, value: T) -> bool {
+        if self.seen.contains(&value) {
+            false
+        } else {
+            self.seen.insert(value.clone());
+            self.queue.push_back(value);
+            true
+        }
+    }
+
+    fn pop_front(&mut self) -> Option<T> {
+        self.queue.pop_front()
+    }
+}
+
+fn patch_elf<S: AsRef<OsStr>, P: AsRef<OsStr>>(mode: S, path: P) -> Result<String, Error> {
+    let output = Command::new("patchelf")
+        .arg(&mode)
+        .arg(&path)
+        .stderr(Stdio::inherit())
+        .output()?;
+    if output.status.success() {
+        Ok(String::from_utf8(output.stdout).expect("Failed to parse output"))
+    } else {
+        Err(Error::new(ErrorKind::Other, format!("failed: patchelf {:?} {:?}", OsStr::new(&mode), OsStr::new(&path))))
+    }
+}
+
+fn copy_file<P: AsRef<Path> + AsRef<OsStr>, S: AsRef<Path>>(
+    source: P,
+    target: S,
+    queue: &mut NonRepeatingQueue<Box<Path>>,
+) -> Result<(), Error> {
+    fs::copy(&source, target)?;
+
+    if !Command::new("ldd").arg(&source).output()?.status.success() {
+        //stdout(Stdio::inherit()).stderr(Stdio::inherit()).
+        println!("{:?} is not dynamically linked. Not recursing.", OsStr::new(&source));
+        return Ok(());
+    }
+
+    let rpath_string = patch_elf("--print-rpath", &source)?;
+    let needed_string = patch_elf("--print-needed", &source)?;
+    // Shared libraries don't have an interpreter
+    if let Ok(interpreter_string) = patch_elf("--print-interpreter", &source) {
+        queue.push_back(Box::from(Path::new(&interpreter_string.trim())));
+    }
+
+    let rpath = rpath_string.trim().split(":").map(|p| Box::<Path>::from(Path::new(p))).collect::<Vec<_>>();
+
+    for line in needed_string.lines() {
+        let mut found = false;
+        for path in &rpath {
+            let lib = path.join(line);
+            if lib.exists() {
+                // No need to recurse. The queue will bring it back round.
+                queue.push_back(Box::from(lib.as_path()));
+                found = true;
+                break;
+            }
+        }
+        if !found {
+            // glibc makes it tricky to make this an error because
+            // none of the files have a useful rpath.
+            println!("Warning: Couldn't satisfy dependency {} for {:?}", line, OsStr::new(&source));
+        }
+    }
+
+    Ok(())
+}
+
+fn queue_dir<P: AsRef<Path>>(
+    source: P,
+    queue: &mut NonRepeatingQueue<Box<Path>>,
+) -> Result<(), Error> {
+    for entry in fs::read_dir(source)? {
+        let entry = entry?;
+        // No need to recurse. The queue will bring us back round here on its own.
+        queue.push_back(Box::from(entry.path().as_path()));
+    }
+
+    Ok(())
+}
+
+fn handle_path(
+    root: &Path,
+    p: &Path,
+    queue: &mut NonRepeatingQueue<Box<Path>>,
+) -> Result<(), Error> {
+    let mut source = PathBuf::new();
+    let mut target = Path::new(root).to_path_buf();
+    let mut iter = p.components().peekable();
+    while let Some(comp) = iter.next() {
+        match comp {
+            Component::Prefix(_) => panic!("This tool is not meant for Windows"),
+            Component::RootDir => {
+                target.clear();
+                target.push(root);
+                source.clear();
+                source.push("/");
+            }
+            Component::CurDir => {}
+            Component::ParentDir => {
+                // Don't over-pop the target if the path has too many ParentDirs
+                if source.pop() {
+                    target.pop();
+                }
+            }
+            Component::Normal(name) => {
+                target.push(name);
+                source.push(name);
+                let typ = fs::symlink_metadata(&source)?.file_type();
+                if typ.is_file() && !target.exists() {
+                    copy_file(&source, &target, queue)?;
+                } else if typ.is_symlink() {
+                    let link_target = fs::read_link(&source)?;
+
+                    // Create the link, then push its target to the queue
+                    if !target.exists() {
+                        unix::fs::symlink(&link_target, &target)?;
+                    }
+                    source.pop();
+                    source.push(link_target);
+                    while let Some(c) = iter.next() {
+                        source.push(c);
+                    }
+                    let link_target_path = source.as_path();
+                    if link_target_path.exists() {
+                        queue.push_back(Box::from(link_target_path));
+                    }
+                    break;
+                } else if typ.is_dir() {
+                    if !target.exists() {
+                        fs::create_dir(&target)?;
+                    }
+
+                    // Only recursively copy if the directory is the target object
+                    if iter.peek().is_none() {
+                        queue_dir(&source, queue)?;
+                    }
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
+fn main() -> Result<(), Error> {
+    let args: Vec<String> = env::args().collect();
+    let input = fs::File::open(&args[1])?;
+    let output = &args[2];
+    let out_path = Path::new(output);
+
+    let mut queue = NonRepeatingQueue::<Box<Path>>::new();
+
+    let mut lines = BufReader::new(input).lines();
+    while let Some(obj) = lines.next() {
+        // Lines should always come in pairs
+        let obj = obj?;
+        let sym = lines.next().unwrap()?;
+
+        let obj_path = Path::new(&obj);
+        queue.push_back(Box::from(obj_path));
+        if !sym.is_empty() {
+            println!("{} -> {}", &sym, &obj);
+            // We don't care about preserving symlink structure here
+            // nearly as much as for the actual objects.
+            let link_string = format!("{}/{}", output, sym);
+            let link_path = Path::new(&link_string);
+            let mut link_parent = link_path.to_path_buf();
+            link_parent.pop();
+            fs::create_dir_all(link_parent)?;
+            unix::fs::symlink(obj_path, link_path)?;
+        }
+    }
+    while let Some(obj) = queue.pop_front() {
+        println!("{:?}", obj);
+        handle_path(out_path, &*obj, &mut queue)?;
+    }
+
+    Ok(())
+}
diff --git a/pkgs/os-specific/linux/systemd/default.nix b/pkgs/os-specific/linux/systemd/default.nix
index 3a3a419093b75..4cbed9b7cbf10 100644
--- a/pkgs/os-specific/linux/systemd/default.nix
+++ b/pkgs/os-specific/linux/systemd/default.nix
@@ -603,7 +603,7 @@ stdenv.mkDerivation {
     # runtime; otherwise we can't and we need to reboot.
     interfaceVersion = 2;
 
-    inherit withCryptsetup;
+    inherit withCryptsetup util-linux;
 
     tests = {
       inherit (nixosTests) switchTest;
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 41002cf3d7e02..801301bb69858 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -451,6 +451,9 @@ with pkgs;
   dockerTools = callPackage ../build-support/docker {
     writePython3 = buildPackages.writers.writePython3;
   };
+
+  fakeNss = callPackage ../build-support/fake-nss { };
+
   tarsum = callPackage ../build-support/docker/tarsum.nix { };
 
   snapTools = callPackage ../build-support/snap { };
@@ -747,6 +750,9 @@ with pkgs;
 
   makeInitrd = callPackage ../build-support/kernel/make-initrd.nix; # Args intentionally left out
 
+  makeInitrdNG = callPackage ../build-support/kernel/make-initrd-ng.nix;
+  makeInitrdNGTool = callPackage ../build-support/kernel/make-initrd-ng-tool.nix {};
+
   makeWrapper = makeSetupHook
     { deps = [ dieHook ];
       substitutions = {