summary refs log tree commit diff
path: root/nixos/modules/services
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/services')
-rw-r--r--nixos/modules/services/audio/botamusique.nix3
-rw-r--r--nixos/modules/services/audio/icecast.nix2
-rw-r--r--nixos/modules/services/audio/liquidsoap.nix12
-rw-r--r--nixos/modules/services/audio/mpd.nix2
-rw-r--r--nixos/modules/services/audio/roon-server.nix1
-rw-r--r--nixos/modules/services/audio/snapserver.nix11
-rw-r--r--nixos/modules/services/backup/borgbackup.nix49
-rw-r--r--nixos/modules/services/backup/borgbackup.xml2
-rw-r--r--nixos/modules/services/cluster/kubernetes/kubelet.nix4
-rw-r--r--nixos/modules/services/cluster/kubernetes/scheduler.nix2
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix2
-rw-r--r--nixos/modules/services/databases/cassandra.nix31
-rw-r--r--nixos/modules/services/databases/clickhouse.nix2
-rw-r--r--nixos/modules/services/databases/mysql.nix10
-rw-r--r--nixos/modules/services/databases/openldap.nix2
-rw-r--r--nixos/modules/services/databases/surrealdb.nix38
-rw-r--r--nixos/modules/services/desktops/pipewire/wireplumber.nix4
-rw-r--r--nixos/modules/services/development/jupyter/default.nix2
-rw-r--r--nixos/modules/services/games/asf.nix8
-rw-r--r--nixos/modules/services/games/factorio.nix11
-rw-r--r--nixos/modules/services/games/terraria.nix2
-rw-r--r--nixos/modules/services/hardware/argonone.nix2
-rw-r--r--nixos/modules/services/hardware/asusd.nix114
-rw-r--r--nixos/modules/services/hardware/bluetooth.nix6
-rw-r--r--nixos/modules/services/hardware/fwupd.nix68
-rw-r--r--nixos/modules/services/hardware/joycond.nix2
-rw-r--r--nixos/modules/services/hardware/supergfxd.nix38
-rw-r--r--nixos/modules/services/home-automation/evcc.nix6
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix4
-rw-r--r--nixos/modules/services/home-automation/zigbee2mqtt.nix5
-rw-r--r--nixos/modules/services/mail/listmonk.nix2
-rw-r--r--nixos/modules/services/mail/mailman.nix2
-rw-r--r--nixos/modules/services/matrix/conduit.nix3
-rw-r--r--nixos/modules/services/matrix/synapse.nix1
-rw-r--r--nixos/modules/services/misc/atuin.nix85
-rw-r--r--nixos/modules/services/misc/autorandr.nix4
-rw-r--r--nixos/modules/services/misc/gitlab.nix4
-rw-r--r--nixos/modules/services/misc/gpsd.nix9
-rw-r--r--nixos/modules/services/misc/heisenbridge.nix3
-rw-r--r--nixos/modules/services/misc/libreddit.nix9
-rw-r--r--nixos/modules/services/misc/portunus.nix4
-rw-r--r--nixos/modules/services/misc/ripple-data-api.nix6
-rw-r--r--nixos/modules/services/misc/sourcehut/default.nix2
-rw-r--r--nixos/modules/services/monitoring/grafana-agent.nix26
-rw-r--r--nixos/modules/services/monitoring/graphite.nix2
-rw-r--r--nixos/modules/services/monitoring/kapacitor.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix18
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix12
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nut.nix50
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix32
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix (renamed from nixos/modules/services/monitoring/prometheus/exporters/unifi-poller.nix)10
-rw-r--r--nixos/modules/services/monitoring/unpoller.nix (renamed from nixos/modules/services/monitoring/unifi-poller.nix)26
-rw-r--r--nixos/modules/services/monitoring/uptime-kuma.nix3
-rw-r--r--nixos/modules/services/monitoring/zabbix-proxy.nix2
-rw-r--r--nixos/modules/services/network-filesystems/tahoe.nix6
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix3
-rw-r--r--nixos/modules/services/networking/cloudflared.nix332
-rw-r--r--nixos/modules/services/networking/ddclient.nix2
-rw-r--r--nixos/modules/services/networking/dnsmasq.nix86
-rw-r--r--nixos/modules/services/networking/ergochat.nix4
-rw-r--r--nixos/modules/services/networking/mmsd.nix38
-rw-r--r--nixos/modules/services/networking/morty.nix2
-rw-r--r--nixos/modules/services/networking/multipath.nix2
-rw-r--r--nixos/modules/services/networking/nomad.nix2
-rw-r--r--nixos/modules/services/networking/nsd.nix4
-rw-r--r--nixos/modules/services/networking/nylon.nix2
-rw-r--r--nixos/modules/services/networking/pdns-recursor.nix4
-rw-r--r--nixos/modules/services/networking/redsocks.nix2
-rw-r--r--nixos/modules/services/networking/resilio.nix27
-rw-r--r--nixos/modules/services/networking/sabnzbd.nix2
-rw-r--r--nixos/modules/services/networking/supplicant.nix6
-rw-r--r--nixos/modules/services/networking/tailscale.nix29
-rw-r--r--nixos/modules/services/networking/tmate-ssh-server.nix2
-rw-r--r--nixos/modules/services/networking/twingate.nix28
-rw-r--r--nixos/modules/services/networking/unifi.nix4
-rw-r--r--nixos/modules/services/networking/v2raya.nix39
-rw-r--r--nixos/modules/services/networking/wireguard.nix2
-rw-r--r--nixos/modules/services/networking/znc/options.nix4
-rw-r--r--nixos/modules/services/printing/ipp-usb.nix63
-rw-r--r--nixos/modules/services/search/elasticsearch-curator.nix2
-rw-r--r--nixos/modules/services/search/elasticsearch.nix2
-rw-r--r--nixos/modules/services/search/meilisearch.nix2
-rw-r--r--nixos/modules/services/security/opensnitch.nix2
-rw-r--r--nixos/modules/services/security/vaultwarden/default.nix4
-rw-r--r--nixos/modules/services/system/kerberos/mit.nix2
-rw-r--r--nixos/modules/services/torrent/transmission.nix8
-rw-r--r--nixos/modules/services/video/unifi-video.nix2
-rw-r--r--nixos/modules/services/web-apps/code-server.nix2
-rw-r--r--nixos/modules/services/web-apps/hedgedoc.nix2
-rw-r--r--nixos/modules/services/web-apps/invidious.nix2
-rw-r--r--nixos/modules/services/web-apps/limesurvey.nix2
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix100
-rw-r--r--nixos/modules/services/web-apps/mattermost.nix4
-rw-r--r--nixos/modules/services/web-apps/miniflux.nix11
-rw-r--r--nixos/modules/services/web-apps/moodle.nix2
-rw-r--r--nixos/modules/services/web-apps/onlyoffice.nix2
-rw-r--r--nixos/modules/services/web-apps/peering-manager.nix265
-rw-r--r--nixos/modules/services/web-apps/zabbix.nix4
-rw-r--r--nixos/modules/services/web-servers/lighttpd/default.nix2
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/cinnamon.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix8
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix31
-rw-r--r--nixos/modules/services/x11/window-managers/katriawm.nix27
105 files changed, 1673 insertions, 286 deletions
diff --git a/nixos/modules/services/audio/botamusique.nix b/nixos/modules/services/audio/botamusique.nix
index 4cd900f945c..5d3f7db12bc 100644
--- a/nixos/modules/services/audio/botamusique.nix
+++ b/nixos/modules/services/audio/botamusique.nix
@@ -103,9 +103,8 @@ in
         StateDirectory = "botamusique";
         SystemCallArchitectures = "native";
         SystemCallFilter = [
-          "@system-service"
+          "@system-service @resources"
           "~@privileged"
-          "~@resources"
         ];
         UMask = "0077";
         WorkingDirectory = "/var/lib/botamusique";
diff --git a/nixos/modules/services/audio/icecast.nix b/nixos/modules/services/audio/icecast.nix
index 759f1ab0db9..f71a13b9626 100644
--- a/nixos/modules/services/audio/icecast.nix
+++ b/nixos/modules/services/audio/icecast.nix
@@ -74,7 +74,7 @@ in {
 
       listen = {
         port = mkOption {
-          type = types.int;
+          type = types.port;
           description = lib.mdDoc "TCP port that will be used to accept client connections.";
           default = 8000;
         };
diff --git a/nixos/modules/services/audio/liquidsoap.nix b/nixos/modules/services/audio/liquidsoap.nix
index c313104c460..5c10d13af5f 100644
--- a/nixos/modules/services/audio/liquidsoap.nix
+++ b/nixos/modules/services/audio/liquidsoap.nix
@@ -38,11 +38,13 @@ in
 
       default = {};
 
-      example = {
-        myStream1 = "/etc/liquidsoap/myStream1.liq";
-        myStream2 = literalExpression "./myStream2.liq";
-        myStream3 = "out(playlist(\"/srv/music/\"))";
-      };
+      example = literalExpression ''
+        {
+          myStream1 = "/etc/liquidsoap/myStream1.liq";
+          myStream2 = ./myStream2.liq;
+          myStream3 = "out(playlist(\"/srv/music/\"))";
+        }
+      '';
 
       type = types.attrsOf (types.either types.path types.str);
     };
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index bbfccec98c4..ba1e4716c9b 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -142,7 +142,7 @@ in {
         };
 
         port = mkOption {
-          type = types.int;
+          type = types.port;
           default = 6600;
           description = lib.mdDoc ''
             This setting is the TCP port that is desired for the daemon to get assigned
diff --git a/nixos/modules/services/audio/roon-server.nix b/nixos/modules/services/audio/roon-server.nix
index 4764ee3e598..74cae909f5d 100644
--- a/nixos/modules/services/audio/roon-server.nix
+++ b/nixos/modules/services/audio/roon-server.nix
@@ -40,6 +40,7 @@ in {
       wantedBy = [ "multi-user.target" ];
 
       environment.ROON_DATAROOT = "/var/lib/${name}";
+      environment.ROON_ID_DIR = "/var/lib/${name}";
 
       serviceConfig = {
         ExecStart = "${pkgs.roon-server}/bin/RoonServer";
diff --git a/nixos/modules/services/audio/snapserver.nix b/nixos/modules/services/audio/snapserver.nix
index fdc1f605bb3..2af42eeb370 100644
--- a/nixos/modules/services/audio/snapserver.nix
+++ b/nixos/modules/services/audio/snapserver.nix
@@ -101,9 +101,7 @@ in {
 
       openFirewall = mkOption {
         type = types.bool;
-        # Make the behavior consistent with other services. Set the default to
-        # false and remove the accompanying warning after NixOS 22.05 is released.
-        default = true;
+        default = false;
         description = lib.mdDoc ''
           Whether to automatically open the specified ports in the firewall.
         '';
@@ -279,12 +277,7 @@ in {
       # https://github.com/badaix/snapcast/blob/98ac8b2fb7305084376607b59173ce4097c620d8/server/streamreader/stream_manager.cpp#L85
       filter (w: w != "") (mapAttrsToList (k: v: if v.type == "spotify" then ''
         services.snapserver.streams.${k}.type = "spotify" is deprecated, use services.snapserver.streams.${k}.type = "librespot" instead.
-      '' else "") cfg.streams)
-      # Remove this warning after NixOS 22.05 is released.
-      ++ optional (options.services.snapserver.openFirewall.highestPrio >= (mkOptionDefault null).priority) ''
-        services.snapserver.openFirewall will no longer default to true starting with NixOS 22.11.
-        Enable it explicitly if you need to control Snapserver remotely.
-      '';
+      '' else "") cfg.streams);
 
     systemd.services.snapserver = {
       after = [ "network.target" ];
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
index 7b29eb41e72..ae8e1dd8463 100644
--- a/nixos/modules/services/backup/borgbackup.nix
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -11,7 +11,11 @@ let
 
   mkExcludeFile = cfg:
     # Write each exclude pattern to a new line
-    pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude);
+    pkgs.writeText "excludefile" (concatMapStrings (s: s + "\n") cfg.exclude);
+
+  mkPatternsFile = cfg:
+    # Write each pattern to a new line
+    pkgs.writeText "patternsfile" (concatMapStrings (s: s + "\n") cfg.patterns);
 
   mkKeepArgs = cfg:
     # If cfg.prune.keep e.g. has a yearly attribute,
@@ -19,7 +23,8 @@ let
     concatStringsSep " "
       (mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
 
-  mkBackupScript = cfg: ''
+  mkBackupScript = name: cfg: pkgs.writeShellScript "${name}-script" (''
+    set -e
     on_exit()
     {
       exitStatus=$?
@@ -46,6 +51,7 @@ let
       borg create $extraArgs \
         --compression ${cfg.compression} \
         --exclude-from ${mkExcludeFile cfg} \
+        --patterns-from ${mkPatternsFile cfg} \
         $extraCreateArgs \
         "::$archiveName$archiveSuffix" \
         ${if cfg.paths == null then "-" else escapeShellArgs cfg.paths}
@@ -58,10 +64,10 @@ let
   '' + optionalString (cfg.prune.keep != { }) ''
     borg prune $extraArgs \
       ${mkKeepArgs cfg} \
-      ${optionalString (cfg.prune.prefix != null) "--prefix ${escapeShellArg cfg.prune.prefix} \\"}
+      ${optionalString (cfg.prune.prefix != null) "--glob-archives ${escapeShellArg "${cfg.prune.prefix}*"}"} \
       $extraPruneArgs
     ${cfg.postPrune}
-  '';
+  '');
 
   mkPassEnv = cfg: with cfg.encryption;
     if passCommand != null then
@@ -73,12 +79,19 @@ let
   mkBackupService = name: cfg:
     let
       userHome = config.users.users.${cfg.user}.home;
-    in nameValuePair "borgbackup-job-${name}" {
+      backupJobName = "borgbackup-job-${name}";
+      backupScript = mkBackupScript backupJobName cfg;
+    in nameValuePair backupJobName {
       description = "BorgBackup job ${name}";
       path = with pkgs; [
         borgbackup openssh
       ];
-      script = mkBackupScript cfg;
+      script = "exec " + optionalString cfg.inhibitsSleep ''\
+        ${pkgs.systemd}/bin/systemd-inhibit \
+            --who="borgbackup" \
+            --what="sleep" \
+            --why="Scheduled backup" \
+        '' + backupScript;
       serviceConfig = {
         User = cfg.user;
         Group = cfg.group;
@@ -341,6 +354,15 @@ in {
             '';
           };
 
+          inhibitsSleep = mkOption {
+            default = false;
+            type = types.bool;
+            example = true;
+            description = lib.mdDoc ''
+              Prevents the system from sleeping while backing up.
+            '';
+          };
+
           user = mkOption {
             type = types.str;
             description = lib.mdDoc ''
@@ -424,6 +446,21 @@ in {
             ];
           };
 
+          patterns = mkOption {
+            type = with types; listOf str;
+            description = lib.mdDoc ''
+              Include/exclude paths matching the given patterns. The first
+              matching patterns is used, so if an include pattern (prefix `+`)
+              matches before an exclude pattern (prefix `-`), the file is
+              backed up. See [{command}`borg help patterns`](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-patterns) for pattern syntax.
+            '';
+            default = [ ];
+            example = [
+              "+ /home/susan"
+              "- /home/*"
+            ];
+          };
+
           readWritePaths = mkOption {
             type = with types; listOf path;
             description = lib.mdDoc ''
diff --git a/nixos/modules/services/backup/borgbackup.xml b/nixos/modules/services/backup/borgbackup.xml
index 8f623c93656..f38064f8677 100644
--- a/nixos/modules/services/backup/borgbackup.xml
+++ b/nixos/modules/services/backup/borgbackup.xml
@@ -179,7 +179,7 @@ sudo borg init --encryption=repokey-blake2  \
           mode = "repokey-blake2";
           passCommand = "cat /run/keys/borgbackup_passphrase";
         };
-        BORG_RSH = "ssh -i /run/keys/id_ed25519_borgbase";
+        environment = { BORG_RSH = "ssh -i /run/keys/id_ed25519_borgbase"; };
         compression = "auto,lzma";
         startAt = "daily";
     };
diff --git a/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixos/modules/services/cluster/kubernetes/kubelet.nix
index 0898fee9bdb..3ede1cb80e8 100644
--- a/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -171,7 +171,7 @@ in
       port = mkOption {
         description = lib.mdDoc "Kubernetes kubelet healthz port.";
         default = 10248;
-        type = int;
+        type = port;
       };
     };
 
@@ -204,7 +204,7 @@ in
     port = mkOption {
       description = lib.mdDoc "Kubernetes kubelet info server listening port.";
       default = 10250;
-      type = int;
+      type = port;
     };
 
     seedDockerImages = mkOption {
diff --git a/nixos/modules/services/cluster/kubernetes/scheduler.nix b/nixos/modules/services/cluster/kubernetes/scheduler.nix
index 2eada43eb4e..f31a92f3684 100644
--- a/nixos/modules/services/cluster/kubernetes/scheduler.nix
+++ b/nixos/modules/services/cluster/kubernetes/scheduler.nix
@@ -43,7 +43,7 @@ in
     port = mkOption {
       description = lib.mdDoc "Kubernetes scheduler listening port.";
       default = 10251;
-      type = int;
+      type = port;
     };
 
     verbosity = mkOption {
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
index d00e0ba3956..680b21dbf21 100644
--- a/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -206,7 +206,7 @@ in {
 
       port = mkOption {
         default = 8010;
-        type = types.int;
+        type = types.port;
         description = lib.mdDoc "Specifies port number on which the buildbot HTTP interface listens.";
       };
 
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index 7556dbfc7b8..e26acb88d8c 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -19,6 +19,10 @@ let
 
   cfg = config.services.cassandra;
 
+  atLeast3 = versionAtLeast cfg.package.version "3";
+  atLeast3_11 = versionAtLeast cfg.package.version "3.11";
+  atLeast4 = versionAtLeast cfg.package.version "4";
+
   defaultUser = "cassandra";
 
   cassandraConfig = flip recursiveUpdate cfg.extraConfig (
@@ -39,7 +43,7 @@ let
           parameters = [{ seeds = concatStringsSep "," cfg.seedAddresses; }];
         }
       ];
-    } // optionalAttrs (versionAtLeast cfg.package.version "3") {
+    } // optionalAttrs atLeast3 {
       hints_directory = "${cfg.homeDir}/hints";
     }
   );
@@ -62,7 +66,7 @@ let
     cassandraLogbackConfig = pkgs.writeText "logback.xml" cfg.logbackConfig;
 
     passAsFile = [ "extraEnvSh" ];
-    inherit (cfg) extraEnvSh;
+    inherit (cfg) extraEnvSh package;
 
     buildCommand = ''
       mkdir -p "$out"
@@ -80,6 +84,10 @@ let
 
       # Delete default password file
       sed -i '/-Dcom.sun.management.jmxremote.password.file=\/etc\/cassandra\/jmxremote.password/d' "$out/cassandra-env.sh"
+
+      ${lib.optionalString atLeast4 ''
+        cp $package/conf/jvm*.options $out/
+      ''}
     '';
   };
 
@@ -95,8 +103,20 @@ let
       "-Dcom.sun.management.jmxremote.password.file=${cfg.jmxRolesFile}"
     ] ++ optionals cfg.remoteJmx [
       "-Djava.rmi.server.hostname=${cfg.rpcAddress}"
+    ] ++ optionals atLeast4 [
+      # Historically, we don't use a log dir, whereas the upstream scripts do
+      # expect this. We override those by providing our own -Xlog:gc flag.
+      "-Xlog:gc=warning,heap*=warning,age*=warning,safepoint=warning,promotion*=warning"
     ];
 
+  commonEnv = {
+    # Sufficient for cassandra 2.x, 3.x
+    CASSANDRA_CONF = "${cassandraEtc}";
+
+    # Required since cassandra 4
+    CASSANDRA_LOGBACK_CONF = "${cassandraEtc}/logback.xml";
+  };
+
 in
 {
   options.services.cassandra = {
@@ -435,7 +455,7 @@ in
     jmxRolesFile = mkOption {
       type = types.nullOr types.path;
       default =
-        if versionAtLeast cfg.package.version "3.11"
+        if atLeast3_11
         then pkgs.writeText "jmx-roles-file" defaultJmxRolesFile
         else null;
       defaultText = literalMD ''generated configuration file if version is at least 3.11, otherwise `null`'';
@@ -486,8 +506,7 @@ in
     systemd.services.cassandra = {
       description = "Apache Cassandra service";
       after = [ "network.target" ];
-      environment = {
-        CASSANDRA_CONF = "${cassandraEtc}";
+      environment = commonEnv // {
         JVM_OPTS = builtins.concatStringsSep " " fullJvmOptions;
         MAX_HEAP_SIZE = toString cfg.maxHeapSize;
         HEAP_NEWSIZE = toString cfg.heapNewSize;
@@ -508,6 +527,7 @@ in
       description = "Perform a full repair on this Cassandra node";
       after = [ "cassandra.service" ];
       requires = [ "cassandra.service" ];
+      environment = commonEnv;
       serviceConfig = {
         User = cfg.user;
         Group = cfg.group;
@@ -536,6 +556,7 @@ in
       description = "Perform an incremental repair on this cassandra node.";
       after = [ "cassandra.service" ];
       requires = [ "cassandra.service" ];
+      environment = commonEnv;
       serviceConfig = {
         User = cfg.user;
         Group = cfg.group;
diff --git a/nixos/modules/services/databases/clickhouse.nix b/nixos/modules/services/databases/clickhouse.nix
index 96607d9a783..04dd20b5f14 100644
--- a/nixos/modules/services/databases/clickhouse.nix
+++ b/nixos/modules/services/databases/clickhouse.nix
@@ -16,7 +16,7 @@ with lib;
       package = mkOption {
         type = types.package;
         default = pkgs.clickhouse;
-        defaultText = "pkgs.clickhouse";
+        defaultText = lib.literalExpression "pkgs.clickhouse";
         description = lib.mdDoc ''
           ClickHouse package to use.
         '';
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index ec4524e9061..128bb086217 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -160,10 +160,12 @@ in
           List of database names and their initial schemas that should be used to create databases on the first startup
           of MySQL. The schema attribute is optional: If not specified, an empty database is created.
         '';
-        example = [
-          { name = "foodatabase"; schema = literalExpression "./foodatabase.sql"; }
-          { name = "bardatabase"; }
-        ];
+        example = literalExpression ''
+          [
+            { name = "foodatabase"; schema = ./foodatabase.sql; }
+            { name = "bardatabase"; }
+          ]
+        '';
       };
 
       initialScript = mkOption {
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index 7a59de372f2..cba3442023c 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -16,7 +16,7 @@ let
       # systemd/systemd#19604
       description = ''
         LDAP value - either a string, or an attrset containing
-        <literal>path</literal> or <literal>base64</literal> for included
+        `path` or `base64` for included
         values or base-64 encoded values respectively.
       '';
       check = x: lib.isString x || (lib.isAttrs x && (x ? path || x ? base64));
diff --git a/nixos/modules/services/databases/surrealdb.nix b/nixos/modules/services/databases/surrealdb.nix
index 27269eb02f6..050a5336cb4 100644
--- a/nixos/modules/services/databases/surrealdb.nix
+++ b/nixos/modules/services/databases/surrealdb.nix
@@ -10,6 +10,15 @@ in {
     services.surrealdb = {
       enable = mkEnableOption (lib.mdDoc "A scalable, distributed, collaborative, document-graph database, for the realtime web ");
 
+      package = mkOption {
+        default = pkgs.surrealdb;
+        defaultText = literalExpression "pkgs.surrealdb";
+        type = types.package;
+        description = lib.mdDoc ''
+          Which surrealdb derivation to use.
+        '';
+      };
+
       dbPath = mkOption {
         type = types.str;
         description = lib.mdDoc ''
@@ -37,21 +46,46 @@ in {
         default = 8000;
         example = 8000;
       };
+
+      userNamePath = mkOption {
+        type = types.path;
+        description = lib.mdDoc ''
+          Path to read the username from.
+        '';
+      };
+
+      passwordPath = mkOption {
+        type = types.path;
+        description = lib.mdDoc ''
+          Path to read the password from.
+        '';
+      };
     };
   };
 
   config = mkIf cfg.enable {
 
     # Used to connect to the running service
-    environment.systemPackages = [ pkgs.surrealdb ] ;
+    environment.systemPackages = [ cfg.package ] ;
 
     systemd.services.surrealdb = {
       description = "A scalable, distributed, collaborative, document-graph database, for the realtime web ";
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
 
+      script = ''
+        ${cfg.package}/bin/surreal start \
+          --user $(${pkgs.systemd}/bin/systemd-creds cat SURREALDB_USERNAME) \
+          --pass $(${pkgs.systemd}/bin/systemd-creds cat SURREALDB_PASSWORD) \
+          --bind ${cfg.host}:${toString cfg.port} \
+          -- ${cfg.dbPath}
+      '';
       serviceConfig = {
-        ExecStart = "${pkgs.surrealdb}/bin/surreal start --bind ${cfg.host}:${toString cfg.port} ${optionalString (cfg.dbPath != null) "-- ${cfg.dbPath}"}";
+        LoadCredential = [
+          "SURREALDB_USERNAME:${cfg.userNamePath}"
+          "SURREALDB_PASSWORD:${cfg.passwordPath}"
+        ];
+
         DynamicUser = true;
         Restart = "on-failure";
         StateDirectory = "surrealdb";
diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix
index 32490773b5e..4b36b99aa7c 100644
--- a/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -32,6 +32,10 @@ in
         assertion = !config.services.pipewire.media-session.enable;
         message = "WirePlumber and pipewire-media-session can't be enabled at the same time.";
       }
+      {
+        assertion = !config.hardware.bluetooth.hsphfpd.enable;
+        message = "Using Wireplumber conflicts with hsphfpd, as it provides the same functionality. `hardware.bluetooth.hsphfpd.enable` needs be set to false";
+      }
     ];
 
     environment.systemPackages = [ cfg.package ];
diff --git a/nixos/modules/services/development/jupyter/default.nix b/nixos/modules/services/development/jupyter/default.nix
index c3ef040ebe6..9f791084446 100644
--- a/nixos/modules/services/development/jupyter/default.nix
+++ b/nixos/modules/services/development/jupyter/default.nix
@@ -57,7 +57,7 @@ in {
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 8888;
       description = lib.mdDoc ''
         Port number Jupyter will be listening on.
diff --git a/nixos/modules/services/games/asf.nix b/nixos/modules/services/games/asf.nix
index 10847e8f11f..d3f7883421c 100644
--- a/nixos/modules/services/games/asf.nix
+++ b/nixos/modules/services/games/asf.nix
@@ -43,12 +43,14 @@ in
     web-ui = mkOption {
       type = types.submodule {
         options = {
-          enable = mkEnableOption
-            (lib.mdDoc "Wheter to start the web-ui. This is the preferred way of configuring things such as the steam guard token");
+          enable = mkEnableOption "" // {
+            description = lib.mdDoc "Whether to start the web-ui. This is the preferred way of configuring things such as the steam guard token.";
+          };
 
           package = mkOption {
             type = types.package;
             default = pkgs.ArchiSteamFarm.ui;
+            defaultText = lib.literalExpression "pkgs.ArchiSteamFarm.ui";
             description =
               lib.mdDoc "Web-UI package to use. Contents must be in lib/dist.";
           };
@@ -56,7 +58,6 @@ in
       };
       default = {
         enable = true;
-        package = pkgs.ArchiSteamFarm.ui;
       };
       example = {
         enable = false;
@@ -67,6 +68,7 @@ in
     package = mkOption {
       type = types.package;
       default = pkgs.ArchiSteamFarm;
+      defaultText = lib.literalExpression "pkgs.ArchiSteamFarm";
       description =
         lib.mdDoc "Package to use. Should always be the latest version, for security reasons, since this module uses very new features and to not get out of sync with the Steam API.";
     };
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 844fd2bce51..9b15cac149d 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -39,7 +39,7 @@ let
   } // cfg.extraSettings;
   serverSettingsFile = pkgs.writeText "server-settings.json" (builtins.toJSON (filterAttrsRecursive (n: v: v != null) serverSettings));
   serverAdminsFile = pkgs.writeText "server-adminlist.json" (builtins.toJSON cfg.admins);
-  modDir = pkgs.factorio-utils.mkModDirDrv cfg.mods;
+  modDir = pkgs.factorio-utils.mkModDirDrv cfg.mods cfg.mods-dat;
 in
 {
   options = {
@@ -136,6 +136,15 @@ in
           derivations via nixos-channel. Until then, this is for experts only.
         '';
       };
+      mods-dat = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = lib.mdDoc ''
+          Mods settings can be changed by specifying a dat file, in the [mod
+          settings file
+          format](https://wiki.factorio.com/Mod_settings_file_format).
+        '';
+      };
       game-name = mkOption {
         type = types.nullOr types.str;
         default = "Factorio Game";
diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix
index 571bcde2c5b..ccdd779165b 100644
--- a/nixos/modules/services/games/terraria.nix
+++ b/nixos/modules/services/games/terraria.nix
@@ -116,7 +116,7 @@ in
       openFirewall = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Wheter to open ports in the firewall";
+        description = lib.mdDoc "Whether to open ports in the firewall";
       };
 
       dataDir = mkOption {
diff --git a/nixos/modules/services/hardware/argonone.nix b/nixos/modules/services/hardware/argonone.nix
index dc90e09e985..e67c2625062 100644
--- a/nixos/modules/services/hardware/argonone.nix
+++ b/nixos/modules/services/hardware/argonone.nix
@@ -9,7 +9,7 @@ in
     package = lib.mkOption {
       type = lib.types.package;
       default = pkgs.argononed;
-      defaultText = "pkgs.argononed";
+      defaultText = lib.literalExpression "pkgs.argononed";
       description = lib.mdDoc ''
         The package implementing the Argon One driver
       '';
diff --git a/nixos/modules/services/hardware/asusd.nix b/nixos/modules/services/hardware/asusd.nix
new file mode 100644
index 00000000000..fba9b059bbb
--- /dev/null
+++ b/nixos/modules/services/hardware/asusd.nix
@@ -0,0 +1,114 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.asusd;
+  json = pkgs.formats.json { };
+  toml = pkgs.formats.toml { };
+in
+{
+  options = {
+    services.asusd = {
+      enable = lib.mkEnableOption (lib.mdDoc "the asusd service for ASUS ROG laptops");
+
+      enableUserService = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Activate the asusd-user service.
+        '';
+      };
+
+      animeConfig = lib.mkOption {
+        type = json.type;
+        default = { };
+        description = lib.mdDoc ''
+          The content of /etc/asusd/anime.conf.
+          See https://asus-linux.org/asusctl/#anime-control.
+        '';
+      };
+
+      asusdConfig = lib.mkOption {
+        type = json.type;
+        default = { };
+        description = lib.mdDoc ''
+          The content of /etc/asusd/asusd.conf.
+          See https://asus-linux.org/asusctl/.
+        '';
+      };
+
+      auraConfig = lib.mkOption {
+        type = json.type;
+        default = { };
+        description = lib.mdDoc ''
+          The content of /etc/asusd/aura.conf.
+          See https://asus-linux.org/asusctl/#led-keyboard-control.
+        '';
+      };
+
+      profileConfig = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = "";
+        description = lib.mdDoc ''
+          The content of /etc/asusd/profile.conf.
+          See https://asus-linux.org/asusctl/#profiles.
+        '';
+      };
+
+      ledModesConfig = lib.mkOption {
+        type = lib.types.nullOr toml.type;
+        default = null;
+        description = lib.mdDoc ''
+          The content of /etc/asusd/asusd-ledmodes.toml. Leave `null` to use default settings.
+          See https://asus-linux.org/asusctl/#led-keyboard-control.
+        '';
+      };
+
+      userLedModesConfig = lib.mkOption {
+        type = lib.types.nullOr toml.type;
+        default = null;
+        description = lib.mdDoc ''
+          The content of /etc/asusd/asusd-user-ledmodes.toml.
+          See https://asus-linux.org/asusctl/#led-keyboard-control.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.asusctl ];
+
+    environment.etc =
+      let
+        maybeConfig = name: cfg: lib.mkIf (cfg != { }) {
+          source = json.generate name cfg;
+          mode = "0644";
+        };
+      in
+      {
+        "asusd/anime.conf" = maybeConfig "anime.conf" cfg.animeConfig;
+        "asusd/asusd.conf" = maybeConfig "asusd.conf" cfg.asusdConfig;
+        "asusd/aura.conf" = maybeConfig "aura.conf" cfg.auraConfig;
+        "asusd/profile.conf" = lib.mkIf (cfg.profileConfig != null) {
+          source = pkgs.writeText "profile.conf" cfg.profileConfig;
+          mode = "0644";
+        };
+        "asusd/asusd-ledmodes.toml" = {
+          source =
+            if cfg.ledModesConfig == null
+            then "${pkgs.asusctl}/share/asusd/data/asusd-ledmodes.toml"
+            else toml.generate "asusd-ledmodes.toml" cfg.ledModesConfig;
+          mode = "0644";
+        };
+      };
+
+    services.dbus.enable = true;
+    systemd.packages = [ pkgs.asusctl ];
+    services.dbus.packages = [ pkgs.asusctl ];
+    services.udev.packages = [ pkgs.asusctl ];
+    services.supergfxd.enable = lib.mkDefault true;
+
+    systemd.user.services.asusd-user.enable = cfg.enableUserService;
+  };
+
+  meta.maintainers = pkgs.asusctl.meta.maintainers;
+}
diff --git a/nixos/modules/services/hardware/bluetooth.nix b/nixos/modules/services/hardware/bluetooth.nix
index 8b90c1913bc..6453e6968dc 100644
--- a/nixos/modules/services/hardware/bluetooth.nix
+++ b/nixos/modules/services/hardware/bluetooth.nix
@@ -50,14 +50,8 @@ in
         type = types.package;
         default = pkgs.bluez;
         defaultText = literalExpression "pkgs.bluez";
-        example = literalExpression "pkgs.bluezFull";
         description = lib.mdDoc ''
           Which BlueZ package to use.
-
-          ::: {.note}
-          Use the `pkgs.bluezFull` package to enable all
-          bluez plugins.
-          :::
         '';
       };
 
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
index 98f837bd782..8d7651f97c3 100644
--- a/nixos/modules/services/hardware/fwupd.nix
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -7,13 +7,16 @@ with lib;
 let
   cfg = config.services.fwupd;
 
+  format = pkgs.formats.ini {
+    listToValue = l: lib.concatStringsSep ";" (map (s: generators.mkValueStringDefault {} s) l);
+    mkKeyValue = generators.mkKeyValueDefault {} "=";
+  };
+
   customEtc = {
     "fwupd/daemon.conf" = {
-      source = pkgs.writeText "daemon.conf" ''
-        [fwupd]
-        DisabledDevices=${lib.concatStringsSep ";" cfg.disabledDevices}
-        DisabledPlugins=${lib.concatStringsSep ";" cfg.disabledPlugins}
-      '';
+      source = format.generate "daemon.conf" {
+        fwupd = cfg.daemonSettings;
+      };
     };
     "fwupd/uefi_capsule.conf" = {
       source = pkgs.writeText "uefi_capsule.conf" ''
@@ -67,24 +70,6 @@ in {
         '';
       };
 
-      disabledDevices = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
-        description = lib.mdDoc ''
-          Allow disabling specific devices by their GUID
-        '';
-      };
-
-      disabledPlugins = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "udev" ];
-        description = lib.mdDoc ''
-          Allow disabling specific plugins
-        '';
-      };
-
       extraTrustedKeys = mkOption {
         type = types.listOf types.path;
         default = [];
@@ -120,18 +105,49 @@ in {
           Which fwupd package to use.
         '';
       };
+
+      daemonSettings = mkOption {
+        type = types.submodule {
+          freeformType = format.type.nestedTypes.elemType;
+          options = {
+            DisabledDevices = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
+              description = lib.mdDoc ''
+                List of device GUIDs to be disabled.
+              '';
+            };
+
+            DisabledPlugins = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "udev" ];
+              description = lib.mdDoc ''
+                List of plugins to be disabled.
+              '';
+            };
+          };
+        };
+        default = {};
+        description = lib.mdDoc ''
+          Configurations for the fwupd daemon.
+        '';
+      };
     };
   };
 
   imports = [
-    (mkRenamedOptionModule [ "services" "fwupd" "blacklistDevices"] [ "services" "fwupd" "disabledDevices" ])
-    (mkRenamedOptionModule [ "services" "fwupd" "blacklistPlugins"] [ "services" "fwupd" "disabledPlugins" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "blacklistDevices"] [ "services" "fwupd" "daemonSettings" "DisabledDevices" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "blacklistPlugins"] [ "services" "fwupd" "daemonSettings" "DisabledPlugins" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "disabledDevices" ] [ "services" "fwupd" "daemonSettings" "DisabledDevices" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "disabledPlugins" ] [ "services" "fwupd" "daemonSettings" "DisabledPlugins" ])
   ];
 
   ###### implementation
   config = mkIf cfg.enable {
     # Disable test related plug-ins implicitly so that users do not have to care about them.
-    services.fwupd.disabledPlugins = cfg.package.defaultDisabledPlugins;
+    services.fwupd.daemonSettings.DisabledPlugins = cfg.package.defaultDisabledPlugins;
 
     environment.systemPackages = [ cfg.package ];
 
diff --git a/nixos/modules/services/hardware/joycond.nix b/nixos/modules/services/hardware/joycond.nix
index f4da00762a4..1af18b3b63d 100644
--- a/nixos/modules/services/hardware/joycond.nix
+++ b/nixos/modules/services/hardware/joycond.nix
@@ -14,7 +14,7 @@ with lib;
     package = mkOption {
       type = types.package;
       default = pkgs.joycond;
-      defaultText = "pkgs.joycond";
+      defaultText = lib.literalExpression "pkgs.joycond";
       description = lib.mdDoc ''
         The joycond package to use.
       '';
diff --git a/nixos/modules/services/hardware/supergfxd.nix b/nixos/modules/services/hardware/supergfxd.nix
new file mode 100644
index 00000000000..cb604db91dc
--- /dev/null
+++ b/nixos/modules/services/hardware/supergfxd.nix
@@ -0,0 +1,38 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.supergfxd;
+  json = pkgs.formats.json { };
+in
+{
+  options = {
+    services.supergfxd = {
+      enable = lib.mkEnableOption (lib.mdDoc "Enable the supergfxd service");
+
+      settings = lib.mkOption {
+        type = lib.types.nullOr json.type;
+        default = null;
+        description = lib.mdDoc ''
+          The content of /etc/supergfxd.conf.
+          See https://gitlab.com/asus-linux/supergfxctl/#config-options-etcsupergfxdconf.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.supergfxctl ];
+
+    environment.etc."supergfxd.conf" = lib.mkIf (cfg.settings != null) { source = json.generate "supergfxd.conf" cfg.settings; };
+
+    services.dbus.enable = true;
+
+    systemd.packages = [ pkgs.supergfxctl ];
+    systemd.services.supergfxd.wantedBy = [ "multi-user.target" ];
+
+    services.dbus.packages = [ pkgs.supergfxctl ];
+    services.udev.packages = [ pkgs.supergfxctl ];
+  };
+
+  meta.maintainers = pkgs.supergfxctl.meta.maintainers;
+}
diff --git a/nixos/modules/services/home-automation/evcc.nix b/nixos/modules/services/home-automation/evcc.nix
index c12ba9d0c1e..efa2cf24431 100644
--- a/nixos/modules/services/home-automation/evcc.nix
+++ b/nixos/modules/services/home-automation/evcc.nix
@@ -48,7 +48,10 @@ in
       wantedBy = [
         "multi-user.target"
       ];
-
+      environment.HOME = "/var/lib/evcc";
+      path = with pkgs; [
+        glibc # requires getent
+      ];
       serviceConfig = {
         ExecStart = "${package}/bin/evcc --config ${configFile} ${escapeShellArgs cfg.extraArgs}";
         CapabilityBoundingSet = [ "" ];
@@ -77,6 +80,7 @@ in
         ProtectKernelModules = true;
         ProtectKernelTunables = true;
         ProtectProc = "invisible";
+        StateDirectory = "evcc";
         SystemCallArchitectures = "native";
         SystemCallFilter = [
           "@system-service"
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index 2962e52c08b..fa06e5391bb 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -435,6 +435,7 @@ in {
           "august"
           "august_ble"
           "airthings_ble"
+          "aranet"
           "bluemaestro"
           "bluetooth"
           "bluetooth_le_tracker"
@@ -453,8 +454,11 @@ in {
           "moat"
           "oralb"
           "qingping"
+          "ruuvitag_ble"
+          "sensirion_ble"
           "sensorpro"
           "sensorpush"
+          "shelly"
           "snooz"
           "switchbot"
           "thermobeacon"
diff --git a/nixos/modules/services/home-automation/zigbee2mqtt.nix b/nixos/modules/services/home-automation/zigbee2mqtt.nix
index 71f6e7a2584..796de3a491e 100644
--- a/nixos/modules/services/home-automation/zigbee2mqtt.nix
+++ b/nixos/modules/services/home-automation/zigbee2mqtt.nix
@@ -119,9 +119,8 @@ in
         ];
         SystemCallArchitectures = "native";
         SystemCallFilter = [
-          "@system-service"
-          "~@privileged"
-          "~@resources"
+          "@system-service @pkey"
+          "~@privileged @resources"
         ];
         UMask = "0077";
       };
diff --git a/nixos/modules/services/mail/listmonk.nix b/nixos/modules/services/mail/listmonk.nix
index c4ea6747196..8b636bd5b1f 100644
--- a/nixos/modules/services/mail/listmonk.nix
+++ b/nixos/modules/services/mail/listmonk.nix
@@ -8,7 +8,7 @@ let
   # Escaping is done according to https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS
   setDatabaseOption = key: value:
     "UPDATE settings SET value = '${
-      lib.replaceChars [ "'" ] [ "''" ] (builtins.toJSON value)
+      lib.replaceStrings [ "'" ] [ "''" ] (builtins.toJSON value)
     }' WHERE key = '${key}';";
   updateDatabaseConfigSQL = pkgs.writeText "update-database-config.sql"
     (concatStringsSep "\n" (mapAttrsToList setDatabaseOption
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 198c2f3280f..5edcd313a91 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -443,7 +443,7 @@ in {
       virtualHosts = lib.genAttrs cfg.webHosts (webHost: {
         locations = {
           ${cfg.serve.virtualRoot}.extraConfig = "uwsgi_pass unix:/run/mailman-web.socket;";
-          "${cfg.serve.virtualRoot}/static/".alias = webSettings.STATIC_ROOT + "/";
+          "${removeSuffix "/" cfg.serve.virtualRoot}/static/".alias = webSettings.STATIC_ROOT + "/";
         };
       });
     };
diff --git a/nixos/modules/services/matrix/conduit.nix b/nixos/modules/services/matrix/conduit.nix
index 812d463e9e8..c8d89ed33f5 100644
--- a/nixos/modules/services/matrix/conduit.nix
+++ b/nixos/modules/services/matrix/conduit.nix
@@ -23,8 +23,7 @@ in
       package = mkOption {
         type = types.package;
         default = pkgs.matrix-conduit;
-        defaultText = "pkgs.matrix-conduit";
-        example = "pkgs.matrix-conduit";
+        defaultText = lib.literalExpression "pkgs.matrix-conduit";
         description = lib.mdDoc ''
           Package of the conduit matrix server to use.
         '';
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index 86662055222..a80d4cabfec 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -286,6 +286,7 @@ in {
             log_config = mkOption {
               type = types.path;
               default = ./synapse-log_config.yaml;
+              defaultText = lib.literalExpression "nixos/modules/services/matrix/synapse-log_config.yaml";
               description = lib.mdDoc ''
                 The file that holds the logging configuration.
               '';
diff --git a/nixos/modules/services/misc/atuin.nix b/nixos/modules/services/misc/atuin.nix
new file mode 100644
index 00000000000..c94852e3aad
--- /dev/null
+++ b/nixos/modules/services/misc/atuin.nix
@@ -0,0 +1,85 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.atuin;
+in
+{
+  options = {
+    services.atuin = {
+      enable = mkEnableOption (mdDoc "Enable server for shell history sync with atuin.");
+
+      openRegistration = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc "Allow new user registrations with the atuin server.";
+      };
+
+      path = mkOption {
+        type = types.str;
+        default = "";
+        description = mdDoc "A path to prepend to all the routes of the server.";
+      };
+
+      host = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = mdDoc "The host address the atuin server should listen on.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8888;
+        description = mdDoc "The port the atuin server should listen on.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc "Open ports in the firewall for the atuin server.";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    # enable postgres to host atuin db
+    services.postgresql = {
+      enable = true;
+      ensureUsers = [{
+        name = "atuin";
+        ensurePermissions = {
+          "DATABASE atuin" = "ALL PRIVILEGES";
+        };
+      }];
+      ensureDatabases = [ "atuin" ];
+    };
+
+    systemd.services.atuin = {
+      description = "atuin server";
+      after = [ "network.target" "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.atuin}/bin/atuin server start";
+        RuntimeDirectory = "atuin";
+        RuntimeDirectoryMode = "0700";
+        DynamicUser = true;
+      };
+
+      environment = {
+        ATUIN_HOST = cfg.host;
+        ATUIN_PORT = toString cfg.port;
+        ATUIN_OPEN_REGISTRATION = boolToString cfg.openRegistration;
+        ATUIN_DB_URI = "postgresql:///atuin";
+        ATUIN_PATH = cfg.path;
+        ATUIN_CONFIG_DIR = "/run/atuin"; # required to start, but not used as configuration is via environment variables
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
+
+  };
+}
diff --git a/nixos/modules/services/misc/autorandr.nix b/nixos/modules/services/misc/autorandr.nix
index 365fdd5fcc3..072064143db 100644
--- a/nixos/modules/services/misc/autorandr.nix
+++ b/nixos/modules/services/misc/autorandr.nix
@@ -258,7 +258,7 @@ in {
         type = hooksModule;
         description = lib.mdDoc "Global hook scripts";
         default = { };
-        example = ''
+        example = literalExpression ''
           {
             postswitch = {
               "notify-i3" = "''${pkgs.i3}/bin/i3-msg restart";
@@ -279,7 +279,7 @@ in {
                     exit 1
                 esac
                 echo "Xft.dpi: $DPI" | ''${pkgs.xorg.xrdb}/bin/xrdb -merge
-              '''
+              ''';
             };
           }
         '';
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index e206d5bb7c6..e7c707228f1 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -560,7 +560,7 @@ in {
           description = lib.mdDoc "GitLab container registry host name.";
         };
         port = mkOption {
-          type = types.int;
+          type = types.port;
           default = 4567;
           description = lib.mdDoc "GitLab container registry port.";
         };
@@ -613,7 +613,7 @@ in {
         };
 
         port = mkOption {
-          type = types.int;
+          type = types.port;
           default = 25;
           description = lib.mdDoc "Port of the SMTP server for GitLab.";
         };
diff --git a/nixos/modules/services/misc/gpsd.nix b/nixos/modules/services/misc/gpsd.nix
index 1ab8d1bbe06..ec0a8e1eaa1 100644
--- a/nixos/modules/services/misc/gpsd.nix
+++ b/nixos/modules/services/misc/gpsd.nix
@@ -77,6 +77,14 @@ in
         '';
       };
 
+      listenany = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Listen on all addresses rather than just loopback.
+        '';
+      };
+
     };
 
   };
@@ -106,6 +114,7 @@ in
             -S "${toString cfg.port}"                             \
             ${optionalString cfg.readonly "-b"}                   \
             ${optionalString cfg.nowait "-n"}                     \
+            ${optionalString cfg.listenany "-G"}                  \
             "${cfg.device}"
         '';
       };
diff --git a/nixos/modules/services/misc/heisenbridge.nix b/nixos/modules/services/misc/heisenbridge.nix
index 13ba362b33d..d07e0e42046 100644
--- a/nixos/modules/services/misc/heisenbridge.nix
+++ b/nixos/modules/services/misc/heisenbridge.nix
@@ -28,8 +28,7 @@ in
     package = mkOption {
       type = types.package;
       default = pkgs.heisenbridge;
-      defaultText = "pkgs.heisenbridge";
-      example = "pkgs.heisenbridge.override { … = …; }";
+      defaultText = lib.literalExpression "pkgs.heisenbridge";
       description = lib.mdDoc ''
         Package of the application to run, exposed for overriding purposes.
       '';
diff --git a/nixos/modules/services/misc/libreddit.nix b/nixos/modules/services/misc/libreddit.nix
index c961d13da47..fd58928d282 100644
--- a/nixos/modules/services/misc/libreddit.nix
+++ b/nixos/modules/services/misc/libreddit.nix
@@ -15,6 +15,13 @@ in
     services.libreddit = {
       enable = mkEnableOption (lib.mdDoc "Private front-end for Reddit");
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.libreddit;
+        defaultText = literalExpression "pkgs.libreddit";
+        description = lib.mdDoc "Libreddit package to use.";
+      };
+
       address = mkOption {
         default = "0.0.0.0";
         example = "127.0.0.1";
@@ -45,7 +52,7 @@ in
         after = [ "network.target" ];
         serviceConfig = {
           DynamicUser = true;
-          ExecStart = "${pkgs.libreddit}/bin/libreddit ${args}";
+          ExecStart = "${cfg.package}/bin/libreddit ${args}";
           AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
           Restart = "on-failure";
           RestartSec = "2s";
diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix
index 0b283ea27d8..1dae605e46f 100644
--- a/nixos/modules/services/misc/portunus.nix
+++ b/nixos/modules/services/misc/portunus.nix
@@ -29,7 +29,7 @@ in
     package = mkOption {
       type = types.package;
       default = pkgs.portunus;
-      defaultText = "pkgs.portunus";
+      defaultText = lib.literalExpression "pkgs.portunus";
       description = lib.mdDoc "The Portunus package to use.";
     };
 
@@ -108,7 +108,7 @@ in
       package = mkOption {
         type = types.package;
         default = pkgs.openldap;
-        defaultText = "pkgs.openldap";
+        defaultText = lib.literalExpression "pkgs.openldap";
         description = lib.mdDoc "The OpenLDAP package to use.";
       };
 
diff --git a/nixos/modules/services/misc/ripple-data-api.nix b/nixos/modules/services/misc/ripple-data-api.nix
index 2663d734980..30623a32133 100644
--- a/nixos/modules/services/misc/ripple-data-api.nix
+++ b/nixos/modules/services/misc/ripple-data-api.nix
@@ -40,7 +40,7 @@ in {
       port = mkOption {
         description = lib.mdDoc "Ripple data api port";
         default = 5993;
-        type = types.int;
+        type = types.port;
       };
 
       importMode = mkOption {
@@ -77,7 +77,7 @@ in {
         port = mkOption {
           description = lib.mdDoc "Ripple data api redis port.";
           default = 5984;
-          type = types.int;
+          type = types.port;
         };
       };
 
@@ -91,7 +91,7 @@ in {
         port = mkOption {
           description = lib.mdDoc "Ripple data api couchdb port.";
           default = 5984;
-          type = types.int;
+          type = types.port;
         };
 
         db = mkOption {
diff --git a/nixos/modules/services/misc/sourcehut/default.nix b/nixos/modules/services/misc/sourcehut/default.nix
index aaa7bb05754..7dd254e3492 100644
--- a/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixos/modules/services/misc/sourcehut/default.nix
@@ -505,7 +505,7 @@ in
             description = lib.mdDoc "Origin URL for API, 100 more than web.";
             type = types.str;
             default = "http://${cfg.listenAddress}:${toString (cfg.meta.port + 100)}";
-            defaultText = ''http://<xref linkend="opt-services.sourcehut.listenAddress"/>:''${toString (<xref linkend="opt-services.sourcehut.meta.port"/> + 100)}'';
+            defaultText = lib.literalMD ''`"http://''${`[](#opt-services.sourcehut.listenAddress)`}:''${toString (`[](#opt-services.sourcehut.meta.port)` + 100)}"`'';
           };
           webhooks = mkOption {
             description = lib.mdDoc "The Redis connection used for the webhooks worker.";
diff --git a/nixos/modules/services/monitoring/grafana-agent.nix b/nixos/modules/services/monitoring/grafana-agent.nix
index ecb39a924f5..270d888afb7 100644
--- a/nixos/modules/services/monitoring/grafana-agent.nix
+++ b/nixos/modules/services/monitoring/grafana-agent.nix
@@ -16,7 +16,7 @@ in
     package = mkOption {
       type = types.package;
       default = pkgs.grafana-agent;
-      defaultText = "pkgs.grafana-agent";
+      defaultText = lib.literalExpression "pkgs.grafana-agent";
       description = lib.mdDoc "The grafana-agent package to use.";
     };
 
@@ -49,17 +49,19 @@ in
       };
 
       default = { };
-      defaultText = ''
-        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;
-        };
+      defaultText = lib.literalExpression ''
+        {
+          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 = [{
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index 017e8a1ba47..8d92e60d3ba 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -225,7 +225,7 @@ in {
       port = mkOption {
         description = lib.mdDoc "Seyren listening port.";
         default = 8081;
-        type = types.int;
+        type = types.port;
       };
 
       seyrenUrl = mkOption {
diff --git a/nixos/modules/services/monitoring/kapacitor.nix b/nixos/modules/services/monitoring/kapacitor.nix
index 61529c2e452..727b694047b 100644
--- a/nixos/modules/services/monitoring/kapacitor.nix
+++ b/nixos/modules/services/monitoring/kapacitor.nix
@@ -66,7 +66,7 @@ in
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 9092;
       description = lib.mdDoc "Port of Kapacitor";
     };
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
index ee2533ef121..0c0931d3d29 100644
--- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -34,7 +34,7 @@ in {
     (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "group" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a group setting.")
     (mkRemovedOptionModule [ "services" "prometheus" "alertmanagerURL" ] ''
       Due to incompatibility, the alertmanagerURL option has been removed,
-      please use 'services.prometheus2.alertmanagers' instead.
+      please use 'services.prometheus.alertmanagers' instead.
     '')
   ];
 
@@ -107,7 +107,7 @@ in {
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = 9093;
         description = lib.mdDoc ''
           Port to listen on for the web interface and API.
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index f6bae8f9e96..f516b75ab10 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -3,7 +3,7 @@
 with lib;
 
 let
-  json = pkgs.formats.json { };
+  yaml = pkgs.formats.yaml { };
   cfg = config.services.prometheus;
   checkConfigEnabled =
     (lib.isBool cfg.checkConfig && cfg.checkConfig)
@@ -11,8 +11,6 @@ let
 
   workingDir = "/var/lib/" + cfg.stateDir;
 
-  prometheusYmlOut = "${workingDir}/prometheus-substituted.yaml";
-
   triggerReload = pkgs.writeShellScriptBin "trigger-reload-prometheus" ''
     PATH="${makeBinPath (with pkgs; [ systemd ])}"
     if systemctl -q is-active prometheus.service; then
@@ -38,7 +36,7 @@ let
         promtool ${what} $out
       '' else file;
 
-  generatedPrometheusYml = json.generate "prometheus.yml" promConfig;
+  generatedPrometheusYml = yaml.generate "prometheus.yml" promConfig;
 
   # This becomes the main config file for Prometheus
   promConfig = {
@@ -73,7 +71,8 @@ let
     "--web.listen-address=${cfg.listenAddress}:${builtins.toString cfg.port}"
     "--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
   ] ++ optional (cfg.webExternalUrl != null) "--web.external-url=${cfg.webExternalUrl}"
-    ++ optional (cfg.retentionTime != null) "--storage.tsdb.retention.time=${cfg.retentionTime}";
+    ++ optional (cfg.retentionTime != null) "--storage.tsdb.retention.time=${cfg.retentionTime}"
+    ++ optional (cfg.webConfigFile != null) "--web.config.file=${cfg.webConfigFile}";
 
   filterValidPrometheus = filterAttrsListRecursive (n: v: !(n == "_module" || v == null));
   filterAttrsListRecursive = pred: x:
@@ -1719,6 +1718,15 @@ in
       '';
     };
 
+    webConfigFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        Specifies which file should be used as web.config.file and be passed on startup.
+        See https://prometheus.io/docs/prometheus/latest/configuration/https/ for valid options.
+      '';
+    };
+
     checkConfig = mkOption {
       type = with types; either bool (enum [ "syntax-only" ]);
       default = true;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 8826d80a70c..2451f46ba7d 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -51,6 +51,7 @@ let
     "nginx"
     "nginxlog"
     "node"
+    "nut"
     "openldap"
     "openvpn"
     "pihole"
@@ -72,7 +73,7 @@ let
     "tor"
     "unbound"
     "unifi"
-    "unifi-poller"
+    "unpoller"
     "v2ray"
     "varnish"
     "wireguard"
@@ -229,6 +230,10 @@ in
   options.services.prometheus.exporters = mkOption {
     type = types.submodule {
       options = (mkSubModules);
+      imports = [
+        ../../../misc/assertions.nix
+        (lib.mkRenamedOptionModule [ "unifi-poller" ] [ "unpoller" ])
+      ];
     };
     description = lib.mdDoc "Prometheus exporter configuration";
     default = {};
@@ -292,13 +297,14 @@ in
         Please specify either 'services.prometheus.exporters.sql.configuration' or
           'services.prometheus.exporters.sql.configFile'
       '';
-    } ] ++ (flip map (attrNames cfg) (exporter: {
+    } ] ++ (flip map (attrNames exporterOpts) (exporter: {
       assertion = cfg.${exporter}.firewallFilter != null -> cfg.${exporter}.openFirewall;
       message = ''
         The `firewallFilter'-option of exporter ${exporter} doesn't have any effect unless
         `openFirewall' is set to `true'!
       '';
-    }));
+    })) ++ config.services.prometheus.exporters.assertions;
+    warnings = config.services.prometheus.exporters.warnings;
   }] ++ [(mkIf config.services.minio.enable {
     services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
     services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nut.nix b/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
new file mode 100644
index 00000000000..1c86b48b450
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.nut;
+in
+{
+  port = 9199;
+  extraOpts = {
+    nutServer = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = lib.mdDoc ''
+        Hostname or address of the NUT server
+      '';
+    };
+    nutUser = mkOption {
+      type = types.str;
+      default = "";
+      example = "nut";
+      description = lib.mdDoc ''
+        The user to log in into NUT server. If set, passwordPath should
+        also be set.
+
+        Default NUT configs usually permit reading variables without
+        authentication.
+      '';
+    };
+    passwordPath = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      apply = final: if final == null then null else toString final;
+      description = lib.mdDoc ''
+        A run-time path to the nutUser password file, which should be
+        provisioned outside of Nix store.
+      '';
+    };
+  };
+  serviceOpts = {
+    script = ''
+      ${optionalString (cfg.passwordPath != null)
+      "export NUT_EXPORTER_PASSWORD=$(cat ${toString cfg.passwordPath})"}
+      ${pkgs.prometheus-nut-exporter}/bin/nut_exporter \
+        --nut.server=${cfg.nutServer} \
+        --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \
+        ${optionalString (cfg.nutUser != "") "--nut.username=${cfg.nutUser}"}
+    '';
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix b/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix
index df424ede606..0c5648c1414 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix
@@ -4,16 +4,12 @@ with lib;
 
 let
   cfg = config.services.prometheus.exporters.smartctl;
-  format = pkgs.formats.yaml {};
-  configFile = format.generate "smartctl-exporter.yml" {
-    smartctl_exporter = {
-      bind_to = "${cfg.listenAddress}:${toString cfg.port}";
-      url_path = "/metrics";
-      smartctl_location = "${pkgs.smartmontools}/bin/smartctl";
-      collect_not_more_than_period = cfg.maxInterval;
-      devices = cfg.devices;
-    };
-  };
+  args = concatStrings [
+    "--web.listen-address=\"${cfg.listenAddress}:${toString cfg.port}\" "
+    "--smartctl.path=\"${pkgs.smartmontools}/bin/smartctl\" "
+    "--smartctl.interval=\"${cfg.maxInterval}\" "
+    "${concatMapStringsSep " " (device: "--smartctl.device=${device}") cfg.devices}"
+  ];
 in {
   port = 9633;
 
@@ -50,17 +46,13 @@ in {
         "CAP_SYS_ADMIN"
       ];
       DevicePolicy = "closed";
-      DeviceAllow = lib.mkOverride 50 (
-        if cfg.devices != [] then
-          cfg.devices
-        else [
-          "block-blkext rw"
-          "block-sd rw"
-          "char-nvme rw"
-        ]
-      );
+      DeviceAllow = lib.mkOverride 50 [
+        "block-blkext rw"
+        "block-sd rw"
+        "char-nvme rw"
+      ];
       ExecStart = ''
-        ${pkgs.prometheus-smartctl-exporter}/bin/smartctl_exporter -config ${configFile}
+        ${pkgs.prometheus-smartctl-exporter}/bin/smartctl_exporter ${args}
       '';
       PrivateDevices = lib.mkForce false;
       ProtectProc = "invisible";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unifi-poller.nix b/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix
index 35de31df88e..5cd1e2c65e9 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/unifi-poller.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix
@@ -3,9 +3,9 @@
 with lib;
 
 let
-  cfg = config.services.prometheus.exporters.unifi-poller;
+  cfg = config.services.prometheus.exporters.unpoller;
 
-  configFile = pkgs.writeText "prometheus-unifi-poller-exporter.json" (generators.toJSON {} {
+  configFile = pkgs.writeText "prometheus-unpoller-exporter.json" (generators.toJSON {} {
     poller = { inherit (cfg.log) debug quiet; };
     unifi = { inherit (cfg) controllers; };
     influxdb.disable = true;
@@ -21,8 +21,8 @@ in {
   port = 9130;
 
   extraOpts = {
-    inherit (options.services.unifi-poller.unifi) controllers;
-    inherit (options.services.unifi-poller) loki;
+    inherit (options.services.unpoller.unifi) controllers;
+    inherit (options.services.unpoller) loki;
     log = {
       debug = mkEnableOption (lib.mdDoc "debug logging including line numbers, high resolution timestamps, per-device logs.");
       quiet = mkEnableOption (lib.mdDoc "startup and error logs only.");
@@ -31,7 +31,7 @@ in {
   };
 
   serviceOpts.serviceConfig = {
-    ExecStart = "${pkgs.unifi-poller}/bin/unpoller --config ${configFile}";
+    ExecStart = "${pkgs.unpoller}/bin/unpoller --config ${configFile}";
     DynamicUser = false;
   };
 }
diff --git a/nixos/modules/services/monitoring/unifi-poller.nix b/nixos/modules/services/monitoring/unpoller.nix
index b30e28a3ecc..f0ced5513d6 100644
--- a/nixos/modules/services/monitoring/unifi-poller.nix
+++ b/nixos/modules/services/monitoring/unpoller.nix
@@ -3,15 +3,19 @@
 with lib;
 
 let
-  cfg = config.services.unifi-poller;
+  cfg = config.services.unpoller;
 
-  configFile = pkgs.writeText "unifi-poller.json" (generators.toJSON {} {
+  configFile = pkgs.writeText "unpoller.json" (generators.toJSON {} {
     inherit (cfg) poller influxdb loki prometheus unifi;
   });
 
 in {
-  options.services.unifi-poller = {
-    enable = mkEnableOption (lib.mdDoc "unifi-poller");
+  imports = [
+    (lib.mkRenamedOptionModule [ "services" "unifi-poller" ] [ "services" "unpoller" ])
+  ];
+
+  options.services.unpoller = {
+    enable = mkEnableOption (lib.mdDoc "unpoller");
 
     poller = {
       debug = mkOption {
@@ -86,8 +90,8 @@ in {
       };
       pass = mkOption {
         type = types.path;
-        default = pkgs.writeText "unifi-poller-influxdb-default.password" "unifipoller";
-        defaultText = literalExpression "unifi-poller-influxdb-default.password";
+        default = pkgs.writeText "unpoller-influxdb-default.password" "unifipoller";
+        defaultText = literalExpression "unpoller-influxdb-default.password";
         description = lib.mdDoc ''
           Path of a file containing the password for influxdb.
           This file needs to be readable by the unifi-poller user.
@@ -135,8 +139,8 @@ in {
       };
       pass = mkOption {
         type = types.path;
-        default = pkgs.writeText "unifi-poller-loki-default.password" "";
-        defaultText = "unifi-poller-influxdb-default.password";
+        default = pkgs.writeText "unpoller-loki-default.password" "";
+        defaultText = "unpoller-influxdb-default.password";
         description = lib.mdDoc ''
           Path of a file containing the password for Loki.
           This file needs to be readable by the unifi-poller user.
@@ -184,8 +188,8 @@ in {
         };
         pass = mkOption {
           type = types.path;
-          default = pkgs.writeText "unifi-poller-unifi-default.password" "unifi";
-          defaultText = literalExpression "unifi-poller-unifi-default.password";
+          default = pkgs.writeText "unpoller-unifi-default.password" "unifi";
+          defaultText = literalExpression "unpoller-unifi-default.password";
           description = lib.mdDoc ''
             Path of a file containing the password for the unifi service user.
             This file needs to be readable by the unifi-poller user.
@@ -303,7 +307,7 @@ in {
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.unifi-poller}/bin/unifi-poller --config ${configFile}";
+        ExecStart = "${pkgs.unpoller}/bin/unpoller --config ${configFile}";
         Restart = "always";
         PrivateTmp = true;
         ProtectHome = true;
diff --git a/nixos/modules/services/monitoring/uptime-kuma.nix b/nixos/modules/services/monitoring/uptime-kuma.nix
index 3a6091de679..b6dc993e6a0 100644
--- a/nixos/modules/services/monitoring/uptime-kuma.nix
+++ b/nixos/modules/services/monitoring/uptime-kuma.nix
@@ -13,9 +13,8 @@ in
 
       package = mkOption {
         type = types.package;
-        example = literalExpression "pkgs.uptime-kuma";
         default = pkgs.uptime-kuma;
-        defaultText = "pkgs.uptime-kuma";
+        defaultText = literalExpression "pkgs.uptime-kuma";
         description = lib.mdDoc "Uptime Kuma package to use.";
       };
 
diff --git a/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixos/modules/services/monitoring/zabbix-proxy.nix
index e7e353f3660..85da416ba6c 100644
--- a/nixos/modules/services/monitoring/zabbix-proxy.nix
+++ b/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -102,7 +102,7 @@ in
         };
 
         port = mkOption {
-          type = types.int;
+          type = types.port;
           default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
           defaultText = literalExpression ''
             if config.${opt.database.type} == "mysql"
diff --git a/nixos/modules/services/network-filesystems/tahoe.nix b/nixos/modules/services/network-filesystems/tahoe.nix
index 4213f437f4b..14c0a3d4725 100644
--- a/nixos/modules/services/network-filesystems/tahoe.nix
+++ b/nixos/modules/services/network-filesystems/tahoe.nix
@@ -18,7 +18,7 @@ in
             };
             tub.port = mkOption {
               default = 3458;
-              type = types.int;
+              type = types.port;
               description = lib.mdDoc ''
                 The port on which the introducer will listen.
               '';
@@ -58,7 +58,7 @@ in
             };
             tub.port = mkOption {
               default = 3457;
-              type = types.int;
+              type = types.port;
               description = lib.mdDoc ''
                 The port on which the tub will listen.
 
@@ -80,7 +80,7 @@ in
             };
             web.port = mkOption {
               default = 3456;
-              type = types.int;
+              type = types.port;
               description = lib.mdDoc ''
                 The port on which the Web server will listen.
 
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 56113bd3459..3933ed5a231 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -106,13 +106,14 @@ in
       default = true;
       description = lib.mdDoc ''
         Whether to open the firewall for UDP port 5353.
+        Disabling this setting also disables discovering of network devices.
       '';
     };
 
     allowPointToPoint = mkOption {
       type = types.bool;
       default = false;
-      description= lib.mdDoc ''
+      description = lib.mdDoc ''
         Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large
         latencies with such links and opens a potential security hole by allowing mDNS access from Internet
         connections.
diff --git a/nixos/modules/services/networking/cloudflared.nix b/nixos/modules/services/networking/cloudflared.nix
new file mode 100644
index 00000000000..c8fc9fafee6
--- /dev/null
+++ b/nixos/modules/services/networking/cloudflared.nix
@@ -0,0 +1,332 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cloudflared;
+
+  originRequest = {
+    connectTimeout = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "30s";
+      description = lib.mdDoc ''
+        Timeout for establishing a new TCP connection to your origin server. This excludes the time taken to establish TLS, which is controlled by [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#tlstimeout](tlsTimeout).
+      '';
+    };
+
+    tlsTimeout = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "10s";
+      description = lib.mdDoc ''
+        Timeout for completing a TLS handshake to your origin server, if you have chosen to connect Tunnel to an HTTPS server.
+      '';
+    };
+
+    tcpKeepAlive = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "30s";
+      description = lib.mdDoc ''
+        The timeout after which a TCP keepalive packet is sent on a connection between Tunnel and the origin server.
+      '';
+    };
+
+    noHappyEyeballs = mkOption {
+      type = with types; nullOr bool;
+      default = null;
+      example = false;
+      description = lib.mdDoc ''
+        Disable the “happy eyeballs” algorithm for IPv4/IPv6 fallback if your local network has misconfigured one of the protocols.
+      '';
+    };
+
+    keepAliveConnections = mkOption {
+      type = with types; nullOr int;
+      default = null;
+      example = 100;
+      description = lib.mdDoc ''
+        Maximum number of idle keepalive connections between Tunnel and your origin. This does not restrict the total number of concurrent connections.
+      '';
+    };
+
+    keepAliveTimeout = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "1m30s";
+      description = lib.mdDoc ''
+        Timeout after which an idle keepalive connection can be discarded.
+      '';
+    };
+
+    httpHostHeader = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        Sets the HTTP `Host` header on requests sent to the local service.
+      '';
+    };
+
+    originServerName = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        Hostname that `cloudflared` should expect from your origin server certificate.
+      '';
+    };
+
+    caPool = mkOption {
+      type = with types; nullOr (either str path);
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        Path to the certificate authority (CA) for the certificate of your origin. This option should be used only if your certificate is not signed by Cloudflare.
+      '';
+    };
+
+    noTLSVerify = mkOption {
+      type = with types; nullOr bool;
+      default = null;
+      example = false;
+      description = lib.mdDoc ''
+        Disables TLS verification of the certificate presented by your origin. Will allow any certificate from the origin to be accepted.
+      '';
+    };
+
+    disableChunkedEncoding = mkOption {
+      type = with types; nullOr bool;
+      default = null;
+      example = false;
+      description = lib.mdDoc ''
+        Disables chunked transfer encoding. Useful if you are running a WSGI server.
+      '';
+    };
+
+    proxyAddress = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "127.0.0.1";
+      description = lib.mdDoc ''
+        `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen address for that proxy.
+      '';
+    };
+
+    proxyPort = mkOption {
+      type = with types; nullOr int;
+      default = null;
+      example = 0;
+      description = lib.mdDoc ''
+        `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen port for that proxy. If set to zero, an unused port will randomly be chosen.
+      '';
+    };
+
+    proxyType = mkOption {
+      type = with types; nullOr (enum [ "" "socks" ]);
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures what type of proxy will be started. Valid options are:
+
+        - `""` for the regular proxy
+        - `"socks"` for a SOCKS5 proxy. Refer to the [https://developers.cloudflare.com/cloudflare-one/tutorials/kubectl/](tutorial on connecting through Cloudflare Access using kubectl) for more information.
+      '';
+    };
+  };
+in
+{
+  options.services.cloudflared = {
+    enable = mkEnableOption (lib.mdDoc "Cloudflare Tunnel client daemon (formerly Argo Tunnel)");
+
+    user = mkOption {
+      type = types.str;
+      default = "cloudflared";
+      description = lib.mdDoc "User account under which Cloudflared runs.";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "cloudflared";
+      description = lib.mdDoc "Group under which cloudflared runs.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.cloudflared;
+      defaultText = "pkgs.cloudflared";
+      description = lib.mdDoc "The package to use for Cloudflared.";
+    };
+
+    tunnels = mkOption {
+      description = lib.mdDoc ''
+        Cloudflare tunnels.
+      '';
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options = {
+          inherit originRequest;
+
+          credentialsFile = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = lib.mdDoc ''
+              Credential file.
+
+              See [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#credentials-file](Credentials file).
+            '';
+          };
+
+          warp-routing = {
+            enabled = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = lib.mdDoc ''
+                Enable warp routing.
+
+                See [https://developers.cloudflare.com/cloudflare-one/tutorials/warp-to-tunnel/](Connect from WARP to a private network on Cloudflare using Cloudflare Tunnel).
+              '';
+            };
+          };
+
+          default = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = lib.mdDoc ''
+              Catch-all service if no ingress matches.
+
+              See `service`.
+            '';
+            example = "http_status:404";
+          };
+
+          ingress = mkOption {
+            type = with types; attrsOf (either str (submodule ({ hostname, ... }: {
+              options = {
+                inherit originRequest;
+
+                service = mkOption {
+                  type = with types; nullOr str;
+                  default = null;
+                  description = lib.mdDoc ''
+                    Service to pass the traffic.
+
+                    See [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#supported-protocols](Supported protocols).
+                  '';
+                  example = "http://localhost:80, tcp://localhost:8000, unix:/home/production/echo.sock, hello_world or http_status:404";
+                };
+
+                path = mkOption {
+                  type = with types; nullOr str;
+                  default = null;
+                  description = lib.mdDoc ''
+                    Path filter.
+
+                    If not specified, all paths will be matched.
+                  '';
+                  example = "/*.(jpg|png|css|js)";
+                };
+
+              };
+            })));
+            default = { };
+            description = lib.mdDoc ''
+              Ingress rules.
+
+              See [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/](Ingress rules).
+            '';
+            example = {
+              "*.domain.com" = "http://localhost:80";
+              "*.anotherone.com" = "http://localhost:80";
+            };
+          };
+        };
+      }));
+
+      default = { };
+      example = {
+        "00000000-0000-0000-0000-000000000000" = {
+          credentialsFile = "/tmp/test";
+          ingress = {
+            "*.domain1.com" = {
+              service = "http://localhost:80";
+            };
+          };
+          default = "http_status:404";
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.targets =
+      mapAttrs'
+        (name: tunnel:
+          nameValuePair "cloudflared-tunnel-${name}" ({
+            description = lib.mdDoc "Cloudflare tunnel '${name}' target";
+            requires = [ "cloudflared-tunnel-${name}.service" ];
+            after = [ "cloudflared-tunnel-${name}.service" ];
+            unitConfig.StopWhenUnneeded = true;
+          })
+        )
+        config.services.cloudflared.tunnels;
+
+    systemd.services =
+      mapAttrs'
+        (name: tunnel:
+          let
+            filterConfig = lib.attrsets.filterAttrsRecursive (_: v: ! builtins.elem v [ null [ ] { } ]);
+
+            filterIngressSet = filterAttrs (_: v: builtins.typeOf v == "set");
+            filterIngressStr = filterAttrs (_: v: builtins.typeOf v == "string");
+
+            ingressesSet = filterIngressSet tunnel.ingress;
+            ingressesStr = filterIngressStr tunnel.ingress;
+
+            fullConfig = {
+              tunnel = name;
+              "credentials-file" = tunnel.credentialsFile;
+              ingress =
+                (map
+                  (key: {
+                    hostname = key;
+                  } // getAttr key (filterConfig (filterConfig ingressesSet)))
+                  (attrNames ingressesSet))
+                ++
+                (map
+                  (key: {
+                    hostname = key;
+                    service = getAttr key ingressesStr;
+                  })
+                  (attrNames ingressesStr))
+                ++ [{ service = tunnel.default; }];
+            };
+            mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig);
+          in
+          nameValuePair "cloudflared-tunnel-${name}" ({
+            after = [ "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+            serviceConfig = {
+              User = cfg.user;
+              Group = cfg.group;
+              ExecStart = "${cfg.package}/bin/cloudflared tunnel --config=${mkConfigFile} --no-autoupdate run";
+              Restart = "always";
+            };
+          })
+        )
+        config.services.cloudflared.tunnels;
+
+    users.users = mkIf (cfg.user == "cloudflared") {
+      cloudflared = {
+        group = cfg.group;
+        isSystemUser = true;
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "cloudflared") {
+      cloudflared = { };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ bbigras ];
+}
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 5c32817979a..4d843641f58 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -71,7 +71,7 @@ with lib;
       package = mkOption {
         type = package;
         default = pkgs.ddclient;
-        defaultText = "pkgs.ddclient";
+        defaultText = lib.literalExpression "pkgs.ddclient";
         description = lib.mdDoc ''
           The ddclient executable package run by the service.
         '';
diff --git a/nixos/modules/services/networking/dnsmasq.nix b/nixos/modules/services/networking/dnsmasq.nix
index cfc37b74b9a..4886654e8c0 100644
--- a/nixos/modules/services/networking/dnsmasq.nix
+++ b/nixos/modules/services/networking/dnsmasq.nix
@@ -7,15 +7,27 @@ let
   dnsmasq = pkgs.dnsmasq;
   stateDir = "/var/lib/dnsmasq";
 
+  # True values are just put as `name` instead of `name=true`, and false values
+  # are turned to comments (false values are expected to be overrides e.g.
+  # mkForce)
+  formatKeyValue =
+    name: value:
+    if value == true
+    then name
+    else if value == false
+    then "# setting `${name}` explicitly set to false"
+    else generators.mkKeyValueDefault { } "=" name value;
+
+  settingsFormat = pkgs.formats.keyValue {
+    mkKeyValue = formatKeyValue;
+    listsAsDuplicateKeys = true;
+  };
+
+  # Because formats.generate is outputting a file, we use of conf-file. Once
+  # `extraConfig` is deprecated we can just use
+  # `dnsmasqConf = format.generate "dnsmasq.conf" cfg.settings`
   dnsmasqConf = pkgs.writeText "dnsmasq.conf" ''
-    dhcp-leasefile=${stateDir}/dnsmasq.leases
-    ${optionalString cfg.resolveLocalQueries ''
-      conf-file=/etc/dnsmasq-conf.conf
-      resolv-file=/etc/dnsmasq-resolv.conf
-    ''}
-    ${flip concatMapStrings cfg.servers (server: ''
-      server=${server}
-    '')}
+    conf-file=${settingsFormat.generate "dnsmasq.conf" cfg.settings}
     ${cfg.extraConfig}
   '';
 
@@ -23,6 +35,10 @@ in
 
 {
 
+  imports = [
+    (mkRenamedOptionModule [ "services" "dnsmasq" "servers" ] [ "services" "dnsmasq" "settings" "server" ])
+  ];
+
   ###### interface
 
   options = {
@@ -46,15 +62,6 @@ in
         '';
       };
 
-      servers = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "8.8.8.8" "8.8.4.4" ];
-        description = lib.mdDoc ''
-          The DNS servers which dnsmasq should query.
-        '';
-      };
-
       alwaysKeepRunning = mkOption {
         type = types.bool;
         default = false;
@@ -63,12 +70,49 @@ in
         '';
       };
 
+      settings = mkOption {
+        type = types.submodule {
+
+          freeformType = settingsFormat.type;
+
+          options.server = mkOption {
+            type = types.listOf types.str;
+            default = [ ];
+            example = [ "8.8.8.8" "8.8.4.4" ];
+            description = lib.mdDoc ''
+              The DNS servers which dnsmasq should query.
+            '';
+          };
+
+        };
+        default = { };
+        description = lib.mdDoc ''
+          Configuration of dnsmasq. Lists get added one value per line (empty
+          lists and false values don't get added, though false values get
+          turned to comments). Gets merged with
+
+              {
+                dhcp-leasefile = "${stateDir}/dnsmasq.leases";
+                conf-file = optional cfg.resolveLocalQueries "/etc/dnsmasq-conf.conf";
+                resolv-file = optional cfg.resolveLocalQueries "/etc/dnsmasq-resolv.conf";
+              }
+        '';
+        example = literalExpression ''
+          {
+            domain-needed = true;
+            dhcp-range = [ "192.168.0.2,192.168.0.254" ];
+          }
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.lines;
         default = "";
         description = lib.mdDoc ''
           Extra configuration directives that should be added to
           `dnsmasq.conf`.
+
+          This option is deprecated, please use {option}`settings` instead.
         '';
       };
 
@@ -81,6 +125,14 @@ in
 
   config = mkIf cfg.enable {
 
+    warnings = lib.optional (cfg.extraConfig != "") "Text based config is deprecated, dnsmasq now supports `services.dnsmasq.settings` for an attribute-set based config";
+
+    services.dnsmasq.settings = {
+      dhcp-leasefile = mkDefault "${stateDir}/dnsmasq.leases";
+      conf-file = mkDefault (optional cfg.resolveLocalQueries "/etc/dnsmasq-conf.conf");
+      resolv-file = mkDefault (optional cfg.resolveLocalQueries "/etc/dnsmasq-resolv.conf");
+    };
+
     networking.nameservers =
       optional cfg.resolveLocalQueries "127.0.0.1";
 
diff --git a/nixos/modules/services/networking/ergochat.nix b/nixos/modules/services/networking/ergochat.nix
index 1a70b1f8613..a003512677e 100644
--- a/nixos/modules/services/networking/ergochat.nix
+++ b/nixos/modules/services/networking/ergochat.nix
@@ -17,10 +17,10 @@ in {
       configFile = lib.mkOption {
         type = lib.types.path;
         default = (pkgs.formats.yaml {}).generate "ergo.conf" cfg.settings;
-        defaultText = "generated config file from <literal>.settings</literal>";
+        defaultText = lib.literalMD "generated config file from `settings`";
         description = lib.mdDoc ''
           Path to configuration file.
-          Setting this will skip any configuration done via `.settings`
+          Setting this will skip any configuration done via `settings`
         '';
       };
 
diff --git a/nixos/modules/services/networking/mmsd.nix b/nixos/modules/services/networking/mmsd.nix
new file mode 100644
index 00000000000..7e262a9326c
--- /dev/null
+++ b/nixos/modules/services/networking/mmsd.nix
@@ -0,0 +1,38 @@
+{ pkgs, lib, config, ... }:
+with lib;
+let
+  cfg = config.services.mmsd;
+  dbusServiceFile = pkgs.writeTextDir "share/dbus-1/services/org.ofono.mms.service" ''
+    [D-BUS Service]
+    Name=org.ofono.mms
+    SystemdService=dbus-org.ofono.mms.service
+
+    # Exec= is still required despite SystemdService= being used:
+    # https://github.com/freedesktop/dbus/blob/ef55a3db0d8f17848f8a579092fb05900cc076f5/test/data/systemd-activation/com.example.SystemdActivatable1.service
+    Exec=${pkgs.coreutils}/bin/false mmsd
+  '';
+in
+{
+  options.services.mmsd = {
+    enable = mkEnableOption (mdDoc "Multimedia Messaging Service Daemon");
+    extraArgs = mkOption {
+      type = with types; listOf str;
+      description = mdDoc "Extra arguments passed to `mmsd-tng`";
+      default = [];
+      example = ["--debug"];
+    };
+  };
+  config = mkIf cfg.enable {
+    services.dbus.packages = [ dbusServiceFile ];
+    systemd.user.services.mmsd = {
+      after = [ "ModemManager.service" ];
+      aliases = [ "dbus-org.ofono.mms.service" ];
+      serviceConfig = {
+        Type = "dbus";
+        ExecStart = "${pkgs.mmsd-tng}/bin/mmsdtng " + escapeShellArgs cfg.extraArgs;
+        BusName = "org.ofono.mms";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/morty.nix b/nixos/modules/services/networking/morty.nix
index 4b20c34cfc9..72514764a7c 100644
--- a/nixos/modules/services/networking/morty.nix
+++ b/nixos/modules/services/networking/morty.nix
@@ -50,7 +50,7 @@ in
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = 3000;
         description = lib.mdDoc "Listing port";
       };
diff --git a/nixos/modules/services/networking/multipath.nix b/nixos/modules/services/networking/multipath.nix
index cb6b6db272c..54ee2a01568 100644
--- a/nixos/modules/services/networking/multipath.nix
+++ b/nixos/modules/services/networking/multipath.nix
@@ -28,7 +28,7 @@ in {
       type = package;
       description = lib.mdDoc "multipath-tools package to use";
       default = pkgs.multipath-tools;
-      defaultText = "pkgs.multipath-tools";
+      defaultText = lib.literalExpression "pkgs.multipath-tools";
     };
 
     devices = mkOption {
diff --git a/nixos/modules/services/networking/nomad.nix b/nixos/modules/services/networking/nomad.nix
index 5e5d9469efc..890ee0b7d8d 100644
--- a/nixos/modules/services/networking/nomad.nix
+++ b/nixos/modules/services/networking/nomad.nix
@@ -67,7 +67,7 @@ in
           Additional plugins dir used to configure nomad.
         '';
         example = literalExpression ''
-          [ "<pluginDir>" "pkgs.<plugins-name>"]
+          [ "<pluginDir>" pkgs.<plugins-name> ]
         '';
       };
 
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index 57da208bd7a..0ded9265209 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -588,7 +588,7 @@ in
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 53;
       description = lib.mdDoc ''
         Port the service should bind do.
@@ -825,7 +825,7 @@ in
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = 8952;
         description = lib.mdDoc ''
           Port number for remote control operations (uses TLS over TCP).
diff --git a/nixos/modules/services/networking/nylon.nix b/nixos/modules/services/networking/nylon.nix
index 6ed832b6fa1..401dbe97c52 100644
--- a/nixos/modules/services/networking/nylon.nix
+++ b/nixos/modules/services/networking/nylon.nix
@@ -81,7 +81,7 @@ let
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = 1080;
         description = lib.mdDoc ''
           What port to listen for client requests, default is 1080.
diff --git a/nixos/modules/services/networking/pdns-recursor.nix b/nixos/modules/services/networking/pdns-recursor.nix
index 473c2a1f1fb..2f07cefc736 100644
--- a/nixos/modules/services/networking/pdns-recursor.nix
+++ b/nixos/modules/services/networking/pdns-recursor.nix
@@ -38,7 +38,7 @@ in {
     };
 
     dns.port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 53;
       description = lib.mdDoc ''
         Port number Recursor DNS server will bind to.
@@ -67,7 +67,7 @@ in {
     };
 
     api.port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 8082;
       description = lib.mdDoc ''
         Port number Recursor REST API server will bind to.
diff --git a/nixos/modules/services/networking/redsocks.nix b/nixos/modules/services/networking/redsocks.nix
index 85ae3125ded..45feb1313c9 100644
--- a/nixos/modules/services/networking/redsocks.nix
+++ b/nixos/modules/services/networking/redsocks.nix
@@ -81,7 +81,7 @@ in
           };
 
           port = mkOption {
-            type = types.int;
+            type = types.port;
             default = 12345;
             description = lib.mdDoc "Port on which redsocks should listen.";
           };
diff --git a/nixos/modules/services/networking/resilio.nix b/nixos/modules/services/networking/resilio.nix
index cc9495bf238..7f6358d00d0 100644
--- a/nixos/modules/services/networking/resilio.nix
+++ b/nixos/modules/services/networking/resilio.nix
@@ -52,17 +52,22 @@ let
 
   runConfigPath = "/run/rslsync/config.json";
 
-  createConfig = pkgs.writeShellScriptBin "create-resilio-config" ''
-    ${pkgs.jq}/bin/jq \
-      '.shared_folders |= map(.secret = $ARGS.named[.dir])' \
-      ${
-        lib.concatMapStringsSep " \\\n  "
-        (entry: ''--arg '${entry.dir}' "$(cat '${entry.secretFile}')"'')
-        sharedFoldersSecretFiles
-      } \
-      <${configFile} \
-      >${runConfigPath}
-  '';
+  createConfig = pkgs.writeShellScriptBin "create-resilio-config" (
+    if cfg.sharedFolders != [ ] then ''
+      ${pkgs.jq}/bin/jq \
+        '.shared_folders |= map(.secret = $ARGS.named[.dir])' \
+        ${
+          lib.concatMapStringsSep " \\\n  "
+          (entry: ''--arg '${entry.dir}' "$(cat '${entry.secretFile}')"'')
+          sharedFoldersSecretFiles
+        } \
+        <${configFile} \
+        >${runConfigPath}
+    '' else ''
+      # no secrets, passing through config
+      cp ${configFile} ${runConfigPath};
+    ''
+  );
 
 in
 {
diff --git a/nixos/modules/services/networking/sabnzbd.nix b/nixos/modules/services/networking/sabnzbd.nix
index 8486be1bc66..8f3545df899 100644
--- a/nixos/modules/services/networking/sabnzbd.nix
+++ b/nixos/modules/services/networking/sabnzbd.nix
@@ -20,7 +20,7 @@ in
       package = mkOption {
         type = types.package;
         default = pkgs.sabnzbd;
-        defaultText = "pkgs.sabnzbd";
+        defaultText = lib.literalExpression "pkgs.sabnzbd";
         description = lib.mdDoc "The sabnzbd executable package run by the service.";
       };
 
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index 0a48e73932e..13d84736e2c 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -13,7 +13,7 @@ let
   serviceName = iface: "supplicant-${if (iface=="WLAN") then "wlan@" else (
                                      if (iface=="LAN") then "lan@" else (
                                      if (iface=="DBUS") then "dbus"
-                                     else (replaceChars [" "] ["-"] iface)))}";
+                                     else (replaceStrings [" "] ["-"] iface)))}";
 
   # TODO: Use proper privilege separation for wpa_supplicant
   supplicantService = iface: suppl:
@@ -27,7 +27,7 @@ let
       driverArg = optionalString (suppl.driver != null) "-D${suppl.driver}";
       bridgeArg = optionalString (suppl.bridge!="") "-b${suppl.bridge}";
       confFileArg = optionalString (suppl.configFile.path!=null) "-c${suppl.configFile.path}";
-      extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceChars [" "] ["-"] iface}" ''
+      extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceStrings [" "] ["-"] iface}" ''
         ${optionalString suppl.userControlled.enable "ctrl_interface=DIR=${suppl.userControlled.socketDir} GROUP=${suppl.userControlled.group}"}
         ${optionalString suppl.configFile.writable "update_config=1"}
         ${suppl.extraConf}
@@ -223,7 +223,7 @@ in
         text = ''
           ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface:
             flip (concatMapStringsSep "\n") (splitString " " iface) (i: ''
-              ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
+              ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceStrings [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
 
           ${optionalString (hasAttr "WLAN" cfg) ''
             ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service"
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index 26997dd9601..233bfdf9ebf 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -4,10 +4,7 @@ with lib;
 
 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 ];
 
@@ -38,14 +35,23 @@ in {
       defaultText = literalExpression "pkgs.tailscale";
       description = lib.mdDoc "The package to use for tailscale";
     };
+
+    useRoutingFeatures = mkOption {
+      type = types.enum [ "none" "client" "server" "both" ];
+      default = "none";
+      example = "server";
+      description = lib.mdDoc ''
+        Enables settings required for Tailscale's routing features like subnet routers and exit nodes.
+
+        To use these these features, you will still need to call `sudo tailscale up` with the relevant flags like `--advertise-exit-node` and `--exit-node`.
+
+        When set to `client` or `both`, reverse path filtering will be set to loose instead of strict.
+        When set to `server` or `both`, IP forwarding will be enabled.
+      '';
+    };
   };
 
   config = mkIf cfg.enable {
-    warnings = optional (firewallOn && rpfIsStrict) ''
-      Strict reverse path filtering breaks Tailscale exit node use and some subnet routing setups. Consider setting:
-
-        networking.firewall.checkReversePath = "loose";
-    '';
     environment.systemPackages = [ cfg.package ]; # for the CLI
     systemd.packages = [ cfg.package ];
     systemd.services.tailscaled = {
@@ -75,6 +81,13 @@ in {
       stopIfChanged = false;
     };
 
+    boot.kernel.sysctl = mkIf (cfg.useRoutingFeatures == "server" || cfg.useRoutingFeatures == "both") {
+      "net.ipv4.conf.all.forwarding" = mkDefault true;
+      "net.ipv6.conf.all.forwarding" = mkDefault true;
+    };
+
+    networking.firewall.checkReversePath = mkIf (cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both") "loose";
+
     networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ];
 
     systemd.network.networks."50-tailscale" = mkIf isNetworkd {
diff --git a/nixos/modules/services/networking/tmate-ssh-server.nix b/nixos/modules/services/networking/tmate-ssh-server.nix
index 1b8f6662ef4..f7740b1ddfc 100644
--- a/nixos/modules/services/networking/tmate-ssh-server.nix
+++ b/nixos/modules/services/networking/tmate-ssh-server.nix
@@ -44,7 +44,7 @@ in
 
     openFirewall = mkOption {
       type = types.bool;
-      default = true;
+      default = false;
       description = mdDoc "Whether to automatically open the specified ports in the firewall.";
     };
 
diff --git a/nixos/modules/services/networking/twingate.nix b/nixos/modules/services/networking/twingate.nix
new file mode 100644
index 00000000000..17140bffd21
--- /dev/null
+++ b/nixos/modules/services/networking/twingate.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.twingate;
+
+in {
+
+  options.services.twingate = {
+    enable = mkEnableOption (lib.mdDoc "Twingate Client daemon");
+  };
+
+  config = mkIf cfg.enable {
+
+    networking.firewall.checkReversePath = lib.mkDefault false;
+    networking.networkmanager.enable = true;
+
+    environment.systemPackages = [ pkgs.twingate ]; # for the CLI
+    systemd.packages = [ pkgs.twingate ];
+
+    systemd.services.twingate.preStart = ''
+      cp -r -n ${pkgs.twingate}/etc/twingate/. /etc/twingate/
+    '';
+
+    systemd.services.twingate.wantedBy = [ "multi-user.target" ];
+  };
+}
diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix
index d30f7c89633..f04242d4ab5 100644
--- a/nixos/modules/services/networking/unifi.nix
+++ b/nixos/modules/services/networking/unifi.nix
@@ -24,8 +24,8 @@ in
 
     services.unifi.jrePackage = mkOption {
       type = types.package;
-      default = pkgs.jre8;
-      defaultText = literalExpression "pkgs.jre8";
+      default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3") then pkgs.jdk11 else pkgs.jre8;
+      defaultText = literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8'';
       description = lib.mdDoc ''
         The JRE package to use. Check the release notes to ensure it is supported.
       '';
diff --git a/nixos/modules/services/networking/v2raya.nix b/nixos/modules/services/networking/v2raya.nix
new file mode 100644
index 00000000000..2d697b4fb56
--- /dev/null
+++ b/nixos/modules/services/networking/v2raya.nix
@@ -0,0 +1,39 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+{
+  options = {
+    services.v2raya = {
+      enable = options.mkEnableOption (mdDoc "the v2rayA service");
+    };
+  };
+
+  config = mkIf config.services.v2raya.enable {
+    environment.systemPackages = [ pkgs.v2raya ];
+
+    systemd.services.v2raya = {
+      unitConfig = {
+        Description = "v2rayA service";
+        Documentation = "https://github.com/v2rayA/v2rayA/wiki";
+        After = [ "network.target" "nss-lookup.target" "iptables.service" "ip6tables.service" ];
+        Wants = [ "network.target" ];
+      };
+
+      serviceConfig = {
+        User = "root";
+        ExecStart = "${getExe pkgs.v2raya} --log-disable-timestamp";
+        Environment = [ "V2RAYA_LOG_FILE=/var/log/v2raya/v2raya.log" ];
+        LimitNPROC = 500;
+        LimitNOFILE = 1000000;
+        Restart = "on-failure";
+        Type = "simple";
+      };
+
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [ iptables bash iproute2 ]; # required by v2rayA TProxy functionality
+    };
+  };
+
+  meta.maintainers = with maintainers; [ elliot ];
+}
diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index ce5616672c1..9c13f8b847d 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -315,7 +315,7 @@ let
 
   peerUnitServiceName = interfaceName: publicKey: dynamicRefreshEnabled:
     let
-      keyToUnitName = replaceChars
+      keyToUnitName = replaceStrings
         [ "/" "-"    " "     "+"     "="      ]
         [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ];
       unitName = keyToUnitName publicKey;
diff --git a/nixos/modules/services/networking/znc/options.nix b/nixos/modules/services/networking/znc/options.nix
index ce8e7a89a4d..bd67ec86d51 100644
--- a/nixos/modules/services/networking/znc/options.nix
+++ b/nixos/modules/services/networking/znc/options.nix
@@ -18,7 +18,7 @@ let
       };
 
       port = mkOption {
-        type = types.ints.u16;
+        type = types.port;
         default = 6697;
         description = lib.mdDoc ''
           IRC server port.
@@ -188,7 +188,7 @@ in
 
         port = mkOption {
           default = 5000;
-          type = types.int;
+          type = types.port;
           description = lib.mdDoc ''
             Specifies the port on which to listen.
           '';
diff --git a/nixos/modules/services/printing/ipp-usb.nix b/nixos/modules/services/printing/ipp-usb.nix
new file mode 100644
index 00000000000..0425eb91373
--- /dev/null
+++ b/nixos/modules/services/printing/ipp-usb.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, ... }: {
+  options = {
+    services.ipp-usb = {
+      enable = lib.mkEnableOption (lib.mdDoc "ipp-usb, a daemon to turn an USB printer/scanner supporting IPP everywhere (aka AirPrint, WSD, AirScan) into a locally accessible network printer/scanner");
+    };
+  };
+  config = lib.mkIf config.services.ipp-usb.enable {
+    systemd.services.ipp-usb = {
+      description = "Daemon for IPP over USB printer support";
+      after = [ "cups.service" "avahi-deamon.service" ];
+      wants = [ "avahi-daemon.service" ];
+      serviceConfig = {
+        ExecStart = [ "${pkgs.ipp-usb}/bin/ipp-usb" ];
+        Type = "simple";
+        Restart = "on-failure";
+        StateDirectory = "ipp-usb";
+        LogsDirectory = "ipp-usb";
+
+        # hardening.
+        ProtectHome = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectControlGroups = true;
+        MemoryDenyWriteExecute = true;
+        # breaks the daemon, presumably because it messes with DeviceAllow
+        ProtectClock = false;
+        ProtectKernelTunables = true;
+        ProtectKernelLogs = true;
+        ProtectSystem = "strict";
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        PrivateMounts = true;
+        ProtectHostname = true;
+        ProtectKernelModules = true;
+        RemoveIPC = true;
+        RestrictNamespaces = true;
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+        NoNewPrivileges = true;
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" "AF_INET" "AF_INET6" ];
+        ProtectProc = "noaccess";
+      };
+    };
+
+    # starts the systemd service
+    services.udev.packages = [ pkgs.ipp-usb ];
+    services.avahi = {
+      enable = true;
+      publish = {
+        enable = true;
+        userServices = true;
+      };
+    };
+    # enable printing and scanning by default, but not required.
+    services.printing.enable = lib.mkDefault true;
+    hardware.sane.enable = lib.mkDefault true;
+    # so that sane discovers scanners
+    hardware.sane.extraBackends = [ pkgs.sane-airscan ];
+  };
+}
+
+
diff --git a/nixos/modules/services/search/elasticsearch-curator.nix b/nixos/modules/services/search/elasticsearch-curator.nix
index f073ec7cf2b..0a21d705ef8 100644
--- a/nixos/modules/services/search/elasticsearch-curator.nix
+++ b/nixos/modules/services/search/elasticsearch-curator.nix
@@ -50,7 +50,7 @@ in {
     };
     port = mkOption {
       description = lib.mdDoc "the port that elasticsearch is listening on";
-      type = types.int;
+      type = types.port;
       default = 9200;
     };
     actionYAML = mkOption {
diff --git a/nixos/modules/services/search/elasticsearch.nix b/nixos/modules/services/search/elasticsearch.nix
index 4a9dd50310e..fa1627566eb 100644
--- a/nixos/modules/services/search/elasticsearch.nix
+++ b/nixos/modules/services/search/elasticsearch.nix
@@ -66,7 +66,7 @@ in
     port = mkOption {
       description = lib.mdDoc "Elasticsearch port to listen for HTTP traffic.";
       default = 9200;
-      type = types.int;
+      type = types.port;
     };
 
     tcp_port = mkOption {
diff --git a/nixos/modules/services/search/meilisearch.nix b/nixos/modules/services/search/meilisearch.nix
index 9262b927cba..3983b1b2c92 100644
--- a/nixos/modules/services/search/meilisearch.nix
+++ b/nixos/modules/services/search/meilisearch.nix
@@ -21,7 +21,7 @@ in
     package = mkOption {
       description = lib.mdDoc "The package to use for meilisearch. Use this if you require specific features to be enabled. The default package has no features.";
       default = pkgs.meilisearch;
-      defaultText = "pkgs.meilisearch";
+      defaultText = lib.literalExpression "pkgs.meilisearch";
       type = types.package;
     };
 
diff --git a/nixos/modules/services/security/opensnitch.nix b/nixos/modules/services/security/opensnitch.nix
index 8f1407b555f..98695b1ef06 100644
--- a/nixos/modules/services/security/opensnitch.nix
+++ b/nixos/modules/services/security/opensnitch.nix
@@ -91,7 +91,7 @@ in {
             InterceptUnknown = mkOption {
               type = types.bool;
               description = mdDoc ''
-                Wheter to intercept spare connections.
+                Whether to intercept spare connections.
               '';
             };
 
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 3ef0bfb090a..aaa3f5507f7 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -162,8 +162,8 @@ in {
 
     webVaultPackage = mkOption {
       type = package;
-      default = pkgs.vaultwarden-vault;
-      defaultText = literalExpression "pkgs.vaultwarden-vault";
+      default = pkgs.vaultwarden.webvault;
+      defaultText = literalExpression "pkgs.vaultwarden.webvault";
       description = lib.mdDoc "Web vault package to use.";
     };
   };
diff --git a/nixos/modules/services/system/kerberos/mit.nix b/nixos/modules/services/system/kerberos/mit.nix
index 25d7d51e808..11200014045 100644
--- a/nixos/modules/services/system/kerberos/mit.nix
+++ b/nixos/modules/services/system/kerberos/mit.nix
@@ -33,7 +33,7 @@ let
 in
 
 {
-  config = mkIf (cfg.enable && kerberos == pkgs.krb5Full) {
+  config = mkIf (cfg.enable && kerberos == pkgs.krb5) {
     systemd.services.kadmind = {
       description = "Kerberos Administration Daemon";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 9b53f5de143..cba4afb79ff 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -174,6 +174,8 @@ in
         };
       };
 
+      package = mkPackageOption pkgs "transmission" {};
+
       downloadDirPermissions = mkOption {
         type = with types; nullOr str;
         default = null;
@@ -287,7 +289,7 @@ in
           install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' /dev/stdin \
            '${cfg.home}/${settingsDir}/settings.json'
         '')];
-        ExecStart="${pkgs.transmission}/bin/transmission-daemon -f -g ${cfg.home}/${settingsDir} ${escapeShellArgs cfg.extraFlags}";
+        ExecStart="${cfg.package}/bin/transmission-daemon -f -g ${cfg.home}/${settingsDir} ${escapeShellArgs cfg.extraFlags}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         User = cfg.user;
         Group = cfg.group;
@@ -385,7 +387,7 @@ in
     };
 
     # It's useful to have transmission in path, e.g. for remote control
-    environment.systemPackages = [ pkgs.transmission ];
+    environment.systemPackages = [ cfg.package ];
 
     users.users = optionalAttrs (cfg.user == "transmission") ({
       transmission = {
@@ -457,7 +459,7 @@ in
     ];
 
     security.apparmor.policies."bin.transmission-daemon".profile = ''
-      include "${pkgs.transmission.apparmor}/bin.transmission-daemon"
+      include "${cfg.package.apparmor}/bin.transmission-daemon"
     '';
     security.apparmor.includes."local/bin.transmission-daemon" = ''
       r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
diff --git a/nixos/modules/services/video/unifi-video.nix b/nixos/modules/services/video/unifi-video.nix
index fcc3cb02a1b..450e92dd9a3 100644
--- a/nixos/modules/services/video/unifi-video.nix
+++ b/nixos/modules/services/video/unifi-video.nix
@@ -148,7 +148,7 @@ in
 
     openFirewall = mkOption {
       type = types.bool;
-      default = true;
+      default = false;
       description = lib.mdDoc ''
         Whether or not to open the required ports on the firewall.
       '';
diff --git a/nixos/modules/services/web-apps/code-server.nix b/nixos/modules/services/web-apps/code-server.nix
index 0d6b6c529b6..24e34e0c583 100644
--- a/nixos/modules/services/web-apps/code-server.nix
+++ b/nixos/modules/services/web-apps/code-server.nix
@@ -15,7 +15,7 @@ in {
 
       package = mkOption {
         default = pkgs.code-server;
-        defaultText = "pkgs.code-server";
+        defaultText = lib.literalExpression "pkgs.code-server";
         description = lib.mdDoc "Which code-server derivation to use.";
         type = types.package;
       };
diff --git a/nixos/modules/services/web-apps/hedgedoc.nix b/nixos/modules/services/web-apps/hedgedoc.nix
index ea27eb7ba39..a623e45691d 100644
--- a/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixos/modules/services/web-apps/hedgedoc.nix
@@ -449,7 +449,7 @@ in
               '';
             };
             port = mkOption {
-              type = types.int;
+              type = types.port;
               default = 9000;
               description = lib.mdDoc ''
                 Minio listen port.
diff --git a/nixos/modules/services/web-apps/invidious.nix b/nixos/modules/services/web-apps/invidious.nix
index e106478628f..a153aa3fb0c 100644
--- a/nixos/modules/services/web-apps/invidious.nix
+++ b/nixos/modules/services/web-apps/invidious.nix
@@ -151,7 +151,7 @@ in
     package = lib.mkOption {
       type = types.package;
       default = pkgs.invidious;
-      defaultText = "pkgs.invidious";
+      defaultText = lib.literalExpression "pkgs.invidious";
       description = lib.mdDoc "The Invidious package to use.";
     };
 
diff --git a/nixos/modules/services/web-apps/limesurvey.nix b/nixos/modules/services/web-apps/limesurvey.nix
index f6a1b559524..7093d1de0da 100644
--- a/nixos/modules/services/web-apps/limesurvey.nix
+++ b/nixos/modules/services/web-apps/limesurvey.nix
@@ -49,7 +49,7 @@ in
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = if cfg.database.type == "pgsql" then 5442 else 3306;
         defaultText = literalExpression "3306";
         description = lib.mdDoc "Database host port.";
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index a221186adf6..b6e2309555f 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -1,7 +1,9 @@
-{ config, lib, pkgs, ... }:
+{ lib, pkgs, config, options, ... }:
 
 let
   cfg = config.services.mastodon;
+  opt = options.services.mastodon;
+
   # We only want to create a database if we're actually going to connect to it.
   databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "/run/postgresql";
 
@@ -23,7 +25,6 @@ let
     REDIS_HOST = cfg.redis.host;
     REDIS_PORT = toString(cfg.redis.port);
     DB_HOST = cfg.database.host;
-    DB_PORT = toString(cfg.database.port);
     DB_NAME = cfg.database.name;
     LOCAL_DOMAIN = cfg.localDomain;
     SMTP_SERVER = cfg.smtp.host;
@@ -37,7 +38,8 @@ let
 
     TRUSTED_PROXY_IP = cfg.trustedProxy;
   }
-  // (if cfg.smtp.authenticate then { SMTP_LOGIN  = cfg.smtp.user; } else {})
+  // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; }
+  // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN  = cfg.smtp.user; }
   // cfg.extraConfig;
 
   systemCallsList = [ "@cpu-emulation" "@debug" "@keyring" "@ipc" "@mount" "@obsolete" "@privileged" "@setuid" ];
@@ -92,12 +94,18 @@ let
       ] else []
     ) env))));
 
-  mastodonEnv = pkgs.writeShellScriptBin "mastodon-env" ''
+  mastodonTootctl = pkgs.writeShellScriptBin "mastodon-tootctl" ''
+    #! ${pkgs.runtimeShell}
     set -a
     export RAILS_ROOT="${cfg.package}"
     source "${envFile}"
     source /var/lib/mastodon/.secrets_env
-    eval -- "\$@"
+
+    sudo=exec
+    if [[ "$USER" != ${cfg.user} ]]; then
+      sudo='exec /run/wrappers/bin/sudo -u ${cfg.user} --preserve-env'
+    fi
+    $sudo ${cfg.package}/bin/tootctl "$@"
   '';
 
 in {
@@ -133,15 +141,10 @@ in {
         description = lib.mdDoc ''
           User under which mastodon runs. If it is set to "mastodon",
           that user will be created, otherwise it should be set to the
-          name of a user created elsewhere.  In both cases,
-          `mastodon` and a package containing only
-          the shell script `mastodon-env` will be added to
-          the user's package set. To run a command from
-          `mastodon` such as `tootctl`
-          with the environment configured by this module use
-          `mastodon-env`, as in:
-
-          `mastodon-env tootctl accounts create newuser --email newuser@example.com`
+          name of a user created elsewhere.
+          In both cases, the `mastodon` package will be added to the user's package set
+          and a tootctl wrapper to system packages that switches to the configured account
+          and load the right environment.
         '';
         type = lib.types.str;
         default = "mastodon";
@@ -313,8 +316,13 @@ in {
         };
 
         port = lib.mkOption {
-          type = lib.types.port;
-          default = 5432;
+          type = lib.types.nullOr lib.types.port;
+          default = if cfg.database.createLocally then null else 5432;
+          defaultText = lib.literalExpression ''
+            if config.${opt.database.createLocally}
+            then null
+            else 5432
+          '';
           description = lib.mdDoc "Database host port.";
         };
 
@@ -332,8 +340,8 @@ in {
 
         passwordFile = lib.mkOption {
           type = lib.types.nullOr lib.types.path;
-          default = "/var/lib/mastodon/secrets/db-password";
-          example = "/run/keys/mastodon-db-password";
+          default = null;
+          example = "/var/lib/mastodon/secrets/db-password";
           description = lib.mdDoc ''
             A file containing the password corresponding to
             {option}`database.user`.
@@ -467,7 +475,18 @@ in {
     assertions = [
       {
         assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user);
-        message = ''For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user and services.mastodon.database.user must be identical.'';
+        message = ''
+          For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer
+            authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user
+            and services.mastodon.database.user must be identical.
+        '';
+      }
+      {
+        assertion = !databaseActuallyCreateLocally -> (cfg.database.host != "/run/postgresql");
+        message = ''
+          <option>services.mastodon.database.host</option> needs to be set if
+            <option>services.mastodon.database.createLocally</option> is not enabled.
+        '';
       }
       {
         assertion = cfg.smtp.authenticate -> (cfg.smtp.user != null);
@@ -485,6 +504,8 @@ in {
       }
     ];
 
+    environment.systemPackages = [ mastodonTootctl ];
+
     systemd.services.mastodon-init-dirs = {
       script = ''
         umask 077
@@ -509,10 +530,11 @@ in {
         OTP_SECRET="$(cat ${cfg.otpSecretFile})"
         VAPID_PRIVATE_KEY="$(cat ${cfg.vapidPrivateKeyFile})"
         VAPID_PUBLIC_KEY="$(cat ${cfg.vapidPublicKeyFile})"
+      '' + lib.optionalString (cfg.database.passwordFile != null) ''
         DB_PASS="$(cat ${cfg.database.passwordFile})"
-      '' + (if cfg.smtp.authenticate then ''
+      '' + lib.optionalString cfg.smtp.authenticate ''
         SMTP_PASSWORD="$(cat ${cfg.smtp.passwordFile})"
-      '' else "") + ''
+      '' + ''
         EOF
       '';
       environment = env;
@@ -527,7 +549,16 @@ in {
     };
 
     systemd.services.mastodon-init-db = lib.mkIf cfg.automaticMigrations {
-      script = ''
+      script = lib.optionalString (!databaseActuallyCreateLocally) ''
+        umask 077
+
+        export PGPASSFILE
+        PGPASSFILE=$(mktemp)
+        cat > $PGPASSFILE <<EOF
+        ${cfg.database.host}:${toString cfg.database.port}:${cfg.database.name}:${cfg.database.user}:$(cat ${cfg.database.passwordFile})
+        EOF
+
+      '' + ''
         if [ `psql ${cfg.database.name} -c \
                 "select count(*) from pg_class c \
                 join pg_namespace s on s.oid = c.relnamespace \
@@ -538,12 +569,18 @@ in {
         else
           rails db:migrate
         fi
+      '' +  lib.optionalString (!databaseActuallyCreateLocally) ''
+        rm $PGPASSFILE
+        unset PGPASSFILE
       '';
       path = [ cfg.package pkgs.postgresql ];
-      environment = env;
+      environment = env // lib.optionalAttrs (!databaseActuallyCreateLocally) {
+        PGHOST = cfg.database.host;
+        PGUSER = cfg.database.user;
+      };
       serviceConfig = {
         Type = "oneshot";
-        EnvironmentFile = "/var/lib/mastodon/.secrets_env";
+        EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
         WorkingDirectory = cfg.package;
         # System Call Filtering
         SystemCallFilter = [ ("~" + lib.concatStringsSep " " (systemCallsList ++ [ "@resources" ])) "@chown" "pipe" "pipe2" ];
@@ -571,7 +608,7 @@ in {
         ExecStart = "${cfg.package}/run-streaming.sh";
         Restart = "always";
         RestartSec = 20;
-        EnvironmentFile = "/var/lib/mastodon/.secrets_env";
+        EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
         WorkingDirectory = cfg.package;
         # Runtime directory and mode
         RuntimeDirectory = "mastodon-streaming";
@@ -598,7 +635,7 @@ in {
         ExecStart = "${cfg.package}/bin/puma -C config/puma.rb";
         Restart = "always";
         RestartSec = 20;
-        EnvironmentFile = "/var/lib/mastodon/.secrets_env";
+        EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
         WorkingDirectory = cfg.package;
         # Runtime directory and mode
         RuntimeDirectory = "mastodon-web";
@@ -626,7 +663,7 @@ in {
         ExecStart = "${cfg.package}/bin/sidekiq -c ${toString cfg.sidekiqThreads} -r ${cfg.package}";
         Restart = "always";
         RestartSec = 20;
-        EnvironmentFile = "/var/lib/mastodon/.secrets_env";
+        EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
         WorkingDirectory = cfg.package;
         # System Call Filtering
         SystemCallFilter = [ ("~" + lib.concatStringsSep " " systemCallsList) "@chown" "pipe" "pipe2" ];
@@ -639,7 +676,7 @@ in {
       environment = env;
       serviceConfig = {
         Type = "oneshot";
-        EnvironmentFile = "/var/lib/mastodon/.secrets_env";
+        EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
       } // cfgService;
       script = let
         olderThanDays = toString cfg.mediaAutoRemove.olderThanDays;
@@ -655,8 +692,9 @@ in {
       recommendedProxySettings = true; # required for redirections to work
       virtualHosts."${cfg.localDomain}" = {
         root = "${cfg.package}/public/";
-        forceSSL = true; # mastodon only supports https
-        enableACME = true;
+        # mastodon only supports https, but you can override this if you offload tls elsewhere.
+        forceSSL = lib.mkDefault true;
+        enableACME = lib.mkDefault true;
 
         locations."/system/".alias = "/var/lib/mastodon/public-system/";
 
@@ -704,7 +742,7 @@ in {
           inherit (cfg) group;
         };
       })
-      (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package mastodonEnv pkgs.imagemagick ])
+      (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package pkgs.imagemagick ])
     ];
 
     users.groups.${cfg.group}.members = lib.optional cfg.configureNginx config.services.nginx.user;
diff --git a/nixos/modules/services/web-apps/mattermost.nix b/nixos/modules/services/web-apps/mattermost.nix
index 71292c47d63..99042821f5e 100644
--- a/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixos/modules/services/web-apps/mattermost.nix
@@ -106,7 +106,7 @@ in
       package = mkOption {
         type = types.package;
         default = pkgs.mattermost;
-        defaultText = "pkgs.mattermost";
+        defaultText = lib.literalExpression "pkgs.mattermost";
         description = lib.mdDoc "Mattermost derivation to use.";
       };
 
@@ -238,7 +238,7 @@ in
         package = mkOption {
           type = types.package;
           default = pkgs.matterircd;
-          defaultText = "pkgs.matterircd";
+          defaultText = lib.literalExpression "pkgs.matterircd";
           description = lib.mdDoc "matterircd derivation to use.";
         };
         parameters = mkOption {
diff --git a/nixos/modules/services/web-apps/miniflux.nix b/nixos/modules/services/web-apps/miniflux.nix
index 34a108cebd2..7cc8ce10ffe 100644
--- a/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixos/modules/services/web-apps/miniflux.nix
@@ -21,6 +21,13 @@ in
     services.miniflux = {
       enable = mkEnableOption (lib.mdDoc "miniflux and creates a local postgres database for it");
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.miniflux;
+        defaultText = literalExpression "pkgs.miniflux";
+        description = lib.mdDoc "Miniflux package to use.";
+      };
+
       config = mkOption {
         type = types.attrsOf types.str;
         example = literalExpression ''
@@ -89,7 +96,7 @@ in
       after = [ "network.target" "postgresql.service" "miniflux-dbsetup.service" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.miniflux}/bin/miniflux";
+        ExecStart = "${cfg.package}/bin/miniflux";
         User = dbUser;
         DynamicUser = true;
         RuntimeDirectory = "miniflux";
@@ -122,6 +129,6 @@ in
 
       environment = cfg.config;
     };
-    environment.systemPackages = [ pkgs.miniflux ];
+    environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixos/modules/services/web-apps/moodle.nix b/nixos/modules/services/web-apps/moodle.nix
index dc434d0fc80..5f8d9c5b15f 100644
--- a/nixos/modules/services/web-apps/moodle.nix
+++ b/nixos/modules/services/web-apps/moodle.nix
@@ -96,7 +96,7 @@ in
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         description = lib.mdDoc "Database host port.";
         default = {
           mysql = 3306;
diff --git a/nixos/modules/services/web-apps/onlyoffice.nix b/nixos/modules/services/web-apps/onlyoffice.nix
index db4a9582794..1478e8da87a 100644
--- a/nixos/modules/services/web-apps/onlyoffice.nix
+++ b/nixos/modules/services/web-apps/onlyoffice.nix
@@ -29,7 +29,7 @@ in
     package = mkOption {
       type = types.package;
       default = pkgs.onlyoffice-documentserver;
-      defaultText = "pkgs.onlyoffice-documentserver";
+      defaultText = lib.literalExpression "pkgs.onlyoffice-documentserver";
       description = lib.mdDoc "Which package to use for the OnlyOffice instance.";
     };
 
diff --git a/nixos/modules/services/web-apps/peering-manager.nix b/nixos/modules/services/web-apps/peering-manager.nix
new file mode 100644
index 00000000000..0db2e8e4aed
--- /dev/null
+++ b/nixos/modules/services/web-apps/peering-manager.nix
@@ -0,0 +1,265 @@
+{ config, lib, pkgs, buildEnv, ... }:
+
+with lib;
+
+let
+  cfg = config.services.peering-manager;
+  configFile = pkgs.writeTextFile {
+    name = "configuration.py";
+    text = ''
+      ALLOWED_HOSTS = ['*']
+      DATABASE = {
+        'NAME': 'peering-manager',
+        'USER': 'peering-manager',
+        'HOST': '/run/postgresql',
+      }
+
+      # Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate
+      # configuration exists for each. Full connection details are required in both sections, and it is strongly recommended
+      # to use two separate database IDs.
+      REDIS = {
+        'tasks': {
+          'UNIX_SOCKET_PATH': '${config.services.redis.servers.peering-manager.unixSocket}',
+          'DATABASE': 0,
+        },
+        'caching': {
+          'UNIX_SOCKET_PATH': '${config.services.redis.servers.peering-manager.unixSocket}',
+          'DATABASE': 1,
+        }
+      }
+
+      with open("${cfg.secretKeyFile}", "r") as file:
+        SECRET_KEY = file.readline()
+    '' + lib.optionalString (cfg.peeringdbApiKeyFile != null) ''
+      with open("${cfg.peeringdbApiKeyFile}", "r") as file:
+        PEERINGDB_API_KEY = file.readline()
+    '' + ''
+
+      ${cfg.extraConfig}
+    '';
+  };
+  pkg = (pkgs.peering-manager.overrideAttrs (old: {
+    postInstall = ''
+      ln -s ${configFile} $out/opt/peering-manager/peering_manager/configuration.py
+    '' + optionalString cfg.enableLdap ''
+      ln -s ${cfg.ldapConfigPath} $out/opt/peering-manager/peering_manager/ldap_config.py
+    '';
+  })).override {
+    inherit (cfg) plugins;
+  };
+  peeringManagerManageScript = with pkgs; (writeScriptBin "peering-manager-manage" ''
+    #!${stdenv.shell}
+    export PYTHONPATH=${pkg.pythonPath}
+    sudo -u peering-manager ${pkg}/bin/peering-manager "$@"
+  '');
+
+in {
+  options.services.peering-manager = {
+    enable = mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Enable Peering Manager.
+
+        This module requires a reverse proxy that serves `/static` separately.
+        See this [example](https://github.com/peering-manager-community/peering-manager/blob/develop/contrib/nginx.conf/) on how to configure this.
+      '';
+    };
+
+    listenAddress = mkOption {
+      type = types.str;
+      default = "[::1]";
+      description = lib.mdDoc ''
+        Address the server will listen on.
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8001;
+      description = lib.mdDoc ''
+        Port the server will listen on.
+      '';
+    };
+
+    plugins = mkOption {
+      type = types.functionTo (types.listOf types.package);
+      default = _: [];
+      defaultText = literalExpression ''
+        python3Packages: with python3Packages; [];
+      '';
+      description = lib.mdDoc ''
+        List of plugin packages to install.
+      '';
+    };
+
+    secretKeyFile = mkOption {
+      type = types.path;
+      description = lib.mdDoc ''
+        Path to a file containing the secret key.
+      '';
+    };
+
+    peeringdbApiKeyFile = mkOption {
+      type = with types; nullOr path;
+      default = null;
+      description = lib.mdDoc ''
+        Path to a file containing the PeeringDB API key.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = lib.mdDoc ''
+        Additional lines of configuration appended to the `configuration.py`.
+        See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
+      '';
+    };
+
+    enableLdap = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Enable LDAP-Authentication for Peering Manager.
+
+        This requires a configuration file being pass through `ldapConfigPath`.
+      '';
+    };
+
+    ldapConfigPath = mkOption {
+      type = types.path;
+      description = lib.mdDoc ''
+        Path to the Configuration-File for LDAP-Authentification, will be loaded as `ldap_config.py`.
+        See the [documentation](https://peering-manager.readthedocs.io/en/stable/setup/6-ldap/#configuration) for possible options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.peering-manager.plugins = mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
+
+    system.build.peeringManagerPkg = pkg;
+
+    services.redis.servers.peering-manager.enable = true;
+
+    services.postgresql = {
+      enable = true;
+      ensureDatabases = [ "peering-manager" ];
+      ensureUsers = [
+        {
+          name = "peering-manager";
+          ensurePermissions = {
+            "DATABASE \"peering-manager\"" = "ALL PRIVILEGES";
+          };
+        }
+      ];
+    };
+
+    environment.systemPackages = [ peeringManagerManageScript ];
+
+    systemd.targets.peering-manager = {
+      description = "Target for all Peering Manager services";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" "redis-peering-manager.service" ];
+    };
+
+    systemd.services = let
+      defaultServiceConfig = {
+        WorkingDirectory = "/var/lib/peering-manager";
+        User = "peering-manager";
+        Group = "peering-manager";
+        StateDirectory = "peering-manager";
+        StateDirectoryMode = "0750";
+        Restart = "on-failure";
+      };
+    in {
+      peering-manager-migration = {
+        description = "Peering Manager migrations";
+        wantedBy = [ "peering-manager.target" ];
+
+        environment = {
+          PYTHONPATH = pkg.pythonPath;
+        };
+
+        serviceConfig = defaultServiceConfig // {
+          Type = "oneshot";
+          ExecStart = ''
+            ${pkg}/bin/peering-manager migrate
+          '';
+        };
+      };
+
+      peering-manager = {
+        description = "Peering Manager WSGI Service";
+        wantedBy = [ "peering-manager.target" ];
+        after = [ "peering-manager-migration.service" ];
+
+        preStart = ''
+          ${pkg}/bin/peering-manager remove_stale_contenttypes --no-input
+        '';
+
+        environment = {
+          PYTHONPATH = pkg.pythonPath;
+        };
+
+        serviceConfig = defaultServiceConfig // {
+          ExecStart = ''
+            ${pkg.python.pkgs.gunicorn}/bin/gunicorn peering_manager.wsgi \
+              --bind ${cfg.listenAddress}:${toString cfg.port} \
+              --pythonpath ${pkg}/opt/peering-manager
+          '';
+        };
+      };
+
+      peering-manager-rq = {
+        description = "Peering Manager Request Queue Worker";
+        wantedBy = [ "peering-manager.target" ];
+        after = [ "peering-manager.service" ];
+
+        environment = {
+          PYTHONPATH = pkg.pythonPath;
+        };
+
+        serviceConfig = defaultServiceConfig // {
+          ExecStart = ''
+            ${pkg}/bin/peering-manager rqworker high default low
+          '';
+        };
+      };
+
+      peering-manager-housekeeping = {
+        description = "Peering Manager housekeeping job";
+        after = [ "peering-manager.service" ];
+
+        environment = {
+          PYTHONPATH = pkg.pythonPath;
+        };
+
+        serviceConfig = defaultServiceConfig // {
+          Type = "oneshot";
+          ExecStart = ''
+            ${pkg}/bin/peering-manager housekeeping
+          '';
+        };
+      };
+    };
+
+    systemd.timers.peering-manager-housekeeping = {
+      description = "Run Peering Manager housekeeping job";
+      wantedBy = [ "timers.target" ];
+
+      timerConfig = {
+        OnCalendar = "daily";
+      };
+    };
+
+    users.users.peering-manager = {
+      home = "/var/lib/peering-manager";
+      isSystemUser = true;
+      group = "peering-manager";
+    };
+    users.groups.peering-manager = {};
+    users.groups."${config.services.redis.servers.peering-manager.user}".members = [ "peering-manager" ];
+  };
+}
diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix
index 0e43922f35d..2cea7e7cea7 100644
--- a/nixos/modules/services/web-apps/zabbix.nix
+++ b/nixos/modules/services/web-apps/zabbix.nix
@@ -51,7 +51,7 @@ in
 
       server = {
         port = mkOption {
-          type = types.int;
+          type = types.port;
           description = lib.mdDoc "The port of the Zabbix server to connect to.";
           default = 10051;
         };
@@ -78,7 +78,7 @@ in
         };
 
         port = mkOption {
-          type = types.int;
+          type = types.port;
           default =
             if cfg.database.type == "mysql" then config.services.mysql.port
             else if cfg.database.type == "pgsql" then config.services.postgresql.port
diff --git a/nixos/modules/services/web-servers/lighttpd/default.nix b/nixos/modules/services/web-servers/lighttpd/default.nix
index ec847495d74..811afe8e0af 100644
--- a/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -137,7 +137,7 @@ in
 
       package = mkOption {
         default = pkgs.lighttpd;
-        defaultText = "pkgs.lighttpd";
+        defaultText = lib.literalExpression "pkgs.lighttpd";
         type = types.package;
         description = lib.mdDoc ''
           lighttpd package to use.
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index e3d4afc074c..2bdc046c0d8 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -29,7 +29,7 @@ with lib;
     listen = mkOption {
       type = with types; listOf (submodule { options = {
         addr = mkOption { type = str;  description = lib.mdDoc "IP address.";  };
-        port = mkOption { type = int;  description = lib.mdDoc "Port number."; default = 80; };
+        port = mkOption { type = port;  description = lib.mdDoc "Port number."; default = 80; };
         ssl  = mkOption { type = bool; description = lib.mdDoc "Enable SSL.";  default = false; };
         extraParameters = mkOption { type = listOf str; description = lib.mdDoc "Extra parameters of this listen directive."; default = []; example = [ "backlog=1024" "deferred" ]; };
       }; });
diff --git a/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index 25de29554b1..aaad1de5f87 100644
--- a/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -67,13 +67,17 @@ in
 
         # Taken from mint-artwork.gschema.override
         theme = mkIf (notExcluded pkgs.cinnamon.mint-themes) {
-          name = mkDefault "Mint-X";
+          name = mkDefault "Mint-Y-Aqua";
           package = mkDefault pkgs.cinnamon.mint-themes;
         };
         iconTheme = mkIf (notExcluded pkgs.cinnamon.mint-x-icons) {
-          name = mkDefault "Mint-X-Dark";
+          name = mkDefault "Mint-Y-Aqua";
           package = mkDefault pkgs.cinnamon.mint-x-icons;
         };
+        cursorTheme = mkIf (notExcluded pkgs.cinnamon.mint-cursor-themes) {
+          name = mkDefault "Bibata-Modern-Classic";
+          package = mkDefault pkgs.cinnamon.mint-cursor-themes;
+        };
       };
       services.xserver.displayManager.sessionCommands = ''
         if test "$XDG_CURRENT_DESKTOP" = "Cinnamon"; then
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index a6ab3053087..2ab24951ec6 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -384,6 +384,11 @@ in
         ++ lib.optionals config.services.samba.enable [ kdenetwork-filesharing pkgs.samba ]
         ++ lib.optional config.services.xserver.wacom.enable pkgs.wacomtablet;
 
+      # Extra services for D-Bus activation
+      services.dbus.packages = [
+        plasma5.kactivitymanagerd
+      ];
+
       environment.pathsToLink = [
         # FIXME: modules should link subdirs of `/share` rather than relying on this
         "/share"
@@ -446,6 +451,9 @@ in
 
       xdg.portal.enable = true;
       xdg.portal.extraPortals = [ plasma5.xdg-desktop-portal-kde ];
+      # xdg-desktop-portal-kde expects PipeWire to be running.
+      # This does not, by default, replace PulseAudio.
+      services.pipewire.enable = mkDefault true;
 
       # Update the start menu for each user that is currently logged in
       system.userActivationScripts.plasmaSetup = activationScript;
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix
index 00fa8af71dc..4456374cc56 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/slick.nix
@@ -11,6 +11,7 @@ let
   theme = cfg.theme.package;
   icons = cfg.iconTheme.package;
   font = cfg.font.package;
+  cursors = cfg.cursorTheme.package;
 
   slickGreeterConf = writeText "slick-greeter.conf" ''
     [Greeter]
@@ -18,6 +19,8 @@ let
     theme-name=${cfg.theme.name}
     icon-theme-name=${cfg.iconTheme.name}
     font-name=${cfg.font.name}
+    cursor-theme-name=${cfg.cursorTheme.name}
+    cursor-theme-size=${toString cfg.cursorTheme.size}
     draw-user-backgrounds=${boolToString cfg.draw-user-backgrounds}
     ${cfg.extraConfig}
   '';
@@ -84,6 +87,33 @@ in
         };
       };
 
+      cursorTheme = {
+        package = mkOption {
+          type = types.package;
+          default = pkgs.gnome.adwaita-icon-theme;
+          defaultText = literalExpression "pkgs.gnome.adwaita-icon-theme";
+          description = lib.mdDoc ''
+            The package path that contains the cursor theme given in the name option.
+          '';
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "Adwaita";
+          description = lib.mdDoc ''
+            Name of the cursor theme to use for the lightdm-slick-greeter.
+          '';
+        };
+
+        size = mkOption {
+          type = types.int;
+          default = 24;
+          description = lib.mdDoc ''
+            Size of the cursor theme to use for the lightdm-slick-greeter.
+          '';
+        };
+      };
+
       draw-user-backgrounds = mkEnableOption (lib.mdDoc "draw user backgrounds");
 
       extraConfig = mkOption {
@@ -107,6 +137,7 @@ in
     };
 
     environment.systemPackages = [
+      cursors
       icons
       theme
     ];
diff --git a/nixos/modules/services/x11/window-managers/katriawm.nix b/nixos/modules/services/x11/window-managers/katriawm.nix
new file mode 100644
index 00000000000..106631792ff
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/katriawm.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mdDoc mkEnableOption mkIf mkPackageOption singleton;
+  cfg = config.services.xserver.windowManager.katriawm;
+in
+{
+  ###### interface
+  options = {
+    services.xserver.windowManager.katriawm = {
+      enable = mkEnableOption (mdDoc "katriawm");
+      package = mkPackageOption pkgs "katriawm" {};
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton {
+      name = "katriawm";
+      start = ''
+        ${cfg.package}/bin/katriawm &
+        waitPID=$!
+      '';
+    };
+    environment.systemPackages = [ cfg.package ];
+  };
+}