summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml21
-rw-r--r--nixos/doc/manual/installation/installing-from-other-distro.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md9
-rw-r--r--nixos/modules/hardware/keyboard/uhk.nix21
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix2
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl12
-rw-r--r--nixos/modules/installer/tools/tools.nix7
-rw-r--r--nixos/modules/misc/mandoc.nix4
-rw-r--r--nixos/modules/misc/version.nix9
-rw-r--r--nixos/modules/module-list.nix3
-rw-r--r--nixos/modules/security/pam.nix4
-rw-r--r--nixos/modules/security/wrappers/default.nix4
-rw-r--r--nixos/modules/services/backup/borgmatic.nix5
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix8
-rw-r--r--nixos/modules/services/desktops/pipewire/daemon/pipewire-pulse.conf.json3
-rw-r--r--nixos/modules/services/games/factorio.nix15
-rw-r--r--nixos/modules/services/hardware/illum.nix1
-rw-r--r--nixos/modules/services/hardware/usbrelayd.nix7
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix9
-rw-r--r--nixos/modules/services/logging/logstash.nix2
-rw-r--r--nixos/modules/services/mail/spamassassin.nix2
-rw-r--r--nixos/modules/services/matrix/matrix-synapse.nix1
-rw-r--r--nixos/modules/services/misc/heisenbridge.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix9
-rw-r--r--nixos/modules/services/networking/gateone.nix4
-rw-r--r--nixos/modules/services/networking/ircd-hybrid/ircd.conf2
-rw-r--r--nixos/modules/services/networking/pleroma.nix8
-rw-r--r--nixos/modules/services/networking/pptpd.nix2
-rw-r--r--nixos/modules/services/networking/prayer.nix2
-rw-r--r--nixos/modules/services/networking/supplicant.nix4
-rw-r--r--nixos/modules/services/networking/tailscale.nix27
-rw-r--r--nixos/modules/services/networking/xl2tpd.nix6
-rw-r--r--nixos/modules/services/security/kanidm.nix345
-rw-r--r--nixos/modules/services/security/sshguard.nix2
-rw-r--r--nixos/modules/services/wayland/cage.nix2
-rw-r--r--nixos/modules/services/web-apps/restya-board.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/phosh.nix (renamed from nixos/modules/programs/phosh.nix)73
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix2
-rw-r--r--nixos/modules/system/boot/modprobe.nix2
-rw-r--r--nixos/modules/system/boot/networkd.nix1
-rw-r--r--nixos/modules/system/boot/plymouth.nix102
-rw-r--r--nixos/modules/system/boot/systemd/nspawn.nix6
-rw-r--r--nixos/modules/tasks/auto-upgrade.nix2
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix199
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix42
-rw-r--r--nixos/modules/tasks/network-interfaces.nix5
-rw-r--r--nixos/modules/testing/test-instrumentation.nix3
-rw-r--r--nixos/modules/virtualisation/amazon-init.nix2
-rw-r--r--nixos/modules/virtualisation/digital-ocean-init.nix2
-rw-r--r--nixos/modules/virtualisation/proxmox-lxc.nix11
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/installed-tests/default.nix1
-rw-r--r--nixos/tests/installed-tests/power-profiles-daemon.nix9
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix2
-rw-r--r--nixos/tests/kanidm.nix75
-rw-r--r--nixos/tests/kernel-generic.nix1
-rw-r--r--nixos/tests/networking.nix20
-rw-r--r--nixos/tests/pleroma.nix4
-rw-r--r--nixos/tests/systemd-nspawn.nix13
-rw-r--r--nixos/tests/virtualbox.nix2
64 files changed, 968 insertions, 194 deletions
diff --git a/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml b/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml
index 525531a4781..024a24379dd 100644
--- a/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml
+++ b/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml
@@ -248,7 +248,7 @@ $ nix-env -p /nix/var/nix/profiles/system -f '<nixpkgs/nixos>' -I nixos-co
         (since your Nix install was probably single user):
       </para>
       <programlisting>
-$ sudo chown -R 0.0 /nix
+$ sudo chown -R 0:0 /nix
 </programlisting>
     </listitem>
     <listitem>
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 6da24e3a8a8..ab0edcebc67 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -455,6 +455,12 @@
           <link xlink:href="options.html#opt-services.nifi.enable">services.nifi</link>.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://kanidm.github.io/kanidm/stable/">kanidm</link>,
+          an identity management server written in Rust.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-22.05-incompatibilities">
@@ -2465,6 +2471,21 @@
           hosts.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The option
+          <link xlink:href="options.html#opt-networking.useDHCP">networking.useDHCP</link>
+          isn’t deprecated anymore. When using
+          <link xlink:href="options.html#opt-networking.useNetworkd"><literal>systemd-networkd</literal></link>,
+          a generic <literal>.network</literal>-unit is added which
+          enables DHCP for each interface matching
+          <literal>en*</literal>, <literal>eth*</literal> or
+          <literal>wl*</literal> with priority 99 (which means that it
+          doesn’t have any effect if such an interface is matched by a
+          <literal>.network-</literal>unit with a lower priority). In
+          case of scripted networking, no behavior was changed.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
 </section>
diff --git a/nixos/doc/manual/installation/installing-from-other-distro.section.md b/nixos/doc/manual/installation/installing-from-other-distro.section.md
index d9060eb89c3..fa8806f791d 100644
--- a/nixos/doc/manual/installation/installing-from-other-distro.section.md
+++ b/nixos/doc/manual/installation/installing-from-other-distro.section.md
@@ -177,7 +177,7 @@ The first steps to all these are the same:
     was probably single user):
 
     ```ShellSession
-    $ sudo chown -R 0.0 /nix
+    $ sudo chown -R 0:0 /nix
     ```
 
 1.  Set up the `/etc/NIXOS` and `/etc/NIXOS_LUSTRATE` files:
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 90d22643701..af65ae46131 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -135,6 +135,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [nifi](https://nifi.apache.org), an easy to use, powerful, and reliable system to process and distribute data. Available as [services.nifi](options.html#opt-services.nifi.enable).
 
+- [kanidm](https://kanidm.github.io/kanidm/stable/), an identity management server written in Rust.
+
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
 ## Backward Incompatibilities {#sec-release-22.05-incompatibilities}
@@ -875,4 +877,11 @@ In addition to numerous new and upgraded packages, this release has the followin
   `true` starting with NixOS 22.11. Enable it explicitly if you need to control
   Snapserver remotely or connect streamig clients from other hosts.
 
+- The option [networking.useDHCP](options.html#opt-networking.useDHCP) isn't deprecated anymore.
+  When using [`systemd-networkd`](options.html#opt-networking.useNetworkd), a generic
+  `.network`-unit is added which enables DHCP for each interface matching `en*`, `eth*`
+  or `wl*` with priority 99 (which means that it doesn't have any effect if such an interface is matched
+  by a `.network-`unit with a lower priority). In case of scripted networking, no behavior
+  was changed.
+
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
diff --git a/nixos/modules/hardware/keyboard/uhk.nix b/nixos/modules/hardware/keyboard/uhk.nix
new file mode 100644
index 00000000000..bf2d739c3a9
--- /dev/null
+++ b/nixos/modules/hardware/keyboard/uhk.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.keyboard.uhk;
+in
+{
+  options.hardware.keyboard.uhk = {
+    enable = mkEnableOption ''
+    non-root access to the firmware of UHK keyboards.
+      You need it when you want to flash a new firmware on the keyboard.
+      Access to the keyboard is granted to users in the "input" group.
+      You may want to install the uhk-agent package.
+    '';
+
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.uhk-udev-rules ];
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index 618057618d0..3f92b779d60 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -46,5 +46,5 @@ with lib;
     done
   '';
 
-  system.stateVersion = mkDefault "18.03";
+  system.stateVersion = lib.mkDefault lib.trivial.release;
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index fb5d3ba4732..b74ec838df4 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -581,17 +581,19 @@ ${\join "", (map { "  $_\n" } (uniq @attrs))}}
 EOF
 
 sub generateNetworkingDhcpConfig {
+    # FIXME disable networking.useDHCP by default when switching to networkd.
     my $config = <<EOF;
-  # The global useDHCP flag is deprecated, therefore explicitly set to false here.
-  # Per-interface useDHCP will be mandatory in the future, so this generated config
-  # replicates the default behaviour.
-  networking.useDHCP = lib.mkDefault false;
+  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+  # (the default) this is the recommended approach. When using systemd-networkd it's
+  # still possible to use this option, but it's recommended to use it in conjunction
+  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+  networking.useDHCP = lib.mkDefault true;
 EOF
 
     foreach my $path (glob "/sys/class/net/*") {
         my $dev = basename($path);
         if ($dev ne "lo") {
-            $config .= "  networking.interfaces.$dev.useDHCP = lib.mkDefault true;\n";
+            $config .= "  # networking.interfaces.$dev.useDHCP = lib.mkDefault true;\n";
         }
     }
 
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index bf5ec0f9690..04be272742c 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -34,7 +34,7 @@ let
     name = "nixos-generate-config";
     src = ./nixos-generate-config.pl;
     perl = "${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl";
-    detectvirt = "${pkgs.systemd}/bin/systemd-detect-virt";
+    detectvirt = "${config.systemd.package}/bin/systemd-detect-virt";
     btrfs = "${pkgs.btrfs-progs}/bin/btrfs";
     inherit (config.system.nixos-generate-config) configuration desktopConfiguration;
     xserverEnabled = config.services.xserver.enable;
@@ -177,6 +177,10 @@ in
         # users.users.jane = {
         #   isNormalUser = true;
         #   extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user.
+        #   packages = with pkgs; [
+        #     firefox
+        #     thunderbird
+        #   ];
         # };
 
         # List packages installed in system profile. To search, run:
@@ -184,7 +188,6 @@ in
         # environment.systemPackages = with pkgs; [
         #   vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
         #   wget
-        #   firefox
         # ];
 
         # Some programs need SUID wrappers, can be configured further or are
diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix
index 3da60f2f8e6..838f2087656 100644
--- a/nixos/modules/misc/mandoc.nix
+++ b/nixos/modules/misc/mandoc.nix
@@ -53,7 +53,9 @@ in {
       # see: https://inbox.vuxu.org/mandoc-tech/20210906171231.GF83680@athene.usta.de/T/#e85f773c1781e3fef85562b2794f9cad7b2909a3c
       extraSetup = lib.mkIf config.documentation.man.generateCaches ''
         ${makewhatis} -T utf8 ${
-          lib.concatMapStringsSep " " (path: "\"$out/${path}\"") cfg.manPath
+          lib.concatMapStringsSep " " (path:
+            "$out/" + lib.escapeShellArg path
+          ) cfg.manPath
         }
       '';
     };
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 931201ade29..010acdb72f6 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -146,6 +146,15 @@ in
       "/etc/os-release".source = initrdRelease;
       "/etc/initrd-release".source = initrdRelease;
     };
+
+    # We have to use `warnings` because when warning in the default of the option
+    # the warning would also be shown when building the manual since the manual
+    # has to evaluate the default.
+    #
+    # TODO Remove this and drop the default of the option so people are forced to set it.
+    # Doing this also means fixing the comment in nixos/modules/testing/test-instrumentation.nix
+    warnings = lib.optional (options.system.stateVersion.highestPrio == (lib.mkOptionDefault { }).priority)
+      "system.stateVersion is not set, defaulting to ${config.system.stateVersion}. Read why this matters on https://nixos.org/manual/nixos/stable/options.html#opt-system.stateVersion.";
   };
 
   # uses version info nixpkgs, which requires a full nixpkgs path
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 9d9f2e9057c..b3efa5d5e6f 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -57,6 +57,7 @@
   ./hardware/sensor/hddtemp.nix
   ./hardware/sensor/iio.nix
   ./hardware/keyboard/teck.nix
+  ./hardware/keyboard/uhk.nix
   ./hardware/keyboard/zsa.nix
   ./hardware/ksm.nix
   ./hardware/ledger.nix
@@ -196,7 +197,6 @@
   ./programs/partition-manager.nix
   ./programs/plotinus.nix
   ./programs/proxychains.nix
-  ./programs/phosh.nix
   ./programs/qt5ct.nix
   ./programs/screen.nix
   ./programs/sedutil.nix
@@ -975,6 +975,7 @@
   ./services/security/hockeypuck.nix
   ./services/security/hologram-server.nix
   ./services/security/hologram-agent.nix
+  ./services/security/kanidm.nix
   ./services/security/munge.nix
   ./services/security/nginx-sso.nix
   ./services/security/oauth2_proxy.nix
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index b1fcb0b461f..23d1344a57a 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -626,7 +626,7 @@ let
             session optional ${pkgs.otpw}/lib/security/pam_otpw.so
           '' +
           optionalString cfg.startSession ''
-            session optional ${pkgs.systemd}/lib/security/pam_systemd.so
+            session optional ${config.systemd.package}/lib/security/pam_systemd.so
           '' +
           optionalString cfg.forwardXAuth ''
             session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99
@@ -1242,7 +1242,7 @@ in
         mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,
       '' +
       optionalString (isEnabled (cfg: cfg.startSession)) ''
-        mr ${pkgs.systemd}/lib/security/pam_systemd.so,
+        mr ${config.systemd.package}/lib/security/pam_systemd.so,
       '' +
       optionalString (isEnabled (cfg: cfg.enableAppArmor)
                      && config.security.apparmor.enable) ''
diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix
index e63f19010de..169ef744262 100644
--- a/nixos/modules/security/wrappers/default.nix
+++ b/nixos/modules/security/wrappers/default.nix
@@ -98,7 +98,7 @@ let
 
       # Prevent races
       chmod 0000 "$wrapperDir/${program}"
-      chown ${owner}.${group} "$wrapperDir/${program}"
+      chown ${owner}:${group} "$wrapperDir/${program}"
 
       # Set desired capabilities on the file plus cap_setpcap so
       # the wrapper program can elevate the capabilities set on
@@ -126,7 +126,7 @@ let
 
       # Prevent races
       chmod 0000 "$wrapperDir/${program}"
-      chown ${owner}.${group} "$wrapperDir/${program}"
+      chown ${owner}:${group} "$wrapperDir/${program}"
 
       chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" "$wrapperDir/${program}"
     '';
diff --git a/nixos/modules/services/backup/borgmatic.nix b/nixos/modules/services/backup/borgmatic.nix
index 5e5c0bbeccc..9414d78aa75 100644
--- a/nixos/modules/services/backup/borgmatic.nix
+++ b/nixos/modules/services/backup/borgmatic.nix
@@ -4,7 +4,8 @@ with lib;
 
 let
   cfg = config.services.borgmatic;
-  cfgfile = pkgs.writeText "config.yaml" (builtins.toJSON cfg.settings);
+  settingsFormat = pkgs.formats.yaml { };
+  cfgfile = settingsFormat.generate "config.yaml" cfg.settings;
 in {
   options.services.borgmatic = {
     enable = mkEnableOption "borgmatic";
@@ -14,7 +15,7 @@ in {
         See https://torsion.org/borgmatic/docs/reference/configuration/
       '';
       type = types.submodule {
-        freeformType = with lib.types; attrsOf anything;
+        freeformType = settingsFormat.type;
         options.location = {
           source_directories = mkOption {
             type = types.listOf types.str;
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index cc5de97d6d1..90adab7fbf2 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -300,17 +300,17 @@ in
         };
         preStart = ''
           mkdir -p ${baseDir}
-          chown hydra.hydra ${baseDir}
+          chown hydra:hydra ${baseDir}
           chmod 0750 ${baseDir}
 
           ln -sf ${hydraConf} ${baseDir}/hydra.conf
 
           mkdir -m 0700 -p ${baseDir}/www
-          chown hydra-www.hydra ${baseDir}/www
+          chown hydra-www:hydra ${baseDir}/www
 
           mkdir -m 0700 -p ${baseDir}/queue-runner
           mkdir -m 0750 -p ${baseDir}/build-logs
-          chown hydra-queue-runner.hydra ${baseDir}/queue-runner ${baseDir}/build-logs
+          chown hydra-queue-runner:hydra ${baseDir}/queue-runner ${baseDir}/build-logs
 
           ${optionalString haveLocalDB ''
             if ! [ -e ${baseDir}/.db-created ]; then
@@ -338,7 +338,7 @@ in
             rmdir /nix/var/nix/gcroots/per-user/hydra-www/hydra-roots
           fi
 
-          chown hydra.hydra ${cfg.gcRootsDir}
+          chown hydra:hydra ${cfg.gcRootsDir}
           chmod 2775 ${cfg.gcRootsDir}
         '';
         serviceConfig.ExecStart = "${hydra-package}/bin/hydra-init";
diff --git a/nixos/modules/services/desktops/pipewire/daemon/pipewire-pulse.conf.json b/nixos/modules/services/desktops/pipewire/daemon/pipewire-pulse.conf.json
index b19fb33ec17..114afbfb0ea 100644
--- a/nixos/modules/services/desktops/pipewire/daemon/pipewire-pulse.conf.json
+++ b/nixos/modules/services/desktops/pipewire/daemon/pipewire-pulse.conf.json
@@ -62,6 +62,9 @@
           "application.process.binary": "teams"
         },
         {
+          "application.process.binary": "teams-insiders"
+        },
+        {
           "application.process.binary": "skypeforlinux"
         }
       ],
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index ff73d7a46ed..bb6898a08c5 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -87,6 +87,18 @@ in
           a new map with default settings will be generated before starting the service.
         '';
       };
+      loadLatestSave = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Load the latest savegame on startup. This overrides saveName, in that the latest
+          save will always be used even if a saved game of the given name exists. It still
+          controls the 'canonical' name of the savegame.
+
+          Set this to true to have the server automatically reload a recent autosave after
+          a crash or desync.
+        '';
+      };
       # TODO Add more individual settings as nixos-options?
       # TODO XXX The server tries to copy a newly created config file over the old one
       #   on shutdown, but fails, because it's in the nix store. When is this needed?
@@ -250,8 +262,9 @@ in
           "--config=${cfg.configFile}"
           "--port=${toString cfg.port}"
           "--bind=${cfg.bind}"
-          "--start-server=${mkSavePath cfg.saveName}"
+          (optionalString (!cfg.loadLatestSave) "--start-server=${mkSavePath cfg.saveName}")
           "--server-settings=${serverSettingsFile}"
+          (optionalString cfg.loadLatestSave "--start-server-load-latest")
           (optionalString (cfg.mods != []) "--mod-directory=${modDir}")
           (optionalString (cfg.admins != []) "--server-adminlist=${serverAdminsFile}")
         ];
diff --git a/nixos/modules/services/hardware/illum.nix b/nixos/modules/services/hardware/illum.nix
index ff73c99a653..7f7a8500023 100644
--- a/nixos/modules/services/hardware/illum.nix
+++ b/nixos/modules/services/hardware/illum.nix
@@ -28,6 +28,7 @@ in {
       description = "Backlight Adjustment Service";
       wantedBy = [ "multi-user.target" ];
       serviceConfig.ExecStart = "${pkgs.illum}/bin/illum-d";
+      serviceConfig.Restart = "on-failure";
     };
 
   };
diff --git a/nixos/modules/services/hardware/usbrelayd.nix b/nixos/modules/services/hardware/usbrelayd.nix
index c0322e89e6b..2cee4e1ff7e 100644
--- a/nixos/modules/services/hardware/usbrelayd.nix
+++ b/nixos/modules/services/hardware/usbrelayd.nix
@@ -26,8 +26,7 @@ in
 
   config = mkIf cfg.enable {
 
-    # TODO: Rename to .conf in upcomming release
-    environment.etc."usbrelayd.ini".text = ''
+    environment.etc."usbrelayd.conf".text = ''
       [MQTT]
       BROKER = ${cfg.broker}
       CLIENTNAME = ${cfg.clientName}
@@ -41,4 +40,8 @@ in
     };
     users.groups.usbrelay = { };
   };
+
+  meta = {
+    maintainers = with lib.maintainers; [ wentasah ];
+  };
 }
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index 6022227f6ea..e255e5d2218 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -360,7 +360,14 @@ in {
   };
 
   config = mkIf cfg.enable {
-    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
+    assertions = [
+      {
+        assertion = cfg.openFirewall -> !isNull cfg.config;
+        message = "openFirewall can only be used with a declarative config";
+      }
+    ];
+
+    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.config.http.server_port ];
 
     systemd.services.home-assistant = {
       description = "Home Assistant";
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index a08203dffe7..5d00feabe1c 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -109,7 +109,7 @@ in
           '''
             # Read from journal
             pipe {
-              command => "''${pkgs.systemd}/bin/journalctl -f -o json"
+              command => "''${config.systemd.package}/bin/journalctl -f -o json"
               type => "syslog" codec => json {}
             }
           '''
diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix
index ac878222b26..3b10d8d2909 100644
--- a/nixos/modules/services/mail/spamassassin.nix
+++ b/nixos/modules/services/mail/spamassassin.nix
@@ -135,7 +135,7 @@ in
         User = "spamd";
         Group = "spamd";
         StateDirectory = "spamassassin";
-        ExecStartPost = "+${pkgs.systemd}/bin/systemctl -q --no-block try-reload-or-restart spamd.service";
+        ExecStartPost = "+${config.systemd.package}/bin/systemctl -q --no-block try-reload-or-restart spamd.service";
       };
 
       script = ''
diff --git a/nixos/modules/services/matrix/matrix-synapse.nix b/nixos/modules/services/matrix/matrix-synapse.nix
index a498aff7a55..87a977f8e1e 100644
--- a/nixos/modules/services/matrix/matrix-synapse.nix
+++ b/nixos/modules/services/matrix/matrix-synapse.nix
@@ -296,6 +296,7 @@ in {
               default = if lib.versionAtLeast config.system.stateVersion "22.05"
                 then "${cfg.dataDir}/media_store"
                 else "${cfg.dataDir}/media";
+              defaultText = "${cfg.dataDir}/media_store for when system.stateVersion is at least 22.05, ${cfg.dataDir}/media when lower than 22.05";
               description = ''
                 Directory where uploaded images and attachments are stored.
               '';
diff --git a/nixos/modules/services/misc/heisenbridge.nix b/nixos/modules/services/misc/heisenbridge.nix
index 7ce8a23d9af..deefb061d8b 100644
--- a/nixos/modules/services/misc/heisenbridge.nix
+++ b/nixos/modules/services/misc/heisenbridge.nix
@@ -204,7 +204,7 @@ in
         NoNewPrivileges = true;
         LockPersonality = true;
         RestrictRealtime = true;
-        SystemCallFilter = ["@system-service" "~@priviledged" "@chown"];
+        SystemCallFilter = ["@system-service" "~@privileged" "@chown"];
         SystemCallArchitectures = "native";
         RestrictAddressFamilies = "AF_INET AF_INET6";
       };
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index 52525e8935b..ceb2db1faef 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -3,6 +3,7 @@
 with lib;
 
 let
+  json = pkgs.formats.json { };
   cfg = config.services.prometheus;
 
   workingDir = "/var/lib/" + cfg.stateDir;
@@ -34,13 +35,7 @@ let
         promtool ${what} $out
       '' else file;
 
-  # Pretty-print JSON to a file
-  writePrettyJSON = name: x:
-    pkgs.runCommandLocal name { } ''
-      echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out
-    '';
-
-  generatedPrometheusYml = writePrettyJSON "prometheus.yml" promConfig;
+  generatedPrometheusYml = json.generate "prometheus.yml" promConfig;
 
   # This becomes the main config file for Prometheus
   promConfig = {
diff --git a/nixos/modules/services/networking/gateone.nix b/nixos/modules/services/networking/gateone.nix
index 3e3a3c1aa94..e68f8a47d5c 100644
--- a/nixos/modules/services/networking/gateone.nix
+++ b/nixos/modules/services/networking/gateone.nix
@@ -36,11 +36,11 @@ config = mkIf cfg.enable {
     preStart = ''
       if [ ! -d ${cfg.settingsDir} ] ; then
         mkdir -m 0750 -p ${cfg.settingsDir}
-        chown -R gateone.gateone ${cfg.settingsDir}
+        chown -R gateone:gateone ${cfg.settingsDir}
       fi
       if [ ! -d ${cfg.pidDir} ] ; then
         mkdir -m 0750 -p ${cfg.pidDir}
-        chown -R gateone.gateone ${cfg.pidDir}
+        chown -R gateone:gateone ${cfg.pidDir}
       fi
       '';
     #unitConfig.RequiresMountsFor = "${cfg.settingsDir}";
diff --git a/nixos/modules/services/networking/ircd-hybrid/ircd.conf b/nixos/modules/services/networking/ircd-hybrid/ircd.conf
index 17ef203840a..b82094cf5f0 100644
--- a/nixos/modules/services/networking/ircd-hybrid/ircd.conf
+++ b/nixos/modules/services/networking/ircd-hybrid/ircd.conf
@@ -98,7 +98,7 @@ serverinfo {
 	 * 
 	 * 	openssl genrsa -out rsa.key 2048
 	 *	openssl rsa -in rsa.key -pubout -out rsa.pub
-	 *	chown <ircd-user>.<ircd.group> rsa.key rsa.pub
+	 *	chown <ircd-user>:<ircd.group> rsa.key rsa.pub
 	 *	chmod 0600 rsa.key
 	 *	chmod 0644 rsa.pub
 	 */
diff --git a/nixos/modules/services/networking/pleroma.nix b/nixos/modules/services/networking/pleroma.nix
index c6d4c14dcb7..9b8382392c0 100644
--- a/nixos/modules/services/networking/pleroma.nix
+++ b/nixos/modules/services/networking/pleroma.nix
@@ -1,7 +1,6 @@
 { config, options, lib, pkgs, stdenv, ... }:
 let
   cfg = config.services.pleroma;
-  cookieFile = "/var/lib/pleroma/.cookie";
 in {
   options = {
     services.pleroma = with lib; {
@@ -9,7 +8,7 @@ in {
 
       package = mkOption {
         type = types.package;
-        default = pkgs.pleroma.override { inherit cookieFile; };
+        default = pkgs.pleroma;
         defaultText = literalExpression "pkgs.pleroma";
         description = "Pleroma package to use.";
       };
@@ -101,6 +100,7 @@ in {
       after = [ "network-online.target" "postgresql.service" ];
       wantedBy = [ "multi-user.target" ];
       restartTriggers = [ config.environment.etc."/pleroma/config.exs".source ];
+      environment.RELEASE_COOKIE = "/var/lib/pleroma/.cookie";
       serviceConfig = {
         User = cfg.user;
         Group = cfg.group;
@@ -118,10 +118,10 @@ in {
         # Better be safe than sorry migration-wise.
         ExecStartPre =
           let preScript = pkgs.writers.writeBashBin "pleromaStartPre" ''
-            if [ ! -f "${cookieFile}" ] || [ ! -s "${cookieFile}" ]
+            if [ ! -f /var/lib/pleroma/.cookie ]
             then
               echo "Creating cookie file"
-              dd if=/dev/urandom bs=1 count=16 | ${pkgs.hexdump}/bin/hexdump -e '16/1 "%02x"' > "${cookieFile}"
+              dd if=/dev/urandom bs=1 count=16 | hexdump -e '16/1 "%02x"' > /var/lib/pleroma/.cookie
             fi
             ${cfg.package}/bin/pleroma_ctl migrate
           '';
diff --git a/nixos/modules/services/networking/pptpd.nix b/nixos/modules/services/networking/pptpd.nix
index 3e7753b9dd3..423e14e998f 100644
--- a/nixos/modules/services/networking/pptpd.nix
+++ b/nixos/modules/services/networking/pptpd.nix
@@ -108,7 +108,7 @@ with lib;
         #username	pptpd	password	*
         EOF
 
-        chown root.root "$secrets"
+        chown root:root "$secrets"
         chmod 600 "$secrets"
       '';
 
diff --git a/nixos/modules/services/networking/prayer.nix b/nixos/modules/services/networking/prayer.nix
index ae9258b2712..513509eaca3 100644
--- a/nixos/modules/services/networking/prayer.nix
+++ b/nixos/modules/services/networking/prayer.nix
@@ -82,7 +82,7 @@ in
       serviceConfig.Type = "forking";
       preStart = ''
         mkdir -m 0755 -p ${stateDir}
-        chown ${prayerUser}.${prayerGroup} ${stateDir}
+        chown ${prayerUser}:${prayerGroup} ${stateDir}
       '';
       script = "${prayer}/sbin/prayer --config-file=${prayerCfg}";
     };
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index 8df450a11c6..e111b311d68 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -226,10 +226,10 @@ in
               ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
 
           ${optionalString (hasAttr "WLAN" cfg) ''
-            ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service"
+            ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service"
           ''}
           ${optionalString (hasAttr "LAN" cfg) ''
-            ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service"
+            ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service"
           ''}
         '';
       })];
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index 1f64113950a..0133874d0e0 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -2,9 +2,13 @@
 
 with lib;
 
-let cfg = config.services.tailscale;
+let
+  cfg = config.services.tailscale;
+  firewallOn = config.networking.firewall.enable;
+  rpfMode = config.networking.firewall.checkReversePath;
+  rpfIsStrict = rpfMode == true || rpfMode == "strict";
 in {
-  meta.maintainers = with maintainers; [ danderson mbaillie ];
+  meta.maintainers = with maintainers; [ danderson mbaillie twitchyliquid64 ];
 
   options.services.tailscale = {
     enable = mkEnableOption "Tailscale client daemon";
@@ -36,17 +40,34 @@ in {
   };
 
   config = mkIf cfg.enable {
+    warnings = optional (firewallOn && rpfIsStrict) "Strict reverse path filtering breaks Tailscale exit node use and some subnet routing setups. Consider setting `networking.firewall.checkReversePath` = 'loose'";
     environment.systemPackages = [ cfg.package ]; # for the CLI
     systemd.packages = [ cfg.package ];
     systemd.services.tailscaled = {
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.openresolv pkgs.procps ];
+      path = [
+        pkgs.openresolv # for configuring DNS in some configs
+        pkgs.procps     # for collecting running services (opt-in feature)
+        pkgs.glibc      # for `getent` to look up user shells
+      ];
       serviceConfig.Environment = [
         "PORT=${toString cfg.port}"
         ''"FLAGS=--tun ${lib.escapeShellArg cfg.interfaceName}"''
       ] ++ (lib.optionals (cfg.permitCertUid != null) [
         "TS_PERMIT_CERT_UID=${cfg.permitCertUid}"
       ]);
+      # Restart tailscaled with a single `systemctl restart` at the
+      # end of activation, rather than a `stop` followed by a later
+      # `start`. Activation over Tailscale can hang for tens of
+      # seconds in the stop+start setup, if the activation script has
+      # a significant delay between the stop and start phases
+      # (e.g. script blocked on another unit with a slow shutdown).
+      #
+      # Tailscale is aware of the correctness tradeoff involved, and
+      # already makes its upstream systemd unit robust against unit
+      # version mismatches on restart for compatibility with other
+      # linux distros.
+      stopIfChanged = false;
     };
   };
 }
diff --git a/nixos/modules/services/networking/xl2tpd.nix b/nixos/modules/services/networking/xl2tpd.nix
index 7dbe51422d9..9418488c1e9 100644
--- a/nixos/modules/services/networking/xl2tpd.nix
+++ b/nixos/modules/services/networking/xl2tpd.nix
@@ -116,18 +116,18 @@ with lib;
         #username	xl2tpd	password	*
         EOF
 
-        chown root.root ppp/chap-secrets
+        chown root:root ppp/chap-secrets
         chmod 600 ppp/chap-secrets
 
         # The documentation says this file should be present but doesn't explain why and things work even if not there:
         [ -f l2tp-secrets ] || (echo -n "* * "; ${pkgs.apg}/bin/apg -n 1 -m 32 -x 32 -a 1 -M LCN) > l2tp-secrets
-        chown root.root l2tp-secrets
+        chown root:root l2tp-secrets
         chmod 600 l2tp-secrets
 
         popd > /dev/null
 
         mkdir -p /run/xl2tpd
-        chown root.root /run/xl2tpd
+        chown root:root /run/xl2tpd
         chmod 700       /run/xl2tpd
       '';
 
diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix
new file mode 100644
index 00000000000..a7c51b9a877
--- /dev/null
+++ b/nixos/modules/services/security/kanidm.nix
@@ -0,0 +1,345 @@
+{ config, lib, options, pkgs, ... }:
+let
+  cfg = config.services.kanidm;
+  settingsFormat = pkgs.formats.toml { };
+  # Remove null values, so we can document optional values that don't end up in the generated TOML file.
+  filterConfig = lib.converge (lib.filterAttrsRecursive (_: v: v != null));
+  serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings);
+  clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings);
+  unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings);
+
+  defaultServiceConfig = {
+    BindReadOnlyPaths = [
+      "/nix/store"
+      "-/etc/resolv.conf"
+      "-/etc/nsswitch.conf"
+      "-/etc/hosts"
+      "-/etc/localtime"
+    ];
+    CapabilityBoundingSet = "";
+    # ProtectClock= adds DeviceAllow=char-rtc r
+    DeviceAllow = "";
+    # Implies ProtectSystem=strict, which re-mounts all paths
+    # DynamicUser = true;
+    LockPersonality = true;
+    MemoryDenyWriteExecute = true;
+    NoNewPrivileges = true;
+    PrivateDevices = true;
+    PrivateMounts = true;
+    PrivateNetwork = true;
+    PrivateTmp = true;
+    PrivateUsers = true;
+    ProcSubset = "pid";
+    ProtectClock = true;
+    ProtectHome = true;
+    ProtectHostname = true;
+    # Would re-mount paths ignored by temporary root
+    #ProtectSystem = "strict";
+    ProtectControlGroups = true;
+    ProtectKernelLogs = true;
+    ProtectKernelModules = true;
+    ProtectKernelTunables = true;
+    ProtectProc = "invisible";
+    RestrictAddressFamilies = [ ];
+    RestrictNamespaces = true;
+    RestrictRealtime = true;
+    RestrictSUIDSGID = true;
+    SystemCallArchitectures = "native";
+    SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ];
+    # Does not work well with the temporary root
+    #UMask = "0066";
+  };
+
+in
+{
+  options.services.kanidm = {
+    enableClient = lib.mkEnableOption "the Kanidm client";
+    enableServer = lib.mkEnableOption "the Kanidm server";
+    enablePam = lib.mkEnableOption "the Kanidm PAM and NSS integration.";
+
+    serverSettings = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+
+        options = {
+          bindaddress = lib.mkOption {
+            description = "Address/port combination the webserver binds to.";
+            example = "[::1]:8443";
+            type = lib.types.str;
+          };
+          # Should be optional but toml does not accept null
+          ldapbindaddress = lib.mkOption {
+            description = ''
+              Address and port the LDAP server is bound to. Setting this to <literal>null</literal> disables the LDAP interface.
+            '';
+            example = "[::1]:636";
+            default = null;
+            type = lib.types.nullOr lib.types.str;
+          };
+          origin = lib.mkOption {
+            description = "The origin of your Kanidm instance. Must have https as protocol.";
+            example = "https://idm.example.org";
+            type = lib.types.strMatching "^https://.*";
+          };
+          domain = lib.mkOption {
+            description = ''
+              The <literal>domain</literal> that Kanidm manages. Must be below or equal to the domain
+              specified in <literal>serverSettings.origin</literal>.
+              This can be left at <literal>null</literal>, only if your instance has the role <literal>ReadOnlyReplica</literal>.
+              While it is possible to change the domain later on, it requires extra steps!
+              Please consider the warnings and execute the steps described
+              <link xlink:href="https://kanidm.github.io/kanidm/stable/administrivia.html#rename-the-domain">in the documentation</link>.
+            '';
+            example = "example.org";
+            default = null;
+            type = lib.types.nullOr lib.types.str;
+          };
+          db_path = lib.mkOption {
+            description = "Path to Kanidm database.";
+            default = "/var/lib/kanidm/kanidm.db";
+            readOnly = true;
+            type = lib.types.path;
+          };
+          log_level = lib.mkOption {
+            description = "Log level of the server.";
+            default = "default";
+            type = lib.types.enum [ "default" "verbose" "perfbasic" "perffull" ];
+          };
+          role = lib.mkOption {
+            description = "The role of this server. This affects the replication relationship and thereby available features.";
+            default = "WriteReplica";
+            type = lib.types.enum [ "WriteReplica" "WriteReplicaNoUI" "ReadOnlyReplica" ];
+          };
+        };
+      };
+      default = { };
+      description = ''
+        Settings for Kanidm, see
+        <link xlink:href="https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/server_configuration.md">the documentation</link>
+        and <link xlink:href="https://github.com/kanidm/kanidm/blob/master/examples/server.toml">example configuration</link>
+        for possible values.
+      '';
+    };
+
+    clientSettings = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+
+        options.uri = lib.mkOption {
+          description = "Address of the Kanidm server.";
+          example = "http://127.0.0.1:8080";
+          type = lib.types.str;
+        };
+      };
+      description = ''
+        Configure Kanidm clients, needed for the PAM daemon. See
+        <link xlink:href="https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/client_tools.md#kanidm-configuration">the documentation</link>
+        and <link xlink:href="https://github.com/kanidm/kanidm/blob/master/examples/config">example configuration</link>
+        for possible values.
+      '';
+    };
+
+    unixSettings = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+
+        options.pam_allowed_login_groups = lib.mkOption {
+          description = "Kanidm groups that are allowed to login using PAM.";
+          example = "my_pam_group";
+          type = lib.types.listOf lib.types.str;
+        };
+      };
+      description = ''
+        Configure Kanidm unix daemon.
+        See <link xlink:href="https://github.com/kanidm/kanidm/blob/master/kanidm_book/src/pam_and_nsswitch.md#the-unix-daemon">the documentation</link>
+        and <link xlink:href="https://github.com/kanidm/kanidm/blob/master/examples/unixd">example configuration</link>
+        for possible values.
+      '';
+    };
+  };
+
+  config = lib.mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) {
+    assertions =
+      [
+        {
+          assertion = !cfg.enableServer || ((cfg.serverSettings.tls_chain or null) == null) || (!lib.isStorePath cfg.serverSettings.tls_chain);
+          message = ''
+            <option>services.kanidm.serverSettings.tls_chain</option> points to
+            a file in the Nix store. You should use a quoted absolute path to
+            prevent this.
+          '';
+        }
+        {
+          assertion = !cfg.enableServer || ((cfg.serverSettings.tls_key or null) == null) || (!lib.isStorePath cfg.serverSettings.tls_key);
+          message = ''
+            <option>services.kanidm.serverSettings.tls_key</option> points to
+            a file in the Nix store. You should use a quoted absolute path to
+            prevent this.
+          '';
+        }
+        {
+          assertion = !cfg.enableClient || options.services.kanidm.clientSettings.isDefined;
+          message = ''
+            <option>services.kanidm.clientSettings</option> needs to be configured
+            if the client is enabled.
+          '';
+        }
+        {
+          assertion = !cfg.enablePam || options.services.kanidm.clientSettings.isDefined;
+          message = ''
+            <option>services.kanidm.clientSettings</option> needs to be configured
+            for the PAM daemon to connect to the Kanidm server.
+          '';
+        }
+        {
+          assertion = !cfg.enableServer || (cfg.serverSettings.domain == null
+            -> cfg.serverSettings.role == "WriteReplica" || cfg.serverSettings.role == "WriteReplicaNoUI");
+          message = ''
+            <option>services.kanidm.serverSettings.domain</option> can only be set if this instance
+            is not a ReadOnlyReplica. Otherwise the db would inherit it from
+            the instance it follows.
+          '';
+        }
+      ];
+
+    environment.systemPackages = lib.mkIf cfg.enableClient [ pkgs.kanidm ];
+
+    systemd.services.kanidm = lib.mkIf cfg.enableServer {
+      description = "kanidm identity management daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      serviceConfig = defaultServiceConfig // {
+        StateDirectory = "kanidm";
+        StateDirectoryMode = "0700";
+        ExecStart = "${pkgs.kanidm}/bin/kanidmd server -c ${serverConfigFile}";
+        User = "kanidm";
+        Group = "kanidm";
+
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+        # This would otherwise override the CAP_NET_BIND_SERVICE capability.
+        PrivateUsers = false;
+        # Port needs to be exposed to the host network
+        PrivateNetwork = false;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        TemporaryFileSystem = "/:ro";
+      };
+      environment.RUST_LOG = "info";
+    };
+
+    systemd.services.kanidm-unixd = lib.mkIf cfg.enablePam {
+      description = "Kanidm PAM daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      restartTriggers = [ unixConfigFile clientConfigFile ];
+      serviceConfig = defaultServiceConfig // {
+        CacheDirectory = "kanidm-unixd";
+        CacheDirectoryMode = "0700";
+        RuntimeDirectory = "kanidm-unixd";
+        ExecStart = "${pkgs.kanidm}/bin/kanidm_unixd";
+        User = "kanidm-unixd";
+        Group = "kanidm-unixd";
+
+        BindReadOnlyPaths = [
+          "/nix/store"
+          "-/etc/resolv.conf"
+          "-/etc/nsswitch.conf"
+          "-/etc/hosts"
+          "-/etc/localtime"
+          "-/etc/kanidm"
+          "-/etc/static/kanidm"
+        ];
+        BindPaths = [
+          # To create the socket
+          "/run/kanidm-unixd:/var/run/kanidm-unixd"
+        ];
+        # Needs to connect to kanidmd
+        PrivateNetwork = false;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+        TemporaryFileSystem = "/:ro";
+      };
+      environment.RUST_LOG = "info";
+    };
+
+    systemd.services.kanidm-unixd-tasks = lib.mkIf cfg.enablePam {
+      description = "Kanidm PAM home management daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "kanidm-unixd.service" ];
+      partOf = [ "kanidm-unixd.service" ];
+      restartTriggers = [ unixConfigFile clientConfigFile ];
+      serviceConfig = {
+        ExecStart = "${pkgs.kanidm}/bin/kanidm_unixd_tasks";
+
+        BindReadOnlyPaths = [
+          "/nix/store"
+          "-/etc/resolv.conf"
+          "-/etc/nsswitch.conf"
+          "-/etc/hosts"
+          "-/etc/localtime"
+          "-/etc/kanidm"
+          "-/etc/static/kanidm"
+        ];
+        BindPaths = [
+          # To manage home directories
+          "/home"
+          # To connect to kanidm-unixd
+          "/run/kanidm-unixd:/var/run/kanidm-unixd"
+        ];
+        # CAP_DAC_OVERRIDE is needed to ignore ownership of unixd socket
+        CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_DAC_READ_SEARCH" ];
+        IPAddressDeny = "any";
+        # Need access to users
+        PrivateUsers = false;
+        # Need access to home directories
+        ProtectHome = false;
+        RestrictAddressFamilies = [ "AF_UNIX" ];
+        TemporaryFileSystem = "/:ro";
+      };
+      environment.RUST_LOG = "info";
+    };
+
+    # These paths are hardcoded
+    environment.etc = lib.mkMerge [
+      (lib.mkIf options.services.kanidm.clientSettings.isDefined {
+        "kanidm/config".source = clientConfigFile;
+      })
+      (lib.mkIf cfg.enablePam {
+        "kanidm/unixd".source = unixConfigFile;
+      })
+    ];
+
+    system.nssModules = lib.mkIf cfg.enablePam [ pkgs.kanidm ];
+
+    system.nssDatabases.group = lib.optional cfg.enablePam "kanidm";
+    system.nssDatabases.passwd = lib.optional cfg.enablePam "kanidm";
+
+    users.groups = lib.mkMerge [
+      (lib.mkIf cfg.enableServer {
+        kanidm = { };
+      })
+      (lib.mkIf cfg.enablePam {
+        kanidm-unixd = { };
+      })
+    ];
+    users.users = lib.mkMerge [
+      (lib.mkIf cfg.enableServer {
+        kanidm = {
+          description = "Kanidm server";
+          isSystemUser = true;
+          group = "kanidm";
+          packages = with pkgs; [ kanidm ];
+        };
+      })
+      (lib.mkIf cfg.enablePam {
+        kanidm-unixd = {
+          description = "Kanidm PAM daemon";
+          isSystemUser = true;
+          group = "kanidm-unixd";
+        };
+      })
+    ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ erictapen Flakebi ];
+  meta.buildDocsInSandbox = false;
+}
diff --git a/nixos/modules/services/security/sshguard.nix b/nixos/modules/services/security/sshguard.nix
index 53bd9efa5ac..3be0a8c700b 100644
--- a/nixos/modules/services/security/sshguard.nix
+++ b/nixos/modules/services/security/sshguard.nix
@@ -17,7 +17,7 @@ let
       else "sshg-fw-ipset";
   in pkgs.writeText "sshguard.conf" ''
     BACKEND="${pkgs.sshguard}/libexec/${backend}"
-    LOGREADER="LANG=C ${pkgs.systemd}/bin/journalctl ${args}"
+    LOGREADER="LANG=C ${config.systemd.package}/bin/journalctl ${args}"
   '';
 
 in {
diff --git a/nixos/modules/services/wayland/cage.nix b/nixos/modules/services/wayland/cage.nix
index a32b81a916f..b818f5c463a 100644
--- a/nixos/modules/services/wayland/cage.nix
+++ b/nixos/modules/services/wayland/cage.nix
@@ -88,7 +88,7 @@ in {
       account required pam_unix.so
       session required pam_unix.so
       session required pam_env.so conffile=/etc/pam/environment readenv=0
-      session required ${pkgs.systemd}/lib/security/pam_systemd.so
+      session required ${config.systemd.package}/lib/security/pam_systemd.so
     '';
 
     hardware.opengl.enable = mkDefault true;
diff --git a/nixos/modules/services/web-apps/restya-board.nix b/nixos/modules/services/web-apps/restya-board.nix
index 4b36cc8754c..0bfa2368787 100644
--- a/nixos/modules/services/web-apps/restya-board.nix
+++ b/nixos/modules/services/web-apps/restya-board.nix
@@ -294,7 +294,7 @@ in
         ln -sf "${cfg.dataDir}/client/img" "${runDir}/client/img"
 
         chmod g+w "${runDir}/tmp/cache"
-        chown -R "${cfg.user}"."${cfg.group}" "${runDir}"
+        chown -R "${cfg.user}":"${cfg.group}" "${runDir}"
 
 
         mkdir -m 0750 -p "${cfg.dataDir}"
@@ -302,9 +302,9 @@ in
         mkdir -m 0750 -p "${cfg.dataDir}/client/img"
         cp -r "${pkgs.restya-board}/media/"* "${cfg.dataDir}/media"
         cp -r "${pkgs.restya-board}/client/img/"* "${cfg.dataDir}/client/img"
-        chown "${cfg.user}"."${cfg.group}" "${cfg.dataDir}"
-        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/media"
-        chown -R "${cfg.user}"."${cfg.group}" "${cfg.dataDir}/client/img"
+        chown "${cfg.user}":"${cfg.group}" "${cfg.dataDir}"
+        chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/media"
+        chown -R "${cfg.user}":"${cfg.group}" "${cfg.dataDir}/client/img"
 
         ${optionalString (cfg.database.host == null) ''
           if ! [ -e "${cfg.dataDir}/.db-initialized" ]; then
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 8247a7e381c..f48eac21bd8 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -18,7 +18,7 @@ in
   # determines the default: later modules (if enabled) are preferred.
   # E.g., if Plasma 5 is enabled, it supersedes xterm.
   imports = [
-    ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix
+    ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ./lumina.nix
     ./lxqt.nix ./enlightenment.nix ./gnome.nix ./retroarch.nix ./kodi.nix
     ./mate.nix ./pantheon.nix ./surf-display.nix ./cde.nix
     ./cinnamon.nix
diff --git a/nixos/modules/programs/phosh.nix b/nixos/modules/services/x11/desktop-managers/phosh.nix
index ad875616ac9..4bf78fa16e7 100644
--- a/nixos/modules/programs/phosh.nix
+++ b/nixos/modules/services/x11/desktop-managers/phosh.nix
@@ -3,7 +3,7 @@
 with lib;
 
 let
-  cfg = config.programs.phosh;
+  cfg = config.services.xserver.desktopManager.phosh;
 
   # Based on https://source.puri.sm/Librem5/librem5-base/-/blob/4596c1056dd75ac7f043aede07887990fd46f572/default/sm.puri.OSK0.desktop
   oskItem = pkgs.makeDesktopItem {
@@ -118,12 +118,39 @@ let
     [cursor]
     theme = ${phoc.cursorTheme}
   '';
-in {
+in
+
+{
   options = {
-    programs.phosh = {
-      enable = mkEnableOption ''
-        Whether to enable, Phosh, related packages and default configurations.
-      '';
+    services.xserver.desktopManager.phosh = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the Phone Shell.";
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.phosh;
+        defaultText = literalExpression "pkgs.phosh";
+        example = literalExpression "pkgs.phosh";
+        description = ''
+          Package that should be used for Phosh.
+        '';
+      };
+
+      user = mkOption {
+        description = "The user to run the Phosh service.";
+        type = types.str;
+        example = "alice";
+      };
+
+      group = mkOption {
+        description = "The group to run the Phosh service.";
+        type = types.str;
+        example = "users";
+      };
+
       phocConfig = mkOption {
         description = ''
           Configurations for the Phoc compositor.
@@ -135,14 +162,42 @@ in {
   };
 
   config = mkIf cfg.enable {
+    systemd.defaultUnit = "graphical.target";
+    # Inspired by https://gitlab.gnome.org/World/Phosh/phosh/-/blob/main/data/phosh.service
+    systemd.services.phosh = {
+      wantedBy = [ "graphical.target" ];
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/phosh";
+        User = cfg.user;
+        Group = cfg.group;
+        PAMName = "login";
+        WorkingDirectory = "~";
+        Restart = "always";
+
+        TTYPath = "/dev/tty7";
+        TTYReset = "yes";
+        TTYVHangup = "yes";
+        TTYVTDisallocate = "yes";
+
+        # Fail to start if not controlling the tty.
+        StandardInput = "tty-fail";
+        StandardOutput = "journal";
+        StandardError = "journal";
+
+        # Log this user with utmp, letting it show up with commands 'w' and 'who'.
+        UtmpIdentifier = "tty7";
+        UtmpMode = "user";
+      };
+    };
+
     environment.systemPackages = [
       pkgs.phoc
-      pkgs.phosh
+      cfg.package
       pkgs.squeekboard
       oskItem
     ];
 
-    systemd.packages = [ pkgs.phosh ];
+    systemd.packages = [ cfg.package ];
 
     programs.feedbackd.enable = true;
 
@@ -152,7 +207,7 @@ in {
 
     services.gnome.core-shell.enable = true;
     services.gnome.core-os-services.enable = true;
-    services.xserver.displayManager.sessionPackages = [ pkgs.phosh ];
+    services.xserver.displayManager.sessionPackages = [ cfg.package ];
 
     environment.etc."phosh/phoc.ini".source =
       if builtins.isPath cfg.phocConfig then cfg.phocConfig
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 70ae6b8978d..4941d13c810 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -298,7 +298,7 @@ in
 
         session  required       pam_succeed_if.so audit quiet_success user = gdm
         session  required       pam_env.so conffile=/etc/pam/environment readenv=0
-        session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
+        session  optional       ${config.systemd.package}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
       '';
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 27dfed3cc14..302c8fe0d91 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -287,7 +287,7 @@ in
 
         session  required       pam_succeed_if.so audit quiet_success user = lightdm
         session  required       pam_env.so conffile=/etc/pam/environment readenv=0
-        session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
+        session  optional       ${config.systemd.package}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
     '';
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 529a086381f..c44f24002e0 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -231,7 +231,7 @@ in
 
         session  required       pam_succeed_if.so audit quiet_success user = sddm
         session  required       pam_env.so conffile=/etc/pam/environment readenv=0
-        session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
+        session  optional       ${config.systemd.package}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
       '';
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
index e683d181729..21be18ef866 100644
--- a/nixos/modules/system/boot/modprobe.nix
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -52,7 +52,7 @@ with lib;
       '';
     environment.etc."modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
 
-    environment.etc."modprobe.d/systemd.conf".source = "${pkgs.systemd}/lib/modprobe.d/systemd.conf";
+    environment.etc."modprobe.d/systemd.conf".source = "${config.systemd.package}/lib/modprobe.d/systemd.conf";
 
     environment.systemPackages = [ pkgs.kmod ];
 
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index d1a6f46bfc4..0336930b3ab 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -779,6 +779,7 @@ let
           "RouteDenyList"
           "RouteAllowList"
           "DHCPv6Client"
+          "RouteMetric"
         ])
         (assertValueOneOf "UseDNS" boolValues)
         (assertValueOneOf "UseDomains" (boolValues ++ ["route"]))
diff --git a/nixos/modules/system/boot/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
index 78ae8e9d20b..8b57cae3c40 100644
--- a/nixos/modules/system/boot/plymouth.nix
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -4,7 +4,10 @@ with lib;
 
 let
 
-  inherit (pkgs) plymouth nixos-icons;
+  inherit (pkgs) nixos-icons;
+  plymouth = pkgs.plymouth.override {
+    systemd = config.boot.initrd.systemd.package;
+  };
 
   cfg = config.boot.plymouth;
   opt = options.boot.plymouth;
@@ -143,7 +146,88 @@ in
     systemd.services.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ];
     systemd.paths.systemd-ask-password-plymouth.wantedBy = [ "multi-user.target" ];
 
-    boot.initrd.extraUtilsCommands = ''
+    boot.initrd.systemd = {
+      extraBin.plymouth = "${plymouth}/bin/plymouth"; # for the recovery shell
+      storePaths = [
+        "${lib.getBin config.boot.initrd.systemd.package}/bin/systemd-tty-ask-password-agent"
+        "${plymouth}/bin/plymouthd"
+        "${plymouth}/sbin/plymouthd"
+      ];
+      packages = [ plymouth ]; # systemd units
+      contents = {
+        # Files
+        "/etc/plymouth/plymouthd.conf".source = configFile;
+        "/etc/plymouth/plymouthd.defaults".source = "${plymouth}/share/plymouth/plymouthd.defaults";
+        "/etc/plymouth/logo.png".source = cfg.logo;
+        # Directories
+        "/etc/plymouth/plugins".source = pkgs.runCommand "plymouth-initrd-plugins" {} ''
+          # Check if the actual requested theme is here
+          if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
+              echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
+              exit 1
+          fi
+
+          moduleName="$(sed -n 's,ModuleName *= *,,p' ${themesEnv}/share/plymouth/themes/${cfg.theme}/${cfg.theme}.plymouth)"
+
+          mkdir -p $out/renderers
+          # module might come from a theme
+          cp ${themesEnv}/lib/plymouth/{text,details,label,$moduleName}.so $out
+          cp ${plymouth}/lib/plymouth/renderers/{drm,frame-buffer}.so $out/renderers
+        '';
+        "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} ''
+          # Check if the actual requested theme is here
+          if [[ ! -d ${themesEnv}/share/plymouth/themes/${cfg.theme} ]]; then
+              echo "The requested theme: ${cfg.theme} is not provided by any of the packages in boot.plymouth.themePackages"
+              exit 1
+          fi
+
+          mkdir $out
+          cp -r ${themesEnv}/share/plymouth/themes/${cfg.theme} $out
+          # Copy more themes if the theme depends on others
+          for theme in $(grep -hRo '/etc/plymouth/themes/.*$' ${themesEnv} | xargs -n1 basename); do
+              if [[ -d "${themesEnv}/theme" ]]; then
+                  cp -r "${themesEnv}/theme" $out
+              fi
+          done
+        '';
+
+        # Fonts
+        "/etc/plymouth/fonts".source = pkgs.runCommand "plymouth-initrd-fonts" {} ''
+          mkdir -p $out
+          cp ${cfg.font} $out
+        '';
+        "/etc/fonts/fonts.conf".text = ''
+          <?xml version="1.0"?>
+          <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
+          <fontconfig>
+              <dir>/etc/plymouth/fonts</dir>
+          </fontconfig>
+        '';
+      };
+      # Properly enable units. These are the units that arch copies
+      services = {
+        plymouth-halt.wantedBy = [ "halt.target" ];
+        plymouth-kexec.wantedBy = [ "kexec.target" ];
+        plymouth-poweroff.wantedBy = [ "poweroff.target" ];
+        plymouth-quit-wait.wantedBy = [ "multi-user.target" ];
+        plymouth-quit.wantedBy = [ "multi-user.target" ];
+        plymouth-read-write.wantedBy = [ "sysinit.target" ];
+        plymouth-reboot.wantedBy = [ "reboot.target" ];
+        plymouth-start.wantedBy = [ "initrd-switch-root.target" "sysinit.target" ];
+        plymouth-switch-root-initramfs.wantedBy = [ "halt.target" "kexec.target" "plymouth-switch-root-initramfs.service" "poweroff.target" "reboot.target" ];
+        plymouth-switch-root.wantedBy = [ "initrd-switch-root.target" ];
+      };
+    };
+
+    # Insert required udev rules. We take stage 2 systemd because the udev
+    # rules are only generated when building with logind.
+    boot.initrd.services.udev.packages = [ (pkgs.runCommand "initrd-plymouth-udev-rules" {} ''
+      mkdir -p $out/etc/udev/rules.d
+      cp ${config.systemd.package.out}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out/etc/udev/rules.d
+      sed -i '/loginctl/d' $out/etc/udev/rules.d/71-seat.rules
+    '') ];
+
+    boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
       copy_bin_and_libs ${plymouth}/bin/plymouth
       copy_bin_and_libs ${plymouth}/bin/plymouthd
 
@@ -198,18 +282,18 @@ in
       EOF
     '';
 
-    boot.initrd.extraUtilsCommandsTest = ''
+    boot.initrd.extraUtilsCommandsTest = mkIf (!config.boot.initrd.enable) ''
       $out/bin/plymouthd --help >/dev/null
       $out/bin/plymouth --help >/dev/null
     '';
 
-    boot.initrd.extraUdevRulesCommands = ''
+    boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.enable) ''
       cp ${config.systemd.package}/lib/udev/rules.d/{70-uaccess,71-seat}.rules $out
       sed -i '/loginctl/d' $out/71-seat.rules
     '';
 
     # We use `mkAfter` to ensure that LUKS password prompt would be shown earlier than the splash screen.
-    boot.initrd.preLVMCommands = mkAfter ''
+    boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.enable) (mkAfter ''
       mkdir -p /etc/plymouth
       mkdir -p /run/plymouth
       ln -s ${configFile} /etc/plymouth/plymouthd.conf
@@ -221,16 +305,16 @@ in
 
       plymouthd --mode=boot --pid-file=/run/plymouth/pid --attach-to-session
       plymouth show-splash
-    '';
+    '');
 
-    boot.initrd.postMountCommands = ''
+    boot.initrd.postMountCommands = mkIf (!config.boot.initrd.enable) ''
       plymouth update-root-fs --new-root-dir="$targetRoot"
     '';
 
     # `mkBefore` to ensure that any custom prompts would be visible.
-    boot.initrd.preFailCommands = mkBefore ''
+    boot.initrd.preFailCommands = mkIf (!config.boot.initrd.enable) (mkBefore ''
       plymouth quit --wait
-    '';
+    '');
 
   };
 
diff --git a/nixos/modules/system/boot/systemd/nspawn.nix b/nixos/modules/system/boot/systemd/nspawn.nix
index bf9995d03cc..da03c60db52 100644
--- a/nixos/modules/system/boot/systemd/nspawn.nix
+++ b/nixos/modules/system/boot/systemd/nspawn.nix
@@ -16,7 +16,7 @@ let
       "LimitNOFILE" "LimitAS" "LimitNPROC" "LimitMEMLOCK" "LimitLOCKS"
       "LimitSIGPENDING" "LimitMSGQUEUE" "LimitNICE" "LimitRTPRIO" "LimitRTTIME"
       "OOMScoreAdjust" "CPUAffinity" "Hostname" "ResolvConf" "Timezone"
-      "LinkJournal"
+      "LinkJournal" "Ephemeral" "AmbientCapability"
     ])
     (assertValueOneOf "Boot" boolValues)
     (assertValueOneOf "ProcessTwo" boolValues)
@@ -26,11 +26,13 @@ let
   checkFiles = checkUnitConfig "Files" [
     (assertOnlyFields [
       "ReadOnly" "Volatile" "Bind" "BindReadOnly" "TemporaryFileSystem"
-      "Overlay" "OverlayReadOnly" "PrivateUsersChown"
+      "Overlay" "OverlayReadOnly" "PrivateUsersChown" "BindUser"
+      "Inaccessible" "PrivateUserOwnership"
     ])
     (assertValueOneOf "ReadOnly" boolValues)
     (assertValueOneOf "Volatile" (boolValues ++ [ "state" ]))
     (assertValueOneOf "PrivateUsersChown" boolValues)
+    (assertValueOneOf "PrivateUserOwnership" [ "off" "chown" "map" "auto" ])
   ];
 
   checkNetwork = checkUnitConfig "Network" [
diff --git a/nixos/modules/tasks/auto-upgrade.nix b/nixos/modules/tasks/auto-upgrade.nix
index d00dc761d6e..21a25cbfa96 100644
--- a/nixos/modules/tasks/auto-upgrade.nix
+++ b/nixos/modules/tasks/auto-upgrade.nix
@@ -190,7 +190,7 @@ in {
         nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
         date     = "${pkgs.coreutils}/bin/date";
         readlink = "${pkgs.coreutils}/bin/readlink";
-        shutdown = "${pkgs.systemd}/bin/shutdown";
+        shutdown = "${config.systemd.package}/bin/shutdown";
         upgradeFlag = optional (cfg.channel == null) "--upgrade";
       in if cfg.allowReboot then ''
         ${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)}
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 5eca68798d5..c8bbfe9769b 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -58,6 +58,13 @@ let
   # latter case it makes one last attempt at importing, allowing the system to
   # (eventually) boot even with a degraded pool.
   importLib = {zpoolCmd, awkCmd, cfgZfs}: ''
+    for o in $(cat /proc/cmdline); do
+      case $o in
+        zfs_force|zfs_force=1|zfs_force=y)
+          ZFS_FORCE="-f"
+          ;;
+      esac
+    done
     poolReady() {
       pool="$1"
       state="$("${zpoolCmd}" import 2>/dev/null | "${awkCmd}" "/pool: $pool/ { found = 1 }; /state:/ { if (found == 1) { print \$2; exit } }; END { if (found == 0) { print \"MISSING\" } }")"
@@ -78,6 +85,95 @@ let
     }
   '';
 
+  getPoolFilesystems = pool:
+    filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
+
+  getPoolMounts = prefix: pool:
+    let
+      # Remove the "/" suffix because even though most mountpoints
+      # won't have it, the "/" mountpoint will, and we can't have the
+      # trailing slash in "/sysroot/" in stage 1.
+      mountPoint = fs: escapeSystemdPath (prefix + (lib.removeSuffix "/" fs.mountPoint));
+    in
+      map (x: "${mountPoint x}.mount") (getPoolFilesystems pool);
+
+  getKeyLocations = pool:
+    if isBool cfgZfs.requestEncryptionCredentials
+    then "${cfgZfs.package}/sbin/zfs list -rHo name,keylocation,keystatus ${pool}"
+    else "${cfgZfs.package}/sbin/zfs list -Ho name,keylocation,keystatus ${toString (filter (x: datasetToPool x == pool) cfgZfs.requestEncryptionCredentials)}";
+
+  createImportService = { pool, systemd, force, prefix ? "" }:
+    nameValuePair "zfs-import-${pool}" {
+      description = "Import ZFS pool \"${pool}\"";
+      # we need systemd-udev-settle to ensure devices are available
+      # In the future, hopefully someone will complete this:
+      # https://github.com/zfsonlinux/zfs/pull/4943
+      requires = [ "systemd-udev-settle.service" ];
+      after = [
+        "systemd-udev-settle.service"
+        "systemd-modules-load.service"
+        "systemd-ask-password-console.service"
+      ];
+      wantedBy = (getPoolMounts prefix pool) ++ [ "local-fs.target" ];
+      before = (getPoolMounts prefix pool) ++ [ "local-fs.target" ];
+      unitConfig = {
+        DefaultDependencies = "no";
+      };
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+      environment.ZFS_FORCE = optionalString force "-f";
+      script = (importLib {
+        # See comments at importLib definition.
+        zpoolCmd = "${cfgZfs.package}/sbin/zpool";
+        awkCmd = "${pkgs.gawk}/bin/awk";
+        inherit cfgZfs;
+      }) + ''
+        poolImported "${pool}" && exit
+        echo -n "importing ZFS pool \"${pool}\"..."
+        # Loop across the import until it succeeds, because the devices needed may not be discovered yet.
+        for trial in `seq 1 60`; do
+          poolReady "${pool}" && poolImport "${pool}" && break
+          sleep 1
+        done
+        poolImported "${pool}" || poolImport "${pool}"  # Try one last time, e.g. to import a degraded pool.
+        if poolImported "${pool}"; then
+          ${optionalString (if isBool cfgZfs.requestEncryptionCredentials
+                            then cfgZfs.requestEncryptionCredentials
+                            else cfgZfs.requestEncryptionCredentials != []) ''
+            ${getKeyLocations pool} | while IFS=$'\t' read ds kl ks; do
+              {
+              if [[ "$ks" != unavailable ]]; then
+                continue
+              fi
+              case "$kl" in
+                none )
+                  ;;
+                prompt )
+                  tries=3
+                  success=false
+                  while [[ $success != true ]] && [[ $tries -gt 0 ]]; do
+                    ${systemd}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds" \
+                      && success=true \
+                      || tries=$((tries - 1))
+                  done
+                  [[ $success = true ]]
+                  ;;
+                * )
+                  ${cfgZfs.package}/sbin/zfs load-key "$ds"
+                  ;;
+              esac
+              } < /dev/null # To protect while read ds kl in case anything reads stdin
+            done
+          ''}
+          echo "Successfully imported ${pool}"
+        else
+          exit 1
+        fi
+      '';
+    };
+
   zedConf = generators.toKeyValue {
     mkKeyValue = generators.mkKeyValueDefault {
       mkValueString = v:
@@ -428,14 +524,6 @@ in
           '';
         postDeviceCommands = concatStringsSep "\n" ([''
             ZFS_FORCE="${optionalString cfgZfs.forceImportRoot "-f"}"
-
-            for o in $(cat /proc/cmdline); do
-              case $o in
-                zfs_force|zfs_force=1)
-                  ZFS_FORCE="-f"
-                  ;;
-              esac
-            done
           ''] ++ [(importLib {
             # See comments at importLib definition.
             zpoolCmd = "zpool";
@@ -464,6 +552,21 @@ in
                 zfs load-key ${fs}
               '') cfgZfs.requestEncryptionCredentials}
         '') rootPools));
+
+        # Systemd in stage 1
+        systemd = {
+          packages = [cfgZfs.package];
+          services = listToAttrs (map (pool: createImportService {
+            inherit pool;
+            systemd = config.boot.initrd.systemd.package;
+            force = cfgZfs.forceImportRoot;
+            prefix = "/sysroot";
+          }) rootPools);
+          extraBin = {
+            # zpool and zfs are already in thanks to fsPackages
+            awk = "${pkgs.gawk}/bin/awk";
+          };
+        };
       };
 
       systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source = pkgs.writeShellScript "zpool-sync-shutdown" ''
@@ -521,79 +624,11 @@ in
       systemd.packages = [ cfgZfs.package ];
 
       systemd.services = let
-        getPoolFilesystems = pool:
-          filter (x: x.fsType == "zfs" && (fsToPool x) == pool) config.system.build.fileSystems;
-
-        getPoolMounts = pool:
-          let
-            mountPoint = fs: escapeSystemdPath fs.mountPoint;
-          in
-            map (x: "${mountPoint x}.mount") (getPoolFilesystems pool);
-
-        createImportService = pool:
-          nameValuePair "zfs-import-${pool}" {
-            description = "Import ZFS pool \"${pool}\"";
-            # we need systemd-udev-settle until https://github.com/zfsonlinux/zfs/pull/4943 is merged
-            requires = [ "systemd-udev-settle.service" ];
-            after = [
-              "systemd-udev-settle.service"
-              "systemd-modules-load.service"
-              "systemd-ask-password-console.service"
-            ];
-            wantedBy = (getPoolMounts pool) ++ [ "local-fs.target" ];
-            before = (getPoolMounts pool) ++ [ "local-fs.target" ];
-            unitConfig = {
-              DefaultDependencies = "no";
-            };
-            serviceConfig = {
-              Type = "oneshot";
-              RemainAfterExit = true;
-            };
-            environment.ZFS_FORCE = optionalString cfgZfs.forceImportAll "-f";
-            script = (importLib {
-              # See comments at importLib definition.
-              zpoolCmd = "${cfgZfs.package}/sbin/zpool";
-              awkCmd = "${pkgs.gawk}/bin/awk";
-              inherit cfgZfs;
-            }) + ''
-              poolImported "${pool}" && exit
-              echo -n "importing ZFS pool \"${pool}\"..."
-              # Loop across the import until it succeeds, because the devices needed may not be discovered yet.
-              for trial in `seq 1 60`; do
-                poolReady "${pool}" && poolImport "${pool}" && break
-                sleep 1
-              done
-              poolImported "${pool}" || poolImport "${pool}"  # Try one last time, e.g. to import a degraded pool.
-              if poolImported "${pool}"; then
-                ${optionalString (if isBool cfgZfs.requestEncryptionCredentials
-                                  then cfgZfs.requestEncryptionCredentials
-                                  else cfgZfs.requestEncryptionCredentials != []) ''
-                  ${cfgZfs.package}/sbin/zfs list -rHo name,keylocation ${pool} | while IFS=$'\t' read ds kl; do
-                    {
-                      ${optionalString (!isBool cfgZfs.requestEncryptionCredentials) ''
-                         if ! echo '${concatStringsSep "\n" cfgZfs.requestEncryptionCredentials}' | grep -qFx "$ds"; then
-                           continue
-                         fi
-                       ''}
-                    case "$kl" in
-                      none )
-                        ;;
-                      prompt )
-                        ${config.systemd.package}/bin/systemd-ask-password "Enter key for $ds:" | ${cfgZfs.package}/sbin/zfs load-key "$ds"
-                        ;;
-                      * )
-                        ${cfgZfs.package}/sbin/zfs load-key "$ds"
-                        ;;
-                    esac
-                    } < /dev/null # To protect while read ds kl in case anything reads stdin
-                  done
-                ''}
-                echo "Successfully imported ${pool}"
-              else
-                exit 1
-              fi
-            '';
-          };
+        createImportService' = pool: createImportService {
+          inherit pool;
+          systemd = config.systemd.package;
+          force = cfgZfs.forceImportAll;
+        };
 
         # This forces a sync of any ZFS pools prior to poweroff, even if they're set
         # to sync=disabled.
@@ -619,7 +654,7 @@ in
             wantedBy = [ "zfs.target" ];
           };
 
-      in listToAttrs (map createImportService dataPools ++
+      in listToAttrs (map createImportService' dataPools ++
                       map createSyncService allPools ++
                       map createZfsService [ "zfs-mount" "zfs-share" "zfs-zed" ]);
 
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 8654539b662..110e84494a3 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -43,12 +43,6 @@ in
     } {
       assertion = cfg.defaultGateway6 == null || cfg.defaultGateway6.interface == null;
       message = "networking.defaultGateway6.interface is not supported by networkd.";
-    } {
-      assertion = cfg.useDHCP == false;
-      message = ''
-        networking.useDHCP is not supported by networkd.
-        Please use per interface configuration and set the global option to false.
-      '';
     } ] ++ flip mapAttrsToList cfg.bridges (n: { rstp, ... }: {
       assertion = !rstp;
       message = "networking.bridges.${n}.rstp is not supported by networkd.";
@@ -80,6 +74,42 @@ in
       in mkMerge [ {
         enable = true;
       }
+      (mkIf cfg.useDHCP {
+        networks."99-ethernet-default-dhcp" = lib.mkIf cfg.useDHCP {
+          # We want to match physical ethernet interfaces as commonly
+          # found on laptops, desktops and servers, to provide an
+          # "out-of-the-box" setup that works for common cases.  This
+          # heuristic isn't perfect (it could match interfaces with
+          # custom names that _happen_ to start with en or eth), but
+          # should be good enough to make the common case easy and can
+          # be overridden on a case-by-case basis using
+          # higher-priority networks or by disabling useDHCP.
+
+          # Type=ether matches veth interfaces as well, and this is
+          # more likely to result in interfaces being configured to
+          # use DHCP when they shouldn't.
+
+          # We set RequiredForOnline to false, because it's fairly
+          # common for such devices to have multiple interfaces and
+          # only one of them to be connected (e.g. a laptop with
+          # ethernet and WiFi interfaces). Maybe one day networkd will
+          # support "any"-style RequiredForOnline...
+          matchConfig.Name = ["en*" "eth*"];
+          DHCP = "yes";
+          linkConfig.RequiredForOnline = lib.mkDefault false;
+        };
+        networks."99-wireless-client-dhcp" = lib.mkIf cfg.useDHCP {
+          # Like above, but this is much more likely to be correct.
+          matchConfig.WLANInterfaceType = "station";
+          DHCP = "yes";
+          linkConfig.RequiredForOnline = lib.mkDefault false;
+          # We also set the route metric to one more than the default
+          # of 1024, so that Ethernet is preferred if both are
+          # available.
+          dhcpV4Config.RouteMetric = 1025;
+          ipv6AcceptRAConfig.RouteMetric = 1025;
+        };
+      })
       (mkMerge (forEach interfaces (i: {
         netdevs = mkIf i.virtual ({
           "40-${i.name}" = {
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index d09e9b99248..d56159f1596 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1254,11 +1254,6 @@ in
         Whether to use DHCP to obtain an IP address and other
         configuration for all network interfaces that are not manually
         configured.
-
-        Using this option is highly discouraged and also incompatible with
-        <option>networking.useNetworkd</option>. Please use
-        <option>networking.interfaces.&lt;name&gt;.useDHCP</option> instead
-        and set this to false.
       '';
     };
 
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 81541477b9e..4ab2578eb81 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -129,6 +129,9 @@ in
     # Make sure we use the Guest Agent from the QEMU package for testing
     # to reduce the closure size required for the tests.
     services.qemuGuest.package = pkgs.qemu_test.ga;
+
+    # Squelch warning about unset system.stateVersion
+    system.stateVersion = lib.mkDefault lib.trivial.release;
   };
 
 }
diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix
index 4f2f8df90eb..9c2adb90bfd 100644
--- a/nixos/modules/virtualisation/amazon-init.nix
+++ b/nixos/modules/virtualisation/amazon-init.nix
@@ -11,7 +11,7 @@ let
     echo "attempting to fetch configuration from EC2 user data..."
 
     export HOME=/root
-    export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused pkgs.xz config.system.build.nixos-rebuild]}:$PATH
+    export PATH=${pkgs.lib.makeBinPath [ config.nix.package config.systemd.package pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused pkgs.xz config.system.build.nixos-rebuild]}:$PATH
     export NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels
 
     userData=/etc/ec2-metadata/user-data
diff --git a/nixos/modules/virtualisation/digital-ocean-init.nix b/nixos/modules/virtualisation/digital-ocean-init.nix
index 4339d91de16..df30104b7d7 100644
--- a/nixos/modules/virtualisation/digital-ocean-init.nix
+++ b/nixos/modules/virtualisation/digital-ocean-init.nix
@@ -46,7 +46,7 @@ in {
         RemainAfterExit = true;
       };
       restartIfChanged = false;
-      path = [ pkgs.jq pkgs.gnused pkgs.gnugrep pkgs.systemd config.nix.package config.system.build.nixos-rebuild ];
+      path = [ pkgs.jq pkgs.gnused pkgs.gnugrep config.systemd.package config.nix.package config.system.build.nixos-rebuild ];
       environment = {
         HOME = "/root";
         NIX_PATH = concatStringsSep ":" [
diff --git a/nixos/modules/virtualisation/proxmox-lxc.nix b/nixos/modules/virtualisation/proxmox-lxc.nix
index 3913b474afb..9b9f99e5b81 100644
--- a/nixos/modules/virtualisation/proxmox-lxc.nix
+++ b/nixos/modules/virtualisation/proxmox-lxc.nix
@@ -20,6 +20,15 @@ with lib;
         configuration from proxmox.
       '';
     };
+    manageHostName = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to manage hostname through nix options
+        When false, the hostname is picked up from /etc/hostname
+        populated by proxmox.
+      '';
+    };
   };
 
   config =
@@ -50,6 +59,8 @@ with lib;
         useDHCP = false;
         useHostResolvConf = false;
         useNetworkd = true;
+        # pick up hostname from /etc/hostname generated by proxmox
+        hostName = mkIf (!cfg.manageHostName) (mkForce "");
       };
 
       services.openssh = {
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index dda1c41f969..0c085b64efa 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -253,6 +253,7 @@ in
   k3s-single-node = handleTest ./k3s-single-node.nix {};
   k3s-single-node-docker = handleTest ./k3s-single-node-docker.nix {};
   kafka = handleTest ./kafka.nix {};
+  kanidm = handleTest ./kanidm.nix {};
   kbd-setfont-decompress = handleTest ./kbd-setfont-decompress.nix {};
   kbd-update-search-paths-patch = handleTest ./kbd-update-search-paths-patch.nix {};
   kea = handleTest ./kea.nix {};
diff --git a/nixos/tests/installed-tests/default.nix b/nixos/tests/installed-tests/default.nix
index fd16b481168..c6fb37cfe58 100644
--- a/nixos/tests/installed-tests/default.nix
+++ b/nixos/tests/installed-tests/default.nix
@@ -106,6 +106,5 @@ in
   malcontent = callInstalledTest ./malcontent.nix {};
   ostree = callInstalledTest ./ostree.nix {};
   pipewire = callInstalledTest ./pipewire.nix {};
-  power-profiles-daemon = callInstalledTest ./power-profiles-daemon.nix {};
   xdg-desktop-portal = callInstalledTest ./xdg-desktop-portal.nix {};
 }
diff --git a/nixos/tests/installed-tests/power-profiles-daemon.nix b/nixos/tests/installed-tests/power-profiles-daemon.nix
deleted file mode 100644
index 43629a0155d..00000000000
--- a/nixos/tests/installed-tests/power-profiles-daemon.nix
+++ /dev/null
@@ -1,9 +0,0 @@
-{ pkgs, lib, makeInstalledTest, ... }:
-
-makeInstalledTest {
-  tested = pkgs.power-profiles-daemon;
-
-  testConfig = {
-    services.power-profiles-daemon.enable = true;
-  };
-}
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index a8b418626e6..d02387ee80e 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -27,7 +27,7 @@
     simpleUefiGrubSpecialisation
     simpleUefiSystemdBoot
     # swraid
-    # zfsroot
+    zfsroot
     ;
 
 }
diff --git a/nixos/tests/kanidm.nix b/nixos/tests/kanidm.nix
new file mode 100644
index 00000000000..d34f680f522
--- /dev/null
+++ b/nixos/tests/kanidm.nix
@@ -0,0 +1,75 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+  let
+    certs = import ./common/acme/server/snakeoil-certs.nix;
+    serverDomain = certs.domain;
+  in
+  {
+    name = "kanidm";
+    meta.maintainers = with pkgs.lib.maintainers; [ erictapen Flakebi ];
+
+    nodes.server = { config, pkgs, lib, ... }: {
+      services.kanidm = {
+        enableServer = true;
+        serverSettings = {
+          origin = "https://${serverDomain}";
+          domain = serverDomain;
+          bindaddress = "[::1]:8443";
+          ldapbindaddress = "[::1]:636";
+        };
+      };
+
+      services.nginx = {
+        enable = true;
+        recommendedProxySettings = true;
+        virtualHosts."${serverDomain}" = {
+          forceSSL = true;
+          sslCertificate = certs."${serverDomain}".cert;
+          sslCertificateKey = certs."${serverDomain}".key;
+          locations."/".proxyPass = "http://[::1]:8443";
+        };
+      };
+
+      security.pki.certificateFiles = [ certs.ca.cert ];
+
+      networking.hosts."::1" = [ serverDomain ];
+      networking.firewall.allowedTCPPorts = [ 80 443 ];
+
+      users.users.kanidm.shell = pkgs.bashInteractive;
+
+      environment.systemPackages = with pkgs; [ kanidm openldap ripgrep ];
+    };
+
+    nodes.client = { pkgs, nodes, ... }: {
+      services.kanidm = {
+        enableClient = true;
+        clientSettings = {
+          uri = "https://${serverDomain}";
+        };
+      };
+
+      networking.hosts."${nodes.server.config.networking.primaryIPAddress}" = [ serverDomain ];
+
+      security.pki.certificateFiles = [ certs.ca.cert ];
+    };
+
+    testScript = { nodes, ... }:
+      let
+        ldapBaseDN = builtins.concatStringsSep "," (map (s: "dc=" + s) (pkgs.lib.splitString "." serverDomain));
+
+        # We need access to the config file in the test script.
+        filteredConfig = pkgs.lib.converge
+          (pkgs.lib.filterAttrsRecursive (_: v: v != null))
+          nodes.server.config.services.kanidm.serverSettings;
+        serverConfigFile = (pkgs.formats.toml { }).generate "server.toml" filteredConfig;
+
+      in
+      ''
+        start_all()
+        server.wait_for_unit("kanidm.service")
+        server.wait_until_succeeds("curl -sf https://${serverDomain} | grep Kanidm")
+        server.wait_until_succeeds("ldapsearch -H ldap://[::1]:636 -b '${ldapBaseDN}' -x '(name=test)'")
+        client.wait_until_succeeds("kanidm login -D anonymous && kanidm self whoami | grep anonymous@${serverDomain}")
+        (rv, result) = server.execute("kanidmd recover_account -d quiet -c ${serverConfigFile} -n admin 2>&1 | rg -o '[A-Za-z0-9]{48}'")
+        assert rv == 0
+      '';
+  })
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index f34d5d60794..1e60198abdd 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -30,6 +30,7 @@ let
       linux_5_4_hardened
       linux_5_10_hardened
       linux_5_15_hardened
+      linux_5_17_hardened
 
       linux_testing;
   };
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index a1150097a09..2cc1e9b0942 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -139,6 +139,26 @@ let
               client.wait_until_succeeds("ping -c 1 192.168.3.1")
         '';
     };
+    dhcpDefault = {
+      name = "useDHCP-by-default";
+      nodes.router = router;
+      nodes.client = { lib, ... }: {
+        # Disable test driver default config
+        networking.interfaces = lib.mkForce {};
+        networking.useNetworkd = networkd;
+        virtualisation.vlans = [ 1 ];
+      };
+      testScript = ''
+        start_all()
+        client.wait_for_unit("multi-user.target")
+        client.wait_until_succeeds("ip addr show dev eth1 | grep '192.168.1'")
+        client.shell_interact()
+        client.succeed("ping -c 1 192.168.1.1")
+        router.succeed("ping -c 1 192.168.1.1")
+        router.succeed("ping -c 1 192.168.1.2")
+        client.succeed("ping -c 1 192.168.1.2")
+      '';
+    };
     dhcpSimple = {
       name = "SimpleDHCP";
       nodes.router = router;
diff --git a/nixos/tests/pleroma.nix b/nixos/tests/pleroma.nix
index 90a9a251104..8998716243a 100644
--- a/nixos/tests/pleroma.nix
+++ b/nixos/tests/pleroma.nix
@@ -158,7 +158,9 @@ import ./make-test-python.nix ({ pkgs, ... }:
 
     # Waiting for pleroma to be up.
     timeout 5m bash -c 'while [[ "$(curl -s -o /dev/null -w '%{http_code}' https://pleroma.nixos.test/api/v1/instance)" != "200" ]]; do sleep 2; done'
-    pleroma_ctl user new jamy jamy@nixos.test --password 'jamy-password' --moderator --admin -y
+    # Toremove the RELEASE_COOKIE bit when https://github.com/NixOS/nixpkgs/issues/166229 gets fixed.
+    RELEASE_COOKIE="/var/lib/pleroma/.cookie" \
+      pleroma_ctl user new jamy jamy@nixos.test --password 'jamy-password' --moderator --admin -y
   '';
 
   tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
diff --git a/nixos/tests/systemd-nspawn.nix b/nixos/tests/systemd-nspawn.nix
index 5bf55060d2e..c2cb92d1130 100644
--- a/nixos/tests/systemd-nspawn.nix
+++ b/nixos/tests/systemd-nspawn.nix
@@ -25,8 +25,15 @@ let
   nspawnImages = (pkgs.runCommand "localhost" { buildInputs = [ pkgs.coreutils pkgs.gnupg ]; } ''
     mkdir -p $out
     cd $out
+
+    # produce a testimage.raw
     dd if=/dev/urandom of=$out/testimage.raw bs=$((1024*1024+7)) count=5
-    sha256sum testimage.raw > SHA256SUMS
+
+    # produce a testimage2.tar.xz, containing the hello store path
+    tar cvJpf testimage2.tar.xz ${pkgs.hello}
+
+    # produce signature(s)
+    sha256sum testimage* > SHA256SUMS
     export GNUPGHOME="$(mktemp -d)"
     cp -R ${gpgKeyring}/* $GNUPGHOME
     gpg --batch --sign --detach-sign --output SHA256SUMS.gpg SHA256SUMS
@@ -56,5 +63,9 @@ in {
     client.succeed(
         "cmp /var/lib/machines/testimage.raw ${nspawnImages}/testimage.raw"
     )
+    client.succeed("machinectl pull-tar --verify=signature http://server/testimage2.tar.xz")
+    client.succeed(
+        "cmp /var/lib/machines/testimage2/${pkgs.hello}/bin/hello ${pkgs.hello}/bin/hello"
+    )
   '';
 })
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index 27093aab96e..4eb402a7d36 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -222,7 +222,7 @@ let
               machine.execute(ru("VBoxManage controlvm ${name} poweroff"))
           machine.succeed("rm -rf ${sharePath}")
           machine.succeed("mkdir -p ${sharePath}")
-          machine.succeed("chown alice.users ${sharePath}")
+          machine.succeed("chown alice:users ${sharePath}")
 
 
       def create_vm_${name}():