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/video/nvidia.nix19
-rw-r--r--nixos/modules/i18n/input-method/fcitx5.nix1
-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/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.nix7
-rw-r--r--nixos/modules/programs/clickshare.nix21
-rw-r--r--nixos/modules/programs/haguichi.nix15
-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/openldap.nix1
-rw-r--r--nixos/modules/services/desktops/pipewire/pipewire.nix5
-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/schleuder.nix162
-rw-r--r--nixos/modules/services/matrix/synapse.nix18
-rw-r--r--nixos/modules/services/misc/gollum.nix9
-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/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/web-apps/hedgedoc.nix3
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix1
-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/lxc-container.nix8
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix6
58 files changed, 1183 insertions, 418 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/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/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/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 495be43ec5614..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
@@ -514,6 +515,7 @@
   ./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/appservice-discord.nix
@@ -658,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
@@ -771,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
@@ -890,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/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/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/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 07b5dd12ffc54..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;
     };
 
@@ -260,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/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/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index b3108484fae12..3d5d10cdf070b 100644
--- a/nixos/modules/services/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`!
               '';
             };
 
@@ -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`!
               '';
             };
 
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/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/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/web-apps/hedgedoc.nix b/nixos/modules/services/web-apps/hedgedoc.nix
index 22270609dbccb..0310c15a4d27e 100644
--- a/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixos/modules/services/web-apps/hedgedoc.nix
@@ -1031,7 +1031,8 @@ in
       '';
       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/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 9aa38ab25c9a3..c441a2a7764e2 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -534,6 +534,7 @@ let
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
       ${poolName} = {
         inherit (cfg) user;
+        phpPackage = pkgs.php80;
         settings = mapAttrs (name: mkDefault) {
           "listen.owner" = "nginx";
           "listen.group" = "nginx";
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/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/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.
           '';
       };