about summary refs log tree commit diff
path: root/nixos/modules
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules')
-rw-r--r--nixos/modules/config/console.nix4
-rw-r--r--nixos/modules/config/debug-info.nix19
-rw-r--r--nixos/modules/config/xdg/icons.nix8
-rw-r--r--nixos/modules/hardware/device-tree.nix18
-rw-r--r--nixos/modules/hardware/gpgsmartcards.nix2
-rw-r--r--nixos/modules/hardware/video/nvidia.nix19
-rw-r--r--nixos/modules/i18n/input-method/fcitx5.nix1
-rw-r--r--nixos/modules/i18n/input-method/ibus.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix3
-rw-r--r--nixos/modules/installer/kexec/kexec-boot.nix51
-rw-r--r--nixos/modules/installer/netboot/netboot.nix31
-rw-r--r--nixos/modules/installer/sd-card/sd-image.nix15
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl9
-rw-r--r--nixos/modules/misc/documentation.nix31
-rw-r--r--nixos/modules/misc/man-db.nix8
-rw-r--r--nixos/modules/misc/nixpkgs.nix108
-rw-r--r--nixos/modules/misc/nixpkgs/test.nix61
-rw-r--r--nixos/modules/module-list.nix23
-rw-r--r--nixos/modules/programs/captive-browser.nix2
-rw-r--r--nixos/modules/programs/clickshare.nix21
-rw-r--r--nixos/modules/programs/haguichi.nix15
-rw-r--r--nixos/modules/programs/kdeconnect.nix4
-rw-r--r--nixos/modules/programs/openvpn3.nix33
-rw-r--r--nixos/modules/security/systemd-confinement.nix11
-rw-r--r--nixos/modules/services/audio/mpd.nix1
-rw-r--r--nixos/modules/services/audio/navidrome.nix2
-rw-r--r--nixos/modules/services/cluster/k3s/default.nix21
-rw-r--r--nixos/modules/services/databases/cassandra.nix127
-rw-r--r--nixos/modules/services/databases/dragonflydb.nix152
-rw-r--r--nixos/modules/services/databases/openldap.nix1
-rw-r--r--nixos/modules/services/desktops/pipewire/pipewire.nix7
-rw-r--r--nixos/modules/services/desktops/pipewire/wireplumber.nix15
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix19
-rw-r--r--nixos/modules/services/mail/postfixadmin.nix2
-rw-r--r--nixos/modules/services/mail/schleuder.nix162
-rw-r--r--nixos/modules/services/matrix/appservice-discord.nix (renamed from nixos/modules/services/misc/matrix-appservice-discord.nix)0
-rw-r--r--nixos/modules/services/matrix/appservice-irc.nix (renamed from nixos/modules/services/misc/matrix-appservice-irc.nix)0
-rw-r--r--nixos/modules/services/matrix/conduit.nix (renamed from nixos/modules/services/misc/matrix-conduit.nix)0
-rw-r--r--nixos/modules/services/matrix/dendrite.nix (renamed from nixos/modules/services/misc/dendrite.nix)0
-rw-r--r--nixos/modules/services/matrix/mautrix-facebook.nix (renamed from nixos/modules/services/misc/mautrix-facebook.nix)0
-rw-r--r--nixos/modules/services/matrix/mautrix-telegram.nix (renamed from nixos/modules/services/misc/mautrix-telegram.nix)0
-rw-r--r--nixos/modules/services/matrix/synapse-log_config.yaml (renamed from nixos/modules/services/matrix/matrix-synapse-log_config.yaml)0
-rw-r--r--nixos/modules/services/matrix/synapse.nix (renamed from nixos/modules/services/matrix/matrix-synapse.nix)22
-rw-r--r--nixos/modules/services/matrix/synapse.xml (renamed from nixos/modules/services/matrix/matrix-synapse.xml)0
-rw-r--r--nixos/modules/services/misc/gollum.nix9
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixos/modules/services/misc/persistent-evdev.nix60
-rw-r--r--nixos/modules/services/misc/sourcehut/builds.nix236
-rw-r--r--nixos/modules/services/misc/sourcehut/default.nix79
-rw-r--r--nixos/modules/services/misc/sourcehut/dispatch.nix127
-rw-r--r--nixos/modules/services/misc/sourcehut/git.nix217
-rw-r--r--nixos/modules/services/misc/sourcehut/hg.nix175
-rw-r--r--nixos/modules/services/misc/sourcehut/hub.nix120
-rw-r--r--nixos/modules/services/misc/sourcehut/lists.nix187
-rw-r--r--nixos/modules/services/misc/sourcehut/man.nix124
-rw-r--r--nixos/modules/services/misc/sourcehut/meta.nix213
-rw-r--r--nixos/modules/services/misc/sourcehut/paste.nix135
-rw-r--r--nixos/modules/services/misc/sourcehut/service.nix2
-rw-r--r--nixos/modules/services/misc/sourcehut/todo.nix163
-rw-r--r--nixos/modules/services/monitoring/grafana-agent.nix143
-rw-r--r--nixos/modules/services/monitoring/grafana.nix5
-rw-r--r--nixos/modules/services/monitoring/mimir.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postfix.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix6
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix2
-rw-r--r--nixos/modules/services/networking/bitlbee.nix1
-rw-r--r--nixos/modules/services/networking/expressvpn.nix29
-rw-r--r--nixos/modules/services/networking/mosquitto.nix40
-rw-r--r--nixos/modules/services/networking/routedns.nix84
-rw-r--r--nixos/modules/services/networking/syncthing.nix108
-rw-r--r--nixos/modules/services/networking/tailscale.nix13
-rw-r--r--nixos/modules/services/networking/trickster.nix20
-rw-r--r--nixos/modules/services/networking/wg-quick.nix29
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix2
-rw-r--r--nixos/modules/services/security/vaultwarden/default.nix72
-rw-r--r--nixos/modules/services/system/cachix-agent/default.nix1
-rw-r--r--nixos/modules/services/system/localtime.nix2
-rw-r--r--nixos/modules/services/web-apps/dokuwiki.nix4
-rw-r--r--nixos/modules/services/web-apps/grocy.nix6
-rw-r--r--nixos/modules/services/web-apps/hedgedoc.nix4
-rw-r--r--nixos/modules/services/web-apps/invoiceplane.nix3
-rw-r--r--nixos/modules/services/web-apps/moodle.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix4
-rw-r--r--nixos/modules/services/web-apps/snipe-it.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome.nix62
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix51
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix4
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix9
-rw-r--r--nixos/modules/services/x11/picom.nix15
-rw-r--r--nixos/modules/system/boot/systemd.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix2
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix25
-rw-r--r--nixos/modules/virtualisation/amazon-ec2-amis.nix50
-rw-r--r--nixos/modules/virtualisation/lxc-container.nix8
-rw-r--r--nixos/modules/virtualisation/podman/default.nix6
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix6
97 files changed, 1549 insertions, 2172 deletions
diff --git a/nixos/modules/config/console.nix b/nixos/modules/config/console.nix
index b60fc55851daf..97e6405db91e1 100644
--- a/nixos/modules/config/console.nix
+++ b/nixos/modules/config/console.nix
@@ -46,9 +46,9 @@ in
       type = with types; either str path;
       default = "Lat2-Terminus16";
       example = "LatArCyrHeb-16";
-      description = ''
+      description = mdDoc ''
         The font used for the virtual consoles.  Leave empty to use
-        whatever the <command>setfont</command> program considers the
+        whatever the {command}`setfont` program considers the
         default font.
         Can be either a font name or a path to a PSF font file.
       '';
diff --git a/nixos/modules/config/debug-info.nix b/nixos/modules/config/debug-info.nix
index 2942ae5905d10..78de26fda4403 100644
--- a/nixos/modules/config/debug-info.nix
+++ b/nixos/modules/config/debug-info.nix
@@ -9,21 +9,20 @@ with lib;
     environment.enableDebugInfo = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = mdDoc ''
         Some NixOS packages provide debug symbols. However, these are
         not included in the system closure by default to save disk
         space. Enabling this option causes the debug symbols to appear
-        in <filename>/run/current-system/sw/lib/debug/.build-id</filename>,
-        where tools such as <command>gdb</command> can find them.
+        in {file}`/run/current-system/sw/lib/debug/.build-id`,
+        where tools such as {command}`gdb` can find them.
         If you need debug symbols for a package that doesn't
         provide them by default, you can enable them as follows:
-        <programlisting>
-        nixpkgs.config.packageOverrides = pkgs: {
-          hello = pkgs.hello.overrideAttrs (oldAttrs: {
-            separateDebugInfo = true;
-          });
-        };
-        </programlisting>
+
+            nixpkgs.config.packageOverrides = pkgs: {
+              hello = pkgs.hello.overrideAttrs (oldAttrs: {
+                separateDebugInfo = true;
+              });
+            };
       '';
     };
 
diff --git a/nixos/modules/config/xdg/icons.nix b/nixos/modules/config/xdg/icons.nix
index c83fdc251ef00..1e91670cf03bf 100644
--- a/nixos/modules/config/xdg/icons.nix
+++ b/nixos/modules/config/xdg/icons.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 {
@@ -23,6 +23,12 @@ with lib;
       "/share/pixmaps"
     ];
 
+    environment.systemPackages = [
+      # Empty icon theme that contains index.theme file describing directories
+      # where toolkits should look for icons installed by apps.
+      pkgs.hicolor-icon-theme
+    ];
+
     # libXcursor looks for cursors in XCURSOR_PATH
     # it mostly follows the spec for icons
     # See: https://www.x.org/releases/current/doc/man/man3/Xcursor.3.xhtml Themes
diff --git a/nixos/modules/hardware/device-tree.nix b/nixos/modules/hardware/device-tree.nix
index be67116ad507d..5a8a8e27bee1b 100644
--- a/nixos/modules/hardware/device-tree.nix
+++ b/nixos/modules/hardware/device-tree.nix
@@ -36,14 +36,11 @@ let
           /plugin/;
           / {
                   compatible = "raspberrypi";
-                  fragment@0 {
-                          target-path = "/soc";
-                          __overlay__ {
-                                  pps {
-                                          compatible = "pps-gpio";
-                                          status = "okay";
-                                  };
-                          };
+          };
+          &{/soc} {
+                  pps {
+                          compatible = "pps-gpio";
+                          status = "okay";
                   };
           };
         '';
@@ -88,13 +85,14 @@ let
 
   # Compile single Device Tree overlay source
   # file (.dts) into its compiled variant (.dtbo)
-  compileDTS = name: f: pkgs.callPackage({ dtc }: pkgs.stdenv.mkDerivation {
+  compileDTS = name: f: pkgs.callPackage({ stdenv, dtc }: stdenv.mkDerivation {
     name = "${name}-dtbo";
 
     nativeBuildInputs = [ dtc ];
 
     buildCommand = ''
-      dtc -I dts ${f} -O dtb -@ -o $out
+      $CC -E -nostdinc -I${getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes -undef -D__DTS__ -x assembler-with-cpp ${f} | \
+        dtc -I dts -O dtb -@ -o $out
     '';
   }) {};
 
diff --git a/nixos/modules/hardware/gpgsmartcards.nix b/nixos/modules/hardware/gpgsmartcards.nix
index 6e5fcda6b8519..4c1d0cc5b2a3f 100644
--- a/nixos/modules/hardware/gpgsmartcards.nix
+++ b/nixos/modules/hardware/gpgsmartcards.nix
@@ -19,7 +19,7 @@ let
   # per debian's udev deb hook (https://man7.org/linux/man-pages/man1/dh_installudev.1.html)
   destination = "60-scdaemon.rules";
 
-  scdaemonUdevRulesPkg = pkgs.runCommandNoCC "scdaemon-udev-rules" {} ''
+  scdaemonUdevRulesPkg = pkgs.runCommand "scdaemon-udev-rules" {} ''
     loc="$out/lib/udev/rules.d/"
     mkdir -p "''${loc}"
     cp "${scdaemonRules}" "''${loc}/${destination}"
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index a9b04bcc85959..b4717719661aa 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -183,6 +183,14 @@ in
       '';
       example = literalExpression "config.boot.kernelPackages.nvidiaPackages.legacy_340";
     };
+
+    hardware.nvidia.open = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Whether to use the open source kernel module
+      '';
+    };
   };
 
   config = let
@@ -231,6 +239,11 @@ in
         );
         message = "Required files for driver based power management don't exist.";
       }
+
+      {
+        assertion = cfg.open -> (cfg.package ? open && cfg.package ? firmware);
+        message = "This version of NVIDIA driver does not provide a corresponding opensource kernel driver";
+      }
     ];
 
     # If Optimus/PRIME is enabled, we:
@@ -364,7 +377,8 @@ in
       ++ optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
         "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
 
-    boot.extraModulePackages = [ nvidia_x11.bin ];
+    boot.extraModulePackages = if cfg.open then [ nvidia_x11.open ] else [ nvidia_x11.bin ];
+    hardware.firmware = lib.optional cfg.open nvidia_x11.firmware;
 
     # nvidia-uvm is required by CUDA applications.
     boot.kernelModules = [ "nvidia-uvm" ] ++
@@ -372,7 +386,8 @@ in
 
     # If requested enable modesetting via kernel parameter.
     boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
-      ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1";
+      ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1"
+      ++ optional cfg.open "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1";
 
     services.udev.extraRules =
       ''
diff --git a/nixos/modules/i18n/input-method/fcitx5.nix b/nixos/modules/i18n/input-method/fcitx5.nix
index b4b887606e95e..9ef0285f7b933 100644
--- a/nixos/modules/i18n/input-method/fcitx5.nix
+++ b/nixos/modules/i18n/input-method/fcitx5.nix
@@ -32,6 +32,7 @@ in {
         GTK_IM_MODULE = "fcitx";
         QT_IM_MODULE = "fcitx";
         XMODIFIERS = "@im=fcitx";
+        QT_PLUGIN_PATH = [ "${fcitx5Package}/${pkgs.qt6.qtbase.qtPluginPrefix}" ];
       };
     }
     (mkIf whetherRimeDataDir {
diff --git a/nixos/modules/i18n/input-method/ibus.nix b/nixos/modules/i18n/input-method/ibus.nix
index c5b0cbc21502a..907f6451fce11 100644
--- a/nixos/modules/i18n/input-method/ibus.nix
+++ b/nixos/modules/i18n/input-method/ibus.nix
@@ -23,6 +23,8 @@ let
       Name=IBus
       Type=Application
       Exec=${ibusPackage}/bin/ibus-daemon --daemonize --xim ${impanel}
+      # GNOME will launch ibus using systemd
+      NotShowIn=GNOME;
     '';
   };
 in
@@ -67,7 +69,7 @@ in
     programs.dconf.packages = [ ibusPackage ];
 
     services.dbus.packages = [
-      ibusAutostart
+      ibusPackage
     ];
 
     environment.variables = {
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix
index 95aeca1a928a6..d015e10c11d81 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix
@@ -18,7 +18,8 @@
     extraGSettingsOverrides = ''
       [org.gnome.shell]
       welcome-dialog-last-shown-version='9999999999'
-
+      [org.gnome.desktop.session]
+      idle-delay=0
       [org.gnome.settings-daemon.plugins.power]
       sleep-inactive-ac-type='nothing'
       sleep-inactive-battery-type='nothing'
diff --git a/nixos/modules/installer/kexec/kexec-boot.nix b/nixos/modules/installer/kexec/kexec-boot.nix
deleted file mode 100644
index 2d062214efc21..0000000000000
--- a/nixos/modules/installer/kexec/kexec-boot.nix
+++ /dev/null
@@ -1,51 +0,0 @@
-# This module exposes a config.system.build.kexecBoot attribute,
-# which returns a directory with kernel, initrd and a shell script
-# running the necessary kexec commands.
-
-# It's meant to be scp'ed to a machine with working ssh and kexec binary
-# installed.
-
-# This is useful for (cloud) providers where you can't boot a custom image, but
-# get some Debian or Ubuntu installation.
-
-{ pkgs
-, modulesPath
-, config
-, ...
-}:
-{
-  imports = [
-    (modulesPath + "/installer/netboot/netboot-minimal.nix")
-  ];
-
-  config = {
-    system.build.kexecBoot =
-      let
-        kexecScript = pkgs.writeScript "kexec-boot" ''
-          #!/usr/bin/env bash
-          if ! kexec -v >/dev/null 2>&1; then
-            echo "kexec not found: please install kexec-tools" 2>&1
-            exit 1
-          fi
-          SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
-          kexec --load ''${SCRIPT_DIR}/bzImage \
-            --initrd=''${SCRIPT_DIR}/initrd.gz \
-            --command-line "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"
-          kexec -e
-        ''; in
-      pkgs.linkFarm "kexec-tree" [
-        {
-          name = "initrd.gz";
-          path = "${config.system.build.netbootRamdisk}/initrd";
-        }
-        {
-          name = "bzImage";
-          path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
-        }
-        {
-          name = "kexec-boot";
-          path = kexecScript;
-        }
-      ];
-  };
-}
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index a459e7304cd41..3127bdc436f92 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -101,6 +101,37 @@ with lib;
       boot
     '';
 
+    # A script invoking kexec on ./bzImage and ./initrd.gz.
+    # Usually used through system.build.kexecTree, but exposed here for composability.
+    system.build.kexecScript = pkgs.writeScript "kexec-boot" ''
+      #!/usr/bin/env bash
+      if ! kexec -v >/dev/null 2>&1; then
+        echo "kexec not found: please install kexec-tools" 2>&1
+        exit 1
+      fi
+      SCRIPT_DIR=$( cd -- "$( dirname -- "''${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+      kexec --load ''${SCRIPT_DIR}/bzImage \
+        --initrd=''${SCRIPT_DIR}/initrd.gz \
+        --command-line "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}"
+      kexec -e
+    '';
+
+    # A tree containing initrd.gz, bzImage and a kexec-boot script.
+    system.build.kexecTree = pkgs.linkFarm "kexec-tree" [
+      {
+        name = "initrd.gz";
+        path = "${config.system.build.netbootRamdisk}/initrd";
+      }
+      {
+        name = "bzImage";
+        path = "${config.system.build.kernel}/${config.system.boot.loader.kernelFile}";
+      }
+      {
+        name = "kexec-boot";
+        path = config.system.build.kexecScript;
+      }
+    ];
+
     boot.loader.timeout = 10;
 
     boot.postBootCommands =
diff --git a/nixos/modules/installer/sd-card/sd-image.nix b/nixos/modules/installer/sd-card/sd-image.nix
index 7560c682517aa..c0335ea487592 100644
--- a/nixos/modules/installer/sd-card/sd-image.nix
+++ b/nixos/modules/installer/sd-card/sd-image.nix
@@ -18,7 +18,7 @@ with lib;
 let
   rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({
     inherit (config.sdImage) storePaths;
-    compressImage = true;
+    compressImage = config.sdImage.compressImage;
     populateImageCommands = config.sdImage.populateRootCommands;
     volumeLabel = "NIXOS_SD";
   } // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
@@ -174,7 +174,8 @@ in
     mtools, libfaketime, util-linux, zstd }: stdenv.mkDerivation {
       name = config.sdImage.imageName;
 
-      nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime util-linux zstd ];
+      nativeBuildInputs = [ dosfstools e2fsprogs libfaketime mtools util-linux ]
+      ++ lib.optional config.sdImage.compressImage zstd;
 
       inherit (config.sdImage) imageName compressImage;
 
@@ -189,14 +190,18 @@ in
           echo "file sd-image $img" >> $out/nix-support/hydra-build-products
         fi
 
+        root_fs=${rootfsImage}
+        ${lib.optionalString config.sdImage.compressImage ''
+        root_fs=./root-fs.img
         echo "Decompressing rootfs image"
-        zstd -d --no-progress "${rootfsImage}" -o ./root-fs.img
+        zstd -d --no-progress "${rootfsImage}" -o $root_fs
+        ''}
 
         # Gap in front of the first partition, in MiB
         gap=${toString config.sdImage.firmwarePartitionOffset}
 
         # Create the image file sized to fit /boot/firmware and /, plus slack for the gap.
-        rootSizeBlocks=$(du -B 512 --apparent-size ./root-fs.img | awk '{ print $1 }')
+        rootSizeBlocks=$(du -B 512 --apparent-size $root_fs | awk '{ print $1 }')
         firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512))
         imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024))
         truncate -s $imageSize $img
@@ -214,7 +219,7 @@ in
 
         # Copy the rootfs into the SD image
         eval $(partx $img -o START,SECTORS --nr 2 --pairs)
-        dd conv=notrunc if=./root-fs.img of=$img seek=$START count=$SECTORS
+        dd conv=notrunc if=$root_fs of=$img seek=$START count=$SECTORS
 
         # Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
         eval $(partx $img -o START,SECTORS --nr 1 --pairs)
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index 8fc8cc4baad58..bd09724ebfa29 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/6mjgljq8sm9bsz6k22as5ar3jw78644m-nix-2.8.1";
-  i686-linux = "/nix/store/c4yjv4l8wncdla6ycicvsjrdf40xjkpp-nix-2.8.1";
-  aarch64-linux = "/nix/store/qkgvks80mdibq7m86hqasgr5lpixbnmh-nix-2.8.1";
-  x86_64-darwin = "/nix/store/riz4mzb1xhp36088ffnp40lz52bpxz01-nix-2.8.1";
-  aarch64-darwin = "/nix/store/dirm8hsnmvvzjs21hrx8i84w8k453jzp-nix-2.8.1";
+  x86_64-linux = "/nix/store/6g4fla3vkcxihph282a0v3cd10709y7c-nix-2.9.1";
+  i686-linux = "/nix/store/j143221z44469zx21f5m9a47x7y1jpr5-nix-2.9.1";
+  aarch64-linux = "/nix/store/c4z3vy1sgm49la8bvmdrrpssgk4iw2nk-nix-2.9.1";
+  x86_64-darwin = "/nix/store/cqdwb7khf6zg94bz7lnvfjqx6z775qaw-nix-2.9.1";
+  aarch64-darwin = "/nix/store/1brkxcs287n1px2i4fq39l7h51hjv0f8-nix-2.9.1";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index b74ec838df42f..1935d8252607c 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -84,6 +84,15 @@ sub debug {
 }
 
 
+# nixpkgs.system
+my ($status, @systemLines) = runCommand("nix-instantiate --impure --eval --expr builtins.currentSystem");
+if ($status != 0 || join("", @systemLines) =~ /error/) {
+    die "Failed to retrieve current system type from nix.\n";
+}
+chomp(my $system = @systemLines[0]);
+push @attrs, "nixpkgs.hostPlatform = lib.mkDefault $system;";
+
+
 my $cpuinfo = read_file "/proc/cpuinfo";
 
 
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
index 8e28d3336fa42..b031ff2f2be29 100644
--- a/nixos/modules/misc/documentation.nix
+++ b/nixos/modules/misc/documentation.nix
@@ -178,19 +178,12 @@ in
       man.generateCaches = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = mdDoc ''
           Whether to generate the manual page index caches.
           This allows searching for a page or
-          keyword using utilities like
-          <citerefentry>
-            <refentrytitle>apropos</refentrytitle>
-            <manvolnum>1</manvolnum>
-          </citerefentry>
-          and the <literal>-k</literal> option of
-          <citerefentry>
-            <refentrytitle>man</refentrytitle>
-            <manvolnum>1</manvolnum>
-          </citerefentry>.
+          keyword using utilities like {manpage}`apropos(1)`
+          and the `-k` option of
+          {manpage}`man(1)`.
         '';
       };
 
@@ -216,16 +209,14 @@ in
       dev.enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = mdDoc ''
           Whether to install documentation targeted at developers.
-          <itemizedlist>
-          <listitem><para>This includes man pages targeted at developers if <option>documentation.man.enable</option> is
-                    set (this also includes "devman" outputs).</para></listitem>
-          <listitem><para>This includes info pages targeted at developers if <option>documentation.info.enable</option>
-                    is set (this also includes "devinfo" outputs).</para></listitem>
-          <listitem><para>This includes other pages targeted at developers if <option>documentation.doc.enable</option>
-                    is set (this also includes "devdoc" outputs).</para></listitem>
-          </itemizedlist>
+          * This includes man pages targeted at developers if {option}`documentation.man.enable` is
+            set (this also includes "devman" outputs).
+          * This includes info pages targeted at developers if {option}`documentation.info.enable`
+            is set (this also includes "devinfo" outputs).
+          * This includes other pages targeted at developers if {option}`documentation.doc.enable`
+            is set (this also includes "devdoc" outputs).
         '';
       };
 
diff --git a/nixos/modules/misc/man-db.nix b/nixos/modules/misc/man-db.nix
index 8bd329bc4e0c3..7aeb02d883ac8 100644
--- a/nixos/modules/misc/man-db.nix
+++ b/nixos/modules/misc/man-db.nix
@@ -23,11 +23,11 @@ in
             ++ lib.optionals config.documentation.dev.enable [ "devman" ];
           ignoreCollisions = true;
         };
-        defaultText = lib.literalDocBook "all man pages in <option>config.environment.systemPackages</option>";
-        description = ''
-          The manual pages to generate caches for if <option>documentation.man.generateCaches</option>
+        defaultText = lib.literalMD "all man pages in {option}`config.environment.systemPackages`";
+        description = lib.mdDoc ''
+          The manual pages to generate caches for if {option}`documentation.man.generateCaches`
           is enabled. Must be a path to a directory with man pages under
-          <literal>/share/man</literal>; see the source for an example.
+          `/share/man`; see the source for an example.
           Advanced users can make this a content-addressed derivation to save a few rebuilds.
         '';
       };
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 866bb35160091..132a9b68ceb49 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -55,9 +55,46 @@ let
     check = builtins.isAttrs;
   };
 
-  defaultPkgs = import ../../.. {
-    inherit (cfg) config overlays localSystem crossSystem;
-  };
+  hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority;
+  hasHostPlatform = opt.hostPlatform.isDefined;
+  hasPlatform = hasHostPlatform || hasBuildPlatform;
+
+  # Context for messages
+  hostPlatformLine = optionalString hasHostPlatform "${showOptionWithDefLocs opt.hostPlatform}";
+  buildPlatformLine = optionalString hasBuildPlatform "${showOptionWithDefLocs opt.buildPlatform}";
+  platformLines = optionalString hasPlatform ''
+    Your system configuration configures nixpkgs with platform parameters:
+    ${hostPlatformLine
+    }${buildPlatformLine
+    }'';
+
+  legacyOptionsDefined =
+    optional opt.system.isDefined opt.system
+    ++ (optional (opt.localSystem.highestPrio < (mkOptionDefault {}).priority) opt.localSystem)
+    ++ (optional (opt.crossSystem.highestPrio < (mkOptionDefault {}).priority) opt.crossSystem)
+    ;
+
+  defaultPkgs =
+    if opt.hostPlatform.isDefined
+    then
+      let isCross = cfg.buildPlatform != cfg.hostPlatform;
+          systemArgs =
+            if isCross
+            then {
+              localSystem = cfg.buildPlatform;
+              crossSystem = cfg.hostPlatform;
+            }
+            else {
+              localSystem = cfg.hostPlatform;
+            };
+      in
+      import ../../.. ({
+        inherit (cfg) config overlays;
+      } // systemArgs)
+    else
+      import ../../.. {
+        inherit (cfg) config overlays localSystem crossSystem;
+      };
 
   finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else defaultPkgs;
 
@@ -157,6 +194,46 @@ in
       '';
     };
 
+    hostPlatform = mkOption {
+      type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
+      example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      # Make sure that the final value has all fields for sake of other modules
+      # referring to this. TODO make `lib.systems` itself use the module system.
+      apply = lib.systems.elaborate;
+      defaultText = literalExpression
+        ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+      description = ''
+        Specifies the platform where the NixOS configuration will run.
+
+        To cross-compile, set also <code>nixpkgs.buildPlatform</code>.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
+    buildPlatform = mkOption {
+      type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
+      default = cfg.hostPlatform;
+      example = { system = "x86_64-linux"; config = "x86_64-unknown-linux-gnu"; };
+      # Make sure that the final value has all fields for sake of other modules
+      # referring to this.
+      apply = lib.systems.elaborate;
+      defaultText = literalExpression
+        ''config.nixpkgs.hostPlatform'';
+      description = ''
+        Specifies the platform on which NixOS should be built.
+        By default, NixOS is built on the system where it runs, but you can
+        change where it's built. Setting this option will cause NixOS to be
+        cross-compiled.
+
+        For instance, if you're doing distributed multi-platform deployment,
+        or if you're building machines, you can set this to match your
+        development system and/or build farm.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
     localSystem = mkOption {
       type = types.attrs; # TODO utilize lib.systems.parsedPlatform
       default = { inherit (cfg) system; };
@@ -176,10 +253,13 @@ in
         deployment, or when building virtual machines. See its
         description in the Nixpkgs manual for more details.
 
-        Ignored when <code>nixpkgs.pkgs</code> is set.
+        Ignored when <code>nixpkgs.pkgs</code> or <code>hostPlatform</code> is set.
       '';
     };
 
+    # TODO deprecate. "crossSystem" is a nonsense identifier, because "cross"
+    #      is a relation between at least 2 systems in the context of a
+    #      specific build step, not a single system.
     crossSystem = mkOption {
       type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
       default = null;
@@ -193,7 +273,7 @@ in
         should be set as null, the default. See its description in the
         Nixpkgs manual for more details.
 
-        Ignored when <code>nixpkgs.pkgs</code> is set.
+        Ignored when <code>nixpkgs.pkgs</code> or <code>hostPlatform</code> is set.
       '';
     };
 
@@ -216,8 +296,7 @@ in
         </programlisting>
         See <code>nixpkgs.localSystem</code> for more information.
 
-        Ignored when <code>nixpkgs.localSystem</code> is set.
-        Ignored when <code>nixpkgs.pkgs</code> is set.
+        Ignored when <code>nixpkgs.pkgs</code>, <code>nixpkgs.localSystem</code> or <code>nixpkgs.hostPlatform</code> is set.
       '';
     };
   };
@@ -240,10 +319,23 @@ in
             else "nixpkgs.localSystem";
           pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
         in {
-          assertion = nixosExpectedSystem == pkgsSystem;
+          assertion = !hasPlatform -> nixosExpectedSystem == pkgsSystem;
           message = "The NixOS nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but NixOS was configured for system ${nixosExpectedSystem} via NixOS option ${nixosOption}. The NixOS system settings must match the Nixpkgs target system.";
         }
       )
+      {
+        assertion = hasPlatform -> legacyOptionsDefined == [];
+        message = ''
+          Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
+          ${hostPlatformLine
+          }${buildPlatformLine
+          }
+          However, it also defines the legacy options:
+          ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
+          For a future proof system configuration, we recommend to remove
+          the legacy definitions.
+        '';
+      }
     ];
   };
 
diff --git a/nixos/modules/misc/nixpkgs/test.nix b/nixos/modules/misc/nixpkgs/test.nix
index ec5fab9fb4a5e..9e8851707f8fc 100644
--- a/nixos/modules/misc/nixpkgs/test.nix
+++ b/nixos/modules/misc/nixpkgs/test.nix
@@ -1,8 +1,63 @@
 { evalMinimalConfig, pkgs, lib, stdenv }:
+let
+  eval = mod: evalMinimalConfig {
+    imports = [ ../nixpkgs.nix mod ];
+  };
+  withHost = eval {
+    nixpkgs.hostPlatform = "aarch64-linux";
+  };
+  withHostAndBuild = eval {
+    nixpkgs.hostPlatform = "aarch64-linux";
+    nixpkgs.buildPlatform = "aarch64-darwin";
+  };
+  ambiguous = {
+    _file = "ambiguous.nix";
+    nixpkgs.hostPlatform = "aarch64-linux";
+    nixpkgs.buildPlatform = "aarch64-darwin";
+    nixpkgs.system = "x86_64-linux";
+    nixpkgs.localSystem.system = "x86_64-darwin";
+    nixpkgs.crossSystem.system = "i686-linux";
+    imports = [
+      { _file = "repeat.nix";
+        nixpkgs.hostPlatform = "aarch64-linux";
+      }
+    ];
+  };
+  getErrors = module:
+    let
+      uncheckedEval = lib.evalModules { modules = [ ../nixpkgs.nix module ]; };
+    in map (ass: ass.message) (lib.filter (ass: !ass.assertion) uncheckedEval.config.assertions);
+in
 lib.recurseIntoAttrs {
   invokeNixpkgsSimple =
-    (evalMinimalConfig ({ config, modulesPath, ... }: {
-      imports = [ (modulesPath + "/misc/nixpkgs.nix") ];
+    (eval {
       nixpkgs.system = stdenv.hostPlatform.system;
-    }))._module.args.pkgs.hello;
+    })._module.args.pkgs.hello;
+  assertions =
+    assert withHost._module.args.pkgs.stdenv.hostPlatform.system == "aarch64-linux";
+    assert withHost._module.args.pkgs.stdenv.buildPlatform.system == "aarch64-linux";
+    assert withHostAndBuild._module.args.pkgs.stdenv.hostPlatform.system == "aarch64-linux";
+    assert withHostAndBuild._module.args.pkgs.stdenv.buildPlatform.system == "aarch64-darwin";
+    assert builtins.trace (lib.head (getErrors ambiguous))
+      getErrors ambiguous ==
+        [''
+          Your system configures nixpkgs with the platform parameters:
+          nixpkgs.hostPlatform, with values defined in:
+            - repeat.nix
+            - ambiguous.nix
+          nixpkgs.buildPlatform, with values defined in:
+            - ambiguous.nix
+
+          However, it also defines the legacy options:
+          nixpkgs.system, with values defined in:
+            - ambiguous.nix
+          nixpkgs.localSystem, with values defined in:
+            - ambiguous.nix
+          nixpkgs.crossSystem, with values defined in:
+            - ambiguous.nix
+
+          For a future proof system configuration, we recommend to remove
+          the legacy definitions.
+        ''];
+    pkgs.emptyFile;
 }
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index d59d7bfe40d9f..b0fd06e66e02f 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -141,7 +141,6 @@
   ./programs/cdemu.nix
   ./programs/cfs-zen-tweaks.nix
   ./programs/chromium.nix
-  ./programs/clickshare.nix
   ./programs/cnping.nix
   ./programs/command-not-found/command-not-found.nix
   ./programs/criu.nix
@@ -169,6 +168,7 @@
   ./programs/gpaste.nix
   ./programs/gnupg.nix
   ./programs/gphoto2.nix
+  ./programs/haguichi.nix
   ./programs/hamster.nix
   ./programs/htop.nix
   ./programs/iftop.nix
@@ -196,6 +196,7 @@
   ./programs/npm.nix
   ./programs/noisetorch.nix
   ./programs/oblogout.nix
+  ./programs/openvpn3.nix
   ./programs/pantheon-tweaks.nix
   ./programs/partition-manager.nix
   ./programs/plotinus.nix
@@ -348,6 +349,7 @@
   ./services/databases/clickhouse.nix
   ./services/databases/cockroachdb.nix
   ./services/databases/couchdb.nix
+  ./services/databases/dragonflydb.nix
   ./services/databases/firebird.nix
   ./services/databases/foundationdb.nix
   ./services/databases/hbase.nix
@@ -513,11 +515,18 @@
   ./services/mail/rspamd.nix
   ./services/mail/rss2email.nix
   ./services/mail/roundcube.nix
+  ./services/mail/schleuder.nix
   ./services/mail/sympa.nix
   ./services/mail/nullmailer.nix
-  ./services/matrix/matrix-synapse.nix
+  ./services/matrix/appservice-discord.nix
+  ./services/matrix/appservice-irc.nix
+  ./services/matrix/conduit.nix
+  ./services/matrix/dendrite.nix
+  ./services/matrix/mautrix-facebook.nix
+  ./services/matrix/mautrix-telegram.nix
   ./services/matrix/mjolnir.nix
   ./services/matrix/pantalaimon.nix
+  ./services/matrix/synapse.nix
   ./services/misc/ananicy.nix
   ./services/misc/airsonic.nix
   ./services/misc/ankisyncd.nix
@@ -536,7 +545,6 @@
   ./services/misc/cpuminer-cryptonight.nix
   ./services/misc/cgminer.nix
   ./services/misc/confd.nix
-  ./services/misc/dendrite.nix
   ./services/misc/devmon.nix
   ./services/misc/dictd.nix
   ./services/misc/duckling.nix
@@ -579,11 +587,6 @@
   ./services/misc/libreddit.nix
   ./services/misc/lifecycled.nix
   ./services/misc/mame.nix
-  ./services/misc/matrix-appservice-discord.nix
-  ./services/misc/matrix-appservice-irc.nix
-  ./services/misc/matrix-conduit.nix
-  ./services/misc/mautrix-facebook.nix
-  ./services/misc/mautrix-telegram.nix
   ./services/misc/mbpfan.nix
   ./services/misc/mediatomb.nix
   ./services/misc/metabase.nix
@@ -605,6 +608,7 @@
   ./services/misc/packagekit.nix
   ./services/misc/paperless.nix
   ./services/misc/parsoid.nix
+  ./services/misc/persistent-evdev.nix
   ./services/misc/plex.nix
   ./services/misc/plikd.nix
   ./services/misc/podgrab.nix
@@ -656,6 +660,7 @@
   ./services/monitoring/do-agent.nix
   ./services/monitoring/fusion-inventory.nix
   ./services/monitoring/grafana.nix
+  ./services/monitoring/grafana-agent.nix
   ./services/monitoring/grafana-image-renderer.nix
   ./services/monitoring/grafana-reporter.nix
   ./services/monitoring/graphite.nix
@@ -769,6 +774,7 @@
   ./services/networking/ergo.nix
   ./services/networking/ergochat.nix
   ./services/networking/eternal-terminal.nix
+  ./services/networking/expressvpn.nix
   ./services/networking/fakeroute.nix
   ./services/networking/ferm.nix
   ./services/networking/fireqos.nix
@@ -888,6 +894,7 @@
   ./services/networking/redsocks.nix
   ./services/networking/resilio.nix
   ./services/networking/robustirc-bridge.nix
+  ./services/networking/routedns.nix
   ./services/networking/rpcbind.nix
   ./services/networking/rxe.nix
   ./services/networking/sabnzbd.nix
diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix
index aad554c2bd66a..1e5c6ff9b2440 100644
--- a/nixos/modules/programs/captive-browser.nix
+++ b/nixos/modules/programs/captive-browser.nix
@@ -98,7 +98,7 @@ in
 
   config = mkIf cfg.enable {
     environment.systemPackages = [
-      (pkgs.runCommandNoCC "captive-browser-desktop-item" { } ''
+      (pkgs.runCommand "captive-browser-desktop-item" { } ''
         install -Dm444 -t $out/share/applications ${desktopItem}/share/applications/*.desktop
       '')
     ];
diff --git a/nixos/modules/programs/clickshare.nix b/nixos/modules/programs/clickshare.nix
deleted file mode 100644
index 9980a7daf5254..0000000000000
--- a/nixos/modules/programs/clickshare.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-
-  options.programs.clickshare-csc1.enable =
-    lib.options.mkEnableOption ''
-      Barco ClickShare CSC-1 driver/client.
-      This allows users in the <literal>clickshare</literal>
-      group to access and use a ClickShare USB dongle
-      that is connected to the machine
-    '';
-
-  config = lib.modules.mkIf config.programs.clickshare-csc1.enable {
-    environment.systemPackages = [ pkgs.clickshare-csc1 ];
-    services.udev.packages = [ pkgs.clickshare-csc1 ];
-    users.groups.clickshare = {};
-  };
-
-  meta.maintainers = [ lib.maintainers.yarny ];
-
-}
diff --git a/nixos/modules/programs/haguichi.nix b/nixos/modules/programs/haguichi.nix
new file mode 100644
index 0000000000000..4f48551cf1dac
--- /dev/null
+++ b/nixos/modules/programs/haguichi.nix
@@ -0,0 +1,15 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+{
+  options.programs.haguichi = {
+    enable = mkEnableOption "Haguichi, a Linux GUI frontend to the proprietary LogMeIn Hamachi";
+  };
+
+  config = mkIf config.programs.haguichi.enable {
+    environment.systemPackages = with pkgs; [ haguichi ];
+
+    services.logmein-hamachi.enable = true;
+  };
+}
diff --git a/nixos/modules/programs/kdeconnect.nix b/nixos/modules/programs/kdeconnect.nix
index df698e84dd702..10d6e18a3d144 100644
--- a/nixos/modules/programs/kdeconnect.nix
+++ b/nixos/modules/programs/kdeconnect.nix
@@ -12,8 +12,8 @@ with lib;
       implementation if you use Gnome.
     '';
     package = mkOption {
-      default = pkgs.kdeconnect;
-      defaultText = literalExpression "pkgs.kdeconnect";
+      default = pkgs.plasma5Packages.kdeconnect-kde;
+      defaultText = literalExpression "pkgs.plasma5Packages.kdeconnect-kde";
       type = types.package;
       example = literalExpression "pkgs.gnomeExtensions.gsconnect";
       description = ''
diff --git a/nixos/modules/programs/openvpn3.nix b/nixos/modules/programs/openvpn3.nix
new file mode 100644
index 0000000000000..f3101d3cebdc9
--- /dev/null
+++ b/nixos/modules/programs/openvpn3.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.openvpn3;
+in
+{
+  options.programs.openvpn3 = {
+    enable = mkEnableOption "the openvpn3 client";
+  };
+
+  config = mkIf cfg.enable {
+    services.dbus.packages = with pkgs; [
+      openvpn3
+    ];
+
+    users.users.openvpn = {
+      isSystemUser = true;
+      uid = config.ids.uids.openvpn;
+      group = "openvpn";
+    };
+
+    users.groups.openvpn = {
+      gid = config.ids.gids.openvpn;
+    };
+
+    environment.systemPackages = with pkgs; [
+      openvpn3
+    ];
+  };
+
+}
diff --git a/nixos/modules/security/systemd-confinement.nix b/nixos/modules/security/systemd-confinement.nix
index f3a2de3bf87a0..07b725effb7d1 100644
--- a/nixos/modules/security/systemd-confinement.nix
+++ b/nixos/modules/security/systemd-confinement.nix
@@ -22,16 +22,17 @@ in {
       options.confinement.fullUnit = lib.mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to include the full closure of the systemd unit file into the
           chroot, instead of just the dependencies for the executables.
 
-          <warning><para>While it may be tempting to just enable this option to
+          ::: {.warning}
+          While it may be tempting to just enable this option to
           make things work quickly, please be aware that this might add paths
           to the closure of the chroot that you didn't anticipate. It's better
-          to use <option>confinement.packages</option> to <emphasis
-          role="strong">explicitly</emphasis> add additional store paths to the
-          chroot.</para></warning>
+          to use {option}`confinement.packages` to **explicitly** add additional store paths to the
+          chroot.
+          :::
         '';
       };
 
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index 586b9ffa68888..11733d99fca65 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -215,6 +215,7 @@ in {
     systemd.sockets.mpd = mkIf cfg.startWhenNeeded {
       wantedBy = [ "sockets.target" ];
       listenStreams = [
+        ""  # Note: this is needed to override the upstream unit
         (if pkgs.lib.hasPrefix "/" cfg.network.listenAddress
           then cfg.network.listenAddress
           else "${optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}")
diff --git a/nixos/modules/services/audio/navidrome.nix b/nixos/modules/services/audio/navidrome.nix
index 3660e05310be1..b50cb04155697 100644
--- a/nixos/modules/services/audio/navidrome.nix
+++ b/nixos/modules/services/audio/navidrome.nix
@@ -45,6 +45,8 @@ in {
         RootDirectory = "/run/navidrome";
         ReadWritePaths = "";
         BindReadOnlyPaths = [
+          # navidrome uses online services to download additional album metadata / covers
+          "${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt"
           builtins.storeDir
         ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder;
         CapabilityBoundingSet = "";
diff --git a/nixos/modules/services/cluster/k3s/default.nix b/nixos/modules/services/cluster/k3s/default.nix
index 3a36cfa3f37b8..421aa0aac60ed 100644
--- a/nixos/modules/services/cluster/k3s/default.nix
+++ b/nixos/modules/services/cluster/k3s/default.nix
@@ -3,8 +3,14 @@
 with lib;
 let
   cfg = config.services.k3s;
+  removeOption = config: instruction:
+    lib.mkRemovedOptionModule ([ "services" "k3s" ] ++ config) instruction;
 in
 {
+  imports = [
+    (removeOption [ "docker" ] "k3s docker option is no longer supported.")
+  ];
+
   # interface
   options.services.k3s = {
     enable = mkEnableOption "k3s";
@@ -48,12 +54,6 @@ in
       default = null;
     };
 
-    docker = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Use docker to run containers rather than the built-in containerd.";
-    };
-
     extraFlags = mkOption {
       description = "Extra flags to pass to the k3s command.";
       type = types.str;
@@ -88,14 +88,11 @@ in
       }
     ];
 
-    virtualisation.docker = mkIf cfg.docker {
-      enable = mkDefault true;
-    };
     environment.systemPackages = [ config.services.k3s.package ];
 
     systemd.services.k3s = {
       description = "k3s service";
-      after = [ "network.service" "firewall.service" ] ++ (optional cfg.docker "docker.service");
+      after = [ "network.service" "firewall.service" ];
       wants = [ "network.service" "firewall.service" ];
       wantedBy = [ "multi-user.target" ];
       path = optional config.boot.zfs.enabled config.boot.zfs.package;
@@ -113,8 +110,8 @@ in
         ExecStart = concatStringsSep " \\\n " (
           [
             "${cfg.package}/bin/k3s ${cfg.role}"
-          ] ++ (optional cfg.docker "--docker")
-          ++ (optional (cfg.docker && config.systemd.enableUnifiedCgroupHierarchy) "--kubelet-arg=cgroup-driver=systemd")
+          ]
+          ++ (optional (config.systemd.enableUnifiedCgroupHierarchy) "--kubelet-arg=cgroup-driver=systemd")
           ++ (optional cfg.disableAgent "--disable-agent")
           ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}")
           ++ (optional (cfg.token != "") "--token ${cfg.token}")
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index b36cac35e7c29..b457e69babaad 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -4,11 +4,12 @@ let
   inherit (lib)
     concatStringsSep
     flip
-    literalDocBook
+    literalMD
     literalExpression
     optionalAttrs
     optionals
     recursiveUpdate
+    mdDoc
     mkEnableOption
     mkIf
     mkOption
@@ -107,7 +108,7 @@ in
     clusterName = mkOption {
       type = types.str;
       default = "Test Cluster";
-      description = ''
+      description = mdDoc ''
         The name of the cluster.
         This setting prevents nodes in one logical cluster from joining
         another. All nodes in a cluster must have the same value.
@@ -117,19 +118,19 @@ in
     user = mkOption {
       type = types.str;
       default = defaultUser;
-      description = "Run Apache Cassandra under this user.";
+      description = mdDoc "Run Apache Cassandra under this user.";
     };
 
     group = mkOption {
       type = types.str;
       default = defaultUser;
-      description = "Run Apache Cassandra under this group.";
+      description = mdDoc "Run Apache Cassandra under this group.";
     };
 
     homeDir = mkOption {
       type = types.path;
       default = "/var/lib/cassandra";
-      description = ''
+      description = mdDoc ''
         Home directory for Apache Cassandra.
       '';
     };
@@ -139,7 +140,7 @@ in
       default = pkgs.cassandra;
       defaultText = literalExpression "pkgs.cassandra";
       example = literalExpression "pkgs.cassandra_3_11";
-      description = ''
+      description = mdDoc ''
         The Apache Cassandra package to use.
       '';
     };
@@ -147,8 +148,8 @@ in
     jvmOpts = mkOption {
       type = types.listOf types.str;
       default = [ ];
-      description = ''
-        Populate the JVM_OPT environment variable.
+      description = mdDoc ''
+        Populate the `JVM_OPT` environment variable.
       '';
     };
 
@@ -156,20 +157,20 @@ in
       type = types.nullOr types.str;
       default = "127.0.0.1";
       example = null;
-      description = ''
+      description = mdDoc ''
         Address or interface to bind to and tell other Cassandra nodes
         to connect to. You _must_ change this if you want multiple
         nodes to be able to communicate!
 
-        Set listenAddress OR listenInterface, not both.
+        Set {option}`listenAddress` OR {option}`listenInterface`, not both.
 
         Leaving it blank leaves it up to
-        InetAddress.getLocalHost(). This will always do the Right
-        Thing _if_ the node is properly configured (hostname, name
+        `InetAddress.getLocalHost()`. This will always do the "Right
+        Thing" _if_ the node is properly configured (hostname, name
         resolution, etc), and the Right Thing is to use the address
         associated with the hostname (it might not be).
 
-        Setting listen_address to 0.0.0.0 is always wrong.
+        Setting {option}`listenAddress` to `0.0.0.0` is always wrong.
       '';
     };
 
@@ -177,8 +178,8 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "eth1";
-      description = ''
-        Set listenAddress OR listenInterface, not both. Interfaces
+      description = mdDoc ''
+        Set `listenAddress` OR `listenInterface`, not both. Interfaces
         must correspond to a single address, IP aliasing is not
         supported.
       '';
@@ -188,18 +189,18 @@ in
       type = types.nullOr types.str;
       default = "127.0.0.1";
       example = null;
-      description = ''
+      description = mdDoc ''
         The address or interface to bind the native transport server to.
 
-        Set rpcAddress OR rpcInterface, not both.
+        Set {option}`rpcAddress` OR {option}`rpcInterface`, not both.
 
-        Leaving rpcAddress blank has the same effect as on
-        listenAddress (i.e. it will be based on the configured hostname
+        Leaving {option}`rpcAddress` blank has the same effect as on
+        {option}`listenAddress` (i.e. it will be based on the configured hostname
         of the node).
 
-        Note that unlike listenAddress, you can specify 0.0.0.0, but you
-        must also set extraConfig.broadcast_rpc_address to a value other
-        than 0.0.0.0.
+        Note that unlike {option}`listenAddress`, you can specify `"0.0.0.0"`, but you
+        must also set `extraConfig.broadcast_rpc_address` to a value other
+        than `"0.0.0.0"`.
 
         For security reasons, you should not expose this port to the
         internet. Firewall it if needed.
@@ -210,8 +211,8 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "eth1";
-      description = ''
-        Set rpcAddress OR rpcInterface, not both. Interfaces must
+      description = mdDoc ''
+        Set {option}`rpcAddress` OR {option}`rpcInterface`, not both. Interfaces must
         correspond to a single address, IP aliasing is not supported.
       '';
     };
@@ -233,7 +234,7 @@ in
           <logger name="com.thinkaurelius.thrift" level="ERROR"/>
         </configuration>
       '';
-      description = ''
+      description = mdDoc ''
         XML logback configuration for cassandra
       '';
     };
@@ -241,24 +242,24 @@ in
     seedAddresses = mkOption {
       type = types.listOf types.str;
       default = [ "127.0.0.1" ];
-      description = ''
+      description = mdDoc ''
         The addresses of hosts designated as contact points in the cluster. A
         joining node contacts one of the nodes in the seeds list to learn the
         topology of the ring.
-        Set to 127.0.0.1 for a single node cluster.
+        Set to `[ "127.0.0.1" ]` for a single node cluster.
       '';
     };
 
     allowClients = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = mdDoc ''
         Enables or disables the native transport server (CQL binary protocol).
-        This server uses the same address as the <literal>rpcAddress</literal>,
-        but the port it uses is not <literal>rpc_port</literal> but
-        <literal>native_transport_port</literal>. See the official Cassandra
+        This server uses the same address as the {option}`rpcAddress`,
+        but the port it uses is not `rpc_port` but
+        `native_transport_port`. See the official Cassandra
         docs for more information on these variables and set them using
-        <literal>extraConfig</literal>.
+        {option}`extraConfig`.
       '';
     };
 
@@ -269,8 +270,8 @@ in
         {
           commitlog_sync_batch_window_in_ms = 3;
         };
-      description = ''
-        Extra options to be merged into cassandra.yaml as nix attribute set.
+      description = mdDoc ''
+        Extra options to be merged into {file}`cassandra.yaml` as nix attribute set.
       '';
     };
 
@@ -278,8 +279,8 @@ in
       type = types.lines;
       default = "";
       example = literalExpression ''"CLASSPATH=$CLASSPATH:''${extraJar}"'';
-      description = ''
-        Extra shell lines to be appended onto cassandra-env.sh.
+      description = mdDoc ''
+        Extra shell lines to be appended onto {file}`cassandra-env.sh`.
       '';
     };
 
@@ -287,13 +288,13 @@ in
       type = types.nullOr types.str;
       default = "3w";
       example = null;
-      description = ''
+      description = mdDoc ''
         Set the interval how often full repairs are run, i.e.
-        <literal>nodetool repair --full</literal> is executed. See
-        https://cassandra.apache.org/doc/latest/operating/repair.html
+        {command}`nodetool repair --full` is executed. See
+        <https://cassandra.apache.org/doc/latest/operating/repair.html>
         for more information.
 
-        Set to <literal>null</literal> to disable full repairs.
+        Set to `null` to disable full repairs.
       '';
     };
 
@@ -301,7 +302,7 @@ in
       type = types.listOf types.str;
       default = [ ];
       example = [ "--partitioner-range" ];
-      description = ''
+      description = mdDoc ''
         Options passed through to the full repair command.
       '';
     };
@@ -310,13 +311,13 @@ in
       type = types.nullOr types.str;
       default = "3d";
       example = null;
-      description = ''
+      description = mdDoc ''
         Set the interval how often incremental repairs are run, i.e.
-        <literal>nodetool repair</literal> is executed. See
-        https://cassandra.apache.org/doc/latest/operating/repair.html
+        {command}`nodetool repair` is executed. See
+        <https://cassandra.apache.org/doc/latest/operating/repair.html>
         for more information.
 
-        Set to <literal>null</literal> to disable incremental repairs.
+        Set to `null` to disable incremental repairs.
       '';
     };
 
@@ -324,7 +325,7 @@ in
       type = types.listOf types.str;
       default = [ ];
       example = [ "--partitioner-range" ];
-      description = ''
+      description = mdDoc ''
         Options passed through to the incremental repair command.
       '';
     };
@@ -333,15 +334,15 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "4G";
-      description = ''
-        Must be left blank or set together with heapNewSize.
+      description = mdDoc ''
+        Must be left blank or set together with {option}`heapNewSize`.
         If left blank a sensible value for the available amount of RAM and CPU
         cores is calculated.
 
         Override to set the amount of memory to allocate to the JVM at
         start-up. For production use you may wish to adjust this for your
-        environment. MAX_HEAP_SIZE is the total amount of memory dedicated
-        to the Java heap. HEAP_NEWSIZE refers to the size of the young
+        environment. `MAX_HEAP_SIZE` is the total amount of memory dedicated
+        to the Java heap. `HEAP_NEWSIZE` refers to the size of the young
         generation.
 
         The main trade-off for the young generation is that the larger it
@@ -354,21 +355,21 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "800M";
-      description = ''
-        Must be left blank or set together with heapNewSize.
+      description = mdDoc ''
+        Must be left blank or set together with {option}`heapNewSize`.
         If left blank a sensible value for the available amount of RAM and CPU
         cores is calculated.
 
         Override to set the amount of memory to allocate to the JVM at
         start-up. For production use you may wish to adjust this for your
-        environment. HEAP_NEWSIZE refers to the size of the young
+        environment. `HEAP_NEWSIZE` refers to the size of the young
         generation.
 
         The main trade-off for the young generation is that the larger it
         is, the longer GC pause times will be. The shorter it is, the more
         expensive GC will be (usually).
 
-        The example HEAP_NEWSIZE assumes a modern 8-core+ machine for decent pause
+        The example `HEAP_NEWSIZE` assumes a modern 8-core+ machine for decent pause
         times. If in doubt, and if you do not particularly want to tweak, go with
         100 MB per physical CPU core.
       '';
@@ -378,7 +379,7 @@ in
       type = types.nullOr types.int;
       default = null;
       example = 4;
-      description = ''
+      description = mdDoc ''
         Set this to control the amount of arenas per-thread in glibc.
       '';
     };
@@ -386,19 +387,19 @@ in
     remoteJmx = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = mdDoc ''
         Cassandra ships with JMX accessible *only* from localhost.
         To enable remote JMX connections set to true.
 
         Be sure to also enable authentication and/or TLS.
-        See: https://wiki.apache.org/cassandra/JmxSecurity
+        See: <https://wiki.apache.org/cassandra/JmxSecurity>
       '';
     };
 
     jmxPort = mkOption {
       type = types.int;
       default = 7199;
-      description = ''
+      description = mdDoc ''
         Specifies the default port over which Cassandra will be available for
         JMX connections.
         For security reasons, you should not expose this port to the internet.
@@ -408,11 +409,11 @@ in
 
     jmxRoles = mkOption {
       default = [ ];
-      description = ''
-        Roles that are allowed to access the JMX (e.g. nodetool)
-        BEWARE: The passwords will be stored world readable in the nix-store.
+      description = mdDoc ''
+        Roles that are allowed to access the JMX (e.g. {command}`nodetool`)
+        BEWARE: The passwords will be stored world readable in the nix store.
                 It's recommended to use your own protected file using
-                <literal>jmxRolesFile</literal>
+                {option}`jmxRolesFile`
 
         Doesn't work in versions older than 3.11 because they don't like that
         it's world readable.
@@ -437,7 +438,7 @@ in
         if versionAtLeast cfg.package.version "3.11"
         then pkgs.writeText "jmx-roles-file" defaultJmxRolesFile
         else null;
-      defaultText = literalDocBook ''generated configuration file if version is at least 3.11, otherwise <literal>null</literal>'';
+      defaultText = literalMD ''generated configuration file if version is at least 3.11, otherwise `null`'';
       example = "/var/lib/cassandra/jmx.password";
       description = ''
         Specify your own jmx roles file.
diff --git a/nixos/modules/services/databases/dragonflydb.nix b/nixos/modules/services/databases/dragonflydb.nix
new file mode 100644
index 0000000000000..e72afa9d90890
--- /dev/null
+++ b/nixos/modules/services/databases/dragonflydb.nix
@@ -0,0 +1,152 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dragonflydb;
+  dragonflydb = pkgs.dragonflydb;
+
+  settings =
+    {
+      port = cfg.port;
+      dir = "/var/lib/dragonflydb";
+      keys_output_limit = cfg.keysOutputLimit;
+    } //
+    (lib.optionalAttrs (cfg.bind != null) { bind = cfg.bind; }) //
+    (lib.optionalAttrs (cfg.requirePass != null) { requirepass = cfg.requirePass; }) //
+    (lib.optionalAttrs (cfg.maxMemory != null) { maxmemory = cfg.maxMemory; }) //
+    (lib.optionalAttrs (cfg.memcachePort != null) { memcache_port = cfg.memcachePort; }) //
+    (lib.optionalAttrs (cfg.dbNum != null) { dbnum = cfg.dbNum; }) //
+    (lib.optionalAttrs (cfg.cacheMode != null) { cache_mode = cfg.cacheMode; });
+in
+{
+
+  ###### interface
+
+  options = {
+    services.dragonflydb = {
+      enable = mkEnableOption "DragonflyDB";
+
+      user = mkOption {
+        type = types.str;
+        default = "dragonfly";
+        description = "The user to run DragonflyDB as";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 6379;
+        description = "The TCP port to accept connections.";
+      };
+
+      bind = mkOption {
+        type = with types; nullOr str;
+        default = "127.0.0.1";
+        description = ''
+          The IP interface to bind to.
+          <literal>null</literal> means "all interfaces".
+        '';
+      };
+
+      requirePass = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = "Password for database";
+        example = "letmein!";
+      };
+
+      maxMemory = mkOption {
+        type = with types; nullOr ints.unsigned;
+        default = null;
+        description = ''
+          The maximum amount of memory to use for storage (in bytes).
+          <literal>null</literal> means this will be automatically set.
+        '';
+      };
+
+      memcachePort = mkOption {
+        type = with types; nullOr port;
+        default = null;
+        description = ''
+          To enable memcached compatible API on this port.
+          <literal>null</literal> means disabled.
+        '';
+      };
+
+      keysOutputLimit = mkOption {
+        type = types.ints.unsigned;
+        default = 8192;
+        description = ''
+          Maximum number of returned keys in keys command.
+          <literal>keys</literal> is a dangerous command.
+          We truncate its result to avoid blowup in memory when fetching too many keys.
+        '';
+      };
+
+      dbNum = mkOption {
+        type = with types; nullOr ints.unsigned;
+        default = null;
+        description = "Maximum number of supported databases for <literal>select</literal>";
+      };
+
+      cacheMode = mkOption {
+        type = with types; nullOr bool;
+        default = null;
+        description = ''
+          Once this mode is on, Dragonfly will evict items least likely to be stumbled
+          upon in the future but only when it is near maxmemory limit.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.dragonflydb.enable {
+
+    users.users = optionalAttrs (cfg.user == "dragonfly") {
+      dragonfly.description = "DragonflyDB server user";
+      dragonfly.isSystemUser = true;
+      dragonfly.group = "dragonfly";
+    };
+    users.groups = optionalAttrs (cfg.user == "dragonfly") { dragonfly = { }; };
+
+    environment.systemPackages = [ dragonflydb ];
+
+    systemd.services.dragonflydb = {
+      description = "DragonflyDB server";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        ExecStart = "${dragonflydb}/bin/dragonfly --alsologtostderr ${builtins.concatStringsSep " " (attrsets.mapAttrsToList (n: v: "--${n} ${strings.escapeShellArg v}") settings)}";
+
+        User = cfg.user;
+
+        # Filesystem access
+        ReadWritePaths = [ settings.dir ];
+        StateDirectory = "dragonflydb";
+        StateDirectoryMode = "0700";
+        # Process Properties
+        LimitMEMLOCK = "infinity";
+        # Caps
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        # Sandboxing
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateDevices = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+        LockPersonality = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictRealtime = true;
+        PrivateMounts = true;
+        MemoryDenyWriteExecute = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index 1967a2371bdd7..d80d1b07b97c3 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -312,6 +312,7 @@ in {
           "-h" (lib.concatStringsSep " " cfg.urlList)
         ]);
         Type = "notify";
+        NotifyAccess = "all";
         PIDFile = cfg.settings.attrs.olcPidFile;
       };
     };
diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix
index 6459b22519dbe..dd1f5e3a018db 100644
--- a/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -234,7 +234,7 @@ in {
     environment.etc."pipewire/pipewire.conf" = {
       source = json.generate "pipewire.conf" configs.pipewire;
     };
-    environment.etc."pipewire/pipewire-pulse.conf" = {
+    environment.etc."pipewire/pipewire-pulse.conf" = mkIf cfg.pulse.enable {
       source = json.generate "pipewire-pulse.conf" configs.pipewire-pulse;
     };
 
@@ -251,6 +251,8 @@ in {
         ] ++ lib.optional config.security.rtkit.enable "rtkit";
         description = "Pipewire system service user";
         isSystemUser = true;
+        home = "/var/lib/pipewire";
+        createHome = true;
       };
       groups.pipewire.gid = config.ids.gids.pipewire;
     };
@@ -258,5 +260,8 @@ in {
     # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/464#note_723554
     systemd.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1";
     systemd.user.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1";
+
+    # pipewire-pulse default config expects pactl to be in PATH
+    systemd.user.services.pipewire-pulse.path = lib.mkIf cfg.pulse.enable [ pkgs.pulseaudio ];
   };
 }
diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix
index 1dbdd842c4a1e..439a3ae68daa1 100644
--- a/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -37,11 +37,19 @@ in
     environment.systemPackages = [ cfg.package ];
 
     environment.etc."wireplumber/main.lua.d/80-nixos.lua" = lib.mkIf (!pwUsedForAudio) {
-     text = ''
+      text = ''
         -- Pipewire is not used for audio, so prevent it from grabbing audio devices
         alsa_monitor.enable = function() end
       '';
     };
+    environment.etc."wireplumber/main.lua.d/80-systemwide.lua" = lib.mkIf config.services.pipewire.systemWide {
+      text = ''
+        -- When running system-wide, these settings need to be disabled (they
+        -- use functions that aren't available on the system dbus).
+        alsa_monitor.properties["alsa.reserve"] = false
+        default_access.properties["enable-flatpak-portal"] = false
+      '';
+    };
 
     systemd.packages = [ cfg.package ];
 
@@ -50,5 +58,10 @@ in
 
     systemd.services.wireplumber.wantedBy = [ "pipewire.service" ];
     systemd.user.services.wireplumber.wantedBy = [ "pipewire.service" ];
+
+    systemd.services.wireplumber.environment = lib.mkIf config.services.pipewire.systemWide {
+      # Force wireplumber to use system dbus.
+      DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/dbus/system_bus_socket";
+    };
   };
 }
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index e255e5d22188b..2aacc5e55c6e2 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -369,6 +369,17 @@ in {
 
     networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.config.http.server_port ];
 
+    # symlink the configuration to /etc/home-assistant
+    environment.etc = lib.mkMerge [
+      (lib.mkIf (cfg.config != null && !cfg.configWritable) {
+        "home-assistant/configuration.yaml".source = configFile;
+      })
+
+      (lib.mkIf (cfg.lovelaceConfig != null && !cfg.lovelaceConfigWritable) {
+        "home-assistant/ui-lovelace.yaml".source = lovelaceConfigFile;
+      })
+    ];
+
     systemd.services.home-assistant = {
       description = "Home Assistant";
       after = [
@@ -378,18 +389,22 @@ in {
         "mysql.service"
         "postgresql.service"
       ];
+      reloadTriggers = [
+        configFile
+        lovelaceConfigFile
+      ];
       preStart = let
         copyConfig = if cfg.configWritable then ''
           cp --no-preserve=mode ${configFile} "${cfg.configDir}/configuration.yaml"
         '' else ''
           rm -f "${cfg.configDir}/configuration.yaml"
-          ln -s ${configFile} "${cfg.configDir}/configuration.yaml"
+          ln -s /etc/home-assistant/configuration.yaml "${cfg.configDir}/configuration.yaml"
         '';
         copyLovelaceConfig = if cfg.lovelaceConfigWritable then ''
           cp --no-preserve=mode ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml"
         '' else ''
           rm -f "${cfg.configDir}/ui-lovelace.yaml"
-          ln -s ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml"
+          ln -s /etc/home-assistant/ui-lovelace.yaml "${cfg.configDir}/ui-lovelace.yaml"
         '';
       in
         (optionalString (cfg.config != null) copyConfig) +
diff --git a/nixos/modules/services/mail/postfixadmin.nix b/nixos/modules/services/mail/postfixadmin.nix
index a0846ad529020..8adae3c1a0138 100644
--- a/nixos/modules/services/mail/postfixadmin.nix
+++ b/nixos/modules/services/mail/postfixadmin.nix
@@ -177,7 +177,7 @@ in
 
     services.phpfpm.pools.postfixadmin = {
       user = user;
-      phpPackage = pkgs.php74;
+      phpPackage = pkgs.php81;
       phpOptions = ''
         error_log = 'stderr'
         log_errors = on
diff --git a/nixos/modules/services/mail/schleuder.nix b/nixos/modules/services/mail/schleuder.nix
new file mode 100644
index 0000000000000..7ba15f1070bde
--- /dev/null
+++ b/nixos/modules/services/mail/schleuder.nix
@@ -0,0 +1,162 @@
+{ config, pkgs, lib, ... }:
+let
+  cfg = config.services.schleuder;
+  settingsFormat = pkgs.formats.yaml { };
+  postfixMap = entries: lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${value}") entries);
+  writePostfixMap = name: entries: pkgs.writeText name (postfixMap entries);
+  configScript = pkgs.writeScript "schleuder-cfg" ''
+    #!${pkgs.runtimeShell}
+    set -exuo pipefail
+    umask 0077
+    ${pkgs.yq}/bin/yq \
+      --slurpfile overrides <(${pkgs.yq}/bin/yq . <${lib.escapeShellArg cfg.extraSettingsFile}) \
+      < ${settingsFormat.generate "schleuder.yml" cfg.settings} \
+      '. * $overrides[0]' \
+      > /etc/schleuder/schleuder.yml
+    chown schleuder: /etc/schleuder/schleuder.yml
+  '';
+in
+{
+  options.services.schleuder = {
+    enable = lib.mkEnableOption "Schleuder secure remailer";
+    enablePostfix = lib.mkEnableOption "automatic postfix integration" // { default = true; };
+    lists = lib.mkOption {
+      description = ''
+        List of list addresses that should be handled by Schleuder.
+
+        Note that this is only handled by the postfix integration, and
+        the setup of the lists, their members and their keys has to be
+        performed separately via schleuder's API, using a tool such as
+        schleuder-cli.
+      '';
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      example = [ "widget-team@example.com" "security@example.com" ];
+    };
+    /* maybe one day....
+      domains = lib.mkOption {
+      description = "Domains for which all mail should be handled by Schleuder.";
+      type = lib.types.listOf lib.types.str;
+      default = [];
+      example = ["securelists.example.com"];
+      };
+    */
+    settings = lib.mkOption {
+      description = ''
+        Settings for schleuder.yml.
+
+        Check the <link xlink:href="https://0xacab.org/schleuder/schleuder/blob/master/etc/schleuder.yml">example configuration</link> for possible values.
+      '';
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+        options.keyserver = lib.mkOption {
+          type = lib.types.str;
+          description = ''
+            Key server from which to fetch and update keys.
+
+            Note that NixOS uses a different default from upstream, since the upstream default sks-keyservers.net is deprecated.
+          '';
+          default = "keys.openpgp.org";
+        };
+      };
+      default = { };
+    };
+    extraSettingsFile = lib.mkOption {
+      description = "YAML file to merge into the schleuder config at runtime. This can be used for secrets such as API keys.";
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+    };
+    listDefaults = lib.mkOption {
+      description = ''
+        Default settings for lists (list-defaults.yml).
+
+        Check the <link xlink:href="https://0xacab.org/schleuder/schleuder/-/blob/master/etc/list-defaults.yml">example configuration</link> for possible values.
+      '';
+      type = settingsFormat.type;
+      default = { };
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !(cfg.settings.api ? valid_api_keys);
+        message = ''
+          services.schleuder.settings.api.valid_api_keys is set. Defining API keys via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store API keys in a non-public location.
+        '';
+      }
+      {
+        assertion = !(lib.any (db: db ? password) (lib.attrValues cfg.settings.database or {}));
+        message = ''
+          A password is defined for at least one database in services.schleuder.settings.database. Defining passwords via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store database passwords in a non-public location.
+        '';
+      }
+    ];
+    users.users.schleuder.isSystemUser = true;
+    users.users.schleuder.group = "schleuder";
+    users.groups.schleuder = {};
+    environment.systemPackages = [
+      pkgs.schleuder-cli
+    ];
+    services.postfix = lib.mkIf cfg.enablePostfix {
+      extraMasterConf = ''
+        schleuder  unix  -       n       n       -       -       pipe
+          flags=DRhu user=schleuder argv=/${pkgs.schleuder}/bin/schleuder work ''${recipient}
+      '';
+      transport = lib.mkIf (cfg.lists != [ ]) (postfixMap (lib.genAttrs cfg.lists (_: "schleuder:")));
+      extraConfig = ''
+        schleuder_destination_recipient_limit = 1
+      '';
+      # review: does this make sense?
+      localRecipients = lib.mkIf (cfg.lists != [ ]) cfg.lists;
+    };
+    systemd.services = let commonServiceConfig = {
+      # We would have liked to use DynamicUser, but since the default
+      # database is SQLite and lives in StateDirectory, and that same
+      # database needs to be readable from the postfix service, this
+      # isn't trivial to do.
+      User = "schleuder";
+      StateDirectory = "schleuder";
+      StateDirectoryMode = "0700";
+    }; in
+      {
+        schleuder-init = {
+          serviceConfig = commonServiceConfig // {
+            ExecStartPre = lib.mkIf (cfg.extraSettingsFile != null) [
+              "+${configScript}"
+            ];
+            ExecStart = [ "${pkgs.schleuder}/bin/schleuder install" ];
+            Type = "oneshot";
+          };
+        };
+        schleuder-api-daemon = {
+          after = [ "local-fs.target" "network.target" "schleuder-init.service" ];
+          wantedBy = [ "multi-user.target" ];
+          requires = [ "schleuder-init.service" ];
+          serviceConfig = commonServiceConfig // {
+            ExecStart = [ "${pkgs.schleuder}/bin/schleuder-api-daemon" ];
+          };
+        };
+        schleuder-weekly-key-maintenance = {
+          after = [ "local-fs.target" "network.target" ];
+          startAt = "weekly";
+          serviceConfig = commonServiceConfig // {
+            ExecStart = [
+              "${pkgs.schleuder}/bin/schleuder refresh_keys"
+              "${pkgs.schleuder}/bin/schleuder check_keys"
+            ];
+          };
+        };
+      };
+
+    environment.etc."schleuder/schleuder.yml" = lib.mkIf (cfg.extraSettingsFile == null) {
+      source = settingsFormat.generate "schleuder.yml" cfg.settings;
+    };
+    environment.etc."schleuder/list-defaults.yml".source = settingsFormat.generate "list-defaults.yml" cfg.listDefaults;
+
+    services.schleuder = {
+      #lists_dir = "/var/lib/schleuder.lists";
+      settings.filters_dir = lib.mkDefault "/var/lib/schleuder/filters";
+      settings.keyword_handlers_dir = lib.mkDefault "/var/lib/schleuder/keyword_handlers";
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/matrix-appservice-discord.nix b/nixos/modules/services/matrix/appservice-discord.nix
index 8a8c7f41e3cbb..8a8c7f41e3cbb 100644
--- a/nixos/modules/services/misc/matrix-appservice-discord.nix
+++ b/nixos/modules/services/matrix/appservice-discord.nix
diff --git a/nixos/modules/services/misc/matrix-appservice-irc.nix b/nixos/modules/services/matrix/appservice-irc.nix
index b041c9c82c56e..b041c9c82c56e 100644
--- a/nixos/modules/services/misc/matrix-appservice-irc.nix
+++ b/nixos/modules/services/matrix/appservice-irc.nix
diff --git a/nixos/modules/services/misc/matrix-conduit.nix b/nixos/modules/services/matrix/conduit.nix
index 108f64de7aa95..108f64de7aa95 100644
--- a/nixos/modules/services/misc/matrix-conduit.nix
+++ b/nixos/modules/services/matrix/conduit.nix
diff --git a/nixos/modules/services/misc/dendrite.nix b/nixos/modules/services/matrix/dendrite.nix
index 54052084b3378..54052084b3378 100644
--- a/nixos/modules/services/misc/dendrite.nix
+++ b/nixos/modules/services/matrix/dendrite.nix
diff --git a/nixos/modules/services/misc/mautrix-facebook.nix b/nixos/modules/services/matrix/mautrix-facebook.nix
index e046c791ac013..e046c791ac013 100644
--- a/nixos/modules/services/misc/mautrix-facebook.nix
+++ b/nixos/modules/services/matrix/mautrix-facebook.nix
diff --git a/nixos/modules/services/misc/mautrix-telegram.nix b/nixos/modules/services/matrix/mautrix-telegram.nix
index 794c4dd9ddcd7..794c4dd9ddcd7 100644
--- a/nixos/modules/services/misc/mautrix-telegram.nix
+++ b/nixos/modules/services/matrix/mautrix-telegram.nix
diff --git a/nixos/modules/services/matrix/matrix-synapse-log_config.yaml b/nixos/modules/services/matrix/synapse-log_config.yaml
index d85bdd1208f9a..d85bdd1208f9a 100644
--- a/nixos/modules/services/matrix/matrix-synapse-log_config.yaml
+++ b/nixos/modules/services/matrix/synapse-log_config.yaml
diff --git a/nixos/modules/services/matrix/matrix-synapse.nix b/nixos/modules/services/matrix/synapse.nix
index 87a977f8e1efb..3d5d10cdf070b 100644
--- a/nixos/modules/services/matrix/matrix-synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -191,12 +191,12 @@ in {
 
       settings = mkOption {
         default = {};
-        description = ''
+        description = mdDoc ''
           The primary synapse configuration. See the
-          <link xlink:href="https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml">sample configuration</link>
+          [sample configuration](https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml)
           for possible values.
 
-          Secrets should be passed in by using the <literal>extraConfigFiles</literal> option.
+          Secrets should be passed in by using the `extraConfigFiles` option.
         '';
         type = with types; submodule {
           freeformType = format.type;
@@ -230,23 +230,23 @@ in {
             registration_shared_secret = mkOption {
               type = types.nullOr types.str;
               default = null;
-              description = ''
+              description = mdDoc ''
                 If set, allows registration by anyone who also has the shared
                 secret, even if registration is otherwise disabled.
 
-                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+                Secrets should be passed in via `extraConfigFiles`!
               '';
             };
 
             macaroon_secret_key = mkOption {
               type = types.nullOr types.str;
               default = null;
-              description = ''
+              description = mdDoc ''
                 Secret key for authentication tokens. If none is specified,
                 the registration_shared_secret is used, if one is given; otherwise,
                 a secret key is derived from the signing key.
 
-                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+                Secrets should be passed in via `extraConfigFiles`!
               '';
             };
 
@@ -285,7 +285,7 @@ in {
 
             log_config = mkOption {
               type = types.path;
-              default = ./matrix-synapse-log_config.yaml;
+              default = ./synapse-log_config.yaml;
               description = ''
                 The file that holds the logging configuration.
               '';
@@ -620,10 +620,10 @@ in {
               example = literalExpression ''
                 config.services.coturn.static-auth-secret
               '';
-              description = ''
+              description = mdDoc ''
                 The shared secret used to compute passwords for the TURN server.
 
-                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+                Secrets should be passed in via `extraConfigFiles`!
               '';
             };
 
@@ -767,7 +767,7 @@ in {
 
   meta = {
     buildDocsInSandbox = false;
-    doc = ./matrix-synapse.xml;
+    doc = ./synapse.xml;
     maintainers = teams.matrix.members;
   };
 
diff --git a/nixos/modules/services/matrix/matrix-synapse.xml b/nixos/modules/services/matrix/synapse.xml
index cf33957d58ece..cf33957d58ece 100644
--- a/nixos/modules/services/matrix/matrix-synapse.xml
+++ b/nixos/modules/services/matrix/synapse.xml
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index 354278fad226b..5a5f488dc565b 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -47,7 +47,7 @@ in
     user-icons = mkOption {
       type = types.nullOr (types.enum [ "gravatar" "identicon" ]);
       default = null;
-      description = "User icons for history view";
+      description = "Enable specific user icons for history view";
     };
 
     emoji = mkOption {
@@ -68,6 +68,12 @@ in
       description = "Disable editing pages";
     };
 
+    local-time = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Use the browser's local timezone instead of the server's for displaying dates.";
+    };
+
     branch = mkOption {
       type = types.str;
       default = "master";
@@ -123,6 +129,7 @@ in
             ${optionalString cfg.emoji "--emoji"} \
             ${optionalString cfg.h1-title "--h1-title"} \
             ${optionalString cfg.no-edit "--no-edit"} \
+            ${optionalString cfg.local-time "--local-time"} \
             ${optionalString (cfg.allowUploads != null) "--allow-uploads ${cfg.allowUploads}"} \
             ${optionalString (cfg.user-icons != null) "--user-icons ${cfg.user-icons}"} \
             ${cfg.stateDir}
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index a4d2d10af70f4..f6f74d12e1063 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -734,7 +734,7 @@ in
             CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy;
             IOSchedulingClass = cfg.daemonIOSchedClass;
             IOSchedulingPriority = cfg.daemonIOSchedPriority;
-            LimitNOFILE = 4096;
+            LimitNOFILE = 1048576;
           };
 
         restartTriggers = [ nixConf ];
diff --git a/nixos/modules/services/misc/persistent-evdev.nix b/nixos/modules/services/misc/persistent-evdev.nix
new file mode 100644
index 0000000000000..401d20010b12a
--- /dev/null
+++ b/nixos/modules/services/misc/persistent-evdev.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.persistent-evdev;
+  settingsFormat = pkgs.formats.json {};
+
+  configFile = settingsFormat.generate "persistent-evdev-config" {
+    cache = "/var/cache/persistent-evdev";
+    devices = lib.mapAttrs (virt: phys: "/dev/input/by-id/${phys}") cfg.devices;
+  };
+in
+{
+  options.services.persistent-evdev = {
+    enable = lib.mkEnableOption "virtual input devices that persist even if the backing device is hotplugged";
+
+    devices = lib.mkOption {
+      default = {};
+      type = with lib.types; attrsOf str;
+      description = ''
+        A set of virtual proxy device labels with backing physical device ids.
+
+        Physical devices should already exist in <filename class="devicefile">/dev/input/by-id/</filename>.
+        Proxy devices will be automatically given a <literal>uinput-</literal> prefix.
+
+        See the <link xlink:href="https://github.com/aiberia/persistent-evdev#example-usage-with-libvirt">
+        project page</link> for example configuration of virtual devices with libvirt
+        and remember to add <literal>uinput-*</literal> devices to the qemu
+        <literal>cgroup_device_acl</literal> list (see <xref linkend="opt-virtualisation.libvirtd.qemu.verbatimConfig"/>).
+      '';
+      example = lib.literalExpression ''
+        {
+          persist-mouse0 = "usb-Logitech_G403_Prodigy_Gaming_Mouse_078738533531-event-if01";
+          persist-mouse1 = "usb-Logitech_G403_Prodigy_Gaming_Mouse_078738533531-event-mouse";
+          persist-mouse2 = "usb-Logitech_G403_Prodigy_Gaming_Mouse_078738533531-if01-event-kbd";
+          persist-keyboard0 = "usb-Microsoft_Natural®_Ergonomic_Keyboard_4000-event-kbd";
+          persist-keyboard1 = "usb-Microsoft_Natural®_Ergonomic_Keyboard_4000-if01-event-kbd";
+        }
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    systemd.services.persistent-evdev = {
+      documentation = [ "https://github.com/aiberia/persistent-evdev/blob/master/README.md" ];
+      description = "Persistent evdev proxy";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Restart = "on-failure";
+        ExecStart = "${pkgs.persistent-evdev}/bin/persistent-evdev.py ${configFile}";
+        CacheDirectory = "persistent-evdev";
+      };
+    };
+
+    services.udev.packages = [ pkgs.persistent-evdev ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ lodi ];
+}
diff --git a/nixos/modules/services/misc/sourcehut/builds.nix b/nixos/modules/services/misc/sourcehut/builds.nix
deleted file mode 100644
index 685a132d35070..0000000000000
--- a/nixos/modules/services/misc/sourcehut/builds.nix
+++ /dev/null
@@ -1,236 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  scfg = cfg.builds;
-  rcfg = config.services.redis;
-  iniKey = "builds.sr.ht";
-
-  drv = pkgs.sourcehut.buildsrht;
-in
-{
-  options.services.sourcehut.builds = {
-    user = mkOption {
-      type = types.str;
-      default = "buildsrht";
-      description = ''
-        User for builds.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5002;
-      description = ''
-        Port on which the "builds" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "builds.sr.ht";
-      description = ''
-        PostgreSQL database name for builds.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/buildsrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/buildsrht"'';
-      description = ''
-        State path for builds.sr.ht.
-      '';
-    };
-
-    enableWorker = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Run workers for builds.sr.ht.
-      '';
-    };
-
-    images = mkOption {
-      type = types.attrsOf (types.attrsOf (types.attrsOf types.package));
-      default = { };
-      example = lib.literalExpression ''(let
-          # Pinning unstable to allow usage with flakes and limit rebuilds.
-          pkgs_unstable = builtins.fetchGit {
-              url = "https://github.com/NixOS/nixpkgs";
-              rev = "ff96a0fa5635770390b184ae74debea75c3fd534";
-              ref = "nixos-unstable";
-          };
-          image_from_nixpkgs = pkgs_unstable: (import ("''${pkgs.sourcehut.buildsrht}/lib/images/nixos/image.nix") {
-            pkgs = (import pkgs_unstable {});
-          });
-        in
-        {
-          nixos.unstable.x86_64 = image_from_nixpkgs pkgs_unstable;
-        }
-      )'';
-      description = ''
-        Images for builds.sr.ht. Each package should be distro.release.arch and point to a /nix/store/package/root.img.qcow2.
-      '';
-    };
-
-  };
-
-  config = with scfg; let
-    image_dirs = lib.lists.flatten (
-      lib.attrsets.mapAttrsToList
-        (distro: revs:
-          lib.attrsets.mapAttrsToList
-            (rev: archs:
-              lib.attrsets.mapAttrsToList
-                (arch: image:
-                  pkgs.runCommand "buildsrht-images" { } ''
-                    mkdir -p $out/${distro}/${rev}/${arch}
-                    ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
-                  '')
-                archs)
-            revs)
-        scfg.images);
-    image_dir_pre = pkgs.symlinkJoin {
-      name = "builds.sr.ht-worker-images-pre";
-      paths = image_dirs ++ [
-        "${pkgs.sourcehut.buildsrht}/lib/images"
-      ];
-    };
-    image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
-      mkdir -p $out/images
-      cp -Lr ${image_dir_pre}/* $out/images
-    '';
-  in
-  lib.mkIf (cfg.enable && elem "builds" cfg.services) {
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          extraGroups = lib.optionals cfg.builds.enableWorker [ "docker" ];
-          description = "builds.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0755 ${user} ${user} -"
-      ] ++ (lib.optionals cfg.builds.enableWorker
-        [ "d ${statePath}/logs 0775 ${user} ${user} - -" ]
-      );
-
-      services = {
-        buildsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey
-          {
-            after = [ "postgresql.service" "network.target" ];
-            requires = [ "postgresql.service" ];
-            wantedBy = [ "multi-user.target" ];
-
-            description = "builds.sr.ht website service";
-
-            serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-
-            # Hack to bypass this hack: https://git.sr.ht/~sircmpwn/core.sr.ht/tree/master/item/srht-update-profiles#L6
-          } // { preStart = " "; };
-
-        buildsrht-worker = {
-          enable = scfg.enableWorker;
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-          partOf = [ "buildsrht.service" ];
-          description = "builds.sr.ht worker service";
-          path = [ pkgs.openssh pkgs.docker ];
-          preStart = let qemuPackage = pkgs.qemu_kvm;
-          in ''
-            if [[ "$(docker images -q qemu:latest 2> /dev/null)" == "" || "$(cat ${statePath}/docker-image-qemu 2> /dev/null || true)" != "${qemuPackage.version}" ]]; then
-              # Create and import qemu:latest image for docker
-              ${
-                pkgs.dockerTools.streamLayeredImage {
-                  name = "qemu";
-                  tag = "latest";
-                  contents = [ qemuPackage ];
-                }
-              } | docker load
-              # Mark down current package version
-              printf "%s" "${qemuPackage.version}" > ${statePath}/docker-image-qemu
-            fi
-          '';
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Group = "nginx";
-            Restart = "always";
-          };
-          serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
-        };
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL builds.sr.ht is being served at (protocol://domain)
-      "builds.sr.ht".origin = mkDefault "http://builds.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "builds.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "builds.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "builds.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "builds.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # builds.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "builds.sr.ht".oauth-client-id = mkDefault null;
-      "builds.sr.ht".oauth-client-secret = mkDefault null;
-      # The redis connection used for the celery worker
-      "builds.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/3";
-      # The shell used for ssh
-      "builds.sr.ht".shell = mkDefault "runner-shell";
-      # Register the builds.sr.ht dispatcher
-      "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.buildsrht}/bin/buildsrht-keys"} = mkDefault "${user}:${user}";
-
-      # Location for build logs, images, and control command
-    } // lib.attrsets.optionalAttrs scfg.enableWorker {
-      # Default worker stores logs that are accessible via this address:port
-      "builds.sr.ht::worker".name = mkDefault "127.0.0.1:5020";
-      "builds.sr.ht::worker".buildlogs = mkDefault "${scfg.statePath}/logs";
-      "builds.sr.ht::worker".images = mkDefault "${image_dir}/images";
-      "builds.sr.ht::worker".controlcmd = mkDefault "${image_dir}/images/control";
-      "builds.sr.ht::worker".timeout = mkDefault "3m";
-    };
-
-    services.nginx.virtualHosts."logs.${cfg.originBase}" =
-      if scfg.enableWorker then {
-        listen = with builtins; let address = split ":" cfg.settings."builds.sr.ht::worker".name;
-        in [{ addr = elemAt address 0; port = lib.toInt (elemAt address 2); }];
-        locations."/logs".root = "${scfg.statePath}";
-      } else { };
-
-    services.nginx.virtualHosts."builds.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.buildsrht}/${pkgs.sourcehut.python.sitePackages}/buildsrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
index 5a6d011a729a3..3ff2837900eca 100644
--- a/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixos/modules/services/misc/sourcehut/default.nix
@@ -83,7 +83,7 @@ let
   python = pkgs.sourcehut.python.withPackages (ps: with ps; [
     gunicorn
     eventlet
-    # For monitoring Celery: sudo -u listssrht celery --app listssrht.process -b redis+socket:///run/redis-sourcehut/redis.sock?virtual_host=5 flower
+    # For monitoring Celery: sudo -u listssrht celery --app listssrht.process -b redis+socket:///run/redis-sourcehut/redis.sock?virtual_host=1 flower
     flower
     # Sourcehut services
     srht
@@ -238,20 +238,32 @@ in
           };
           smtp-user = mkOptionNullOrStr "Outgoing SMTP user.";
           smtp-password = mkOptionNullOrStr "Outgoing SMTP password.";
-          smtp-from = mkOptionNullOrStr "Outgoing SMTP FROM.";
+          smtp-from = mkOption {
+            type = types.str;
+            description = "Outgoing SMTP FROM.";
+          };
           error-to = mkOptionNullOrStr "Address receiving application exceptions";
           error-from = mkOptionNullOrStr "Address sending application exceptions";
-          pgp-privkey = mkOptionNullOrStr ''
-            An absolute file path (which should be outside the Nix-store)
-            to an OpenPGP private key.
+          pgp-privkey = mkOption {
+            type = types.str;
+            description = ''
+              An absolute file path (which should be outside the Nix-store)
+              to an OpenPGP private key.
 
-            Your PGP key information (DO NOT mix up pub and priv here)
-            You must remove the password from your secret key, if present.
-            You can do this with <code>gpg --edit-key [key-id]</code>,
-            then use the <code>passwd</code> command and do not enter a new password.
-          '';
-          pgp-pubkey = mkOptionNullOrStr "OpenPGP public key.";
-          pgp-key-id = mkOptionNullOrStr "OpenPGP key identifier.";
+              Your PGP key information (DO NOT mix up pub and priv here)
+              You must remove the password from your secret key, if present.
+              You can do this with <code>gpg --edit-key [key-id]</code>,
+              then use the <code>passwd</code> command and do not enter a new password.
+            '';
+          };
+          pgp-pubkey = mkOption {
+            type = with types; either path str;
+            description = "OpenPGP public key.";
+          };
+          pgp-key-id = mkOption {
+            type = types.str;
+            description = "OpenPGP key identifier.";
+          };
         };
         options.objects = {
           s3-upstream = mkOption {
@@ -905,6 +917,11 @@ in
       inherit configIniOfService;
       srvsrht = "buildsrht";
       port = 5002;
+      extraServices.buildsrht-api = {
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = "5s";
+        serviceConfig.ExecStart = "${pkgs.sourcehut.buildsrht}/bin/buildsrht-api -b ${cfg.listenAddress}:${toString (cfg.builds.port + 100)}";
+      };
       # TODO: a celery worker on the master and worker are apparently needed
       extraServices.buildsrht-worker = let
         qemuPackage = pkgs.qemu_kvm;
@@ -928,13 +945,13 @@ in
           fi
         '';
         serviceConfig = {
-          ExecStart = "${pkgs.sourcehut.buildsrht}/bin/builds.sr.ht-worker";
+          ExecStart = "${pkgs.sourcehut.buildsrht}/bin/buildsrht-worker";
           BindPaths = [ cfg.settings."builds.sr.ht::worker".buildlogs ];
           LogsDirectory = [ "sourcehut/${serviceName}" ];
           RuntimeDirectory = [ "sourcehut/${serviceName}/subdir" ];
           StateDirectory = [ "sourcehut/${serviceName}" ];
           TimeoutStartSec = "1800s";
-          # builds.sr.ht-worker looks up ../config.ini
+          # buildsrht-worker looks up ../config.ini
           WorkingDirectory = "-"+"/run/sourcehut/${serviceName}/subdir";
         };
       };
@@ -952,12 +969,12 @@ in
           ) cfg.builds.images
         );
         image_dir_pre = pkgs.symlinkJoin {
-          name = "builds.sr.ht-worker-images-pre";
+          name = "buildsrht-worker-images-pre";
           paths = image_dirs;
             # FIXME: not working, apparently because ubuntu/latest is a broken link
             # ++ [ "${pkgs.sourcehut.buildsrht}/lib/images" ];
         };
-        image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
+        image_dir = pkgs.runCommand "buildsrht-worker-images" { } ''
           mkdir -p $out/images
           cp -Lr ${image_dir_pre}/* $out/images
         '';
@@ -1081,6 +1098,11 @@ in
           };
         })
       ];
+      extraServices.gitsrht-api = {
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = "5s";
+        serviceConfig.ExecStart = "${pkgs.sourcehut.gitsrht}/bin/gitsrht-api -b ${cfg.listenAddress}:${toString (cfg.git.port + 100)}";
+      };
       extraServices.gitsrht-fcgiwrap = mkIf cfg.nginx.enable {
         serviceConfig = {
           # Socket is passed by gitsrht-fcgiwrap.socket
@@ -1124,6 +1146,11 @@ in
         timerConfig.OnCalendar = ["daily"];
         timerConfig.AccuracySec = "1h";
       };
+      extraServices.hgsrht-api = {
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = "5s";
+        serviceConfig.ExecStart = "${pkgs.sourcehut.hgsrht}/bin/hgsrht-api -b ${cfg.listenAddress}:${toString (cfg.hg.port + 100)}";
+      };
       extraConfig = mkMerge [
         {
           users.users.${cfg.hg.user}.shell = pkgs.bash;
@@ -1184,6 +1211,11 @@ in
       inherit configIniOfService;
       port = 5006;
       webhooks = true;
+      extraServices.listssrht-api = {
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = "5s";
+        serviceConfig.ExecStart = "${pkgs.sourcehut.listssrht}/bin/listssrht-api -b ${cfg.listenAddress}:${toString (cfg.lists.port + 100)}";
+      };
       # Receive the mail from Postfix and enqueue them into Redis and PostgreSQL
       extraServices.listssrht-lmtp = {
         wants = [ "postfix.service" ];
@@ -1232,9 +1264,13 @@ in
       inherit configIniOfService;
       port = 5000;
       webhooks = true;
+      extraTimers.metasrht-daily.timerConfig = {
+        OnCalendar = ["daily"];
+        AccuracySec = "1h";
+      };
       extraServices.metasrht-api = {
         serviceConfig.Restart = "always";
-        serviceConfig.RestartSec = "2s";
+        serviceConfig.RestartSec = "5s";
         preStart = "set -x\n" + concatStringsSep "\n\n" (attrValues (mapAttrs (k: s:
           let srvMatch = builtins.match "^([a-z]*)\\.sr\\.ht$" k;
               srv = head srvMatch;
@@ -1248,10 +1284,6 @@ in
           ) cfg.settings));
         serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b ${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
       };
-      extraTimers.metasrht-daily.timerConfig = {
-        OnCalendar = ["daily"];
-        AccuracySec = "1h";
-      };
       extraConfig = mkMerge [
         {
           assertions = [
@@ -1348,6 +1380,11 @@ in
       inherit configIniOfService;
       port = 5003;
       webhooks = true;
+      extraServices.todosrht-api = {
+        serviceConfig.Restart = "always";
+        serviceConfig.RestartSec = "5s";
+        serviceConfig.ExecStart = "${pkgs.sourcehut.todosrht}/bin/todosrht-api -b ${cfg.listenAddress}:${toString (cfg.todo.port + 100)}";
+      };
       extraServices.todosrht-lmtp = {
         wants = [ "postfix.service" ];
         unitConfig.JoinsNamespaceOf = optional cfg.postfix.enable "postfix.service";
diff --git a/nixos/modules/services/misc/sourcehut/dispatch.nix b/nixos/modules/services/misc/sourcehut/dispatch.nix
deleted file mode 100644
index 292a51d3e1c5d..0000000000000
--- a/nixos/modules/services/misc/sourcehut/dispatch.nix
+++ /dev/null
@@ -1,127 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.dispatch;
-  iniKey = "dispatch.sr.ht";
-
-  drv = pkgs.sourcehut.dispatchsrht;
-in
-{
-  options.services.sourcehut.dispatch = {
-    user = mkOption {
-      type = types.str;
-      default = "dispatchsrht";
-      description = ''
-        User for dispatch.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5005;
-      description = ''
-        Port on which the "dispatch" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "dispatch.sr.ht";
-      description = ''
-        PostgreSQL database name for dispatch.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/dispatchsrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/dispatchsrht"'';
-      description = ''
-        State path for dispatch.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "dispatch" cfg.services) {
-
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          description = "dispatch.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services.dispatchsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-        after = [ "postgresql.service" "network.target" ];
-        requires = [ "postgresql.service" ];
-        wantedBy = [ "multi-user.target" ];
-
-        description = "dispatch.sr.ht website service";
-
-        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL dispatch.sr.ht is being served at (protocol://domain)
-      "dispatch.sr.ht".origin = mkDefault "http://dispatch.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "dispatch.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "dispatch.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "dispatch.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "dispatch.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # dispatch.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "dispatch.sr.ht".oauth-client-id = mkDefault null;
-      "dispatch.sr.ht".oauth-client-secret = mkDefault null;
-
-      # Github Integration
-      "dispatch.sr.ht::github".oauth-client-id = mkDefault null;
-      "dispatch.sr.ht::github".oauth-client-secret = mkDefault null;
-
-      # Gitlab Integration
-      "dispatch.sr.ht::gitlab".enabled = mkDefault null;
-      "dispatch.sr.ht::gitlab".canonical-upstream = mkDefault "gitlab.com";
-      "dispatch.sr.ht::gitlab".repo-cache = mkDefault "./repo-cache";
-      # "dispatch.sr.ht::gitlab"."gitlab.com" = mkDefault "GitLab:application id:secret";
-    };
-
-    services.nginx.virtualHosts."dispatch.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.dispatchsrht}/${pkgs.sourcehut.python.sitePackages}/dispatchsrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/git.nix b/nixos/modules/services/misc/sourcehut/git.nix
deleted file mode 100644
index ff110905d1841..0000000000000
--- a/nixos/modules/services/misc/sourcehut/git.nix
+++ /dev/null
@@ -1,217 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  scfg = cfg.git;
-  iniKey = "git.sr.ht";
-
-  rcfg = config.services.redis;
-  drv = pkgs.sourcehut.gitsrht;
-in
-{
-  options.services.sourcehut.git = {
-    user = mkOption {
-      type = types.str;
-      visible = false;
-      internal = true;
-      readOnly = true;
-      default = "git";
-      description = ''
-        User for git.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5001;
-      description = ''
-        Port on which the "git" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "git.sr.ht";
-      description = ''
-        PostgreSQL database name for git.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/gitsrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/gitsrht"'';
-      description = ''
-        State path for git.sr.ht.
-      '';
-    };
-
-    package = mkOption {
-      type = types.package;
-      default = pkgs.git;
-      defaultText = literalExpression "pkgs.git";
-      example = literalExpression "pkgs.gitFull";
-      description = ''
-        Git package for git.sr.ht. This can help silence collisions.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "git" cfg.services) {
-    # sshd refuses to run with `Unsafe AuthorizedKeysCommand ... bad ownership or modes for directory /nix/store`
-    environment.etc."ssh/gitsrht-dispatch" = {
-      mode = "0755";
-      text = ''
-        #! ${pkgs.stdenv.shell}
-        ${cfg.python}/bin/gitsrht-dispatch "$@"
-      '';
-    };
-
-    # Needs this in the $PATH when sshing into the server
-    environment.systemPackages = [ cfg.git.package ];
-
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          # https://stackoverflow.com/questions/22314298/git-push-results-in-fatal-protocol-error-bad-line-length-character-this
-          # Probably could use gitsrht-shell if output is restricted to just parameters...
-          shell = pkgs.bash;
-          description = "git.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services = {
-      cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/gitsrht-periodic" ];
-      fcgiwrap.enable = true;
-
-      openssh.authorizedKeysCommand = ''/etc/ssh/gitsrht-dispatch "%u" "%h" "%t" "%k"'';
-      openssh.authorizedKeysCommandUser = "root";
-      openssh.extraConfig = ''
-        PermitUserEnvironment SRHT_*
-      '';
-
-      postgresql = {
-        authentication = ''
-          local ${database} ${user} trust
-        '';
-        ensureDatabases = [ database ];
-        ensureUsers = [
-          {
-            name = user;
-            ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-          }
-        ];
-      };
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        # /var/log is owned by root
-        "f /var/log/git-srht-shell 0644 ${user} ${user} -"
-
-        "d ${statePath} 0750 ${user} ${user} -"
-        "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
-      ];
-
-      services = {
-        gitsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-          after = [ "redis.service" "postgresql.service" "network.target" ];
-          requires = [ "redis.service" "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          # Needs internally to create repos at the very least
-          path = [ pkgs.git ];
-          description = "git.sr.ht website service";
-
-          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-        };
-
-        gitsrht-webhooks = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "git.sr.ht webhooks service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-          };
-
-          serviceConfig.ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
-        };
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL git.sr.ht is being served at (protocol://domain)
-      "git.sr.ht".origin = mkDefault "http://git.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "git.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "git.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "git.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "git.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # The redis connection used for the webhooks worker
-      "git.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
-
-      # A post-update script which is installed in every git repo.
-      "git.sr.ht".post-update-script = mkDefault "${pkgs.sourcehut.gitsrht}/bin/gitsrht-update-hook";
-
-      # git.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "git.sr.ht".oauth-client-id = mkDefault null;
-      "git.sr.ht".oauth-client-secret = mkDefault null;
-      # Path to git repositories on disk
-      "git.sr.ht".repos = mkDefault "/var/lib/git";
-
-      "git.sr.ht".outgoing-domain = mkDefault "http://git.${cfg.originBase}";
-
-      # The authorized keys hook uses this to dispatch to various handlers
-      # The format is a program to exec into as the key, and the user to match as the
-      # value. When someone tries to log in as this user, this program is executed
-      # and is expected to omit an AuthorizedKeys file.
-      #
-      # Discard of the string context is in order to allow derivation-derived strings.
-      # This is safe if the relevant package is installed which will be the case if the setting is utilized.
-      "git.sr.ht::dispatch".${builtins.unsafeDiscardStringContext "${pkgs.sourcehut.gitsrht}/bin/gitsrht-keys"} = mkDefault "${user}:${user}";
-    };
-
-    services.nginx.virtualHosts."git.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.gitsrht}/${pkgs.sourcehut.python.sitePackages}/gitsrht";
-      extraConfig = ''
-            location = /authorize {
-            proxy_pass http://${cfg.address}:${toString port};
-            proxy_pass_request_body off;
-            proxy_set_header Content-Length "";
-            proxy_set_header X-Original-URI $request_uri;
-        }
-            location ~ ^/([^/]+)/([^/]+)/(HEAD|info/refs|objects/info/.*|git-upload-pack).*$ {
-                auth_request /authorize;
-                root /var/lib/git;
-                fastcgi_pass unix:/run/fcgiwrap.sock;
-                fastcgi_param SCRIPT_FILENAME ${pkgs.git}/bin/git-http-backend;
-                fastcgi_param PATH_INFO $uri;
-                fastcgi_param GIT_PROJECT_ROOT $document_root;
-                fastcgi_read_timeout 500s;
-                include ${config.services.nginx.package}/conf/fastcgi_params;
-                gzip off;
-            }
-      '';
-
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/hg.nix b/nixos/modules/services/misc/sourcehut/hg.nix
deleted file mode 100644
index 6ba1df8b6ddb7..0000000000000
--- a/nixos/modules/services/misc/sourcehut/hg.nix
+++ /dev/null
@@ -1,175 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  scfg = cfg.hg;
-  iniKey = "hg.sr.ht";
-
-  rcfg = config.services.redis;
-  drv = pkgs.sourcehut.hgsrht;
-in
-{
-  options.services.sourcehut.hg = {
-    user = mkOption {
-      type = types.str;
-      internal = true;
-      readOnly = true;
-      default = "hg";
-      description = ''
-        User for hg.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5010;
-      description = ''
-        Port on which the "hg" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "hg.sr.ht";
-      description = ''
-        PostgreSQL database name for hg.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/hgsrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/hgsrht"'';
-      description = ''
-        State path for hg.sr.ht.
-      '';
-    };
-
-    cloneBundles = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Generate clonebundles (which require more disk space but dramatically speed up cloning large repositories).
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "hg" cfg.services) {
-    # In case it ever comes into being
-    environment.etc."ssh/hgsrht-dispatch" = {
-      mode = "0755";
-      text = ''
-        #! ${pkgs.stdenv.shell}
-        ${cfg.python}/bin/gitsrht-dispatch $@
-      '';
-    };
-
-    environment.systemPackages = [ pkgs.mercurial ];
-
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          # Assuming hg.sr.ht needs this too
-          shell = pkgs.bash;
-          description = "hg.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services = {
-      cron.systemCronJobs = [ "*/20 * * * * ${cfg.python}/bin/hgsrht-periodic" ]
-        ++ optional cloneBundles "0 * * * * ${cfg.python}/bin/hgsrht-clonebundles";
-
-      openssh.authorizedKeysCommand = ''/etc/ssh/hgsrht-dispatch "%u" "%h" "%t" "%k"'';
-      openssh.authorizedKeysCommandUser = "root";
-      openssh.extraConfig = ''
-        PermitUserEnvironment SRHT_*
-      '';
-
-      postgresql = {
-        authentication = ''
-          local ${database} ${user} trust
-        '';
-        ensureDatabases = [ database ];
-        ensureUsers = [
-          {
-            name = user;
-            ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-          }
-        ];
-      };
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        # /var/log is owned by root
-        "f /var/log/hg-srht-shell 0644 ${user} ${user} -"
-
-        "d ${statePath} 0750 ${user} ${user} -"
-        "d ${cfg.settings."${iniKey}".repos} 2755 ${user} ${user} -"
-      ];
-
-      services.hgsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-        after = [ "redis.service" "postgresql.service" "network.target" ];
-        requires = [ "redis.service" "postgresql.service" ];
-        wantedBy = [ "multi-user.target" ];
-
-        path = [ pkgs.mercurial ];
-        description = "hg.sr.ht website service";
-
-        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL hg.sr.ht is being served at (protocol://domain)
-      "hg.sr.ht".origin = mkDefault "http://hg.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "hg.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "hg.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "hg.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # The redis connection used for the webhooks worker
-      "hg.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
-      # A post-update script which is installed in every mercurial repo.
-      "hg.sr.ht".changegroup-script = mkDefault "${cfg.python}/bin/hgsrht-hook-changegroup";
-      # hg.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "hg.sr.ht".oauth-client-id = mkDefault null;
-      "hg.sr.ht".oauth-client-secret = mkDefault null;
-      # Path to mercurial repositories on disk
-      "hg.sr.ht".repos = mkDefault "/var/lib/hg";
-      # Path to the srht mercurial extension
-      # (defaults to where the hgsrht code is)
-      # "hg.sr.ht".srhtext = mkDefault null;
-      # .hg/store size (in MB) past which the nightly job generates clone bundles.
-      # "hg.sr.ht".clone_bundle_threshold = mkDefault 50;
-      # Path to hg-ssh (if not in $PATH)
-      # "hg.sr.ht".hg_ssh = mkDefault /path/to/hg-ssh;
-
-      # The authorized keys hook uses this to dispatch to various handlers
-      # The format is a program to exec into as the key, and the user to match as the
-      # value. When someone tries to log in as this user, this program is executed
-      # and is expected to omit an AuthorizedKeys file.
-      #
-      # Uncomment the relevant lines to enable the various sr.ht dispatchers.
-      "hg.sr.ht::dispatch"."/run/current-system/sw/bin/hgsrht-keys" = mkDefault "${user}:${user}";
-    };
-
-    # TODO: requires testing and addition of hg-specific requirements
-    services.nginx.virtualHosts."hg.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.hgsrht}/${pkgs.sourcehut.python.sitePackages}/hgsrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/hub.nix b/nixos/modules/services/misc/sourcehut/hub.nix
deleted file mode 100644
index 7d137a765056c..0000000000000
--- a/nixos/modules/services/misc/sourcehut/hub.nix
+++ /dev/null
@@ -1,120 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.hub;
-  iniKey = "hub.sr.ht";
-
-  drv = pkgs.sourcehut.hubsrht;
-in
-{
-  options.services.sourcehut.hub = {
-    user = mkOption {
-      type = types.str;
-      default = "hubsrht";
-      description = ''
-        User for hub.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5014;
-      description = ''
-        Port on which the "hub" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "hub.sr.ht";
-      description = ''
-        PostgreSQL database name for hub.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/hubsrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/hubsrht"'';
-      description = ''
-        State path for hub.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "hub" cfg.services) {
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          description = "hub.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services.hubsrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-        after = [ "postgresql.service" "network.target" ];
-        requires = [ "postgresql.service" ];
-        wantedBy = [ "multi-user.target" ];
-
-        description = "hub.sr.ht website service";
-
-        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL hub.sr.ht is being served at (protocol://domain)
-      "hub.sr.ht".origin = mkDefault "http://hub.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "hub.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "hub.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "hub.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "hub.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # hub.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "hub.sr.ht".oauth-client-id = mkDefault null;
-      "hub.sr.ht".oauth-client-secret = mkDefault null;
-    };
-
-    services.nginx.virtualHosts."${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.hubsrht}/${pkgs.sourcehut.python.sitePackages}/hubsrht";
-    };
-    services.nginx.virtualHosts."hub.${cfg.originBase}" = {
-      globalRedirect = "${cfg.originBase}";
-      forceSSL = true;
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/lists.nix b/nixos/modules/services/misc/sourcehut/lists.nix
deleted file mode 100644
index 76f155caa05bc..0000000000000
--- a/nixos/modules/services/misc/sourcehut/lists.nix
+++ /dev/null
@@ -1,187 +0,0 @@
-# Email setup is fairly involved, useful references:
-# https://drewdevault.com/2018/08/05/Local-mail-server.html
-
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.lists;
-  iniKey = "lists.sr.ht";
-
-  rcfg = config.services.redis;
-  drv = pkgs.sourcehut.listssrht;
-in
-{
-  options.services.sourcehut.lists = {
-    user = mkOption {
-      type = types.str;
-      default = "listssrht";
-      description = ''
-        User for lists.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5006;
-      description = ''
-        Port on which the "lists" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "lists.sr.ht";
-      description = ''
-        PostgreSQL database name for lists.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/listssrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/listssrht"'';
-      description = ''
-        State path for lists.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "lists" cfg.services) {
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          extraGroups = [ "postfix" ];
-          description = "lists.sr.ht user";
-        };
-      };
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services = {
-        listssrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "lists.sr.ht website service";
-
-          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-        };
-
-        listssrht-process = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "lists.sr.ht process service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.process worker --loglevel=info";
-          };
-        };
-
-        listssrht-lmtp = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "lists.sr.ht process service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-            ExecStart = "${cfg.python}/bin/listssrht-lmtp";
-          };
-        };
-
-
-        listssrht-webhooks = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "lists.sr.ht webhooks service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
-          };
-        };
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL lists.sr.ht is being served at (protocol://domain)
-      "lists.sr.ht".origin = mkDefault "http://lists.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "lists.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "lists.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "lists.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "lists.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # lists.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "lists.sr.ht".oauth-client-id = mkDefault null;
-      "lists.sr.ht".oauth-client-secret = mkDefault null;
-      # Outgoing email for notifications generated by users
-      "lists.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
-      # The redis connection used for the webhooks worker
-      "lists.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/2";
-      # The redis connection used for the celery worker
-      "lists.sr.ht".redis = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/4";
-      # Network-key
-      "lists.sr.ht".network-key = mkDefault null;
-      # Allow creation
-      "lists.sr.ht".allow-new-lists = mkDefault "no";
-      # Posting Domain
-      "lists.sr.ht".posting-domain = mkDefault "lists.${cfg.originBase}";
-
-      # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
-      # Alternatively, specify IP:PORT and an SMTP server will be run instead.
-      "lists.sr.ht::worker".sock = mkDefault "/tmp/lists.sr.ht-lmtp.sock";
-      # The lmtp daemon will make the unix socket group-read/write for users in this
-      # group.
-      "lists.sr.ht::worker".sock-group = mkDefault "postfix";
-      "lists.sr.ht::worker".reject-url = mkDefault "https://man.sr.ht/lists.sr.ht/etiquette.md";
-      "lists.sr.ht::worker".reject-mimetypes = mkDefault "text/html";
-
-    };
-
-    services.nginx.virtualHosts."lists.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.listssrht}/${pkgs.sourcehut.python.sitePackages}/listssrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/man.nix b/nixos/modules/services/misc/sourcehut/man.nix
deleted file mode 100644
index 8ca271c32ee3a..0000000000000
--- a/nixos/modules/services/misc/sourcehut/man.nix
+++ /dev/null
@@ -1,124 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.man;
-  iniKey = "man.sr.ht";
-
-  drv = pkgs.sourcehut.mansrht;
-in
-{
-  options.services.sourcehut.man = {
-    user = mkOption {
-      type = types.str;
-      default = "mansrht";
-      description = ''
-        User for man.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5004;
-      description = ''
-        Port on which the "man" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "man.sr.ht";
-      description = ''
-        PostgreSQL database name for man.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/mansrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/mansrht"'';
-      description = ''
-        State path for man.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "man" cfg.services) {
-    assertions =
-      [
-        {
-          assertion = hasAttrByPath [ "git.sr.ht" "oauth-client-id" ] cfgIni;
-          message = "man.sr.ht needs access to git.sr.ht.";
-        }
-      ];
-
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          description = "man.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services.mansrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-        after = [ "postgresql.service" "network.target" ];
-        requires = [ "postgresql.service" ];
-        wantedBy = [ "multi-user.target" ];
-
-        description = "man.sr.ht website service";
-
-        serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL man.sr.ht is being served at (protocol://domain)
-      "man.sr.ht".origin = mkDefault "http://man.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "man.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "man.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "man.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "man.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # man.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "man.sr.ht".oauth-client-id = mkDefault null;
-      "man.sr.ht".oauth-client-secret = mkDefault null;
-    };
-
-    services.nginx.virtualHosts."man.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.mansrht}/${pkgs.sourcehut.python.sitePackages}/mansrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/meta.nix b/nixos/modules/services/misc/sourcehut/meta.nix
deleted file mode 100644
index 33e4f2332b53c..0000000000000
--- a/nixos/modules/services/misc/sourcehut/meta.nix
+++ /dev/null
@@ -1,213 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.meta;
-  iniKey = "meta.sr.ht";
-
-  rcfg = config.services.redis;
-  drv = pkgs.sourcehut.metasrht;
-in
-{
-  options.services.sourcehut.meta = {
-    user = mkOption {
-      type = types.str;
-      default = "metasrht";
-      description = ''
-        User for meta.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5000;
-      description = ''
-        Port on which the "meta" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "meta.sr.ht";
-      description = ''
-        PostgreSQL database name for meta.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/metasrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/metasrht"'';
-      description = ''
-        State path for meta.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "meta" cfg.services) {
-    assertions =
-      [
-        {
-          assertion = with cfgIni."meta.sr.ht::billing"; enabled == "yes" -> (stripe-public-key != null && stripe-secret-key != null);
-          message = "If meta.sr.ht::billing is enabled, the keys should be defined.";
-        }
-      ];
-
-    users = {
-      users = {
-        ${user} = {
-          isSystemUser = true;
-          group = user;
-          description = "meta.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.cron.systemCronJobs = [ "0 0 * * * ${cfg.python}/bin/metasrht-daily" ];
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services = {
-        metasrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "meta.sr.ht website service";
-
-          preStart = ''
-            # Configure client(s) as "preauthorized"
-            ${concatMapStringsSep "\n\n"
-              (attr: ''
-                if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
-                  # Configure ${attr}'s OAuth client as "preauthorized"
-                  psql ${database} \
-                    -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
-
-                  printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
-                fi
-              '')
-              (builtins.attrNames (filterAttrs
-                (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
-                cfg.settings))}
-          '';
-
-          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-        };
-
-        metasrht-api = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "meta.sr.ht api service";
-
-          preStart = ''
-            # Configure client(s) as "preauthorized"
-            ${concatMapStringsSep "\n\n"
-              (attr: ''
-                if ! test -e "${statePath}/${attr}.oauth" || [ "$(cat ${statePath}/${attr}.oauth)" != "${cfgIni."${attr}".oauth-client-id}" ]; then
-                  # Configure ${attr}'s OAuth client as "preauthorized"
-                  psql ${database} \
-                    -c "UPDATE oauthclient SET preauthorized = true WHERE client_id = '${cfgIni."${attr}".oauth-client-id}'"
-
-                  printf "%s" "${cfgIni."${attr}".oauth-client-id}" > "${statePath}/${attr}.oauth"
-                fi
-              '')
-              (builtins.attrNames (filterAttrs
-                (k: v: !(hasInfix "::" k) && builtins.hasAttr "oauth-client-id" v && v.oauth-client-id != null)
-                cfg.settings))}
-          '';
-
-          serviceConfig.ExecStart = "${pkgs.sourcehut.metasrht}/bin/metasrht-api -b :${toString (port + 100)}";
-        };
-
-        metasrht-webhooks = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "meta.sr.ht webhooks service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
-          };
-
-        };
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL meta.sr.ht is being served at (protocol://domain)
-      "meta.sr.ht".origin = mkDefault "https://meta.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "meta.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "meta.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "meta.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "meta.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # If "yes", the user will be sent the stock sourcehut welcome emails after
-      # signup (requires cron to be configured properly). These are specific to the
-      # sr.ht instance so you probably want to patch these before enabling this.
-      "meta.sr.ht".welcome-emails = mkDefault "no";
-
-      # The redis connection used for the webhooks worker
-      "meta.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/6";
-
-      # If "no", public registration will not be permitted.
-      "meta.sr.ht::settings".registration = mkDefault "no";
-      # Where to redirect new users upon registration
-      "meta.sr.ht::settings".onboarding-redirect = mkDefault "https://meta.${cfg.originBase}";
-      # How many invites each user is issued upon registration (only applicable if
-      # open registration is disabled)
-      "meta.sr.ht::settings".user-invites = mkDefault 5;
-
-      # Origin URL for API, 100 more than web
-      "meta.sr.ht".api-origin = mkDefault "http://localhost:5100";
-
-      # You can add aliases for the client IDs of commonly used OAuth clients here.
-      #
-      # Example:
-      "meta.sr.ht::aliases" = mkDefault { };
-      # "meta.sr.ht::aliases"."git.sr.ht" = 12345;
-
-      # "yes" to enable the billing system
-      "meta.sr.ht::billing".enabled = mkDefault "no";
-      # Get your keys at https://dashboard.stripe.com/account/apikeys
-      "meta.sr.ht::billing".stripe-public-key = mkDefault null;
-      "meta.sr.ht::billing".stripe-secret-key = mkDefault null;
-    };
-
-    services.nginx.virtualHosts."meta.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.metasrht}/${pkgs.sourcehut.python.sitePackages}/metasrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/paste.nix b/nixos/modules/services/misc/sourcehut/paste.nix
deleted file mode 100644
index b481ebaf89178..0000000000000
--- a/nixos/modules/services/misc/sourcehut/paste.nix
+++ /dev/null
@@ -1,135 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.paste;
-  iniKey = "paste.sr.ht";
-
-  rcfg = config.services.redis;
-  drv = pkgs.sourcehut.pastesrht;
-in
-{
-  options.services.sourcehut.paste = {
-    user = mkOption {
-      type = types.str;
-      default = "pastesrht";
-      description = ''
-        User for paste.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5011;
-      description = ''
-        Port on which the "paste" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "paste.sr.ht";
-      description = ''
-        PostgreSQL database name for paste.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/pastesrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/pastesrht"'';
-      description = ''
-        State path for pastesrht.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "paste" cfg.services) {
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          description = "paste.sr.ht user";
-        };
-      };
-
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services = {
-        pastesrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "paste.sr.ht website service";
-
-          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-        };
-
-        pastesrht-webhooks = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "paste.sr.ht webhooks service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
-          };
-
-        };
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL paste.sr.ht is being served at (protocol://domain)
-      "paste.sr.ht".origin = mkDefault "http://paste.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "paste.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "paste.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "paste.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "paste.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # paste.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "paste.sr.ht".oauth-client-id = mkDefault null;
-      "paste.sr.ht".oauth-client-secret = mkDefault null;
-      "paste.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/5";
-    };
-
-    services.nginx.virtualHosts."paste.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.pastesrht}/${pkgs.sourcehut.python.sitePackages}/pastesrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index f1706ad0a6a8a..4ecc7a7266907 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -148,7 +148,7 @@ in
     redis = {
       host = mkOption {
         type = types.str;
-        default = "unix:/run/redis-sourcehut-${srvsrht}/redis.sock?db=0";
+        default = "unix:///run/redis-sourcehut-${srvsrht}/redis.sock?db=0";
         example = "redis://shared.wireguard:6379/0";
         description = ''
           The redis host URL. This is used for caching and temporary storage, and must
diff --git a/nixos/modules/services/misc/sourcehut/todo.nix b/nixos/modules/services/misc/sourcehut/todo.nix
deleted file mode 100644
index 262fa48f59d4d..0000000000000
--- a/nixos/modules/services/misc/sourcehut/todo.nix
+++ /dev/null
@@ -1,163 +0,0 @@
-{ config, lib, options, pkgs, ... }:
-
-with lib;
-let
-  cfg = config.services.sourcehut;
-  opt = options.services.sourcehut;
-  cfgIni = cfg.settings;
-  scfg = cfg.todo;
-  iniKey = "todo.sr.ht";
-
-  rcfg = config.services.redis;
-  drv = pkgs.sourcehut.todosrht;
-in
-{
-  options.services.sourcehut.todo = {
-    user = mkOption {
-      type = types.str;
-      default = "todosrht";
-      description = ''
-        User for todo.sr.ht.
-      '';
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 5003;
-      description = ''
-        Port on which the "todo" module should listen.
-      '';
-    };
-
-    database = mkOption {
-      type = types.str;
-      default = "todo.sr.ht";
-      description = ''
-        PostgreSQL database name for todo.sr.ht.
-      '';
-    };
-
-    statePath = mkOption {
-      type = types.path;
-      default = "${cfg.statePath}/todosrht";
-      defaultText = literalExpression ''"''${config.${opt.statePath}}/todosrht"'';
-      description = ''
-        State path for todo.sr.ht.
-      '';
-    };
-  };
-
-  config = with scfg; lib.mkIf (cfg.enable && elem "todo" cfg.services) {
-    users = {
-      users = {
-        "${user}" = {
-          isSystemUser = true;
-          group = user;
-          extraGroups = [ "postfix" ];
-          description = "todo.sr.ht user";
-        };
-      };
-      groups = {
-        "${user}" = { };
-      };
-    };
-
-    services.postgresql = {
-      authentication = ''
-        local ${database} ${user} trust
-      '';
-      ensureDatabases = [ database ];
-      ensureUsers = [
-        {
-          name = user;
-          ensurePermissions = { "DATABASE \"${database}\"" = "ALL PRIVILEGES"; };
-        }
-      ];
-    };
-
-    systemd = {
-      tmpfiles.rules = [
-        "d ${statePath} 0750 ${user} ${user} -"
-      ];
-
-      services = {
-        todosrht = import ./service.nix { inherit config pkgs lib; } scfg drv iniKey {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "todo.sr.ht website service";
-
-          serviceConfig.ExecStart = "${cfg.python}/bin/gunicorn ${drv.pname}.app:app -b ${cfg.address}:${toString port}";
-        };
-
-       todosrht-lmtp = {
-         after = [ "postgresql.service" "network.target" ];
-         bindsTo = [ "postgresql.service" ];
-         wantedBy = [ "multi-user.target" ];
-
-         description = "todo.sr.ht process service";
-         serviceConfig = {
-           Type = "simple";
-           User = user;
-           Restart = "always";
-           ExecStart = "${cfg.python}/bin/todosrht-lmtp";
-         };
-       };
-
-        todosrht-webhooks = {
-          after = [ "postgresql.service" "network.target" ];
-          requires = [ "postgresql.service" ];
-          wantedBy = [ "multi-user.target" ];
-
-          description = "todo.sr.ht webhooks service";
-          serviceConfig = {
-            Type = "simple";
-            User = user;
-            Restart = "always";
-            ExecStart = "${cfg.python}/bin/celery -A ${drv.pname}.webhooks worker --loglevel=info";
-          };
-
-        };
-      };
-    };
-
-    services.sourcehut.settings = {
-      # URL todo.sr.ht is being served at (protocol://domain)
-      "todo.sr.ht".origin = mkDefault "http://todo.${cfg.originBase}";
-      # Address and port to bind the debug server to
-      "todo.sr.ht".debug-host = mkDefault "0.0.0.0";
-      "todo.sr.ht".debug-port = mkDefault port;
-      # Configures the SQLAlchemy connection string for the database.
-      "todo.sr.ht".connection-string = mkDefault "postgresql:///${database}?user=${user}&host=/var/run/postgresql";
-      # Set to "yes" to automatically run migrations on package upgrade.
-      "todo.sr.ht".migrate-on-upgrade = mkDefault "yes";
-      # todo.sr.ht's OAuth client ID and secret for meta.sr.ht
-      # Register your client at meta.example.org/oauth
-      "todo.sr.ht".oauth-client-id = mkDefault null;
-      "todo.sr.ht".oauth-client-secret = mkDefault null;
-      # Outgoing email for notifications generated by users
-      "todo.sr.ht".notify-from = mkDefault "CHANGEME@example.org";
-      # The redis connection used for the webhooks worker
-      "todo.sr.ht".webhooks = mkDefault "redis://${rcfg.bind}:${toString rcfg.port}/1";
-      # Network-key
-      "todo.sr.ht".network-key = mkDefault null;
-
-      # Path for the lmtp daemon's unix socket. Direct incoming mail to this socket.
-      # Alternatively, specify IP:PORT and an SMTP server will be run instead.
-      "todo.sr.ht::mail".sock = mkDefault "/tmp/todo.sr.ht-lmtp.sock";
-      # The lmtp daemon will make the unix socket group-read/write for users in this
-      # group.
-      "todo.sr.ht::mail".sock-group = mkDefault "postfix";
-
-      "todo.sr.ht::mail".posting-domain = mkDefault "todo.${cfg.originBase}";
-    };
-
-    services.nginx.virtualHosts."todo.${cfg.originBase}" = {
-      forceSSL = true;
-      locations."/".proxyPass = "http://${cfg.address}:${toString port}";
-      locations."/query".proxyPass = "http://${cfg.address}:${toString (port + 100)}";
-      locations."/static".root = "${pkgs.sourcehut.todosrht}/${pkgs.sourcehut.python.sitePackages}/todosrht";
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/grafana-agent.nix b/nixos/modules/services/monitoring/grafana-agent.nix
new file mode 100644
index 0000000000000..bbeda18464707
--- /dev/null
+++ b/nixos/modules/services/monitoring/grafana-agent.nix
@@ -0,0 +1,143 @@
+{ lib, pkgs, config, generators, ... }:
+with lib;
+let
+  cfg = config.services.grafana-agent;
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "grafana-agent.yaml" cfg.settings;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ flokli zimbatm ];
+  };
+
+  options.services.grafana-agent = {
+    enable = mkEnableOption "grafana-agent";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.grafana-agent;
+      defaultText = "pkgs.grafana-agent";
+      description = "The grafana-agent package to use.";
+    };
+
+    credentials = mkOption {
+      description = ''
+        Credentials to load at service startup. Keys that are UPPER_SNAKE will be loaded as env vars. Values are absolute paths to the credentials.
+      '';
+      type = types.attrsOf types.str;
+      default = { };
+
+      example = {
+        logs_remote_write_password = "/run/keys/grafana_agent_logs_remote_write_password";
+        LOGS_REMOTE_WRITE_URL = "/run/keys/grafana_agent_logs_remote_write_url";
+        LOGS_REMOTE_WRITE_USERNAME = "/run/keys/grafana_agent_logs_remote_write_username";
+        metrics_remote_write_password = "/run/keys/grafana_agent_metrics_remote_write_password";
+        METRICS_REMOTE_WRITE_URL = "/run/keys/grafana_agent_metrics_remote_write_url";
+        METRICS_REMOTE_WRITE_USERNAME = "/run/keys/grafana_agent_metrics_remote_write_username";
+      };
+    };
+
+    settings = mkOption {
+      description = ''
+        Configuration for <package>grafana-agent</package>.
+
+        See https://grafana.com/docs/agent/latest/configuration/
+      '';
+
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+      };
+
+      default = {
+        metrics = {
+          wal_directory = "\${STATE_DIRECTORY}";
+          global.scrape_interval = "5s";
+        };
+        integrations = {
+          agent.enabled = true;
+          agent.scrape_integration = true;
+          node_exporter.enabled = true;
+          replace_instance_label = true;
+        };
+      };
+
+      example = {
+        metrics.global.remote_write = [{
+          url = "\${METRICS_REMOTE_WRITE_URL}";
+          basic_auth.username = "\${METRICS_REMOTE_WRITE_USERNAME}";
+          basic_auth.password_file = "\${CREDENTIALS_DIRECTORY}/metrics_remote_write_password";
+        }];
+        logs.configs = [{
+          name = "default";
+          scrape_configs = [
+            {
+              job_name = "journal";
+              journal = {
+                max_age = "12h";
+                labels.job = "systemd-journal";
+              };
+              relabel_configs = [
+                {
+                  source_labels = [ "__journal__systemd_unit" ];
+                  target_label = "systemd_unit";
+                }
+                {
+                  source_labels = [ "__journal__hostname" ];
+                  target_label = "nodename";
+                }
+                {
+                  source_labels = [ "__journal_syslog_identifier" ];
+                  target_label = "syslog_identifier";
+                }
+              ];
+            }
+          ];
+          positions.filename = "\${STATE_DIRECTORY}/loki_positions.yaml";
+          clients = [{
+            url = "\${LOGS_REMOTE_WRITE_URL}";
+            basic_auth.username = "\${LOGS_REMOTE_WRITE_USERNAME}";
+            basic_auth.password_file = "\${CREDENTIALS_DIRECTORY}/logs_remote_write_password";
+          }];
+        }];
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.grafana-agent = {
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        set -euo pipefail
+        shopt -u nullglob
+
+        # Load all credentials into env if they are in UPPER_SNAKE form.
+        if [[ -n "''${CREDENTIALS_DIRECTORY:-}" ]]; then
+          for file in "$CREDENTIALS_DIRECTORY"/*; do
+            key=$(basename "$file")
+            if [[ $key =~ ^[A-Z0-9_]+$ ]]; then
+              echo "Environ $key"
+              export "$key=$(< "$file")"
+            fi
+          done
+        fi
+
+        # We can't use Environment=HOSTNAME=%H, as it doesn't include the domain part.
+        export HOSTNAME=$(< /proc/sys/kernel/hostname)
+
+        exec ${cfg.package}/bin/agent -config.expand-env -config.file ${configFile}
+      '';
+      serviceConfig = {
+        Restart = "always";
+        DynamicUser = true;
+        RestartSec = 2;
+        SupplementaryGroups = [
+          # allow to read the systemd journal for loki log forwarding
+          "systemd-journal"
+        ];
+        StateDirectory = "grafana-agent";
+        LoadCredential = lib.mapAttrsToList (key: value: "${key}:${value}") cfg.credentials;
+        Type = "simple";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 497d46741381b..68b4796f4f4ec 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -124,6 +124,11 @@ let
         default = 1;
         description = "Org id. will default to orgId 1 if not specified.";
       };
+      uid = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "Custom UID which can be used to reference this datasource in other parts of the configuration, if not specified will be generated automatically.";
+      };
       url = mkOption {
         type = types.str;
         description = "Url of the datasource.";
diff --git a/nixos/modules/services/monitoring/mimir.nix b/nixos/modules/services/monitoring/mimir.nix
index f85f3ab02c37e..83c0b23c59dd8 100644
--- a/nixos/modules/services/monitoring/mimir.nix
+++ b/nixos/modules/services/monitoring/mimir.nix
@@ -53,7 +53,7 @@ in {
                else cfg.configFile;
       in
       {
-        ExecStart = "${pkgs.grafana-mimir}/bin/mimir --config.file=${conf}";
+        ExecStart = "${pkgs.mimir}/bin/mimir --config.file=${conf}";
         DynamicUser = true;
         Restart = "always";
         ProtectSystem = "full";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
index 4d3c1fa267e5f..53509b7a385b4 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -74,11 +74,13 @@ in
     };
   };
   serviceOpts = {
+    after = mkIf cfg.systemd.enable [ cfg.systemd.unit ];
     serviceConfig = {
       DynamicUser = false;
       # By default, each prometheus exporter only gets AF_INET & AF_INET6,
       # but AF_UNIX is needed to read from the `showq`-socket.
       RestrictAddressFamilies = [ "AF_UNIX" ];
+      SupplementaryGroups = mkIf cfg.systemd.enable [ "systemd-journal" ];
       ExecStart = ''
         ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
index d4aa69629ec89..2d329a1af1cbc 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
@@ -57,9 +57,9 @@ in {
         ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
           -p ${toString cfg.port} \
           -l ${cfg.listenAddress} \
-          ${optionalString cfg.verbose "-v"} \
-          ${optionalString cfg.singleSubnetPerField "-s"} \
-          ${optionalString cfg.withRemoteIp "-r"} \
+          ${optionalString cfg.verbose "-v true"} \
+          ${optionalString cfg.singleSubnetPerField "-s true"} \
+          ${optionalString cfg.withRemoteIp "-r true"} \
           ${optionalString (cfg.wireguardConfig != null) "-n ${escapeShellArg cfg.wireguardConfig}"}
       '';
       RestrictAddressFamilies = [
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index 395b9788855ff..a5f8f55a682c9 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -257,7 +257,7 @@ in
       '' + optionalString cfg.autoMigrate ''
         ${pkgs.ipfs-migrator}/bin/fs-repo-migrations -to '${cfg.package.repoVersion}' -y
       '' + ''
-          ipfs --offline config profile apply ${profile}
+          ipfs --offline config profile apply ${profile} >/dev/null
         fi
       '' + optionalString cfg.autoMount ''
         ipfs --offline config Mounts.FuseAllowOther --json true
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index 8bf04e3a1a23c..f76cffc79bfa4 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -174,6 +174,7 @@ in
         serviceConfig = {
           DynamicUser = true;
           StateDirectory = "bitlbee";
+          ReadWritePaths = [ cfg.configDir ];
           ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
         };
       };
diff --git a/nixos/modules/services/networking/expressvpn.nix b/nixos/modules/services/networking/expressvpn.nix
new file mode 100644
index 0000000000000..d8ae6528a4d4e
--- /dev/null
+++ b/nixos/modules/services/networking/expressvpn.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  options.services.expressvpn.enable = mkOption {
+    type = types.bool;
+    default = false;
+    description = ''
+      Enable the ExpressVPN daemon.
+    '';
+  };
+
+  config = mkIf config.services.expressvpn.enable {
+    boot.kernelModules = [ "tun" ];
+
+    systemd.services.expressvpn = {
+      description = "ExpressVPN Daemon";
+      serviceConfig = {
+        ExecStart = "${pkgs.expressvpn}/bin/expressvpnd";
+        Restart = "on-failure";
+        RestartSec = 5;
+      };
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "network-online.target" ];
+    };
+  };
+
+  meta.maintainers = with maintainers; [ yureien ];
+}
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 256d9457d3960..70c6725d1035c 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -54,10 +54,10 @@ let
       hashedPassword = mkOption {
         type = uniq (nullOr str);
         default = null;
-        description = ''
+        description = mdDoc ''
           Specifies the hashed password for the MQTT User.
-          To generate hashed password install <literal>mosquitto</literal>
-          package and use <literal>mosquitto_passwd</literal>.
+          To generate hashed password install `mosquitto`
+          package and use `mosquitto_passwd`.
         '';
       };
 
@@ -65,11 +65,11 @@ let
         type = uniq (nullOr types.path);
         example = "/path/to/file";
         default = null;
-        description = ''
+        description = mdDoc ''
           Specifies the path to a file containing the
           hashed password for the MQTT user.
-          To generate hashed password install <literal>mosquitto</literal>
-          package and use <literal>mosquitto_passwd</literal>.
+          To generate hashed password install `mosquitto`
+          package and use `mosquitto_passwd`.
         '';
       };
 
@@ -155,24 +155,24 @@ let
     options = {
       plugin = mkOption {
         type = path;
-        description = ''
-          Plugin path to load, should be a <literal>.so</literal> file.
+        description = mdDoc ''
+          Plugin path to load, should be a `.so` file.
         '';
       };
 
       denySpecialChars = mkOption {
         type = bool;
-        description = ''
-          Automatically disallow all clients using <literal>#</literal>
-          or <literal>+</literal> in their name/id.
+        description = mdDoc ''
+          Automatically disallow all clients using `#`
+          or `+` in their name/id.
         '';
         default = true;
       };
 
       options = mkOption {
         type = attrsOf optionType;
-        description = ''
-          Options for the auth plugin. Each key turns into a <literal>auth_opt_*</literal>
+        description = mdDoc ''
+          Options for the auth plugin. Each key turns into a `auth_opt_*`
            line in the config.
         '';
         default = {};
@@ -239,8 +239,8 @@ let
 
       address = mkOption {
         type = nullOr str;
-        description = ''
-          Address to listen on. Listen on <literal>0.0.0.0</literal>/<literal>::</literal>
+        description = mdDoc ''
+          Address to listen on. Listen on `0.0.0.0`/`::`
           when unset.
         '';
         default = null;
@@ -248,10 +248,10 @@ let
 
       authPlugins = mkOption {
         type = listOf authPluginOptions;
-        description = ''
+        description = mdDoc ''
           Authentication plugin to attach to this listener.
-          Refer to the <link xlink:href="https://mosquitto.org/man/mosquitto-conf-5.html">
-          mosquitto.conf documentation</link> for details on authentication plugins.
+          Refer to the [mosquitto.conf documentation](https://mosquitto.org/man/mosquitto-conf-5.html)
+          for details on authentication plugins.
         '';
         default = [];
       };
@@ -472,10 +472,10 @@ let
 
     includeDirs = mkOption {
       type = listOf path;
-      description = ''
+      description = mdDoc ''
         Directories to be scanned for further config files to include.
         Directories will processed in the order given,
-        <literal>*.conf</literal> files in the directory will be
+        `*.conf` files in the directory will be
         read in case-sensistive alphabetical order.
       '';
       default = [];
diff --git a/nixos/modules/services/networking/routedns.nix b/nixos/modules/services/networking/routedns.nix
new file mode 100644
index 0000000000000..e0f5eedd2c8e5
--- /dev/null
+++ b/nixos/modules/services/networking/routedns.nix
@@ -0,0 +1,84 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+with lib;
+
+let
+  cfg = config.services.routedns;
+  settingsFormat = pkgs.formats.toml { };
+in
+{
+  options.services.routedns = {
+    enable = mkEnableOption "RouteDNS - DNS stub resolver, proxy and router";
+
+    settings = mkOption {
+      type = settingsFormat.type;
+      example = literalExpression ''
+        {
+          resolvers.cloudflare-dot = {
+            address = "1.1.1.1:853";
+            protocol = "dot";
+          };
+          groups.cloudflare-cached = {
+            type = "cache";
+            resolvers = ["cloudflare-dot"];
+          };
+          listeners.local-udp = {
+            address = "127.0.0.1:53";
+            protocol = "udp";
+            resolver = "cloudflare-cached";
+          };
+          listeners.local-tcp = {
+            address = "127.0.0.1:53";
+            protocol = "tcp";
+            resolver = "cloudflare-cached";
+          };
+        }
+      '';
+      description = ''
+        Configuration for RouteDNS, see <link xlink:href="https://github.com/folbricht/routedns/blob/master/doc/configuration.md"/>
+        for more information.
+      '';
+    };
+
+    configFile = mkOption {
+      default = settingsFormat.generate "routedns.toml" cfg.settings;
+      defaultText = "A RouteDNS configuration file automatically generated by values from services.routedns.*";
+      type = types.path;
+      example = literalExpression ''"''${pkgs.routedns}/cmd/routedns/example-config/use-case-1.toml"'';
+      description = "Path to RouteDNS TOML configuration file.";
+    };
+
+    package = mkOption {
+      default = pkgs.routedns;
+      defaultText = literalExpression "pkgs.routedns";
+      type = types.package;
+      description = "RouteDNS package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.routedns = {
+      description = "RouteDNS - DNS stub resolver, proxy and router";
+      after = [ "network.target" ]; # in case a bootstrap resolver is used, this might fail a few times until the respective server is actually reachable
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+      startLimitIntervalSec = 30;
+      startLimitBurst = 5;
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = "5s";
+        LimitNPROC = 512;
+        LimitNOFILE = 1048576;
+        DynamicUser = true;
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        NoNewPrivileges = true;
+        ExecStart = "${getBin cfg.package}/bin/routedns -l 4 ${cfg.configFile}";
+      };
+    };
+  };
+  meta.maintainers = with maintainers; [ jsimonetti ];
+}
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index 3a3d4c80ecff4..6a90f28dc5f7b 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -72,39 +72,39 @@ in {
       cert = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = ''
-          Path to the <literal>cert.pem</literal> file, which will be copied into Syncthing's
-          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        description = mdDoc ''
+          Path to the `cert.pem` file, which will be copied into Syncthing's
+          [configDir](#opt-services.syncthing.configDir).
         '';
       };
 
       key = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = ''
-          Path to the <literal>key.pem</literal> file, which will be copied into Syncthing's
-          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        description = mdDoc ''
+          Path to the `key.pem` file, which will be copied into Syncthing's
+          [configDir](#opt-services.syncthing.configDir).
         '';
       };
 
       overrideDevices = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = mdDoc ''
           Whether to delete the devices which are not configured via the
-          <link linkend="opt-services.syncthing.devices">devices</link> option.
-          If set to <literal>false</literal>, devices added via the web
+          [devices](#opt-services.syncthing.devices) option.
+          If set to `false`, devices added via the web
           interface will persist and will have to be deleted manually.
         '';
       };
 
       devices = mkOption {
         default = {};
-        description = ''
+        description = mdDoc ''
           Peers/devices which Syncthing should communicate with.
 
           Note that you can still add devices manually, but those changes
-          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices)
           is enabled.
         '';
         example = {
@@ -135,27 +135,27 @@ in {
 
             id = mkOption {
               type = types.str;
-              description = ''
-                The device ID. See <link xlink:href="https://docs.syncthing.net/dev/device-ids.html"/>.
+              description = mdDoc ''
+                The device ID. See <https://docs.syncthing.net/dev/device-ids.html>.
               '';
             };
 
             introducer = mkOption {
               type = types.bool;
               default = false;
-              description = ''
+              description = mdDoc ''
                 Whether the device should act as an introducer and be allowed
                 to add folders on this computer.
-                See <link xlink:href="https://docs.syncthing.net/users/introducer.html"/>.
+                See <https://docs.syncthing.net/users/introducer.html>.
               '';
             };
 
             autoAcceptFolders = mkOption {
               type = types.bool;
               default = false;
-              description = ''
+              description = mdDoc ''
                 Automatically create or share folders that this device advertises at the default path.
-                See <link xlink:href="https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format"/>.
+                See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>.
               '';
             };
 
@@ -166,21 +166,21 @@ in {
       overrideFolders = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = mdDoc ''
           Whether to delete the folders which are not configured via the
-          <link linkend="opt-services.syncthing.folders">folders</link> option.
-          If set to <literal>false</literal>, folders added via the web
+          [folders](#opt-services.syncthing.folders) option.
+          If set to `false`, folders added via the web
           interface will persist and will have to be deleted manually.
         '';
       };
 
       folders = mkOption {
         default = {};
-        description = ''
+        description = mdDoc ''
           Folders which should be shared by Syncthing.
 
           Note that you can still add devices manually, but those changes
-          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices)
           is enabled.
         '';
         example = literalExpression ''
@@ -231,18 +231,18 @@ in {
             devices = mkOption {
               type = types.listOf types.str;
               default = [];
-              description = ''
+              description = mdDoc ''
                 The devices this folder should be shared with. Each device must
-                be defined in the <link linkend="opt-services.syncthing.devices">devices</link> option.
+                be defined in the [devices](#opt-services.syncthing.devices) option.
               '';
             };
 
             versioning = mkOption {
               default = null;
-              description = ''
+              description = mdDoc ''
                 How to keep changed/deleted files with Syncthing.
                 There are 4 different types of versioning with different parameters.
-                See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                See <https://docs.syncthing.net/users/versioning.html>.
               '';
               example = literalExpression ''
                 [
@@ -284,17 +284,17 @@ in {
                 options = {
                   type = mkOption {
                     type = enum [ "external" "simple" "staggered" "trashcan" ];
-                    description = ''
+                    description = mdDoc ''
                       The type of versioning.
-                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                      See <https://docs.syncthing.net/users/versioning.html>.
                     '';
                   };
                   params = mkOption {
                     type = attrsOf (either str path);
-                    description = ''
+                    description = mdDoc ''
                       The parameters for versioning. Structure depends on
-                      <link linkend="opt-services.syncthing.folders._name_.versioning.type">versioning.type</link>.
-                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                      [versioning.type](#opt-services.syncthing.folders._name_.versioning.type).
+                      See <https://docs.syncthing.net/users/versioning.html>.
                     '';
                   };
                 };
@@ -345,9 +345,9 @@ in {
             ignoreDelete = mkOption {
               type = types.bool;
               default = false;
-              description = ''
+              description = mdDoc ''
                 Whether to skip deleting files that are deleted by peers.
-                See <link xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"/>.
+                See <https://docs.syncthing.net/advanced/folder-ignoredelete.html>.
               '';
             };
           };
@@ -357,9 +357,9 @@ in {
       extraOptions = mkOption {
         type = types.addCheck (pkgs.formats.json {}).type isAttrs;
         default = {};
-        description = ''
+        description = mdDoc ''
           Extra configuration options for Syncthing.
-          See <link xlink:href="https://docs.syncthing.net/users/config.html"/>.
+          See <https://docs.syncthing.net/users/config.html>.
         '';
         example = {
           options.localAnnounceEnabled = false;
@@ -387,9 +387,9 @@ in {
         type = types.str;
         default = defaultUser;
         example = "yourUser";
-        description = ''
+        description = mdDoc ''
           The user to run Syncthing as.
-          By default, a user named <literal>${defaultUser}</literal> will be created.
+          By default, a user named `${defaultUser}` will be created.
         '';
       };
 
@@ -397,9 +397,9 @@ in {
         type = types.str;
         default = defaultGroup;
         example = "yourGroup";
-        description = ''
+        description = mdDoc ''
           The group to run Syncthing under.
-          By default, a group named <literal>${defaultGroup}</literal> will be created.
+          By default, a group named `${defaultGroup}` will be created.
         '';
       };
 
@@ -407,11 +407,11 @@ in {
         type = with types; nullOr str;
         default = null;
         example = "socks5://address.com:1234";
-        description = ''
+        description = mdDoc ''
           Overwrites the all_proxy environment variable for the Syncthing process to
           the given value. This is normally used to let Syncthing connect
           through a SOCKS5 proxy server.
-          See <link xlink:href="https://docs.syncthing.net/users/proxying.html"/>.
+          See <https://docs.syncthing.net/users/proxying.html>.
         '';
       };
 
@@ -432,25 +432,13 @@ in {
           The path where the settings and keys will exist.
         '';
         default = cfg.dataDir + optionalString cond "/.config/syncthing";
-        defaultText = literalDocBook ''
-          <variablelist>
-            <varlistentry>
-              <term><literal>stateVersion >= 19.03</literal></term>
-              <listitem>
-                <programlisting>
-                  config.${opt.dataDir} + "/.config/syncthing"
-                </programlisting>
-              </listitem>
-            </varlistentry>
-            <varlistentry>
-              <term>otherwise</term>
-              <listitem>
-                <programlisting>
-                  config.${opt.dataDir}
-                </programlisting>
-              </listitem>
-            </varlistentry>
-          </variablelist>
+        defaultText = literalMD ''
+          * if `stateVersion >= 19.03`:
+
+                config.${opt.dataDir} + "/.config/syncthing"
+          * otherwise:
+
+                config.${opt.dataDir}
         '';
       };
 
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index 0133874d0e0d0..f84252289abff 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -6,6 +6,7 @@ let
   cfg = config.services.tailscale;
   firewallOn = config.networking.firewall.enable;
   rpfMode = config.networking.firewall.checkReversePath;
+  isNetworkd = config.networking.useNetworkd;
   rpfIsStrict = rpfMode == true || rpfMode == "strict";
 in {
   meta.maintainers = with maintainers; [ danderson mbaillie twitchyliquid64 ];
@@ -69,5 +70,17 @@ in {
       # linux distros.
       stopIfChanged = false;
     };
+
+    networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ];
+
+    systemd.network.networks."50-tailscale" = mkIf isNetworkd {
+      matchConfig = {
+        Name = cfg.interfaceName;
+      };
+      linkConfig = {
+        Unmanaged = true;
+        ActivationPolicy = "manual";
+      };
+    };
   };
 }
diff --git a/nixos/modules/services/networking/trickster.nix b/nixos/modules/services/networking/trickster.nix
index e48bba8fa587f..ac260a14d9a2d 100644
--- a/nixos/modules/services/networking/trickster.nix
+++ b/nixos/modules/services/networking/trickster.nix
@@ -6,6 +6,9 @@ let
   cfg = config.services.trickster;
 in
 {
+  imports = [
+    (mkRenamedOptionModule [ "services" "trickster" "origin" ] [ "services" "trickster" "origin-url" ])
+  ];
 
   options = {
     services.trickster = {
@@ -58,11 +61,19 @@ in
         '';
       };
 
-      origin = mkOption {
+      origin-type = mkOption {
+        type = types.enum [ "prometheus" "influxdb" ];
+        default = "prometheus";
+        description = ''
+          Type of origin (prometheus, influxdb)
+        '';
+      };
+
+      origin-url = mkOption {
         type = types.str;
         default = "http://prometheus:9090";
         description = ''
-          URL to the Prometheus Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090).
+          URL to the Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090).
         '';
       };
 
@@ -87,7 +98,7 @@ in
 
   config = mkIf cfg.enable {
     systemd.services.trickster = {
-      description = "Dashboard Accelerator for Prometheus";
+      description = "Reverse proxy cache and time series dashboard accelerator";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
@@ -96,7 +107,8 @@ in
           ${cfg.package}/bin/trickster \
           -log-level ${cfg.log-level} \
           -metrics-port ${toString cfg.metrics-port} \
-          -origin ${cfg.origin} \
+          -origin-type ${cfg.origin-type} \
+          -origin-url ${cfg.origin-url} \
           -proxy-port ${toString cfg.proxy-port} \
           ${optionalString (cfg.configFile != null) "-config ${cfg.configFile}"} \
           ${optionalString (cfg.profiler-port != null) "-profiler-port ${cfg.profiler-port}"} \
diff --git a/nixos/modules/services/networking/wg-quick.nix b/nixos/modules/services/networking/wg-quick.nix
index 0b3815d0cc628..236d3f452e7e6 100644
--- a/nixos/modules/services/networking/wg-quick.nix
+++ b/nixos/modules/services/networking/wg-quick.nix
@@ -10,6 +10,18 @@ let
 
   interfaceOpts = { ... }: {
     options = {
+
+      configFile = mkOption {
+        example = "/secret/wg0.conf";
+        default = null;
+        type = with types; nullOr str;
+        description = ''
+          wg-quick .conf file, describing the interface.
+          This overrides any other configuration interface configuration options.
+          See wg-quick manpage for more details.
+        '';
+      };
+
       address = mkOption {
         example = [ "192.168.2.1/24" ];
         default = [];
@@ -205,7 +217,7 @@ let
   writeScriptFile = name: text: ((pkgs.writeShellScriptBin name text) + "/bin/${name}");
 
   generateUnit = name: values:
-    assert assertMsg ((values.privateKey != null) != (values.privateKeyFile != null)) "Only one of privateKey or privateKeyFile may be set";
+    assert assertMsg (values.configFile != null || ((values.privateKey != null) != (values.privateKeyFile != null))) "Only one of privateKey, configFile or privateKeyFile may be set";
     let
       preUpFile = if values.preUp != "" then writeScriptFile "preUp.sh" values.preUp else null;
       postUp =
@@ -247,7 +259,12 @@ let
           optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep "," peer.allowedIPs}\n"
         ) values.peers;
       };
-      configPath = "${configDir}/${name}.conf";
+      configPath =
+        if values.configFile != null then
+          # This uses bind-mounted private tmp folder (/tmp/systemd-private-***)
+          "/tmp/${name}.conf"
+        else
+          "${configDir}/${name}.conf";
     in
     nameValuePair "wg-quick-${name}"
       {
@@ -265,9 +282,17 @@ let
 
         script = ''
           ${optionalString (!config.boot.isContainer) "modprobe wireguard"}
+          ${optionalString (values.configFile != null) ''
+            cp ${values.configFile} ${configPath}
+          ''}
           wg-quick up ${configPath}
         '';
 
+        serviceConfig = {
+          # Used to privately store renamed copies of external config files during activation
+          PrivateTmp = true;
+        };
+
         preStop = ''
           wg-quick down ${configPath}
         '';
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index c2e1d37e28bf4..5a7975ae17828 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -114,7 +114,7 @@ let
 
       script =
       ''
-        ${optionalString configIsGenerated ''
+        ${optionalString (configIsGenerated && !cfg.allowAuxiliaryImperativeNetworks) ''
           if [ -f /etc/wpa_supplicant.conf ]; then
             echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
           fi
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 8277f493639c2..756e0ee93b219 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -62,20 +62,52 @@ in {
       default = {};
       example = literalExpression ''
         {
-          domain = "https://bw.domain.tld:8443";
-          signupsAllowed = true;
-          rocketPort = 8222;
-          rocketLog = "critical";
+          DOMAIN = "https://bitwarden.example.com";
+          SIGNUPS_ALLOWED = false;
+
+          # Vaultwarden currently recommends running behind a reverse proxy
+          # (nginx or similar) for TLS termination, see
+          # https://github.com/dani-garcia/vaultwarden/wiki/Hardening-Guide#reverse-proxying
+          # > you should avoid enabling HTTPS via vaultwarden's built-in Rocket TLS support,
+          # > especially if your instance is publicly accessible.
+          #
+          # A suitable NixOS nginx reverse proxy example config might be:
+          #
+          #     services.nginx.virtualHosts."bitwarden.example.com" = {
+          #       enableACME = true;
+          #       forceSSL = true;
+          #       locations."/" = {
+          #         proxyPass = "http://127.0.0.1:''${toString config.services.vaultwarden.config.ROCKET_PORT}";
+          #       };
+          #     };
+          ROCKET_ADDRESS = "127.0.0.1";
+          ROCKET_PORT = 8222;
+
+          ROCKET_LOG = "critical";
+
+          # This example assumes a mailserver running on localhost,
+          # thus without transport encryption.
+          # If you use an external mail server, follow:
+          #   https://github.com/dani-garcia/vaultwarden/wiki/SMTP-configuration
+          SMTP_HOST = "127.0.0.1";
+          SMTP_PORT = 25;
+          SMTP_SSL = false;
+
+          SMTP_FROM = "admin@bitwarden.example.com";
+          SMTP_FROM_NAME = "example.com Bitwarden server";
         }
       '';
       description = ''
         The configuration of vaultwarden is done through environment variables,
-        therefore the names are converted from camel case (e.g. disable2FARemember)
-        to upper case snake case (e.g. DISABLE_2FA_REMEMBER).
+        therefore it is recommended to use upper snake case (e.g. <envar>DISABLE_2FA_REMEMBER</envar>).
+
+        However, camel case (e.g. <literal>disable2FARemember</literal>) is also supported:
+        The NixOS module will convert it automatically to
+        upper case snake case (e.g. <envar>DISABLE_2FA_REMEMBER</envar>).
         In this conversion digits (0-9) are handled just like upper case characters,
-        so foo2 would be converted to FOO_2.
-        Names already in this format remain unchanged, so FOO2 remains FOO2 if passed as such,
-        even though foo2 would have been converted to FOO_2.
+        so <literal>foo2</literal> would be converted to <envar>FOO_2</envar>.
+        Names already in this format remain unchanged, so <literal>FOO2</literal> remains <literal>FOO2</literal> if passed as such,
+        even though <literal>foo2</literal> would have been converted to <envar>FOO_2</envar>.
         This allows working around any potential future conflicting naming conventions.
 
         Based on the attributes passed to this config option an environment file will be generated
@@ -83,13 +115,16 @@ in {
 
         The available configuration options can be found in
         <link xlink:href="https://github.com/dani-garcia/vaultwarden/blob/${vaultwarden.version}/.env.template">the environment template file</link>.
+
+        See <xref linkend="opt-services.vaultwarden.environmentFile" /> for how
+        to set up access to the Admin UI to invite initial users.
       '';
     };
 
     environmentFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      example = "/root/vaultwarden.env";
+      example = "/var/lib/vaultwarden.env";
       description = ''
         Additional environment file as defined in <citerefentry>
         <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
@@ -100,6 +135,23 @@ in {
 
         Note that this file needs to be available on the host on which
         <literal>vaultwarden</literal> is running.
+
+        As a concrete example, to make the Admin UI available
+        (from which new users can be invited initially),
+        the secret <envar>ADMIN_TOKEN</envar> needs to be defined as described
+        <link xlink:href="https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page">here</link>.
+        Setting <literal>environmentFile</literal> to <literal>/var/lib/vaultwarden.env</literal>
+        and ensuring permissions with e.g.
+        <literal>chown vaultwarden:vaultwarden /var/lib/vaultwarden.env</literal>
+        (the <literal>vaultwarden</literal> user will only exist after activating with
+        <literal>enable = true;</literal> before this), we can set the contents of the file to have
+        contents such as:
+
+<programlisting>
+# Admin secret token, see
+# https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page
+ADMIN_TOKEN=...copy-paste a unique generated secret token here...
+</programlisting>
       '';
     };
 
diff --git a/nixos/modules/services/system/cachix-agent/default.nix b/nixos/modules/services/system/cachix-agent/default.nix
index 496e0b90355ba..f11d80d244d23 100644
--- a/nixos/modules/services/system/cachix-agent/default.nix
+++ b/nixos/modules/services/system/cachix-agent/default.nix
@@ -47,6 +47,7 @@ in {
       wantedBy = [ "multi-user.target" ];
       # don't restart while changing
       reloadIfChanged = true;
+      environment.USER = "root";
       serviceConfig = {
         Restart = "on-failure";
         EnvironmentFile = cfg.credentialsFile;
diff --git a/nixos/modules/services/system/localtime.nix b/nixos/modules/services/system/localtime.nix
index 6383e454e76ba..689453375f5ac 100644
--- a/nixos/modules/services/system/localtime.nix
+++ b/nixos/modules/services/system/localtime.nix
@@ -5,6 +5,8 @@ with lib;
 let
   cfg = config.services.localtimed;
 in {
+  imports = [ (lib.mkRenamedOptionModule [ "services" "localtime" ] [ "services" "localtimed" ]) ];
+
   options = {
     services.localtimed = {
       enable = mkOption {
diff --git a/nixos/modules/services/web-apps/dokuwiki.nix b/nixos/modules/services/web-apps/dokuwiki.nix
index 1f8ca742db951..d8fc978774e48 100644
--- a/nixos/modules/services/web-apps/dokuwiki.nix
+++ b/nixos/modules/services/web-apps/dokuwiki.nix
@@ -293,9 +293,7 @@ in
         inherit user;
         group = webserver.group;
 
-        # Not yet compatible with php 8 https://www.dokuwiki.org/requirements
-        # https://github.com/splitbrain/dokuwiki/issues/3545
-        phpPackage = pkgs.php74;
+        phpPackage = pkgs.php81;
         phpEnv = {
           DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig hostName cfg}";
           DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig hostName cfg}";
diff --git a/nixos/modules/services/web-apps/grocy.nix b/nixos/modules/services/web-apps/grocy.nix
index be2de638dd961..a77fddf1f2f45 100644
--- a/nixos/modules/services/web-apps/grocy.nix
+++ b/nixos/modules/services/web-apps/grocy.nix
@@ -115,9 +115,9 @@ in {
       user = "grocy";
       group = "nginx";
 
-      # PHP 7.4 is the only version which is supported/tested by upstream:
-      # https://github.com/grocy/grocy/blob/v3.0.0/README.md#how-to-install
-      phpPackage = pkgs.php74;
+      # PHP 8.0 is the only version which is supported/tested by upstream:
+      # https://github.com/grocy/grocy/blob/v3.3.0/README.md#how-to-install
+      phpPackage = pkgs.php80;
 
       inherit (cfg.phpfpm) settings;
 
diff --git a/nixos/modules/services/web-apps/hedgedoc.nix b/nixos/modules/services/web-apps/hedgedoc.nix
index 13893cbf005af..0310c15a4d27e 100644
--- a/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixos/modules/services/web-apps/hedgedoc.nix
@@ -1027,10 +1027,12 @@ in
         ${pkgs.envsubst}/bin/envsubst \
           -o ${cfg.workDir}/config.json \
           -i ${prettyJSON cfg.configuration}
+        mkdir -p ${cfg.configuration.uploadsPath}
       '';
       serviceConfig = {
         WorkingDirectory = cfg.workDir;
-        StateDirectory = [ cfg.workDir cfg.configuration.uploadsPath ];
+        StateDirectory = [ (builtins.replaceStrings [ "/var/lib/"  ] [ "" ] cfg.workDir) ];
+        ReadWritePaths = [ cfg.configuration.uploadsPath ];
         ExecStart = "${cfg.package}/bin/hedgedoc";
         EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
         Environment = [
diff --git a/nixos/modules/services/web-apps/invoiceplane.nix b/nixos/modules/services/web-apps/invoiceplane.nix
index 095eec36dec38..527e248f65b0b 100644
--- a/nixos/modules/services/web-apps/invoiceplane.nix
+++ b/nixos/modules/services/web-apps/invoiceplane.nix
@@ -236,7 +236,7 @@ in
     };
 
     services.phpfpm = {
-      phpPackage = pkgs.php74;
+      phpPackage = pkgs.php81;
       pools = mapAttrs' (hostName: cfg: (
         nameValuePair "invoiceplane-${hostName}" {
           inherit user;
@@ -302,4 +302,3 @@ in
 
   ]);
 }
-
diff --git a/nixos/modules/services/web-apps/moodle.nix b/nixos/modules/services/web-apps/moodle.nix
index 19f3e754691e0..55e5ac9281e7b 100644
--- a/nixos/modules/services/web-apps/moodle.nix
+++ b/nixos/modules/services/web-apps/moodle.nix
@@ -56,7 +56,7 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  phpExt = pkgs.php74.withExtensions
+  phpExt = pkgs.php81.withExtensions
         ({ enabled, all }: with all; [ iconv mbstring curl openssl tokenizer xmlrpc soap ctype zip gd simplexml dom  intl json sqlite3 pgsql pdo_sqlite pdo_pgsql pdo_odbc pdo_mysql pdo mysqli session zlib xmlreader fileinfo filter opcache ]);
 in
 {
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index dd2f2479f4e99..e0aaafd463331 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -157,7 +157,7 @@ in {
     };
     phpPackage = mkOption {
       type = types.package;
-      relatedPackages = [ "php74" "php80" "php81" ];
+      relatedPackages = [ "php80" "php81" ];
       defaultText = "pkgs.php";
       description = ''
         PHP package to use for Nextcloud.
@@ -632,7 +632,7 @@ in {
       services.nextcloud.datadir = mkOptionDefault config.services.nextcloud.home;
 
       services.nextcloud.phpPackage =
-        if versionOlder cfg.package.version "21" then pkgs.php74
+        if versionOlder cfg.package.version "24" then pkgs.php80
         # FIXME: Use PHP 8.1 with Nextcloud 24 and higher, once issues like this one are fixed:
         #
         # https://github.com/nextcloud/twofactor_totp/issues/1192
diff --git a/nixos/modules/services/web-apps/snipe-it.nix b/nixos/modules/services/web-apps/snipe-it.nix
index f14171d02f351..842e0715c0256 100644
--- a/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixos/modules/services/web-apps/snipe-it.nix
@@ -344,7 +344,7 @@ in {
 
     services.phpfpm.pools.snipe-it = {
       inherit user group;
-      phpPackage = pkgs.php74;
+      phpPackage = pkgs.php81;
       phpOptions = ''
         post_max_size = ${cfg.maxUploadSize}
         upload_max_filesize = ${cfg.maxUploadSize}
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix
index e7e626c66f021..ff9d08ea99701 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -189,7 +189,6 @@ in
 
           Note that this should be a last resort; patching the package is preferred (see GPaste).
         '';
-        apply = list: list ++ [ pkgs.gnome.gnome-shell pkgs.gnome.gnome-shell-extensions ];
       };
 
       favoriteAppsOverride = mkOption {
@@ -367,6 +366,10 @@ in
       services.upower.enable = config.powerManagement.enable;
       services.xserver.libinput.enable = mkDefault true; # for controlling touchpad settings via gnome control center
 
+      # Explicitly enabled since GNOME will be severely broken without these.
+      xdg.mime.enable = true;
+      xdg.icons.enable = true;
+
       xdg.portal.enable = true;
       xdg.portal.extraPortals = [
         pkgs.xdg-desktop-portal-gnome
@@ -400,6 +403,18 @@ in
     })
 
     (mkIf serviceCfg.core-shell.enable {
+      services.xserver.desktopManager.gnome.sessionPath =
+        let
+          mandatoryPackages = [
+            pkgs.gnome.gnome-shell
+          ];
+          optionalPackages = [
+            pkgs.gnome.gnome-shell-extensions
+          ];
+        in
+        mandatoryPackages
+        ++ utils.removePackagesByName optionalPackages config.environment.gnome.excludePackages;
+
       services.colord.enable = mkDefault true;
       services.gnome.chrome-gnome-shell.enable = mkDefault true;
       services.gnome.glib-networking.enable = true;
@@ -452,26 +467,31 @@ in
       ];
 
       # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/gnome-3-38/elements/core/meta-gnome-core-shell.bst
-      environment.systemPackages = with pkgs.gnome; [
-        adwaita-icon-theme
-        nixos-background-info
-        gnome-backgrounds
-        gnome-bluetooth
-        gnome-color-manager
-        gnome-control-center
-        gnome-shell
-        gnome-shell-extensions
-        gnome-themes-extra
-        pkgs.gnome-tour # GNOME Shell detects the .desktop file on first log-in.
-        pkgs.gnome-user-docs
-        pkgs.orca
-        pkgs.glib # for gsettings
-        pkgs.gnome-menus
-        pkgs.gtk3.out # for gtk-launch
-        pkgs.hicolor-icon-theme
-        pkgs.shared-mime-info # for update-mime-database
-        pkgs.xdg-user-dirs # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
-      ];
+      environment.systemPackages =
+        let
+          mandatoryPackages = with pkgs.gnome; [
+            gnome-shell
+          ];
+          optionalPackages = with pkgs.gnome; [
+            adwaita-icon-theme
+            nixos-background-info
+            gnome-backgrounds
+            gnome-bluetooth
+            gnome-color-manager
+            gnome-control-center
+            gnome-shell-extensions
+            gnome-themes-extra
+            pkgs.gnome-tour # GNOME Shell detects the .desktop file on first log-in.
+            pkgs.gnome-user-docs
+            pkgs.orca
+            pkgs.glib # for gsettings program
+            pkgs.gnome-menus
+            pkgs.gtk3.out # for gtk-launch program
+            pkgs.xdg-user-dirs # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
+          ];
+        in
+        mandatoryPackages
+        ++ utils.removePackagesByName optionalPackages config.environment.gnome.excludePackages;
     })
 
     # Adapt from https://gitlab.gnome.org/GNOME/gnome-build-meta/blob/gnome-3-38/elements/core/meta-gnome-core-utilities.bst
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index 083b12193dab0..d04e565f7d310 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -50,10 +50,6 @@ in
 
           Note that this should be a last resort; patching the package is preferred (see GPaste).
         '';
-        apply = list: list ++
-        [
-          pkgs.pantheon.pantheon-agent-geoclue2
-        ];
       };
 
       extraWingpanelIndicators = mkOption {
@@ -96,6 +92,9 @@ in
 
   config = mkMerge [
     (mkIf cfg.enable {
+      services.xserver.desktopManager.pantheon.sessionPath = utils.removePackagesByName [
+        pkgs.pantheon.pantheon-agent-geoclue2
+      ] config.environment.pantheon.excludePackages;
 
       services.xserver.displayManager.sessionPackages = [ pkgs.pantheon.elementary-session-settings ];
 
@@ -177,19 +176,28 @@ in
       networking.networkmanager.enable = mkDefault true;
 
       # Global environment
-      environment.systemPackages = with pkgs; [
+      environment.systemPackages = (with pkgs.pantheon; [
+        elementary-session-settings
+        elementary-settings-daemon
+        gala
+        gnome-settings-daemon
+        (switchboard-with-plugs.override {
+          plugs = cfg.extraSwitchboardPlugs;
+        })
+        (wingpanel-with-indicators.override {
+          indicators = cfg.extraWingpanelIndicators;
+        })
+      ]) ++ utils.removePackagesByName ((with pkgs; [
         desktop-file-utils
-        glib
+        glib # for gsettings program
         gnome-menus
         gnome.adwaita-icon-theme
-        gtk3.out
-        hicolor-icon-theme
+        gtk3.out # for gtk-launch program
         onboard
         qgnomeplatform
-        shared-mime-info
         sound-theme-freedesktop
-        xdg-user-dirs
-      ] ++ (with pkgs.pantheon; [
+        xdg-user-dirs # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
+      ]) ++ (with pkgs.pantheon; [
         # Artwork
         elementary-gtk-theme
         elementary-icon-theme
@@ -199,31 +207,21 @@ in
         # Desktop
         elementary-default-settings
         elementary-dock
-        elementary-session-settings
         elementary-shortcut-overlay
-        gala
-        (switchboard-with-plugs.override {
-          plugs = cfg.extraSwitchboardPlugs;
-        })
-        (wingpanel-with-indicators.override {
-          indicators = cfg.extraWingpanelIndicators;
-        })
 
         # Services
         elementary-capnet-assist
         elementary-notifications
-        elementary-settings-daemon
-        gnome-settings-daemon
         pantheon-agent-geoclue2
         pantheon-agent-polkit
-      ]);
-
-      programs.evince.enable = mkDefault true;
-      programs.file-roller.enable = mkDefault true;
+      ])) config.environment.pantheon.excludePackages;
 
       # Settings from elementary-default-settings
       environment.etc."gtk-3.0/settings.ini".source = "${pkgs.pantheon.elementary-default-settings}/etc/gtk-3.0/settings.ini";
 
+      xdg.mime.enable = true;
+      xdg.icons.enable = true;
+
       xdg.portal.enable = true;
       xdg.portal.extraPortals = with pkgs.pantheon; [
         elementary-files
@@ -271,6 +269,9 @@ in
     })
 
     (mkIf serviceCfg.apps.enable {
+      programs.evince.enable = mkDefault true;
+      programs.file-roller.enable = mkDefault true;
+
       environment.systemPackages = utils.removePackagesByName ([
         pkgs.gnome.gnome-font-viewer
       ] ++ (with pkgs.pantheon; [
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 3ca044ad5bc85..144cb00e480ef 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -234,11 +234,11 @@ in
     (mkIf (cfg.enable || cfg.mobile.enable) {
 
       security.wrappers = {
-        kcheckpass = {
+        kscreenlocker_greet = {
           setuid = true;
           owner = "root";
           group = "root";
-          source = "${getBin libsForQt5.kscreenlocker}/libexec/kcheckpass";
+          source = "${getBin libsForQt5.kscreenlocker}/libexec/kscreenlocker_greet";
         };
         start_kdeinit = {
           setuid = true;
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
index c23e479140f09..1566e38da0839 100644
--- a/nixos/modules/services/x11/display-managers/xpra.nix
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -26,6 +26,13 @@ in
         description = "Bind xpra to TCP";
       };
 
+      desktop = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "gnome-shell";
+        description = "Start a desktop environment instead of seamless mode";
+      };
+
       auth = mkOption {
         type = types.str;
         default = "pam";
@@ -222,7 +229,7 @@ in
     services.xserver.displayManager.job.execCmd = ''
       ${optionalString (cfg.pulseaudio)
         "export PULSE_COOKIE=/run/pulse/.config/pulse/cookie"}
-      exec ${pkgs.xpra}/bin/xpra start \
+      exec ${pkgs.xpra}/bin/xpra ${if cfg.desktop == null then "start" else "start-desktop --start=${cfg.desktop}"} \
         --daemon=off \
         --log-dir=/var/log \
         --log-file=xpra.log \
diff --git a/nixos/modules/services/x11/picom.nix b/nixos/modules/services/x11/picom.nix
index b40e20bcd3572..2eef71f71fcbf 100644
--- a/nixos/modules/services/x11/picom.nix
+++ b/nixos/modules/services/x11/picom.nix
@@ -51,6 +51,11 @@ in {
 
   imports = [
     (mkAliasOptionModule [ "services" "compton" ] [ "services" "picom" ])
+    (mkRemovedOptionModule [ "services" "picom" "refreshRate" ] ''
+      This option corresponds to `refresh-rate`, which has been unused
+      since picom v6 and was subsequently removed by upstream.
+      See https://github.com/yshui/picom/commit/bcbc410
+    '')
   ];
 
   options.services.picom = {
@@ -235,15 +240,6 @@ in {
       '';
     };
 
-    refreshRate = mkOption {
-      type = types.ints.unsigned;
-      default = 0;
-      example = 60;
-      description = ''
-       Screen refresh rate (0 = automatically detect).
-      '';
-    };
-
     settings = with types;
     let
       scalar = oneOf [ bool int float str ]
@@ -306,7 +302,6 @@ in {
       # other options
       backend          = cfg.backend;
       vsync            = cfg.vSync;
-      refresh-rate     = cfg.refreshRate;
     };
 
     systemd.user.services.picom = {
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 679c5210a6b32..645fbc2b713a9 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -8,8 +8,6 @@ let
 
   cfg = config.systemd;
 
-  systemd = cfg.package;
-
   inherit (systemdUtils.lib)
     generateUnits
     targetToUnit
@@ -439,7 +437,7 @@ in
 
     system.build.units = cfg.units;
 
-    system.nssModules = [ systemd.out ];
+    system.nssModules = [ cfg.package.out ];
     system.nssDatabases = {
       hosts = (mkMerge [
         (mkOrder 400 ["mymachines"]) # 400 to ensure it comes before resolve (which is mkBefore'd)
@@ -453,7 +451,7 @@ in
       ]);
     };
 
-    environment.systemPackages = [ systemd ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc = let
       # generate contents for /etc/systemd/system-${type} from attrset of links and packages
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 66fdc61d28357..dce72b36fc501 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -90,7 +90,7 @@ let
           bindsTo = [ "network-setup.service" ];
         };
 
-        networkSetup =
+        networkSetup = lib.mkIf (config.networking.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null)
           { description = "Networking Setup";
 
             after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ];
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 80808e0c08fa5..1657fabcd9b1c 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -59,15 +59,14 @@ in
         genericNetwork = override:
           let gateway = optional (cfg.defaultGateway != null && (cfg.defaultGateway.address or "") != "") cfg.defaultGateway.address
             ++ optional (cfg.defaultGateway6 != null && (cfg.defaultGateway6.address or "") != "") cfg.defaultGateway6.address;
-          in optionalAttrs (gateway != [ ]) {
-            routes = override [
-              {
+              makeGateway = gateway: {
                 routeConfig = {
                   Gateway = gateway;
                   GatewayOnLink = false;
                 };
-              }
-            ];
+              };
+          in optionalAttrs (gateway != [ ]) {
+            routes = override (map makeGateway gateway);
           } // optionalAttrs (domains != [ ]) {
             domains = override domains;
           };
@@ -89,20 +88,22 @@ in
           # 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...
+          # When wait-online.anyInterface is enabled, RequiredForOnline really
+          # means "sufficient for online", so we can enable it.
+          # Otherwise, don't block the network coming online because of default networks.
           matchConfig.Name = ["en*" "eth*"];
           DHCP = "yes";
-          linkConfig.RequiredForOnline = lib.mkDefault false;
+          linkConfig.RequiredForOnline =
+            lib.mkDefault config.systemd.network.wait-online.anyInterface;
+          networkConfig.IPv6PrivacyExtensions = "kernel";
         };
         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;
+          linkConfig.RequiredForOnline =
+            lib.mkDefault config.systemd.network.wait-online.anyInterface;
+          networkConfig.IPv6PrivacyExtensions = "kernel";
           # We also set the route metric to one more than the default
           # of 1024, so that Ethernet is preferred if both are
           # available.
diff --git a/nixos/modules/virtualisation/amazon-ec2-amis.nix b/nixos/modules/virtualisation/amazon-ec2-amis.nix
index 91b5237e3371d..0324c5332cffd 100644
--- a/nixos/modules/virtualisation/amazon-ec2-amis.nix
+++ b/nixos/modules/virtualisation/amazon-ec2-amis.nix
@@ -440,5 +440,53 @@ let self = {
   "21.11".ap-east-1.aarch64-linux.hvm-ebs = "ami-0aa3b50a4f2822a00";
   "21.11".sa-east-1.aarch64-linux.hvm-ebs = "ami-00f68eff453d3fe69";
 
-  latest = self."21.11";
+  # 22.05.342.a634c8f6c1f
+
+  "22.05".eu-west-1.x86_64-linux.hvm-ebs = "ami-00badba5cfa0a0c0d";
+  "22.05".af-south-1.x86_64-linux.hvm-ebs = "ami-0d3a6166c1ea4d7b4";
+  "22.05".ap-east-1.x86_64-linux.hvm-ebs = "ami-06445325c360470d8";
+  "22.05".ap-northeast-1.x86_64-linux.hvm-ebs = "ami-009c422293bcf3721";
+  "22.05".ap-northeast-2.x86_64-linux.hvm-ebs = "ami-0bfc0397525a67ed8";
+  "22.05".ap-northeast-3.x86_64-linux.hvm-ebs = "ami-0a1fb4d4e08a6065e";
+  "22.05".ap-south-1.x86_64-linux.hvm-ebs = "ami-07ad258dcc69239d2";
+  "22.05".ap-southeast-1.x86_64-linux.hvm-ebs = "ami-0f59f7f33cba8b1a4";
+  "22.05".ap-southeast-2.x86_64-linux.hvm-ebs = "ami-0d1e49fe30aec165d";
+  "22.05".ap-southeast-3.x86_64-linux.hvm-ebs = "ami-0f5cb24a1e3fc62dd";
+  "22.05".ca-central-1.x86_64-linux.hvm-ebs = "ami-0551a595ba7916462";
+  "22.05".eu-central-1.x86_64-linux.hvm-ebs = "ami-0702eee2e75d541d1";
+  "22.05".eu-north-1.x86_64-linux.hvm-ebs = "ami-0fc6838942cb7d9cb";
+  "22.05".eu-south-1.x86_64-linux.hvm-ebs = "ami-0df9463b8965cdb80";
+  "22.05".eu-west-2.x86_64-linux.hvm-ebs = "ami-08f3c1eb533a42ac1";
+  "22.05".eu-west-3.x86_64-linux.hvm-ebs = "ami-04b50c79dc4009c97";
+  "22.05".me-south-1.x86_64-linux.hvm-ebs = "ami-05c52087afab7024d";
+  "22.05".sa-east-1.x86_64-linux.hvm-ebs = "ami-0732aa0f0c28f281b";
+  "22.05".us-east-1.x86_64-linux.hvm-ebs = "ami-0223db08811f6fb2d";
+  "22.05".us-east-2.x86_64-linux.hvm-ebs = "ami-0a743534fa3e51b41";
+  "22.05".us-west-1.x86_64-linux.hvm-ebs = "ami-0d72ab697beab5ea5";
+  "22.05".us-west-2.x86_64-linux.hvm-ebs = "ami-034946f0c47088751";
+
+  "22.05".eu-west-1.aarch64-linux.hvm-ebs = "ami-08114069426233360";
+  "22.05".af-south-1.aarch64-linux.hvm-ebs = "ami-0a9b83913abd61694";
+  "22.05".ap-east-1.aarch64-linux.hvm-ebs = "ami-03966ad4547f532b7";
+  "22.05".ap-northeast-1.aarch64-linux.hvm-ebs = "ami-0eb7e152c8d5aae7d";
+  "22.05".ap-northeast-2.aarch64-linux.hvm-ebs = "ami-08369e00c5528762b";
+  "22.05".ap-northeast-3.aarch64-linux.hvm-ebs = "ami-0fa14b8d48cdd57c3";
+  "22.05".ap-south-1.aarch64-linux.hvm-ebs = "ami-0f2ca3b542ff0913b";
+  "22.05".ap-southeast-1.aarch64-linux.hvm-ebs = "ami-087def0511ef2687d";
+  "22.05".ap-southeast-2.aarch64-linux.hvm-ebs = "ami-0aa90985199011f04";
+  "22.05".ap-southeast-3.aarch64-linux.hvm-ebs = "ami-0c86c52790deefa23";
+  "22.05".ca-central-1.aarch64-linux.hvm-ebs = "ami-06e932cc9c20403e4";
+  "22.05".eu-central-1.aarch64-linux.hvm-ebs = "ami-07680df1026a9b54c";
+  "22.05".eu-north-1.aarch64-linux.hvm-ebs = "ami-0cbe9f2725e4de706";
+  "22.05".eu-south-1.aarch64-linux.hvm-ebs = "ami-01a83c3892925765f";
+  "22.05".eu-west-2.aarch64-linux.hvm-ebs = "ami-049024d086d039b54";
+  "22.05".eu-west-3.aarch64-linux.hvm-ebs = "ami-0c0ebe20ebfc635a1";
+  "22.05".me-south-1.aarch64-linux.hvm-ebs = "ami-0d662fcaac553e945";
+  "22.05".sa-east-1.aarch64-linux.hvm-ebs = "ami-0888c8f703e00fdb8";
+  "22.05".us-east-1.aarch64-linux.hvm-ebs = "ami-03536a13324333073";
+  "22.05".us-east-2.aarch64-linux.hvm-ebs = "ami-067611519fa817aaa";
+  "22.05".us-west-1.aarch64-linux.hvm-ebs = "ami-0f96be48071c13ab2";
+  "22.05".us-west-2.aarch64-linux.hvm-ebs = "ami-084bc5d777585adfb";
+
+  latest = self."22.05";
 }; in self
diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix
index 9816cc2332fbd..d3a2e0ed151d3 100644
--- a/nixos/modules/virtualisation/lxc-container.nix
+++ b/nixos/modules/virtualisation/lxc-container.nix
@@ -63,18 +63,18 @@ in
         default = {};
         example = literalExpression ''
           {
-            # create /etc/hostname on container creation
+            # create /etc/hostname on container creation. also requires networking.hostName = "" to be set
             "hostname" = {
               enable = true;
               target = "/etc/hostname";
-              template = builtins.writeFile "hostname.tpl" "{{ container.name }}";
+              template = builtins.toFile "hostname.tpl" "{{ container.name }}";
               when = [ "create" ];
             };
             # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied
             "hostname-nix" = {
               enable = true;
               target = "/etc/nixos/hostname.nix";
-              template = builtins.writeFile "hostname-nix.tpl" "{ ... }: { networking.hostName = "{{ container.name }}"; }";
+              template = builtins.toFile "hostname-nix.tpl" "{ ... }: { networking.hostName = \"{{ container.name }}\"; }";
               # copy keeps the file updated when the container is changed
               when = [ "create" "copy" ];
             };
@@ -82,7 +82,7 @@ in
             "configuration-nix" = {
               enable = true;
               target = "/etc/nixos/configuration.nix";
-              template = builtins.writeFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}";
+              template = builtins.toFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}";
               when = [ "create" ];
             };
           };
diff --git a/nixos/modules/virtualisation/podman/default.nix b/nixos/modules/virtualisation/podman/default.nix
index b7e7f78ded78f..361caeff70bef 100644
--- a/nixos/modules/virtualisation/podman/default.nix
+++ b/nixos/modules/virtualisation/podman/default.nix
@@ -153,6 +153,12 @@ in
       systemd.sockets.podman.wantedBy = [ "sockets.target" ];
       systemd.sockets.podman.socketConfig.SocketGroup = "podman";
 
+      systemd.user.services.podman.serviceConfig = {
+        ExecStart = [ "" "${cfg.package}/bin/podman $LOGGING system service" ];
+      };
+
+      systemd.user.sockets.podman.wantedBy = [ "sockets.target" ];
+
       systemd.tmpfiles.packages = [
         # The /run/podman rule interferes with our podman group, so we remove
         # it and let the systemd socket logic take care of it.
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index 975eed10cd267..a999efcb44e64 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -23,12 +23,12 @@ in
         default = false;
         type = types.bool;
         description =
-          ''
+          mdDoc ''
             Setting this option enables the Xen hypervisor, a
             virtualisation technology that allows multiple virtual
-            machines, known as <emphasis>domains</emphasis>, to run
+            machines, known as *domains*, to run
             concurrently on the physical machine.  NixOS runs as the
-            privileged <emphasis>Domain 0</emphasis>.  This option
+            privileged *Domain 0*.  This option
             requires a reboot to take effect.
           '';
       };