about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/modularity.xml1
-rw-r--r--nixos/doc/manual/configuration/xfce.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-2003.xml21
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml15
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh2
-rw-r--r--nixos/modules/config/system-path.nix1
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix2
-rw-r--r--nixos/modules/hardware/video/ati.nix2
-rw-r--r--nixos/modules/hardware/video/nvidia.nix138
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix2
-rw-r--r--nixos/modules/module-list.nix2
-rw-r--r--nixos/modules/security/acme.nix54
-rw-r--r--nixos/modules/security/rngd.nix7
-rw-r--r--nixos/modules/services/hardware/tlp.nix142
-rw-r--r--nixos/modules/services/mail/mailman.nix8
-rw-r--r--nixos/modules/services/misc/disnix.nix5
-rw-r--r--nixos/modules/services/misc/home-assistant.nix15
-rw-r--r--nixos/modules/services/misc/parsoid.nix25
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix13
-rw-r--r--nixos/modules/services/networking/git-daemon.nix4
-rw-r--r--nixos/modules/services/networking/kresd.nix6
-rw-r--r--nixos/modules/services/networking/nat.nix2
-rw-r--r--nixos/modules/services/networking/nix-store-gcs-proxy.nix75
-rw-r--r--nixos/modules/services/networking/sslh.nix9
-rw-r--r--nixos/modules/services/networking/stubby.nix2
-rw-r--r--nixos/modules/services/networking/zerotierone.nix10
-rw-r--r--nixos/modules/services/torrent/transmission.nix3
-rw-r--r--nixos/modules/services/wayland/cage.nix99
-rw-r--r--nixos/modules/services/web-apps/codimd.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix5
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.xml130
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix26
-rw-r--r--nixos/modules/services/x11/xserver.nix76
-rw-r--r--nixos/modules/system/boot/initrd-network.nix64
-rw-r--r--nixos/modules/system/boot/networkd.nix65
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh6
-rw-r--r--nixos/modules/system/boot/stage-1.nix15
-rw-r--r--nixos/modules/system/boot/systemd-lib.nix5
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix6
-rw-r--r--nixos/modules/testing/service-runner.nix28
-rw-r--r--nixos/tests/all-tests.nix5
-rw-r--r--nixos/tests/buildbot.nix2
-rw-r--r--nixos/tests/cage.nix43
-rw-r--r--nixos/tests/docker-tools.nix210
-rw-r--r--nixos/tests/gitdaemon.nix64
-rw-r--r--nixos/tests/initrd-network.nix15
-rw-r--r--nixos/tests/opensmtpd.nix2
-rw-r--r--nixos/tests/predictable-interface-names.nix6
-rw-r--r--nixos/tests/prometheus-exporters.nix2
-rw-r--r--nixos/tests/rsyslogd.nix30
-rw-r--r--nixos/tests/service-runner.nix36
-rw-r--r--nixos/tests/systemd-networkd-vrf.nix18
-rw-r--r--nixos/tests/systemd-networkd.nix (renamed from nixos/tests/systemd-networkd-wireguard.nix)35
54 files changed, 1187 insertions, 379 deletions
diff --git a/nixos/doc/manual/configuration/modularity.xml b/nixos/doc/manual/configuration/modularity.xml
index 7ad0ae80a48a..532a2c615e4d 100644
--- a/nixos/doc/manual/configuration/modularity.xml
+++ b/nixos/doc/manual/configuration/modularity.xml
@@ -36,6 +36,7 @@
 { <xref linkend="opt-services.xserver.enable"/> = true;
   <xref linkend="opt-services.xserver.displayManager.sddm.enable"/> = true;
   <xref linkend="opt-services.xserver.desktopManager.plasma5.enable"/> = true;
+  <xref linkend="opt-environment.systemPackages"/> = [ pkgs.vim ];
 }
 </programlisting>
   Note that both <filename>configuration.nix</filename> and
diff --git a/nixos/doc/manual/configuration/xfce.xml b/nixos/doc/manual/configuration/xfce.xml
index a81a327c09b6..ebf1f493c5ce 100644
--- a/nixos/doc/manual/configuration/xfce.xml
+++ b/nixos/doc/manual/configuration/xfce.xml
@@ -9,7 +9,6 @@
 <programlisting>
 <xref linkend="opt-services.xserver.desktopManager.xfce.enable" /> = true;
 <xref linkend="opt-services.xserver.displayManager.defaultSession" /> = "xfce";
-};
 </programlisting>
  </para>
  <para>
diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml
index 31f08d9da341..918906d27e72 100644
--- a/nixos/doc/manual/release-notes/rl-2003.xml
+++ b/nixos/doc/manual/release-notes/rl-2003.xml
@@ -154,7 +154,7 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
    </listitem>
    <listitem>
     <para>
-     The <literal>99-main.network</literal> file was removed. Maching all
+     The <literal>99-main.network</literal> file was removed. Matching all
      network interfaces caused many breakages, see
      <link xlink:href="https://github.com/NixOS/nixpkgs/pull/18962">#18962</link>
        and <link xlink:href="https://github.com/NixOS/nixpkgs/pull/71106">#71106</link>.
@@ -631,6 +631,16 @@ auth required pam_succeed_if.so uid >= 1000 quiet
      </citerefentry>-script now uses the python test-driver.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     The <package>riot-web</package> package now accepts configuration overrides as an attribute set instead of a string.
+     A formerly used JSON configuration can be converted to an attribute set with <literal>builtins.fromJSON</literal>.
+    </para>
+    <para>
+     The new default configuration also disables automatic guest account registration and analytics to improve privacy.
+     The previous behavior can be restored by setting <literal>config.riot-web.conf = { disable_guests = false; piwik = true; }</literal>.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 
@@ -693,6 +703,15 @@ auth required pam_succeed_if.so uid >= 1000 quiet
     via <option>boot.initrd.luks.fido2Support</option>.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     Predicatbly named network-interfaces get renamed in stage-1. This means that it's possible
+     to use the proper interface name for e.g. dropbear-setups.
+    </para>
+    <para>
+     For further reference, please read <link xlink:href="https://github.com/NixOS/nixpkgs/pull/68953">#68953</link> or the corresponding <link xlink:href="https://discourse.nixos.org/t/predictable-network-interface-names-in-initrd/4055">discourse thread</link>.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 </section>
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index d07b7cf49c39..892208b01d7d 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -20,7 +20,7 @@
   <itemizedlist>
    <listitem>
     <para>
-     Support is planned until the end of October 2020, handing over to 20.09.
+     Support is planned until the end of April 2021, handing over to 21.03.
     </para>
    </listitem>
   </itemizedlist>
@@ -59,7 +59,18 @@
 
   <itemizedlist>
    <listitem>
-    <para />
+    <para>
+     Grafana is now built without support for phantomjs by default. Phantomjs support has been
+     <link xlink:href="https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-4/">deprecated in Grafana</link>
+     and the <package>phantomjs</package> project is
+     <link xlink:href="https://github.com/ariya/phantomjs/issues/15344#issue-302015362">currently unmaintained</link>.
+     It can still be enabled by providing <literal>phantomJsSupport = true</literal> to the package instanciation:
+<programlisting>{
+  services.grafana.package = pkgs.grafana.overrideAttrs (oldAttrs: rec {
+    phantomJsSupport = false;
+  });
+}</programlisting>
+    </para>
    </listitem>
   </itemizedlist>
  </section>
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index 5dc1c5aaed57..145eb49ced7a 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -18,7 +18,7 @@ state_dir=$HOME/amis/ec2-images
 home_region=eu-west-1
 bucket=nixos-amis
 
-regions=(eu-west-1 eu-west-2 eu-west-3 eu-central-1
+regions=(eu-west-1 eu-west-2 eu-west-3 eu-central-1 eu-north-1
          us-east-1 us-east-2 us-west-1 us-west-2
          ca-central-1
          ap-southeast-1 ap-southeast-2 ap-northeast-1 ap-northeast-2
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index aba9bc0945b1..4100ec897016 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -116,6 +116,7 @@ in
         "/lib" # FIXME: remove and update debug-info.nix
         "/sbin"
         "/share/emacs"
+        "/share/hunspell"
         "/share/nano"
         "/share/org"
         "/share/themes"
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
index 8e91e9d2baa9..ec1c8c2d57a1 100644
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -30,7 +30,7 @@ in
     nixpkgs.config.xorg.abiCompat = "1.19";
 
     services.xserver.drivers = singleton
-      { name = "amdgpu"; modules = [ package ]; };
+      { name = "amdgpu"; modules = [ package ]; display = true; };
 
     hardware.opengl.package = package;
     hardware.opengl.package32 = package32;
diff --git a/nixos/modules/hardware/video/ati.nix b/nixos/modules/hardware/video/ati.nix
index 0aab7bd6b92c..06d3ea324d8d 100644
--- a/nixos/modules/hardware/video/ati.nix
+++ b/nixos/modules/hardware/video/ati.nix
@@ -21,7 +21,7 @@ in
     nixpkgs.config.xorg.abiCompat = "1.17";
 
     services.xserver.drivers = singleton
-      { name = "fglrx"; modules = [ ati_x11 ]; };
+      { name = "fglrx"; modules = [ ati_x11 ]; display = true; };
 
     hardware.opengl.package = ati_x11;
     hardware.opengl.package32 = pkgs.pkgsi686Linux.linuxPackages.ati_drivers_x11.override { libsOnly = true; kernel = null; };
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 1794bb4b433d..7461e231402a 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -34,26 +34,57 @@ let
   enabled = nvidia_x11 != null;
 
   cfg = config.hardware.nvidia;
-  optimusCfg = cfg.optimus_prime;
+  pCfg = cfg.prime;
+  syncCfg = pCfg.sync;
+  offloadCfg = pCfg.offload;
+  primeEnabled = syncCfg.enable || offloadCfg.enable;
 in
 
 {
+  imports =
+    [
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "enable" ] [ "hardware" "nvidia" "prime" "sync" "enable" ])
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "allowExternalGpu" ] [ "hardware" "nvidia" "prime" "sync" "allowExternalGpu" ])
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "nvidiaBusId" ] [ "hardware" "nvidia" "prime" "nvidiaBusId" ])
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "intelBusId" ] [ "hardware" "nvidia" "prime" "intelBusId" ])
+    ];
+
   options = {
-    hardware.nvidia.modesetting.enable = lib.mkOption {
-      type = lib.types.bool;
+    hardware.nvidia.modesetting.enable = mkOption {
+      type = types.bool;
       default = false;
       description = ''
         Enable kernel modesetting when using the NVIDIA proprietary driver.
 
         Enabling this fixes screen tearing when using Optimus via PRIME (see
-        <option>hardware.nvidia.optimus_prime.enable</option>. This is not enabled
+        <option>hardware.nvidia.prime.sync.enable</option>. This is not enabled
         by default because it is not officially supported by NVIDIA and would not
         work with SLI.
       '';
     };
 
-    hardware.nvidia.optimus_prime.enable = lib.mkOption {
-      type = lib.types.bool;
+    hardware.nvidia.prime.nvidiaBusId = mkOption {
+      type = types.str;
+      default = "";
+      example = "PCI:1:0:0";
+      description = ''
+        Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
+        shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
+      '';
+    };
+
+    hardware.nvidia.prime.intelBusId = mkOption {
+      type = types.str;
+      default = "";
+      example = "PCI:0:2:0";
+      description = ''
+        Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
+        shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
+      '';
+    };
+
+    hardware.nvidia.prime.sync.enable = mkOption {
+      type = types.bool;
       default = false;
       description = ''
         Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME.
@@ -66,8 +97,8 @@ in
         be the only driver there.
 
         If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
-        specified (<option>hardware.nvidia.optimus_prime.nvidiaBusId</option> and
-        <option>hardware.nvidia.optimus_prime.intelBusId</option>).
+        specified (<option>hardware.nvidia.prime.nvidiaBusId</option> and
+        <option>hardware.nvidia.prime.intelBusId</option>).
 
         If you enable this, you may want to also enable kernel modesetting for the
         NVIDIA driver (<option>hardware.nvidia.modesetting.enable</option>) in order
@@ -79,31 +110,23 @@ in
       '';
     };
 
-    hardware.nvidia.optimus_prime.allowExternalGpu = lib.mkOption {
-      type = lib.types.bool;
+    hardware.nvidia.prime.sync.allowExternalGpu = mkOption {
+      type = types.bool;
       default = false;
       description = ''
         Configure X to allow external NVIDIA GPUs when using optimus.
       '';
     };
 
-    hardware.nvidia.optimus_prime.nvidiaBusId = lib.mkOption {
-      type = lib.types.str;
-      default = "";
-      example = "PCI:1:0:0";
+    hardware.nvidia.prime.offload.enable = mkOption {
+      type = types.bool;
+      default = false;
       description = ''
-        Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
-        shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
-      '';
-    };
+        Enable render offload support using the NVIDIA proprietary driver via PRIME.
 
-    hardware.nvidia.optimus_prime.intelBusId = lib.mkOption {
-      type = lib.types.str;
-      default = "";
-      example = "PCI:0:2:0";
-      description = ''
-        Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
-        shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
+        If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
+        specified (<option>hardware.nvidia.prime.nvidiaBusId</option> and
+        <option>hardware.nvidia.prime.intelBusId</option>).
       '';
     };
   };
@@ -116,12 +139,19 @@ in
       }
 
       {
-        assertion = !optimusCfg.enable ||
-          (optimusCfg.nvidiaBusId != "" && optimusCfg.intelBusId != "");
+        assertion = primeEnabled -> pCfg.nvidiaBusId != "" && pCfg.intelBusId != "";
         message = ''
-          When NVIDIA Optimus via PRIME is enabled, the GPU bus IDs must configured.
+          When NVIDIA PRIME is enabled, the GPU bus IDs must configured.
         '';
       }
+      {
+        assertion = offloadCfg.enable -> versionAtLeast nvidia_x11.version "435.21";
+        message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
+      }
+      {
+        assertion = !(syncCfg.enable && offloadCfg.enable);
+        message = "Only one NVIDIA PRIME solution may be used at a time.";
+      }
     ];
 
     # If Optimus/PRIME is enabled, we:
@@ -136,36 +166,38 @@ in
     # - Configure the display manager to run specific `xrandr` commands which will
     #   configure/enable displays connected to the Intel GPU.
 
-    services.xserver.drivers = singleton {
+    services.xserver.useGlamor = mkDefault offloadCfg.enable;
+
+    services.xserver.drivers = optional primeEnabled {
+      name = "modesetting";
+      display = offloadCfg.enable;
+      deviceSection = ''
+        BusID "${pCfg.intelBusId}"
+        ${optionalString syncCfg.enable ''Option "AccelMethod" "none"''}
+      '';
+    } ++ singleton {
       name = "nvidia";
       modules = [ nvidia_x11.bin ];
-      deviceSection = optionalString optimusCfg.enable
+      display = !offloadCfg.enable;
+      deviceSection = optionalString primeEnabled
         ''
-          BusID "${optimusCfg.nvidiaBusId}"
-          ${optionalString optimusCfg.allowExternalGpu "Option \"AllowExternalGpus\""}
+          BusID "${pCfg.nvidiaBusId}"
+          ${optionalString syncCfg.allowExternalGpu "Option \"AllowExternalGpus\""}
         '';
       screenSection =
         ''
           Option "RandRRotation" "on"
-          ${optionalString optimusCfg.enable "Option \"AllowEmptyInitialConfiguration\""}
+          ${optionalString syncCfg.enable "Option \"AllowEmptyInitialConfiguration\""}
         '';
     };
 
-    services.xserver.extraConfig = optionalString optimusCfg.enable
-      ''
-        Section "Device"
-          Identifier "nvidia-optimus-intel"
-          Driver "modesetting"
-          BusID  "${optimusCfg.intelBusId}"
-          Option "AccelMethod" "none"
-        EndSection
-      '';
-    services.xserver.serverLayoutSection = optionalString optimusCfg.enable
-      ''
-        Inactive "nvidia-optimus-intel"
-      '';
+    services.xserver.serverLayoutSection = optionalString syncCfg.enable ''
+      Inactive "Device-modesetting[0]"
+    '' + optionalString offloadCfg.enable ''
+      Option "AllowNVIDIAGPUScreens"
+    '';
 
-    services.xserver.displayManager.setupCommands = optionalString optimusCfg.enable ''
+    services.xserver.displayManager.setupCommands = optionalString syncCfg.enable ''
       # Added by nvidia configuration module for Optimus/PRIME.
       ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource modesetting NVIDIA-0
       ${pkgs.xorg.xrandr}/bin/xrandr --auto
@@ -175,11 +207,13 @@ in
       source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
     };
 
-    hardware.opengl.package = nvidia_x11.out;
-    hardware.opengl.package32 = nvidia_libs32;
+    hardware.opengl.package = mkIf (!offloadCfg.enable) nvidia_x11.out;
+    hardware.opengl.package32 = mkIf (!offloadCfg.enable) nvidia_libs32;
+    hardware.opengl.extraPackages = optional offloadCfg.enable nvidia_x11.out;
+    hardware.opengl.extraPackages32 = optional offloadCfg.enable nvidia_libs32;
 
     environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ]
-      ++ lib.filter (p: p != null) [ nvidia_x11.persistenced ];
+      ++ filter (p: p != null) [ nvidia_x11.persistenced ];
 
     systemd.tmpfiles.rules = optional config.virtualisation.docker.enableNvidia
         "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin"
@@ -190,10 +224,10 @@ in
 
     # nvidia-uvm is required by CUDA applications.
     boot.kernelModules = [ "nvidia-uvm" ] ++
-      lib.optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
+      optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
 
     # If requested enable modesetting via kernel parameter.
-    boot.kernelParams = optional cfg.modesetting.enable "nvidia-drm.modeset=1";
+    boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1";
 
     # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
     services.udev.extraRules =
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 72b5850a4d92..2068f27f1c9f 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,5 +1,5 @@
 {
-  x86_64-linux = "/nix/store/68mycwwczrciryylq2a66jwfhxp09zsg-nix-2.3.3-debug";
+  x86_64-linux = "/nix/store/ddmmzn4ggz1f66lwxjy64n89864yj9w9-nix-2.3.3";
   i686-linux = "/nix/store/5axys7hsggb4282dsbps5k5p0v59yv13-nix-2.3.3";
   aarch64-linux = "/nix/store/k80nwvi19hxwbz3c9cxgp24f1jjxwmcc-nix-2.3.3";
   x86_64-darwin = "/nix/store/lrnvapsqmf0ja6zfyx4cpxr7ahdr7f9b-nix-2.3.3";
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 6734929b9d4e..e70a853624bf 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -660,6 +660,7 @@
   ./services/networking/ngircd.nix
   ./services/networking/nghttpx/default.nix
   ./services/networking/nix-serve.nix
+  ./services/networking/nix-store-gcs-proxy.nix
   ./services/networking/nixops-dns.nix
   ./services/networking/nntp-proxy.nix
   ./services/networking/nsd.nix
@@ -807,6 +808,7 @@
   ./services/ttys/agetty.nix
   ./services/ttys/gpm.nix
   ./services/ttys/kmscon.nix
+  ./services/wayland/cage.nix
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 7da6666f79c6..4c7f0ee657ce 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -136,6 +136,27 @@ let
           challenge to ensure the DNS entries required are available.
         '';
       };
+
+      ocspMustStaple = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Turns on the OCSP Must-Staple TLS extension.
+          Make sure you know what you're doing! See:
+          <itemizedlist>
+            <listitem><para><link xlink:href="https://blog.apnic.net/2019/01/15/is-the-web-ready-for-ocsp-must-staple/" /></para></listitem>
+            <listitem><para><link xlink:href="https://blog.hboeck.de/archives/886-The-Problem-with-OCSP-Stapling-and-Must-Staple-and-why-Certificate-Revocation-is-still-broken.html" /></para></listitem>
+          </itemizedlist>
+        '';
+      };
+
+      extraLegoRenewFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Additional flags to pass to lego renew.
+        '';
+      };
     };
   };
 
@@ -174,7 +195,7 @@ in
 
       renewInterval = mkOption {
         type = types.str;
-        default = "weekly";
+        default = "daily";
         description = ''
           Systemd calendar expression when to check for renewal. See
           <citerefentry><refentrytitle>systemd.time</refentrytitle>
@@ -288,12 +309,16 @@ in
                           ++ concatLists (mapAttrsToList (name: root: [ "-d" name ]) data.extraDomains)
                           ++ (if data.dnsProvider != null then [ "--dns" data.dnsProvider ] else [ "--http" "--http.webroot" data.webroot ])
                           ++ optionals (cfg.server != null || data.server != null) ["--server" (if data.server == null then cfg.server else data.server)];
-                runOpts = escapeShellArgs (globalOpts ++ [ "run" ]);
-                renewOpts = escapeShellArgs (globalOpts ++ [ "renew" "--days" (toString cfg.validMinDays) ]);
+                certOpts = optionals data.ocspMustStaple [ "--must-staple" ];
+                runOpts = escapeShellArgs (globalOpts ++ [ "run" ] ++ certOpts);
+                renewOpts = escapeShellArgs (globalOpts ++
+                  [ "renew" "--days" (toString cfg.validMinDays) ] ++
+                  certOpts ++ data.extraLegoRenewFlags);
                 acmeService = {
                   description = "Renew ACME Certificate for ${cert}";
                   after = [ "network.target" "network-online.target" ];
                   wants = [ "network-online.target" ];
+                  wantedBy = [ "multi-user.target" ];
                   serviceConfig = {
                     Type = "oneshot";
                     # With RemainAfterExit the service is considered active even
@@ -325,10 +350,10 @@ in
                           KEY=${spath}/certificates/${keyName}.key
                           if [ -e $KEY -a $KEY -nt key.pem ]; then
                             cp -p ${spath}/certificates/${keyName}.key key.pem
-                            cp -p ${spath}/certificates/${keyName}.crt cert.pem
+                            cp -p ${spath}/certificates/${keyName}.crt fullchain.pem
                             cp -p ${spath}/certificates/${keyName}.issuer.crt chain.pem
-                            cat cert.pem chain.pem > fullchain.pem
-                            cat key.pem cert.pem chain.pem > full.pem
+                            ln -sf fullchain.pem cert.pem
+                            cat key.pem fullchain.pem > full.pem
                             chmod ${rights} *.pem
                             chown '${data.user}:${data.group}' *.pem
                           fi
@@ -399,7 +424,17 @@ in
       systemd.tmpfiles.rules =
         map (data: "d ${data.webroot}/.well-known/acme-challenge - ${data.user} ${data.group}") (filter (data: data.webroot != null) (attrValues cfg.certs));
 
-      systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair
+      systemd.timers = let
+        # Allow systemd to pick a convenient time within the day
+        # to run the check.
+        # This allows the coalescing of multiple timer jobs.
+        # We divide by the number of certificates so that if you
+        # have many certificates, the renewals are distributed over
+        # the course of the day to avoid rate limits.
+        numCerts = length (attrNames cfg.certs);
+        _24hSecs = 60 * 60 * 24;
+        AccuracySec = "${toString (_24hSecs / numCerts)}s";
+      in flip mapAttrs' cfg.certs (cert: data: nameValuePair
         ("acme-${cert}")
         ({
           description = "Renew ACME Certificate for ${cert}";
@@ -408,8 +443,9 @@ in
             OnCalendar = cfg.renewInterval;
             Unit = "acme-${cert}.service";
             Persistent = "yes";
-            AccuracySec = "5m";
-            RandomizedDelaySec = "1h";
+            inherit AccuracySec;
+            # Skew randomly within the day, per https://letsencrypt.org/docs/integration-guide/.
+            RandomizedDelaySec = "24h";
           };
         })
       );
diff --git a/nixos/modules/security/rngd.nix b/nixos/modules/security/rngd.nix
index 5566c53897dc..cffa1a5849f9 100644
--- a/nixos/modules/security/rngd.nix
+++ b/nixos/modules/security/rngd.nix
@@ -37,6 +37,13 @@ in
 
       after = [ "dev-random.device" ];
 
+      # Clean shutdown without DefaultDependencies
+      conflicts = [ "shutdown.target" ];
+      before = [
+        "sysinit.target"
+        "shutdown.target"
+      ];
+
       description = "Hardware RNG Entropy Gatherer Daemon";
 
       # rngd may have to start early to avoid entropy starvation during boot with encrypted swap
diff --git a/nixos/modules/services/hardware/tlp.nix b/nixos/modules/services/hardware/tlp.nix
index 955a60677997..3962d7b15989 100644
--- a/nixos/modules/services/hardware/tlp.nix
+++ b/nixos/modules/services/hardware/tlp.nix
@@ -1,39 +1,26 @@
 { config, lib, pkgs, ... }:
-
 with lib;
-
 let
-
-cfg = config.services.tlp;
-
-enableRDW = config.networking.networkmanager.enable;
-
-tlp = pkgs.tlp.override {
-  inherit enableRDW;
-};
-
-# XXX: We can't use writeTextFile + readFile here because it triggers
-# TLP build to get the .drv (even on --dry-run).
-confFile = pkgs.runCommand "tlp"
-  { config = cfg.extraConfig;
-    passAsFile = [ "config" ];
-    preferLocalBuild = true;
-  }
-  ''
-    cat ${tlp}/etc/default/tlp > $out
-    cat $configPath >> $out
-  '';
-
+  cfg = config.services.tlp;
+  enableRDW = config.networking.networkmanager.enable;
+  tlp = pkgs.tlp.override { inherit enableRDW; };
+  # TODO: Use this for having proper parameters in the future
+  mkTlpConfig = tlpConfig: generators.toKeyValue {
+    mkKeyValue = generators.mkKeyValueDefault {
+      mkValueString = val:
+        if isInt val then toString val
+        else if isString val then val
+        else if true == val then "1"
+        else if false == val then "0"
+        else if isList val then "\"" + (concatStringsSep " " val) + "\""
+        else err "invalid value provided to mkTlpConfig:" (toString val);
+    } "=";
+  } tlpConfig;
 in
-
 {
-
   ###### interface
-
   options = {
-
     services.tlp = {
-
       enable = mkOption {
         type = types.bool;
         default = false;
@@ -45,77 +32,64 @@ in
         default = "";
         description = "Additional configuration variables for TLP";
       };
-
     };
-
   };
 
-
   ###### implementation
-
   config = mkIf cfg.enable {
+    boot.kernelModules = [ "msr" ];
 
-    powerManagement.scsiLinkPolicy = null;
-    powerManagement.cpuFreqGovernor = null;
-    powerManagement.cpufreq.max = null;
-    powerManagement.cpufreq.min = null;
+    environment.etc = {
+      "tlp.conf".text = cfg.extraConfig;
+    } // optionalAttrs enableRDW {
+      "NetworkManager/dispatcher.d/99tlp-rdw-nm".source =
+        "${tlp}/etc/NetworkManager/dispatcher.d/99tlp-rdw-nm";
+    };
 
-    systemd.sockets.systemd-rfkill.enable = false;
+    environment.systemPackages = [ tlp ];
 
-    systemd.services = {
-      "systemd-rfkill@".enable = false;
-      systemd-rfkill.enable = false;
+    # FIXME: When the config is parametrized we need to move these into a
+    # conditional on the relevant options being enabled.
+    powerManagement = {
+      scsiLinkPolicy = null;
+      cpuFreqGovernor = null;
+      cpufreq.max = null;
+      cpufreq.min = null;
+    };
 
-      tlp = {
-        description = "TLP system startup/shutdown";
+    services.udev.packages = [ tlp ];
 
-        after = [ "multi-user.target" ];
+    systemd = {
+      packages = [ tlp ];
+      # XXX: These must always be disabled/masked according to [1].
+      #
+      # [1]: https://github.com/linrunner/TLP/blob/a9ada09e0821f275ce5f93dc80a4d81a7ff62ae4/tlp-stat.in#L319
+      sockets.systemd-rfkill.enable = false;
+      services.systemd-rfkill.enable = false;
+
+      services.tlp = {
+        # XXX: The service should reload whenever the configuration changes,
+        # otherwise newly set power options remain inactive until reboot (or
+        # manual unit restart.)
+        restartTriggers = [ config.environment.etc."tlp.conf".source ];
+        # XXX: When using systemd.packages (which we do above) the [Install]
+        # section of systemd units does not work (citation needed) so we manually
+        # enforce it here.
         wantedBy = [ "multi-user.target" ];
-        before = [ "shutdown.target" ];
-        restartTriggers = [ confFile ];
-
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-          ExecStart = "${tlp}/bin/tlp init start";
-          ExecStop = "${tlp}/bin/tlp init stop";
-        };
       };
 
-      tlp-sleep = {
-        description = "TLP suspend/resume";
-
-        wantedBy = [ "sleep.target" ];
+      services.tlp-sleep = {
+        # XXX: When using systemd.packages (which we do above) the [Install]
+        # section of systemd units does not work (citation needed) so we manually
+        # enforce it here.
         before = [ "sleep.target" ];
-
-        unitConfig = {
-          StopWhenUnneeded = true;
-        };
-
-        serviceConfig = {
-          Type = "oneshot";
-          RemainAfterExit = true;
-          ExecStart = "${tlp}/bin/tlp suspend";
-          ExecStop = "${tlp}/bin/tlp resume";
-        };
+        wantedBy = [ "sleep.target" ];
+        # XXX: `tlp suspend` requires /var/lib/tlp to exist in order to save
+        # some stuff in there. There is no way, that I know of, to do this in
+        # the package itself, so we do it here instead making sure the unit
+        # won't fail due to the save dir not existing.
+        serviceConfig.StateDirectory = "tlp";
       };
     };
-
-    services.udev.packages = [ tlp ];
-
-    environment.etc =
-      {
-        "default/tlp".source = confFile;
-      } // optionalAttrs enableRDW {
-        "NetworkManager/dispatcher.d/99tlp-rdw-nm" = {
-          source = "${tlp}/etc/NetworkManager/dispatcher.d/99tlp-rdw-nm";
-        };
-      };
-
-    environment.systemPackages = [ tlp ];
-
-    boot.kernelModules = [ "msr" ];
-
   };
-
 }
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 43dc185cdd77..f5e78b182933 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -265,6 +265,11 @@ in {
       '';
       serviceConfig = {
         Type = "oneshot";
+        # RemainAfterExit makes restartIfChanged work for this service, so
+        # downstream services will get updated automatically when things like
+        # services.mailman.hyperkitty.baseUrl change.  Otherwise users have to
+        # restart things manually, which is confusing.
+        RemainAfterExit = "yes";
       };
     };
 
@@ -282,6 +287,9 @@ in {
       serviceConfig = {
         User = cfg.webUser;
         Type = "oneshot";
+        # Similar to mailman-settings.service, this makes restartTriggers work
+        # properly for this service.
+        RemainAfterExit = "yes";
         WorkingDirectory = "/var/lib/mailman-web";
       };
     };
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index c21cb2afc3ca..b7b6eb7cd66e 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -61,10 +61,7 @@ in
       ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
     services.tomcat.webapps = optional cfg.useWebServiceInterface pkgs.DisnixWebService;
 
-    users.groups = singleton
-      { name = "disnix";
-        gid = config.ids.gids.disnix;
-      };
+    users.groups.disnix.gid = config.ids.gids.disnix;
 
     systemd.services = {
       disnix = mkIf cfg.enableMultiUser {
diff --git a/nixos/modules/services/misc/home-assistant.nix b/nixos/modules/services/misc/home-assistant.nix
index d63f38e93b8e..86033d02bf3f 100644
--- a/nixos/modules/services/misc/home-assistant.nix
+++ b/nixos/modules/services/misc/home-assistant.nix
@@ -96,7 +96,20 @@ in {
 
     config = mkOption {
       default = null;
-      type = with types; nullOr attrs;
+      # Migrate to new option types later: https://github.com/NixOS/nixpkgs/pull/75584
+      type =  with lib.types; let
+          valueType = nullOr (oneOf [
+            bool
+            int
+            float
+            str
+            (lazyAttrsOf valueType)
+            (listOf valueType)
+          ]) // {
+            description = "Yaml value";
+            emptyValue.value = {};
+          };
+        in valueType;
       example = literalExample ''
         {
           homeassistant = {
diff --git a/nixos/modules/services/misc/parsoid.nix b/nixos/modules/services/misc/parsoid.nix
index 61626e78f8b3..09b7f977bfbf 100644
--- a/nixos/modules/services/misc/parsoid.nix
+++ b/nixos/modules/services/misc/parsoid.nix
@@ -6,7 +6,7 @@ let
 
   cfg = config.services.parsoid;
 
-  parsoid = pkgs.nodePackages."parsoid-git://github.com/abbradar/parsoid#stable";
+  parsoid = pkgs.nodePackages.parsoid;
 
   confTree = {
     worker_heartbeat_timeout = 300000;
@@ -98,8 +98,29 @@ in
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        User = "nobody";
         ExecStart = "${parsoid}/lib/node_modules/parsoid/bin/server.js -c ${confFile} -n ${toString cfg.workers}";
+
+        DynamicUser = true;
+        User = "parsoid";
+        Group = "parsoid";
+
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectHostname = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        LockPersonality = true;
+        #MemoryDenyWriteExecute = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        RemoveIPC = true;
       };
     };
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index ba852fea4336..56cddfc55b71 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -30,7 +30,17 @@ in
         Whether to perform certificate verification for https.
       '';
     };
-
+    constLabels = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [
+        "label1=value1"
+        "label2=value2"
+      ];
+      description = ''
+        A list of constant labels that will be used in every metric.
+      '';
+    };
   };
   serviceOpts = {
     serviceConfig = {
@@ -40,6 +50,7 @@ in
           --nginx.ssl-verify ${toString cfg.sslVerify} \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
           --web.telemetry-path ${cfg.telemetryPath} \
+          --prometheus.const-labels ${concatStringsSep "," cfg.constLabels} \
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
     };
diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix
index 6f2e149433f3..52c895215fbe 100644
--- a/nixos/modules/services/networking/git-daemon.nix
+++ b/nixos/modules/services/networking/git-daemon.nix
@@ -104,14 +104,14 @@ in
 
   config = mkIf cfg.enable {
 
-    users.users = optionalAttrs (cfg.user != "git") {
+    users.users = optionalAttrs (cfg.user == "git") {
       git = {
         uid = config.ids.uids.git;
         description = "Git daemon user";
       };
     };
 
-    users.groups = optionalAttrs (cfg.group != "git") {
+    users.groups = optionalAttrs (cfg.group == "git") {
       git.gid = config.ids.gids.git;
     };
 
diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix
index a2f91a4200bf..c5a84eebd46f 100644
--- a/nixos/modules/services/networking/kresd.nix
+++ b/nixos/modules/services/networking/kresd.nix
@@ -32,9 +32,9 @@ let
     + cfg.extraConfig
   );
 
-  package = pkgs.knot-resolver.override {
-    extraFeatures = cfg.listenDoH != [];
-  };
+  package = if cfg.listenDoH == []
+    then pkgs.knot-resolver # never force `extraFeatures = false`
+    else pkgs.knot-resolver.override { extraFeatures = true; };
 in {
   meta.maintainers = [ maintainers.vcunat /* upstream developer */ ];
 
diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix
index 9c658af30f75..21ae9eb8b6d4 100644
--- a/nixos/modules/services/networking/nat.nix
+++ b/nixos/modules/services/networking/nat.nix
@@ -65,7 +65,7 @@ let
         let
           m                = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination;
           destinationIP    = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0;
-          destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 1;
+          destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (elemAt m 1);
         in ''
           # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself
           iptables -w -t nat -A nixos-nat-out \
diff --git a/nixos/modules/services/networking/nix-store-gcs-proxy.nix b/nixos/modules/services/networking/nix-store-gcs-proxy.nix
new file mode 100644
index 000000000000..3f2ce5bca4da
--- /dev/null
+++ b/nixos/modules/services/networking/nix-store-gcs-proxy.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  opts = { name, config, ... }: {
+    options = {
+      enable = mkOption {
+        default = true;
+        type = types.bool;
+        example = true;
+        description = "Whether to enable proxy for this bucket";
+      };
+      bucketName = mkOption {
+        type = types.str;
+        default = name;
+        example = "my-bucket-name";
+        description = "Name of Google storage bucket";
+      };
+      address = mkOption {
+        type = types.str;
+        example = "localhost:3000";
+        description = "The address of the proxy.";
+      };
+    };
+  };
+  enabledProxies = lib.filterAttrs (n: v: v.enable) config.services.nix-store-gcs-proxy;
+  mapProxies = function: lib.mkMerge (lib.mapAttrsToList function enabledProxies);
+in
+{
+  options.services.nix-store-gcs-proxy = mkOption {
+    type = types.attrsOf (types.submodule opts);
+    default = {};
+    description = ''
+      An attribute set describing an HTTP to GCS proxy that allows us to use GCS
+      bucket via HTTP protocol.
+    '';
+  };
+
+  config.systemd.services = mapProxies (name: cfg: {
+    "nix-store-gcs-proxy-${name}" = {
+      description = "A HTTP nix store that proxies requests to Google Storage";
+      wantedBy = ["multi-user.target"];
+
+      serviceConfig = {
+        RestartSec = 5;
+        StartLimitInterval = 10;
+        ExecStart = ''
+          ${pkgs.nix-store-gcs-proxy}/bin/nix-store-gcs-proxy \
+            --bucket-name ${cfg.bucketName} \
+            --addr ${cfg.address}
+        '';
+
+        DynamicUser = true;
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateUsers = true;
+
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        NoNewPrivileges = true;
+        LockPersonality = true;
+        RestrictRealtime = true;
+      };
+    };
+  });
+
+  meta.maintainers = [ maintainers.mrkkrp ];
+}
diff --git a/nixos/modules/services/networking/sslh.nix b/nixos/modules/services/networking/sslh.nix
index 0222e8ce8b58..c4fa370a5fef 100644
--- a/nixos/modules/services/networking/sslh.nix
+++ b/nixos/modules/services/networking/sslh.nix
@@ -77,19 +77,14 @@ in
 
   config = mkMerge [
     (mkIf cfg.enable {
-      users.users.${user} = {
-        description = "sslh daemon user";
-        isSystemUser = true;
-      };
-
       systemd.services.sslh = {
         description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)";
         after = [ "network.target" ];
         wantedBy = [ "multi-user.target" ];
 
         serviceConfig = {
-          User                 = user;
-          Group                = "nogroup";
+          DynamicUser          = true;
+          User                 = "sslh";
           PermissionsStartOnly = true;
           Restart              = "always";
           RestartSec           = "1s";
diff --git a/nixos/modules/services/networking/stubby.nix b/nixos/modules/services/networking/stubby.nix
index b38bcd4cec05..849d266576d5 100644
--- a/nixos/modules/services/networking/stubby.nix
+++ b/nixos/modules/services/networking/stubby.nix
@@ -72,6 +72,7 @@ let
     resolution_type: GETDNS_RESOLUTION_STUB
     dns_transport_list:
       ${fallbacks}
+    appdata_dir: "/var/cache/stubby"
     tls_authentication: ${cfg.authenticationMode}
     tls_query_padding_blocksize: ${toString cfg.queryPaddingBlocksize}
     edns_client_subnet_private: ${if cfg.subnetPrivate then "1" else "0"}
@@ -208,6 +209,7 @@ in
         CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
         ExecStart = "${pkgs.stubby}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}";
         DynamicUser = true;
+        CacheDirectory = "stubby";
       };
     };
   };
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 069e15a909b7..042c4d5adddd 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -67,5 +67,15 @@ in
     networking.firewall.allowedUDPPorts = [ cfg.port ];
 
     environment.systemPackages = [ cfg.package ];
+
+    # Prevent systemd from potentially changing the MAC address
+    environment.etc."systemd/network/50-zerotier.link".text = ''
+      [Match]
+      OriginalName=zt*
+
+      [Link]
+      AutoNegotiation=false
+      MACAddressPolicy=none
+    '';
   };
 }
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 5ba72e8d7730..fd28b94f7be3 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -23,7 +23,8 @@ let
     for DIR in "${homeDir}" "${settingsDir}" "${fullSettings.download-dir}" "${fullSettings.incomplete-dir}"; do
       mkdir -p "$DIR"
     done
-    chmod 700 "${homeDir}" "${settingsDir}"
+    chmod 755 "${homeDir}"
+    chmod 700 "${settingsDir}"
     chmod ${downloadDirPermissions} "${fullSettings.download-dir}" "${fullSettings.incomplete-dir}"
     cp -f ${settingsFile} ${settingsDir}/settings.json
   '';
diff --git a/nixos/modules/services/wayland/cage.nix b/nixos/modules/services/wayland/cage.nix
new file mode 100644
index 000000000000..cac5c042ec16
--- /dev/null
+++ b/nixos/modules/services/wayland/cage.nix
@@ -0,0 +1,99 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cage;
+in {
+  options.services.cage.enable = mkEnableOption "cage kiosk service";
+
+  options.services.cage.user = mkOption {
+    type = types.str;
+    default = "demo";
+    description = ''
+      User to log-in as.
+    '';
+  };
+
+  options.services.cage.extraArguments = mkOption {
+    type = types.listOf types.str;
+    default = [];
+    defaultText = "[]";
+    description = "Additional command line arguments to pass to Cage.";
+    example = ["-d"];
+  };
+
+  options.services.cage.program = mkOption {
+    type = types.path;
+    default = "${pkgs.xterm}/bin/xterm";
+    description = ''
+      Program to run in cage.
+    '';
+  };
+
+  config = mkIf cfg.enable {
+
+    # The service is partially based off of the one provided in the
+    # cage wiki at
+    # https://github.com/Hjdskes/cage/wiki/Starting-Cage-on-boot-with-systemd.
+    systemd.services."cage-tty1" = {
+      enable = true;
+      after = [
+        "systemd-user-sessions.service"
+        "plymouth-start.service"
+        "plymouth-quit.service"
+        "systemd-logind.service"
+        "getty@tty1.service"
+      ];
+      before = [ "graphical.target" ];
+      wants = [ "dbus.socket" "systemd-logind.service" "plymouth-quit.service"];
+      wantedBy = [ "graphical.target" ];
+      conflicts = [ "getty@tty1.service" ];
+
+      restartIfChanged = false;
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.cage}/bin/cage \
+            ${escapeShellArgs cfg.extraArguments} \
+            -- ${cfg.program}
+        '';
+        User = cfg.user;
+
+        ConditionPathExists = "/dev/tty1";
+        IgnoreSIGPIPE = "no";
+
+        # Log this user with utmp, letting it show up with commands 'w' and
+        # 'who'. This is needed since we replace (a)getty.
+        UtmpIdentifier = "%n";
+        UtmpMode = "user";
+        # A virtual terminal is needed.
+        TTYPath = "/dev/tty1";
+        TTYReset = "yes";
+        TTYVHangup = "yes";
+        TTYVTDisallocate = "yes";
+        # Fail to start if not controlling the virtual terminal.
+        StandardInput = "tty-fail";
+        StandardOutput = "syslog";
+        StandardError = "syslog";
+        # Set up a full (custom) user session for the user, required by Cage.
+        PAMName = "cage";
+      };
+    };
+
+    security.pam.services.cage.text = ''
+      auth    required pam_unix.so nullok
+      account required pam_unix.so
+      session required pam_unix.so
+      session required ${pkgs.systemd}/lib/security/pam_systemd.so
+    '';
+
+    hardware.opengl.enable = mkDefault true;
+
+    systemd.targets.graphical.wants = [ "cage-tty1.service" ];
+
+    systemd.defaultUnit = "graphical.target";
+  };
+
+  meta.maintainers = with lib.maintainers; [ matthewbauer flokli ];
+
+}
diff --git a/nixos/modules/services/web-apps/codimd.nix b/nixos/modules/services/web-apps/codimd.nix
index 5f56f8ed5a09..751f81649ddb 100644
--- a/nixos/modules/services/web-apps/codimd.nix
+++ b/nixos/modules/services/web-apps/codimd.nix
@@ -156,7 +156,7 @@ in
       };
       useCDN = mkOption {
         type = types.bool;
-        default = true;
+        default = false;
         description = ''
           Whether to use CDN resources or not.
         '';
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index d79f2bb735fa..912e05d6d400 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -443,7 +443,7 @@ in {
         pools.nextcloud = {
           user = "nextcloud";
           group = "nginx";
-          phpOptions = phpOptionsExtensions + phpOptionsStr;
+          phpOptions = phpOptionsStr;
           phpPackage = phpPackage;
           phpEnv = {
             NEXTCLOUD_CONFIG_DIR = "${cfg.home}/config";
@@ -533,6 +533,7 @@ in {
                 add_header X-Robots-Tag none;
                 add_header X-Download-Options noopen;
                 add_header X-Permitted-Cross-Domain-Policies none;
+                add_header X-Frame-Options sameorigin;
                 add_header Referrer-Policy no-referrer;
                 access_log off;
               '';
@@ -547,6 +548,7 @@ in {
               add_header X-Robots-Tag none;
               add_header X-Download-Options noopen;
               add_header X-Permitted-Cross-Domain-Policies none;
+              add_header X-Frame-Options sameorigin;
               add_header Referrer-Policy no-referrer;
               add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
               error_page 403 /core/templates/403.php;
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index b46a2d189ef9..869c66944897 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -16,7 +16,10 @@ in
 
 {
 
-  meta.maintainers = pkgs.pantheon.maintainers;
+  meta = {
+    doc = ./pantheon.xml;
+    maintainers = pkgs.pantheon.maintainers;
+  };
 
   options = {
 
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.xml b/nixos/modules/services/x11/desktop-managers/pantheon.xml
new file mode 100644
index 000000000000..4d92a7446c0d
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.xml
@@ -0,0 +1,130 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="chap-pantheon">
+ <title>Pantheon Destkop</title>
+ <para>
+  Pantheon is the desktop environment created for the elementary OS distribution. It is written from scratch in Vala, utilizing GNOME technologies with GTK 3 and Granite.
+ </para>
+ <section xml:id="sec-pantheon-enable">
+  <title>Enabling Pantheon</title>
+
+  <para>
+   All of Pantheon is working in NixOS and the applications should be available, aside from a few <link xlink:href="https://github.com/NixOS/nixpkgs/issues/58161">exceptions</link>. To enable Pantheon, set
+<programlisting>
+<xref linkend="opt-services.xserver.desktopManager.pantheon.enable"/> = true;
+</programlisting>
+   This automatically enables LightDM and Pantheon's LightDM greeter. If you'd like to disable this, set
+<programlisting>
+<xref linkend="opt-services.xserver.displayManager.lightdm.greeters.pantheon.enable"/> = false;
+<xref linkend="opt-services.xserver.displayManager.lightdm.enable"/> = false;
+</programlisting>
+   but please be aware using Pantheon without LightDM as a display manager will break screenlocking from the UI. The NixOS module for Pantheon installs all of Pantheon's default applications. If you'd like to not install Pantheon's apps, set
+<programlisting>
+<xref linkend="opt-services.pantheon.apps.enable"/> = false;
+</programlisting>
+   You can also use <xref linkend="opt-environment.pantheon.excludePackages"/> to remove any other app (like <package>geary</package>).
+  </para>
+ </section>
+ <section xml:id="sec-pantheon-wingpanel-switchboard">
+  <title>Wingpanel and Switchboard plugins</title>
+
+  <para>
+   Wingpanel and Switchboard work differently than they do in other distributions, as far as using plugins. You cannot install a plugin globally (like with <option>environment.systemPackages</option>) to start using it. You should instead be using the following options:
+   <itemizedlist>
+    <listitem>
+     <para>
+      <xref linkend="opt-services.xserver.desktopManager.pantheon.extraWingpanelIndicators"/>
+     </para>
+    </listitem>
+    <listitem>
+     <para>
+      <xref linkend="opt-services.xserver.desktopManager.pantheon.extraSwitchboardPlugs"/>
+     </para>
+    </listitem>
+   </itemizedlist>
+   to configure the programs with plugs or indicators.
+  </para>
+
+  <para>
+   The difference in NixOS is both these programs are patched to load plugins from a directory that is the value of an environment variable. All of which is controlled in Nix. If you need to configure the particular packages manually you can override the packages like:
+<programlisting>
+wingpanel-with-indicators.override {
+  indicators = [
+    pkgs.some-special-indicator
+  ];
+};
+
+switchboard-with-plugs.override {
+  plugs = [
+    pkgs.some-special-plug
+  ];
+};
+</programlisting>
+   please note that, like how the NixOS options describe these as extra plugins, this would only add to the default plugins included with the programs. If for some reason you'd like to configure which plugins to use exactly, both packages have an argument for this:
+<programlisting>
+wingpanel-with-indicators.override {
+  useDefaultIndicators = false;
+  indicators = specialListOfIndicators;
+};
+
+switchboard-with-plugs.override {
+  useDefaultPlugs = false;
+  plugs = specialListOfPlugs;
+};
+</programlisting>
+   this could be most useful for testing a particular plug-in in isolation.
+  </para>
+ </section>
+ <section xml:id="sec-pantheon-faq">
+  <title>FAQ</title>
+
+  <variablelist>
+   <varlistentry xml:id="sec-pantheon-faq-messed-up-theme">
+    <term>
+     I have switched from a different desktop and Pantheon’s theming looks messed up.
+    </term>
+    <listitem>
+     <para>
+      Open Switchboard and go to: <guilabel>Administration</guilabel> → <guilabel>About</guilabel> → <guilabel>Restore Default Settings</guilabel> → <guibutton>Restore Settings</guibutton>. This will reset any dconf settings to their Pantheon defaults. Note this could reset certain GNOME specific preferences if that desktop was used prior.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry xml:id="sec-pantheon-faq-slow-shutdown">
+    <term>
+     Using Pantheon sometimes makes my shutdown take a long time.
+    </term>
+    <listitem>
+     <para>
+      We have not yet determined what processes fight with systemd during shutdown, there are many reports. In elementary OS the default system timeout is lowered to lessen the impact of the issue. If you'd like to do this in NixOS, set
+<programlisting>
+ <xref linkend="opt-systemd.extraConfig"/> = ''
+  DefaultTimeoutStopSec=10s
+  DefaultTimeoutStartSec=10s
+'';
+</programlisting>
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry xml:id="sec-pantheon-faq-gnome3-and-pantheon">
+    <term>
+     I cannot enable both GNOME 3 and Pantheon.
+    </term>
+    <listitem>
+     <para>
+      This is a known <link xlink:href="https://github.com/NixOS/nixpkgs/issues/64611">issue</link> and there is no known workaround.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry xml:id="sec-pantheon-faq-appcenter">
+    <term>
+     Does AppCenter work, or is it available?
+    </term>
+    <listitem>
+     <para>
+      AppCenter has been available since 20.03, but it is of little use. This is because there is no functioning PackageKit backend for Nix 2.0. In the near future you will be able to install Flatpak applications from AppCenter on NixOS. See this <link xlink:href="https://github.com/NixOS/nixpkgs/issues/70214">issue</link>.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </section>
+</chapter>
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index f3bf9268b293..60ef0159ff1a 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -52,6 +52,8 @@ let
   '';
 
   activationScript = ''
+    ${set_XDG_CONFIG_HOME}
+
     # The KDE icon cache is supposed to update itself automatically, but it uses
     # the timestamp on the icon theme directory as a trigger. This doesn't work
     # on NixOS because the timestamp never changes. As a workaround, delete the
@@ -62,7 +64,7 @@ let
     # xdg-desktop-settings generates this empty file but
     # it makes kbuildsyscoca5 fail silently. To fix this
     # remove that menu if it exists.
-    rm -fv ''${XDG_CONFIG_HOME:?}/menus/applications-merged/xdg-desktop-menu-dummy.menu
+    rm -fv ''${XDG_CONFIG_HOME}/menus/applications-merged/xdg-desktop-menu-dummy.menu
 
     # Qt writes a weird ‘libraryPath’ line to
     # ~/.config/Trolltech.conf that causes the KDE plugin
@@ -71,7 +73,7 @@ let
     # disastrous, so here we nuke references to the Nix store
     # in Trolltech.conf.  A better solution would be to stop
     # Qt from doing this wackiness in the first place.
-    trolltech_conf="''${XDG_CONFIG_HOME:?}/Trolltech.conf"
+    trolltech_conf="''${XDG_CONFIG_HOME}/Trolltech.conf"
     if [ -e "$trolltech_conf" ]; then
         ${sed} -i "$trolltech_conf" -e '/nix\\store\|nix\/store/ d'
     fi
@@ -84,10 +86,20 @@ let
     ${pkgs.libsForQt5.kservice}/bin/kbuildsycoca5
   '';
 
+  set_XDG_CONFIG_HOME = ''
+      # Set the default XDG_CONFIG_HOME if it is unset.
+      # Per the XDG Base Directory Specification:
+      # https://specifications.freedesktop.org/basedir-spec/latest
+      # 1. Never export this variable! If it is unset, then child processes are
+      # expected to set the default themselves.
+      # 2. Contaminate / if $HOME is unset; do not check if $HOME is set.
+      XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config}
+  '';
+
   startplasma =
     ''
-      export XDG_CONFIG_HOME="''${XDG_CONFIG_HOME:-$HOME/.config}"
-      mkdir -p "''${XDG_CONFIG_HOME:?}"
+      ${set_XDG_CONFIG_HOME}
+      mkdir -p "''${XDG_CONFIG_HOME}"
 
     ''
     + optionalString pulseaudio.enable ''
@@ -100,10 +112,10 @@ let
       ${activationScript}
 
       # Create default configurations if Plasma has never been started.
-      kdeglobals="''${XDG_CONFIG_HOME:?}/kdeglobals"
+      kdeglobals="''${XDG_CONFIG_HOME}/kdeglobals"
       if ! [ -f "$kdeglobals" ]
       then
-          kcminputrc="''${XDG_CONFIG_HOME:?}/kcminputrc"
+          kcminputrc="''${XDG_CONFIG_HOME}/kcminputrc"
           if ! [ -f "$kcminputrc" ]
           then
               cat ${kcminputrc} >"$kcminputrc"
@@ -115,7 +127,7 @@ let
               cat ${gtkrc2} >"$gtkrc2"
           fi
 
-          gtk3_settings="''${XDG_CONFIG_HOME:?}/gtk-3.0/settings.ini"
+          gtk3_settings="''${XDG_CONFIG_HOME}/gtk-3.0/settings.ini"
           if ! [ -f "$gtk3_settings" ]
           then
               mkdir -p "$(dirname "$gtk3_settings")"
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 7f0de96d2084..74d702ea1c3d 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -573,7 +573,7 @@ in
            then { modules = [xorg.${"xf86video" + name}]; }
            else null)
           knownVideoDrivers;
-      in optional (driver != null) ({ inherit name; modules = []; driverName = name; } // driver));
+      in optional (driver != null) ({ inherit name; modules = []; driverName = name; display = true; } // driver));
 
     assertions = [
       { assertion = config.security.polkit.enable;
@@ -740,7 +740,7 @@ in
           ${cfg.serverLayoutSection}
           # Reference the Screen sections for each driver.  This will
           # cause the X server to try each in turn.
-          ${flip concatMapStrings cfg.drivers (d: ''
+          ${flip concatMapStrings (filter (d: d.display) cfg.drivers) (d: ''
             Screen "Screen-${d.name}[0]"
           '')}
         EndSection
@@ -764,42 +764,44 @@ in
             ${driver.deviceSection or ""}
             ${xrandrDeviceSection}
           EndSection
+          ${optionalString driver.display ''
+
+            Section "Screen"
+              Identifier "Screen-${driver.name}[0]"
+              Device "Device-${driver.name}[0]"
+              ${optionalString (cfg.monitorSection != "") ''
+                Monitor "Monitor[0]"
+              ''}
+
+              ${cfg.screenSection}
+              ${driver.screenSection or ""}
+
+              ${optionalString (cfg.defaultDepth != 0) ''
+                DefaultDepth ${toString cfg.defaultDepth}
+              ''}
+
+              ${optionalString
+                  (driver.name != "virtualbox" &&
+                  (cfg.resolutions != [] ||
+                    cfg.extraDisplaySettings != "" ||
+                    cfg.virtualScreen != null))
+                (let
+                  f = depth:
+                    ''
+                      SubSection "Display"
+                        Depth ${toString depth}
+                        ${optionalString (cfg.resolutions != [])
+                          "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"}
+                        ${cfg.extraDisplaySettings}
+                        ${optionalString (cfg.virtualScreen != null)
+                          "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
+                      EndSubSection
+                    '';
+                in concatMapStrings f [8 16 24]
+              )}
 
-          Section "Screen"
-            Identifier "Screen-${driver.name}[0]"
-            Device "Device-${driver.name}[0]"
-            ${optionalString (cfg.monitorSection != "") ''
-              Monitor "Monitor[0]"
-            ''}
-
-            ${cfg.screenSection}
-            ${driver.screenSection or ""}
-
-            ${optionalString (cfg.defaultDepth != 0) ''
-              DefaultDepth ${toString cfg.defaultDepth}
-            ''}
-
-            ${optionalString
-                (driver.name != "virtualbox" &&
-                 (cfg.resolutions != [] ||
-                  cfg.extraDisplaySettings != "" ||
-                  cfg.virtualScreen != null))
-              (let
-                f = depth:
-                  ''
-                    SubSection "Display"
-                      Depth ${toString depth}
-                      ${optionalString (cfg.resolutions != [])
-                        "Modes ${concatMapStrings (res: ''"${toString res.x}x${toString res.y}"'') cfg.resolutions}"}
-                      ${cfg.extraDisplaySettings}
-                      ${optionalString (cfg.virtualScreen != null)
-                        "Virtual ${toString cfg.virtualScreen.x} ${toString cfg.virtualScreen.y}"}
-                    EndSubSection
-                  '';
-              in concatMapStrings f [8 16 24]
-            )}
-
-          EndSection
+            EndSection
+          ''}
         '')}
 
         ${xrandrMonitorSections}
diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix
index cb8fc957a990..0ab6e626b340 100644
--- a/nixos/modules/system/boot/initrd-network.nix
+++ b/nixos/modules/system/boot/initrd-network.nix
@@ -6,7 +6,11 @@ let
 
   cfg = config.boot.initrd.network;
 
-  dhcpinterfaces = lib.attrNames (lib.filterAttrs (iface: v: v.useDHCP == true) (config.networking.interfaces or {}));
+  dhcpInterfaces = lib.attrNames (lib.filterAttrs (iface: v: v.useDHCP == true) (config.networking.interfaces or {}));
+  doDhcp = config.networking.useDHCP || dhcpInterfaces != [];
+  dhcpIfShellExpr = if config.networking.useDHCP
+                      then "$(ls /sys/class/net/ | grep -v ^lo$)"
+                      else lib.concatMapStringsSep " " lib.escapeShellArg dhcpInterfaces;
 
   udhcpcScript = pkgs.writeScript "udhcp-script"
     ''
@@ -62,6 +66,16 @@ in
       '';
     };
 
+    boot.initrd.network.flushBeforeStage2 = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to clear the configuration of the interfaces that were set up in
+        the initrd right before stage 2 takes over. Stage 2 will do the regular network
+        configuration based on the NixOS networking options.
+      '';
+    };
+
     boot.initrd.network.udhcpc.extraArgs = mkOption {
       default = [];
       type = types.listOf types.str;
@@ -89,49 +103,45 @@ in
     boot.initrd.kernelModules = [ "af_packet" ];
 
     boot.initrd.extraUtilsCommands = ''
-      copy_bin_and_libs ${pkgs.mkinitcpio-nfs-utils}/bin/ipconfig
+      copy_bin_and_libs ${pkgs.klibc}/lib/klibc/bin.static/ipconfig
     '';
 
     boot.initrd.preLVMCommands = mkBefore (
       # Search for interface definitions in command line.
       ''
+        ifaces=""
         for o in $(cat /proc/cmdline); do
           case $o in
             ip=*)
-              ipconfig $o && hasNetwork=1
+              ipconfig $o && ifaces="$ifaces $(echo $o | cut -d: -f6)"
               ;;
           esac
         done
       ''
 
       # Otherwise, use DHCP.
-      + optionalString (config.networking.useDHCP || dhcpinterfaces != []) ''
-        if [ -z "$hasNetwork" ]; then
-
-          # Bring up all interfaces.
-          for iface in $(ls /sys/class/net/); do
-            echo "bringing up network interface $iface..."
-            ip link set "$iface" up
-          done
+      + optionalString doDhcp ''
+        # Bring up all interfaces.
+        for iface in ${dhcpIfShellExpr}; do
+          echo "bringing up network interface $iface..."
+          ip link set "$iface" up && ifaces="$ifaces $iface"
+        done
 
-          # Acquire DHCP leases.
-          for iface in ${ if config.networking.useDHCP then
-                            "$(ls /sys/class/net/ | grep -v ^lo$)"
-                          else
-                            lib.concatMapStringsSep " " lib.escapeShellArg dhcpinterfaces
-                        }; do
-            echo "acquiring IP address via DHCP on $iface..."
-            udhcpc --quit --now -i $iface -O staticroutes --script ${udhcpcScript} ${udhcpcArgs} && hasNetwork=1
-          done
-        fi
+        # Acquire DHCP leases.
+        for iface in ${dhcpIfShellExpr}; do
+          echo "acquiring IP address via DHCP on $iface..."
+          udhcpc --quit --now -i $iface -O staticroutes --script ${udhcpcScript} ${udhcpcArgs}
+        done
       ''
 
-      + ''
-        if [ -n "$hasNetwork" ]; then
-          echo "networking is up!"
-          ${cfg.postCommands}
-        fi
-      '');
+      + cfg.postCommands);
+
+    boot.initrd.postMountCommands = mkIf cfg.flushBeforeStage2 ''
+      for iface in $ifaces; do
+        ip address flush "$iface"
+        ip link down "$iface"
+      done
+    '';
 
   };
 
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index a77dbc609f46..6dfbe66fc647 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -67,7 +67,12 @@ let
     (assertOnlyFields [
       "PrivateKeyFile" "ListenPort" "FwMark"
     ])
-    (assertRange "FwMark" 1 4294967295)
+    # The following check won't work on nix <= 2.2
+    # see https://github.com/NixOS/nix/pull/2378
+    #
+    # Add this again when we'll have drop the
+    # nix < 2.2 support.
+    # (assertRange "FwMark" 1 4294967295)
   ];
 
   # NOTE The PresharedKey directive is missing on purpose here, please
@@ -181,7 +186,12 @@ let
     (assertOnlyFields [
       "InterfaceId" "Independent"
     ])
-    (assertRange "InterfaceId" 1 4294967295)
+    # The following check won't work on nix <= 2.2
+    # see https://github.com/NixOS/nix/pull/2378
+    #
+    # Add this again when we'll have drop the
+    # nix < 2.2 support.
+    # (assertRange "InterfaceId" 1 4294967295)
     (assertValueOneOf "Independent" boolValues)
   ];
 
@@ -235,6 +245,26 @@ let
     (assertValueOneOf "AutoJoin" boolValues)
   ];
 
+  checkRoutingPolicyRule = checkUnitConfig "RoutingPolicyRule" [
+    (assertOnlyFields [
+      "TypeOfService" "From" "To" "FirewallMark" "Table" "Priority"
+      "IncomingInterface" "OutgoingInterface" "SourcePort" "DestinationPort"
+      "IPProtocol" "InvertRule" "Family"
+    ])
+    (assertRange "TypeOfService" 0 255)
+    # The following check won't work on nix <= 2.2
+    # see https://github.com/NixOS/nix/pull/2378
+    #
+    # Add this again when we'll have drop the
+    # nix < 2.2 support.
+    #  (assertRange "FirewallMark" 1 4294967295)
+    (assertInt "Priority")
+    (assertPort "SourcePort")
+    (assertPort "DestinationPort")
+    (assertValueOneOf "InvertRule" boolValues)
+    (assertValueOneOf "Family" ["ipv4" "ipv6" "both"])
+  ];
+
   checkRoute = checkUnitConfig "Route" [
     (assertOnlyFields [
       "Gateway" "GatewayOnLink" "Destination" "Source" "Metric"
@@ -535,6 +565,22 @@ let
     };
   };
 
+  routingPolicyRulesOptions = {
+    options = {
+      routingPolicyRuleConfig = mkOption {
+        default = { };
+        example = { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; } ;};
+        type = types.addCheck (types.attrsOf unitOption) checkRoutingPolicyRule;
+        description = ''
+          Each attribute in this set specifies an option in the
+          <literal>[RoutingPolicyRule]</literal> section of the unit.  See
+          <citerefentry><refentrytitle>systemd.network</refentrytitle>
+          <manvolnum>5</manvolnum></citerefentry> for details.
+        '';
+      };
+    };
+  };
+
   routeOptions = {
     options = {
       routeConfig = mkOption {
@@ -772,6 +818,16 @@ let
       '';
     };
 
+    routingPolicyRules = mkOption {
+      default = [ ];
+      type = with types; listOf (submodule routingPolicyRulesOptions);
+      description = ''
+        A list of routing policy rules sections to be added to the unit.  See
+        <citerefentry><refentrytitle>systemd.network</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
     routes = mkOption {
       default = [ ];
       type = with types; listOf (submodule routeOptions);
@@ -929,6 +985,11 @@ let
             ${attrsToSection x.routeConfig}
 
           '')}
+          ${flip concatMapStrings def.routingPolicyRules (x: ''
+            [RoutingPolicyRule]
+            ${attrsToSection x.routingPolicyRuleConfig}
+
+          '')}
           ${def.extraConfig}
         '';
     };
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 8736613c3d25..607aec87f01e 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -210,6 +210,8 @@ done
 # Create device nodes in /dev.
 @preDeviceCommands@
 echo "running udev..."
+mkdir -p /etc/systemd
+ln -sfn @linkUnits@ /etc/systemd/network
 mkdir -p /etc/udev
 ln -sfn @udevRules@ /etc/udev/rules.d
 mkdir -p /dev/.mdadm
@@ -266,7 +268,7 @@ checkFS() {
         return 0
     fi
 
-    # Device might be already mounted manually 
+    # Device might be already mounted manually
     # e.g. NBD-device or the host filesystem of the file which contains encrypted root fs
     if mount | grep -q "^$device on "; then
         echo "skip checking already mounted $device"
@@ -351,7 +353,7 @@ mountFS() {
             elif [ "$fsType" = f2fs ]; then
                 echo "resizing $device..."
                 fsck.f2fs -fp "$device"
-                resize.f2fs "$device" 
+                resize.f2fs "$device"
             fi
             ;;
     esac
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 4c2d130d5a5d..26117cffeda2 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -120,6 +120,7 @@ let
 
       # Copy udev.
       copy_bin_and_libs ${udev}/lib/systemd/systemd-udevd
+      copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
       copy_bin_and_libs ${udev}/bin/udevadm
       for BIN in ${udev}/lib/udev/*_id; do
         copy_bin_and_libs $BIN
@@ -198,6 +199,14 @@ let
     ''; # */
 
 
+  linkUnits = pkgs.runCommand "link-units" {
+      allowedReferences = [ extraUtils ];
+      preferLocalBuild = true;
+    } ''
+      mkdir -p $out
+      cp -v ${udev}/lib/systemd/network/*.link $out/
+    '';
+
   udevRules = pkgs.runCommand "udev-rules" {
       allowedReferences = [ extraUtils ];
       preferLocalBuild = true;
@@ -208,7 +217,9 @@ let
 
       cp -v ${udev}/lib/udev/rules.d/60-cdrom_id.rules $out/
       cp -v ${udev}/lib/udev/rules.d/60-persistent-storage.rules $out/
+      cp -v ${udev}/lib/udev/rules.d/75-net-description.rules $out/
       cp -v ${udev}/lib/udev/rules.d/80-drivers.rules $out/
+      cp -v ${udev}/lib/udev/rules.d/80-net-setup-link.rules $out/
       cp -v ${pkgs.lvm2}/lib/udev/rules.d/*.rules $out/
       ${config.boot.initrd.extraUdevRulesCommands}
 
@@ -222,7 +233,7 @@ let
             --replace ${pkgs.lvm2}/sbin ${extraUtils}/bin \
             --replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \
             --replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \
-            --replace ${udev}/bin/udevadm ${extraUtils}/bin/udevadm
+            --replace ${udev} ${extraUtils}
       done
 
       # Work around a bug in QEMU, which doesn't implement the "READ
@@ -257,7 +268,7 @@ let
       ${pkgs.buildPackages.busybox}/bin/ash -n $target
     '';
 
-    inherit udevRules extraUtils modulesClosure;
+    inherit linkUnits udevRules extraUtils modulesClosure;
 
     inherit (config.boot) resumeDevice;
 
diff --git a/nixos/modules/system/boot/systemd-lib.nix b/nixos/modules/system/boot/systemd-lib.nix
index fd1a5b9f62c5..a33602915867 100644
--- a/nixos/modules/system/boot/systemd-lib.nix
+++ b/nixos/modules/system/boot/systemd-lib.nix
@@ -59,6 +59,11 @@ in rec {
     optional (attr ? ${name} && ! isMacAddress attr.${name})
       "Systemd ${group} field `${name}' must be a valid mac address.";
 
+  isPort = i: i >= 0 && i <= 65535;
+
+  assertPort = name: group: attr:
+    optional (attr ? ${name} && ! isPort attr.${name})
+      "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
 
   assertValueOneOf = name: values: group: attr:
     optional (attr ? ${name} && !elem attr.${name} values)
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index d14ba98ec48b..09c7e074e121 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -623,7 +623,11 @@ in
         after = [ "zfs-import.target" ];
         path = [ packages.zfsUser ];
         startAt = cfgTrim.interval;
-        serviceConfig.ExecStart = "${pkgs.runtimeShell} -c 'zpool list -H -o name | xargs --no-run-if-empty -n1 zpool trim'";
+        # By default we ignore errors returned by the trim command, in case:
+        # - HDDs are mixed with SSDs
+        # - There is a SSDs in a pool that is currently trimmed.
+        # - There are only HDDs and we would set the system in a degraded state
+        serviceConfig.ExecStart = ''${pkgs.runtimeShell} -c 'for pool in $(zpool list -H -o name); do zpool trim $pool;  done || true' '';
       };
     })
   ];
diff --git a/nixos/modules/testing/service-runner.nix b/nixos/modules/testing/service-runner.nix
index 17d5e3376908..99a9f979068d 100644
--- a/nixos/modules/testing/service-runner.nix
+++ b/nixos/modules/testing/service-runner.nix
@@ -12,7 +12,10 @@ let
 
       sub run {
           my ($cmd) = @_;
-          my @args = split " ", $cmd;
+          my @args = ();
+          while ($cmd =~ /([^ \t\n']+)|(\'([^'])\')\s*/g) {
+            push @args, $1;
+          }
           my $prog;
           if (substr($args[0], 0, 1) eq "@") {
               $prog = substr($args[0], 1);
@@ -48,15 +51,20 @@ let
       '') service.environment)}
 
       # Run the ExecStartPre program.  FIXME: this could be a list.
-      my $preStart = '${service.serviceConfig.ExecStartPre or ""}';
-      if ($preStart ne "") {
+      my $preStart = <<END_CMD;
+      ${service.serviceConfig.ExecStartPre or ""}
+      END_CMD
+      if (defined $preStart && $preStart ne "\n") {
           print STDERR "running ExecStartPre: $preStart\n";
           my $res = run_wait $preStart;
           die "$0: ExecStartPre failed with status $res\n" if $res;
       };
 
       # Run the ExecStart program.
-      my $cmd = '${service.serviceConfig.ExecStart}';
+      my $cmd = <<END_CMD;
+      ${service.serviceConfig.ExecStart}
+      END_CMD
+
       print STDERR "running ExecStart: $cmd\n";
       my $mainPid = run $cmd;
       $ENV{'MAINPID'} = $mainPid;
@@ -70,8 +78,10 @@ let
       $SIG{'QUIT'} = \&intHandler;
 
       # Run the ExecStartPost program.
-      my $postStart = '${service.serviceConfig.ExecStartPost or ""}';
-      if ($postStart ne "") {
+      my $postStart = <<END_CMD;
+      ${service.serviceConfig.ExecStartPost or ""}
+      END_CMD
+      if (defined $postStart && $postStart ne "\n") {
           print STDERR "running ExecStartPost: $postStart\n";
           my $res = run_wait $postStart;
           die "$0: ExecStartPost failed with status $res\n" if $res;
@@ -82,8 +92,10 @@ let
       my $mainRes = $?;
 
       # Run the ExecStopPost program.
-      my $postStop = '${service.serviceConfig.ExecStopPost or ""}';
-      if ($postStop ne "") {
+      my $postStop = <<END_CMD;
+      ${service.serviceConfig.ExecStopPost or ""}
+      END_CMD
+      if (defined $postStop && $postStop ne "\n") {
           print STDERR "running ExecStopPost: $postStop\n";
           my $res = run_wait $postStop;
           die "$0: ExecStopPost failed with status $res\n" if $res;
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 2e547780439a..7dd0f23df658 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -39,6 +39,7 @@ in
   buildbot = handleTest ./buildbot.nix {};
   caddy = handleTest ./caddy.nix {};
   cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {};
+  cage = handleTest ./cage.nix {};
   cassandra = handleTest ./cassandra.nix {};
   ceph-single-node = handleTestOn ["x86_64-linux"] ./ceph-single-node.nix {};
   ceph-multi-node = handleTestOn ["x86_64-linux"] ./ceph-multi-node.nix {};
@@ -98,6 +99,7 @@ in
   fsck = handleTest ./fsck.nix {};
   gotify-server = handleTest ./gotify-server.nix {};
   grocy = handleTest ./grocy.nix {};
+  gitdaemon = handleTest ./gitdaemon.nix {};
   gitea = handleTest ./gitea.nix {};
   gitlab = handleTest ./gitlab.nix {};
   gitolite = handleTest ./gitolite.nix {};
@@ -262,6 +264,7 @@ in
   samba = handleTest ./samba.nix {};
   sanoid = handleTest ./sanoid.nix {};
   sddm = handleTest ./sddm.nix {};
+  service-runner = handleTest ./service-runner.nix {};
   shiori = handleTest ./shiori.nix {};
   signal-desktop = handleTest ./signal-desktop.nix {};
   simple = handleTest ./simple.nix {};
@@ -282,7 +285,7 @@ in
   systemd-confinement = handleTest ./systemd-confinement.nix {};
   systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
   systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {};
-  systemd-networkd-wireguard = handleTest ./systemd-networkd-wireguard.nix {};
+  systemd-networkd = handleTest ./systemd-networkd.nix {};
   systemd-nspawn = handleTest ./systemd-nspawn.nix {};
   pdns-recursor = handleTest ./pdns-recursor.nix {};
   taskserver = handleTest ./taskserver.nix {};
diff --git a/nixos/tests/buildbot.nix b/nixos/tests/buildbot.nix
index 5655a34a8b51..0d979dc2d054 100644
--- a/nixos/tests/buildbot.nix
+++ b/nixos/tests/buildbot.nix
@@ -41,7 +41,7 @@ import ./make-test-python.nix {
       systemd.services.git-daemon = {
         description   = "Git daemon for the test";
         wantedBy      = [ "multi-user.target" ];
-        after         = [ "network.target" ];
+        after         = [ "network.target" "sshd.service" ];
 
         serviceConfig.Restart = "always";
         path = with pkgs; [ coreutils git openssh ];
diff --git a/nixos/tests/cage.nix b/nixos/tests/cage.nix
new file mode 100644
index 000000000000..a6f73e00c066
--- /dev/null
+++ b/nixos/tests/cage.nix
@@ -0,0 +1,43 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+
+{
+  name = "cage";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ matthewbauer flokli ];
+  };
+
+  machine = { ... }:
+
+  {
+    imports = [ ./common/user-account.nix ];
+    services.cage = {
+      enable = true;
+      user = "alice";
+      program = "${pkgs.xterm}/bin/xterm -cm -pc"; # disable color and bold to make OCR easier
+    };
+
+    # this needs a fairly recent kernel, otherwise:
+    #   [backend/drm/util.c:215] Unable to add DRM framebuffer: No such file or directory
+    #   [backend/drm/legacy.c:15] Virtual-1: Failed to set CRTC: No such file or directory
+    #   [backend/drm/util.c:215] Unable to add DRM framebuffer: No such file or directory
+    #   [backend/drm/legacy.c:15] Virtual-1: Failed to set CRTC: No such file or directory
+    #   [backend/drm/drm.c:618] Failed to initialize renderer on connector 'Virtual-1': initial page-flip failed
+    #   [backend/drm/drm.c:701] Failed to initialize renderer for plane
+    boot.kernelPackages = pkgs.linuxPackages_latest;
+
+    virtualisation.memorySize = 1024;
+  };
+
+  enableOCR = true;
+
+  testScript = { nodes, ... }: let
+    user = nodes.machine.config.users.users.alice;
+  in ''
+    with subtest("Wait for cage to boot up"):
+        start_all()
+        machine.wait_for_file("/run/user/${toString user.uid}/wayland-0.lock")
+        machine.wait_until_succeeds("pgrep xterm")
+        machine.wait_for_text("alice@machine")
+        machine.screenshot("screen")
+  '';
+})
diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix
index ca750e8ba3cd..54dd97e5b139 100644
--- a/nixos/tests/docker-tools.nix
+++ b/nixos/tests/docker-tools.nix
@@ -1,93 +1,141 @@
 # this test creates a simple GNU image with docker tools and sees if it executes
 
-import ./make-test.nix ({ pkgs, ... }: {
+import ./make-test-python.nix ({ pkgs, ... }: {
   name = "docker-tools";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ lnl7 ];
   };
 
   nodes = {
-    docker =
-      { ... }: {
-        virtualisation = {
-          diskSize = 2048;
-          docker.enable = true;
-        };
+    docker = { ... }: {
+      virtualisation = {
+        diskSize = 2048;
+        docker.enable = true;
       };
+    };
   };
 
-  testScript =
-    ''
-      $docker->waitForUnit("sockets.target");
-
-      # Ensure Docker images use a stable date by default
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'");
-      $docker->succeed("[ '1970-01-01T00:00:01Z' = \"\$(docker inspect ${pkgs.dockerTools.examples.bash.imageName} | ${pkgs.jq}/bin/jq -r .[].Created)\" ]");
-
-      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.bash.imageName} bash --version");
-      $docker->succeed("docker rmi ${pkgs.dockerTools.examples.bash.imageName}");
-
-      # Check if the nix store is correctly initialized by listing dependencies of the installed Nix binary
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nix}'");
-      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.nix.imageName} nix-store -qR ${pkgs.nix}");
-      $docker->succeed("docker rmi ${pkgs.dockerTools.examples.nix.imageName}");
-
-      # To test the pullImage tool
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nixFromDockerHub}'");
-      $docker->succeed("docker run --rm nix:2.2.1 nix-store --version");
-      $docker->succeed("docker rmi nix:2.2.1");
-
-      # To test runAsRoot and entry point
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.nginx}'");
-      $docker->succeed("docker run --name nginx -d -p 8000:80 ${pkgs.dockerTools.examples.nginx.imageName}");
-      $docker->waitUntilSucceeds('curl http://localhost:8000/');
-      $docker->succeed("docker rm --force nginx");
-      $docker->succeed("docker rmi '${pkgs.dockerTools.examples.nginx.imageName}'");
-
-      # An pulled image can be used as base image
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.onTopOfPulledImage}'");
-      $docker->succeed("docker run --rm ontopofpulledimage hello");
-      $docker->succeed("docker rmi ontopofpulledimage");
-
-      # Regression test for issue #34779
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.runAsRootExtraCommands}'");
-      $docker->succeed("docker run --rm runasrootextracommands cat extraCommands");
-      $docker->succeed("docker run --rm runasrootextracommands cat runAsRoot");
-      $docker->succeed("docker rmi '${pkgs.dockerTools.examples.runAsRootExtraCommands.imageName}'");
-
-      # Ensure Docker images can use an unstable date
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bash}'");
-      $docker->succeed("[ '1970-01-01T00:00:01Z' != \"\$(docker inspect ${pkgs.dockerTools.examples.unstableDate.imageName} | ${pkgs.jq}/bin/jq -r .[].Created)\" ]");
-
-      # Ensure Layered Docker images work
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.layered-image}'");
-      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.layered-image.imageName}");
-      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.layered-image.imageName} cat extraCommands");
-
-      # Ensure building an image on top of a layered Docker images work
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.layered-on-top}'");
-      $docker->succeed("docker run --rm ${pkgs.dockerTools.examples.layered-on-top.imageName}");
-
-      # Ensure layers are shared between images
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.another-layered-image}'");
-      $docker->succeed("docker inspect ${pkgs.dockerTools.examples.layered-image.imageName} | ${pkgs.jq}/bin/jq -r '.[] | .RootFS.Layers | .[]' | sort > layers1.sha256");
-      $docker->succeed("docker inspect ${pkgs.dockerTools.examples.another-layered-image.imageName} | ${pkgs.jq}/bin/jq -r '.[] | .RootFS.Layers | .[]' | sort > layers2.sha256");
-      $docker->succeed('[ $(comm -1 -2 layers1.sha256 layers2.sha256 | wc -l) -ne 0 ]');
-
-      # Ensure order of layers is correct
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.layersOrder}'");
-      $docker->succeed("docker run --rm  ${pkgs.dockerTools.examples.layersOrder.imageName} cat /tmp/layer1 | grep -q layer1");
-      # This is to be sure the order of layers of the parent image is preserved
-      $docker->succeed("docker run --rm  ${pkgs.dockerTools.examples.layersOrder.imageName} cat /tmp/layer2 | grep -q layer2");
-      $docker->succeed("docker run --rm  ${pkgs.dockerTools.examples.layersOrder.imageName} cat /tmp/layer3 | grep -q layer3");
-
-      # Ensure image with only 2 layers can be loaded
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.two-layered-image}'");
-
-      # Ensure the bulk layer didn't miss store paths
-      # Regression test for https://github.com/NixOS/nixpkgs/issues/78744
-      $docker->succeed("docker load --input='${pkgs.dockerTools.examples.bulk-layer}'");
-      # This ensure the two output paths (ls and hello) are in the layer
-      $docker->succeed("docker run bulk-layer ls /bin/hello");
-    '';
+  testScript = with pkgs.dockerTools; ''
+    unix_time_second1 = "1970-01-01T00:00:01Z"
+
+    docker.wait_for_unit("sockets.target")
+
+    with subtest("Ensure Docker images use a stable date by default"):
+        docker.succeed(
+            "docker load --input='${examples.bash}'"
+        )
+        assert unix_time_second1 in docker.succeed(
+            "docker inspect ${examples.bash.imageName} "
+            + "| ${pkgs.jq}/bin/jq -r .[].Created",
+        )
+
+    docker.succeed("docker run --rm ${examples.bash.imageName} bash --version")
+    docker.succeed("docker rmi ${examples.bash.imageName}")
+
+    with subtest(
+        "Check if the nix store is correctly initialized by listing "
+        "dependencies of the installed Nix binary"
+    ):
+        docker.succeed(
+            "docker load --input='${examples.nix}'",
+            "docker run --rm ${examples.nix.imageName} nix-store -qR ${pkgs.nix}",
+            "docker rmi ${examples.nix.imageName}",
+        )
+
+    with subtest("The pullImage tool works"):
+        docker.succeed(
+            "docker load --input='${examples.nixFromDockerHub}'",
+            "docker run --rm nix:2.2.1 nix-store --version",
+            "docker rmi nix:2.2.1",
+        )
+
+    with subtest("runAsRoot and entry point work"):
+        docker.succeed(
+            "docker load --input='${examples.nginx}'",
+            "docker run --name nginx -d -p 8000:80 ${examples.nginx.imageName}",
+        )
+        docker.wait_until_succeeds("curl http://localhost:8000/")
+        docker.succeed(
+            "docker rm --force nginx", "docker rmi '${examples.nginx.imageName}'",
+        )
+
+    with subtest("A pulled image can be used as base image"):
+        docker.succeed(
+            "docker load --input='${examples.onTopOfPulledImage}'",
+            "docker run --rm ontopofpulledimage hello",
+            "docker rmi ontopofpulledimage",
+        )
+
+    with subtest("Regression test for issue #34779"):
+        docker.succeed(
+            "docker load --input='${examples.runAsRootExtraCommands}'",
+            "docker run --rm runasrootextracommands cat extraCommands",
+            "docker run --rm runasrootextracommands cat runAsRoot",
+            "docker rmi '${examples.runAsRootExtraCommands.imageName}'",
+        )
+
+    with subtest("Ensure Docker images can use an unstable date"):
+        docker.succeed(
+            "docker load --input='${examples.bash}'"
+        )
+        assert unix_time_second1 not in docker.succeed(
+            "docker inspect ${examples.unstableDate.imageName} "
+            + "| ${pkgs.jq}/bin/jq -r .[].Created"
+        )
+
+    with subtest("Ensure Layered Docker images work"):
+        docker.succeed(
+            "docker load --input='${examples.layered-image}'",
+            "docker run --rm ${examples.layered-image.imageName}",
+            "docker run --rm ${examples.layered-image.imageName} cat extraCommands",
+        )
+
+    with subtest("Ensure building an image on top of a layered Docker images work"):
+        docker.succeed(
+            "docker load --input='${examples.layered-on-top}'",
+            "docker run --rm ${examples.layered-on-top.imageName}",
+        )
+
+
+    def set_of_layers(image_name):
+        return set(
+            docker.succeed(
+                f"docker inspect {image_name} "
+                + "| ${pkgs.jq}/bin/jq -r '.[] | .RootFS.Layers | .[]'"
+            ).split()
+        )
+
+
+    with subtest("Ensure layers are shared between images"):
+        docker.succeed(
+            "docker load --input='${examples.another-layered-image}'"
+        )
+        layers1 = set_of_layers("${examples.layered-image.imageName}")
+        layers2 = set_of_layers("${examples.another-layered-image.imageName}")
+        assert bool(layers1 & layers2)
+
+    with subtest("Ensure order of layers is correct"):
+        docker.succeed(
+            "docker load --input='${examples.layersOrder}'"
+        )
+
+        for index in 1, 2, 3:
+            assert f"layer{index}" in docker.succeed(
+                f"docker run --rm  ${examples.layersOrder.imageName} cat /tmp/layer{index}"
+            )
+
+    with subtest("Ensure image with only 2 layers can be loaded"):
+        docker.succeed(
+            "docker load --input='${examples.two-layered-image}'"
+        )
+
+    with subtest(
+        "Ensure the bulk layer doesn't miss store paths (regression test for #78744)"
+    ):
+        docker.succeed(
+            "docker load --input='${pkgs.dockerTools.examples.bulk-layer}'",
+            # Ensure the two output paths (ls and hello) are in the layer
+            "docker run bulk-layer ls /bin/hello",
+        )
+  '';
 })
diff --git a/nixos/tests/gitdaemon.nix b/nixos/tests/gitdaemon.nix
new file mode 100644
index 000000000000..b610caf06fb2
--- /dev/null
+++ b/nixos/tests/gitdaemon.nix
@@ -0,0 +1,64 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+
+let
+  hashes = pkgs.writeText "hashes" ''
+    b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c  /project/bar
+  '';
+in {
+  name = "gitdaemon";
+
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ tilpner ];
+  };
+
+  nodes = {
+    server =
+      { config, ... }: {
+        networking.firewall.allowedTCPPorts = [ config.services.gitDaemon.port ];
+
+        environment.systemPackages = [ pkgs.git ];
+
+        services.gitDaemon = {
+          enable = true;
+          basePath = "/git";
+        };
+      };
+
+    client =
+      { pkgs, ... }: {
+        environment.systemPackages = [ pkgs.git ];
+      };
+  };
+
+  testScript = ''
+    start_all()
+
+    with subtest("create project.git"):
+        server.succeed(
+            "mkdir /git",
+            "git init --bare /git/project.git",
+            "touch /git/project.git/git-daemon-export-ok",
+        )
+
+    with subtest("add file to project.git"):
+        server.succeed(
+            "git clone /git/project.git /project",
+            "echo foo > /project/bar",
+            "git config --global user.email 'you@example.com'",
+            "git config --global user.name 'Your Name'",
+            "git -C /project add bar",
+            "git -C /project commit -m 'quux'",
+            "git -C /project push",
+            "rm -r /project",
+        )
+
+    with subtest("git daemon starts"):
+        server.wait_for_unit("git-daemon.service")
+
+    with subtest("client can clone project.git"):
+        client.succeed(
+            "git clone git://server/project.git /project",
+            "sha256sum -c ${hashes}",
+        )
+  '';
+})
diff --git a/nixos/tests/initrd-network.nix b/nixos/tests/initrd-network.nix
index 4796ff9b7c8d..9c35b7305768 100644
--- a/nixos/tests/initrd-network.nix
+++ b/nixos/tests/initrd-network.nix
@@ -1,4 +1,4 @@
-import ./make-test-python.nix ({ pkgs, ...} : {
+import ./make-test-python.nix ({ pkgs, lib, ...} : {
   name = "initrd-network";
 
   meta.maintainers = [ pkgs.stdenv.lib.maintainers.eelco ];
@@ -8,15 +8,26 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     boot.initrd.network.enable = true;
     boot.initrd.network.postCommands =
       ''
+        ip addr show
+        ip route show
         ip addr | grep 10.0.2.15 || exit 1
         ping -c1 10.0.2.2 || exit 1
       '';
+    # Check if cleanup was done correctly
+    boot.initrd.postMountCommands = lib.mkAfter
+      ''
+        ip addr show
+        ip route show
+        ip addr | grep 10.0.2.15 && exit 1
+        ping -c1 10.0.2.2 && exit 1
+      '';
   };
 
   testScript =
     ''
       start_all()
       machine.wait_for_unit("multi-user.target")
-      machine.succeed("ip link >&2")
+      machine.succeed("ip addr show >&2")
+      machine.succeed("ip route show >&2")
     '';
 })
diff --git a/nixos/tests/opensmtpd.nix b/nixos/tests/opensmtpd.nix
index e6f52db1d984..17c1a569ba0d 100644
--- a/nixos/tests/opensmtpd.nix
+++ b/nixos/tests/opensmtpd.nix
@@ -121,5 +121,5 @@ import ./make-test-python.nix {
     client.succeed("check-mail-landed >&2")
   '';
 
-  meta.timeout = 30;
+  meta.timeout = 1800;
 }
diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix
index 83883477a5cc..bab091d57acf 100644
--- a/nixos/tests/predictable-interface-names.nix
+++ b/nixos/tests/predictable-interface-names.nix
@@ -17,6 +17,12 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
       networking.useNetworkd = withNetworkd;
       networking.dhcpcd.enable = !withNetworkd;
       networking.useDHCP = !withNetworkd;
+
+      # Check if predictable interface names are working in stage-1
+      boot.initrd.postDeviceCommands = ''
+        ip link
+        ip link show eth0 ${if predictable then "&&" else "||"} exit 1
+      '';
     };
 
     testScript = ''
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 563f24726477..3d0d00bfbe63 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -287,7 +287,7 @@ let
         services.nginx = {
           enable = true;
           statusPage = true;
-          virtualHosts."/".extraConfig = "return 204;";
+          virtualHosts."test".extraConfig = "return 204;";
         };
       };
       exporterTest = ''
diff --git a/nixos/tests/rsyslogd.nix b/nixos/tests/rsyslogd.nix
index f17e61814c5e..50523920c60b 100644
--- a/nixos/tests/rsyslogd.nix
+++ b/nixos/tests/rsyslogd.nix
@@ -3,40 +3,38 @@
   pkgs ? import ../.. { inherit system config; }
 }:
 
-with import ../lib/testing.nix { inherit system pkgs; };
+with import ../lib/testing-python.nix { inherit system pkgs; };
 with pkgs.lib;
 
 {
   test1 = makeTest {
     name = "rsyslogd-test1";
-    meta.maintainers = [ maintainers.aanderse ];
+    meta.maintainers = [ pkgs.stdenv.lib.maintainers.aanderse ];
 
-    machine =
-      { config, pkgs, ... }:
-      { services.rsyslogd.enable = true;
-        services.journald.forwardToSyslog = false;
-      };
+    machine = { config, pkgs, ... }: {
+      services.rsyslogd.enable = true;
+      services.journald.forwardToSyslog = false;
+    };
 
     # ensure rsyslogd isn't receiving messages from journald if explicitly disabled
     testScript = ''
-      $machine->waitForUnit("default.target");
-      $machine->fail("test -f /var/log/messages");
+      machine.wait_for_unit("default.target")
+      machine.fail("test -f /var/log/messages")
     '';
   };
 
   test2 = makeTest {
     name = "rsyslogd-test2";
-    meta.maintainers = [ maintainers.aanderse ];
+    meta.maintainers = [ pkgs.stdenv.lib.maintainers.aanderse ];
 
-    machine =
-      { config, pkgs, ... }:
-      { services.rsyslogd.enable = true;
-      };
+    machine = { config, pkgs, ... }: {
+      services.rsyslogd.enable = true;
+    };
 
     # ensure rsyslogd is receiving messages from journald
     testScript = ''
-      $machine->waitForUnit("default.target");
-      $machine->succeed("test -f /var/log/messages");
+      machine.wait_for_unit("default.target")
+      machine.succeed("test -f /var/log/messages")
     '';
   };
 }
diff --git a/nixos/tests/service-runner.nix b/nixos/tests/service-runner.nix
new file mode 100644
index 000000000000..adb3fcd36d7a
--- /dev/null
+++ b/nixos/tests/service-runner.nix
@@ -0,0 +1,36 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "service-runner";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ roberth ];
+  };
+
+  nodes = {
+    machine = { pkgs, lib, ... }: {
+      services.nginx.enable = true;
+      services.nginx.virtualHosts.machine.root = pkgs.runCommand "webroot" {} ''
+        mkdir $out
+        echo 'yay' >$out/index.html
+      '';
+      systemd.services.nginx.enable = false;
+    };
+
+  };
+
+  testScript = { nodes, ... }: ''
+    url = "http://localhost/index.html"
+
+    with subtest("check systemd.services.nginx.runner"):
+        machine.fail(f"curl {url}")
+        machine.succeed(
+            """
+            mkdir -p /run/nginx /var/spool/nginx/logs
+            ${nodes.machine.config.systemd.services.nginx.runner} &
+            echo $!>my-nginx.pid
+            """
+        )
+        machine.wait_for_open_port(80)
+        machine.succeed(f"curl {url}")
+        machine.succeed("kill -INT $(cat my-nginx.pid)")
+        machine.wait_for_closed_port(80)
+  '';
+})
diff --git a/nixos/tests/systemd-networkd-vrf.nix b/nixos/tests/systemd-networkd-vrf.nix
index 5bc824531e82..af7813a2e604 100644
--- a/nixos/tests/systemd-networkd-vrf.nix
+++ b/nixos/tests/systemd-networkd-vrf.nix
@@ -197,15 +197,15 @@ in {
     # Test whether SSH through a VRF IP is possible.
     # (Note: this seems to be an issue on Linux 5.x, so I decided to add this to
     # ensure that we catch this when updating the default kernel).
-    with subtest("tcp traffic through vrf works"):
-        node1.wait_for_open_port(22)
-        client.succeed(
-            "cat ${snakeOilPrivateKey} > privkey.snakeoil"
-        )
-        client.succeed("chmod 600 privkey.snakeoil")
-        client.succeed(
-            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.1.2 true"
-        )
+    # with subtest("tcp traffic through vrf works"):
+    #     node1.wait_for_open_port(22)
+    #     client.succeed(
+    #         "cat ${snakeOilPrivateKey} > privkey.snakeoil"
+    #     )
+    #     client.succeed("chmod 600 privkey.snakeoil")
+    #     client.succeed(
+    #         "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil root@192.168.1.2 true"
+    #     )
 
     # Only configured routes through the VRF from the main routing table should
     # work. Additional IPs are only reachable when binding to the vrf interface.
diff --git a/nixos/tests/systemd-networkd-wireguard.nix b/nixos/tests/systemd-networkd.nix
index be5c0da981d2..319e5e94eceb 100644
--- a/nixos/tests/systemd-networkd-wireguard.nix
+++ b/nixos/tests/systemd-networkd.nix
@@ -41,15 +41,25 @@ let generateNodeConf = { lib, pkgs, config, privk, pubk, peerId, nodeId, ...}: {
               { routeConfig = { Gateway = "10.0.0.${nodeId}"; Destination = "10.0.0.0/24"; }; }
             ];
           };
-          "90-eth1" = {
+          "30-eth1" = {
             matchConfig = { Name = "eth1"; };
-            address = [ "192.168.1.${nodeId}/24" ];
+            address = [
+              "192.168.1.${nodeId}/24"
+              "fe80::${nodeId}/64"
+            ];
+            routingPolicyRules = [
+              { routingPolicyRuleConfig = { Table = 10; IncomingInterface = "eth1"; Family = "both"; };}
+              { routingPolicyRuleConfig = { Table = 20; OutgoingInterface = "eth1"; };}
+              { routingPolicyRuleConfig = { Table = 30; From = "192.168.1.1"; To = "192.168.1.2"; SourcePort = 666 ; DestinationPort = 667; };}
+              { routingPolicyRuleConfig = { Table = 40; IPProtocol = "tcp"; InvertRule = true; };}
+              { routingPolicyRuleConfig = { Table = 50; IncomingInterface = "eth1"; Family = "ipv4"; };}
+            ];
           };
         };
       };
     };
 in import ./make-test-python.nix ({pkgs, ... }: {
-  name = "networkd-wireguard";
+  name = "networkd";
   meta = with pkgs.stdenv.lib.maintainers; {
     maintainers = [ ninjatrappeur ];
   };
@@ -76,9 +86,28 @@ testScript = ''
     start_all()
     node1.wait_for_unit("systemd-networkd-wait-online.service")
     node2.wait_for_unit("systemd-networkd-wait-online.service")
+
+    # ================================
+    # Wireguard
+    # ================================
     node1.succeed("ping -c 5 10.0.0.2")
     node2.succeed("ping -c 5 10.0.0.1")
     # Is the fwmark set?
     node2.succeed("wg | grep -q 42")
+
+    # ================================
+    # Routing Policies
+    # ================================
+    # Testing all the routingPolicyRuleConfig members:
+    # Table + IncomingInterface
+    node1.succeed("sudo ip rule | grep 'from all iif eth1 lookup 10'")
+    # OutgoingInterface
+    node1.succeed("sudo ip rule | grep 'from all oif eth1 lookup 20'")
+    # From + To + SourcePort + DestinationPort
+    node1.succeed(
+        "sudo ip rule | grep 'from 192.168.1.1 to 192.168.1.2 sport 666 dport 667 lookup 30'"
+    )
+    # IPProtocol + InvertRule
+    node1.succeed("sudo ip rule | grep 'not from all ipproto tcp lookup 40'")
 '';
 })