about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2023-10-20 22:09:03 +0000
committerAlyssa Ross <hi@alyssa.is>2023-10-20 22:09:03 +0000
commit50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e (patch)
treef2556b911180125ccbb7ed0e78a54e92da89adce /nixpkgs/nixos/modules/services
parent4c16d4548a98563c9d9ad76f4e5b2202864ccd54 (diff)
parentcfc75eec4603c06503ae750f88cf397e00796ea8 (diff)
downloadnixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.tar
nixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.tar.gz
nixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.tar.bz2
nixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.tar.lz
nixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.tar.xz
nixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.tar.zst
nixlib-50c21d167f7114fa1dbd95e5c4fb30eeb1a2d02e.zip
Merge commit 'cfc75eec4603c06503ae750f88cf397e00796ea8'
Conflicts:
	nixpkgs/pkgs/build-support/rust/build-rust-package/default.nix
Diffstat (limited to 'nixpkgs/nixos/modules/services')
-rw-r--r--nixpkgs/nixos/modules/services/audio/castopod.md22
-rw-r--r--nixpkgs/nixos/modules/services/audio/castopod.nix287
-rw-r--r--nixpkgs/nixos/modules/services/audio/gonic.nix1
-rw-r--r--nixpkgs/nixos/modules/services/audio/wyoming/openwakeword.nix157
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgbackup.nix11
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgmatic.nix57
-rw-r--r--nixpkgs/nixos/modules/services/backup/btrbk.nix4
-rw-r--r--nixpkgs/nixos/modules/services/backup/znapzend.nix20
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/buildbot/master.nix18
-rw-r--r--nixpkgs/nixos/modules/services/databases/cassandra.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/ferretdb.nix79
-rw-r--r--nixpkgs/nixos/modules/services/databases/pgmanage.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/postgresql.nix25
-rw-r--r--nixpkgs/nixos/modules/services/databases/redis.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/surrealdb.nix2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/deepin/app-services.nix2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/deepin/dde-api.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/deepin/dde-daemon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/gnome/gnome-browser-connector.nix6
-rw-r--r--nixpkgs/nixos/modules/services/games/xonotic.nix198
-rw-r--r--nixpkgs/nixos/modules/services/hardware/kanata.nix16
-rw-r--r--nixpkgs/nixos/modules/services/hardware/keyd.nix32
-rw-r--r--nixpkgs/nixos/modules/services/hardware/supergfxd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/hardware/tlp.nix2
-rw-r--r--nixpkgs/nixos/modules/services/hardware/tuxedo-rs.nix49
-rw-r--r--nixpkgs/nixos/modules/services/home-automation/home-assistant.nix5
-rw-r--r--nixpkgs/nixos/modules/services/logging/ulogd.nix25
-rw-r--r--nixpkgs/nixos/modules/services/mail/dovecot.nix2
-rw-r--r--nixpkgs/nixos/modules/services/mail/mailman.nix8
-rw-r--r--nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix4
-rw-r--r--nixpkgs/nixos/modules/services/matrix/mjolnir.nix4
-rw-r--r--nixpkgs/nixos/modules/services/matrix/synapse.md2
-rw-r--r--nixpkgs/nixos/modules/services/matrix/synapse.nix111
-rw-r--r--nixpkgs/nixos/modules/services/misc/dysnomia.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitea.nix15
-rw-r--r--nixpkgs/nixos/modules/services/misc/gollum.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/gpsd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/misc/klipper.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/mbpfan.nix19
-rw-r--r--nixpkgs/nixos/modules/services/misc/moonraker.nix10
-rw-r--r--nixpkgs/nixos/modules/services/misc/packagekit.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless.nix28
-rw-r--r--nixpkgs/nixos/modules/services/misc/rshim.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/sourcehut/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/tp-auto-kbbl.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/xmr-stak.nix8
-rw-r--r--nixpkgs/nixos/modules/services/misc/zoneminder.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/librenms.nix624
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/mackerel-agent.nix6
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix2
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix46
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix2
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix71
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix145
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix47
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix2
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/kubo.nix2
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/openafs/server.nix4
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix2
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/samba.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/connman.nix69
-rw-r--r--nixpkgs/nixos/modules/services/networking/coredns.nix9
-rw-r--r--nixpkgs/nixos/modules/services/networking/create_ap.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/dae.nix63
-rw-r--r--nixpkgs/nixos/modules/services/networking/deconz.nix125
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnsmasq.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/firefox-syncserver.md2
-rw-r--r--nixpkgs/nixos/modules/services/networking/firefox-syncserver.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/go-neb.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/hostapd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/hylafax/options.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/i2pd.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/knot.nix136
-rw-r--r--nixpkgs/nixos/modules/services/networking/mtr-exporter.nix111
-rw-r--r--nixpkgs/nixos/modules/services/networking/nar-serve.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/netclient.nix27
-rw-r--r--nixpkgs/nixos/modules/services/networking/nftables.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/searx.nix33
-rw-r--r--nixpkgs/nixos/modules/services/networking/snowflake-proxy.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix56
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix32
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinyproxy.nix103
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/wg-quick.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/wireguard.nix11
-rw-r--r--nixpkgs/nixos/modules/services/networking/yggdrasil.nix13
-rw-r--r--nixpkgs/nixos/modules/services/search/typesense.nix4
-rw-r--r--nixpkgs/nixos/modules/services/security/fail2ban.nix2
-rw-r--r--nixpkgs/nixos/modules/services/security/opensnitch.nix2
-rw-r--r--nixpkgs/nixos/modules/services/security/tang.nix95
-rw-r--r--nixpkgs/nixos/modules/services/security/usbguard.nix21
-rw-r--r--nixpkgs/nixos/modules/services/system/earlyoom.nix2
-rw-r--r--nixpkgs/nixos/modules/services/system/systembus-notify.nix2
-rw-r--r--nixpkgs/nixos/modules/services/torrent/flexget.nix2
-rw-r--r--nixpkgs/nixos/modules/services/torrent/transmission.nix2
-rw-r--r--nixpkgs/nixos/modules/services/video/mediamtx.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/audiobookshelf.nix90
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/cloudlog.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dex.nix7
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/grocy.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix1226
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/hledger-web.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/honk.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/isso.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mediawiki.nix94
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/microbin.nix93
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/netbox.nix13
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.md8
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix122
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/outline.nix50
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/peering-manager.nix234
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/phylactery.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plausible.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/rimgo.nix107
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/snipe-it.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/wordpress.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/writefreely.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/zitadel.nix223
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/garage.nix23
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/keter/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix35
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/rustus.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix22
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix10
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix29
-rw-r--r--nixpkgs/nixos/modules/services/x11/extra-layouts.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/xserver.nix119
136 files changed, 4104 insertions, 1664 deletions
diff --git a/nixpkgs/nixos/modules/services/audio/castopod.md b/nixpkgs/nixos/modules/services/audio/castopod.md
new file mode 100644
index 000000000000..ee8590737a7c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/castopod.md
@@ -0,0 +1,22 @@
+# Castopod {#module-services-castopod}
+
+Castopod is an open-source hosting platform made for podcasters who want to engage and interact with their audience.
+
+## Quickstart {#module-services-castopod-quickstart}
+
+Use the following configuration to start a public instance of Castopod on `castopod.example.com` domain:
+
+```nix
+networking.firewall.allowedTCPPorts = [ 80 443 ];
+services.castopod = {
+  enable = true;
+  database.createLocally = true;
+  nginx.virtualHost = {
+    serverName = "castopod.example.com";
+    enableACME = true;
+    forceSSL = true;
+  };
+};
+```
+
+Go to `https://castopod.example.com/cp-install` to create superadmin account after applying the above configuration.
diff --git a/nixpkgs/nixos/modules/services/audio/castopod.nix b/nixpkgs/nixos/modules/services/audio/castopod.nix
new file mode 100644
index 000000000000..b782b5489147
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/castopod.nix
@@ -0,0 +1,287 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.castopod;
+  fpm = config.services.phpfpm.pools.castopod;
+
+  user = "castopod";
+  stateDirectory = "/var/lib/castopod";
+
+  # https://docs.castopod.org/getting-started/install.html#requirements
+  phpPackage = pkgs.php.withExtensions ({ enabled, all }: with all; [
+    intl
+    curl
+    mbstring
+    gd
+    exif
+    mysqlnd
+  ] ++ enabled);
+in
+{
+  meta.doc = ./castopod.md;
+  meta.maintainers = with lib.maintainers; [ alexoundos misuzu ];
+
+  options.services = {
+    castopod = {
+      enable = lib.mkEnableOption (lib.mdDoc "Castopod");
+      package = lib.mkOption {
+        type = lib.types.package;
+        default = pkgs.castopod;
+        defaultText = lib.literalMD "pkgs.castopod";
+        description = lib.mdDoc "Which Castopod package to use.";
+      };
+      database = {
+        createLocally = lib.mkOption {
+          type = lib.types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Create the database and database user locally.
+          '';
+        };
+        hostname = lib.mkOption {
+          type = lib.types.str;
+          default = "localhost";
+          description = lib.mdDoc "Database hostname.";
+        };
+        name = lib.mkOption {
+          type = lib.types.str;
+          default = "castopod";
+          description = lib.mdDoc "Database name.";
+        };
+        user = lib.mkOption {
+          type = lib.types.str;
+          default = user;
+          description = lib.mdDoc "Database user.";
+        };
+        passwordFile = lib.mkOption {
+          type = lib.types.nullOr lib.types.path;
+          default = null;
+          example = "/run/keys/castopod-dbpassword";
+          description = lib.mdDoc ''
+            A file containing the password corresponding to
+            [](#opt-services.castopod.database.user).
+          '';
+        };
+      };
+      settings = lib.mkOption {
+        type = with lib.types; attrsOf (oneOf [ str int bool ]);
+        default = { };
+        example = {
+          "email.protocol" = "smtp";
+          "email.SMTPHost" = "localhost";
+          "email.SMTPUser" = "myuser";
+          "email.fromEmail" = "castopod@example.com";
+        };
+        description = lib.mdDoc ''
+          Environment variables used for Castopod.
+          See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
+          for available environment variables.
+        '';
+      };
+      environmentFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/run/keys/castopod-env";
+        description = lib.mdDoc ''
+          Environment file to inject e.g. secrets into the configuration.
+          See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
+          for available environment variables.
+        '';
+      };
+      configureNginx = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc "Configure nginx as a reverse proxy for CastoPod.";
+      };
+      localDomain = lib.mkOption {
+        type = lib.types.str;
+        example = "castopod.example.org";
+        description = lib.mdDoc "The domain serving your CastoPod instance.";
+      };
+      poolSettings = lib.mkOption {
+        type = with lib.types; attrsOf (oneOf [ str int bool ]);
+        default = {
+          "pm" = "dynamic";
+          "pm.max_children" = "32";
+          "pm.start_servers" = "2";
+          "pm.min_spare_servers" = "2";
+          "pm.max_spare_servers" = "4";
+          "pm.max_requests" = "500";
+        };
+        description = lib.mdDoc ''
+          Options for Castopod's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.castopod.settings =
+      let
+        sslEnabled = with config.services.nginx.virtualHosts.${cfg.localDomain}; addSSL || forceSSL || onlySSL || enableACME || useACMEHost != null;
+        baseURL = "http${lib.optionalString sslEnabled "s"}://${cfg.localDomain}";
+      in
+      lib.mapAttrs (name: lib.mkDefault) {
+        "app.forceGlobalSecureRequests" = sslEnabled;
+        "app.baseURL" = baseURL;
+
+        "media.baseURL" = "/";
+        "media.root" = "media";
+        "media.storage" = stateDirectory;
+
+        "admin.gateway" = "admin";
+        "auth.gateway" = "auth";
+
+        "database.default.hostname" = cfg.database.hostname;
+        "database.default.database" = cfg.database.name;
+        "database.default.username" = cfg.database.user;
+        "database.default.DBPrefix" = "cp_";
+
+        "cache.handler" = "file";
+      };
+
+    services.phpfpm.pools.castopod = {
+      inherit user;
+      group = config.services.nginx.group;
+      phpPackage = phpPackage;
+      phpOptions = ''
+        # https://code.castopod.org/adaures/castopod/-/blob/main/docker/production/app/uploads.ini
+        file_uploads = On
+        memory_limit = 512M
+        upload_max_filesize = 500M
+        post_max_size = 512M
+        max_execution_time = 300
+        max_input_time = 300
+      '';
+      settings = {
+        "listen.owner" = config.services.nginx.user;
+        "listen.group" = config.services.nginx.group;
+      } // cfg.poolSettings;
+    };
+
+    systemd.services.castopod-setup = {
+      after = lib.optional config.services.mysql.enable "mysql.service";
+      requires = lib.optional config.services.mysql.enable "mysql.service";
+      wantedBy = [ "multi-user.target" ];
+      path = [ pkgs.openssl phpPackage ];
+      script =
+        let
+          envFile = "${stateDirectory}/.env";
+          media = "${cfg.settings."media.storage"}/${cfg.settings."media.root"}";
+        in
+        ''
+          mkdir -p ${stateDirectory}/writable/{cache,logs,session,temp,uploads}
+
+          if [ ! -d ${lib.escapeShellArg media} ]; then
+            cp --no-preserve=mode,ownership -r ${cfg.package}/share/castopod/public/media ${lib.escapeShellArg media}
+          fi
+
+          if [ ! -f ${stateDirectory}/salt ]; then
+            openssl rand -base64 33 > ${stateDirectory}/salt
+          fi
+
+          cat <<'EOF' > ${envFile}
+          ${lib.generators.toKeyValue { } cfg.settings}
+          EOF
+
+          echo "analytics.salt=$(cat ${stateDirectory}/salt)" >> ${envFile}
+
+          ${if (cfg.database.passwordFile != null) then ''
+            echo "database.default.password=$(cat ${lib.escapeShellArg cfg.database.passwordFile})" >> ${envFile}
+          '' else ''
+            echo "database.default.password=" >> ${envFile}
+          ''}
+
+          ${lib.optionalString (cfg.environmentFile != null) ''
+            cat ${lib.escapeShellArg cfg.environmentFile}) >> ${envFile}
+          ''}
+
+          php spark castopod:database-update
+        '';
+      serviceConfig = {
+        StateDirectory = "castopod";
+        WorkingDirectory = "${cfg.package}/share/castopod";
+        Type = "oneshot";
+        RemainAfterExit = true;
+        User = user;
+        Group = config.services.nginx.group;
+      };
+    };
+
+    systemd.services.castopod-scheduled = {
+      after = [ "castopod-setup.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ phpPackage ];
+      script = ''
+        php public/index.php scheduled-activities
+        php public/index.php scheduled-websub-publish
+        php public/index.php scheduled-video-clips
+      '';
+      serviceConfig = {
+        StateDirectory = "castopod";
+        WorkingDirectory = "${cfg.package}/share/castopod";
+        Type = "oneshot";
+        User = user;
+        Group = config.services.nginx.group;
+      };
+    };
+
+    systemd.timers.castopod-scheduled = {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = "*-*-* *:*:00";
+        Unit = "castopod-scheduled.service";
+      };
+    };
+
+    services.mysql = lib.mkIf cfg.database.createLocally {
+      enable = true;
+      package = lib.mkDefault pkgs.mariadb;
+      ensureDatabases = [ cfg.database.name ];
+      ensureUsers = [{
+        name = cfg.database.user;
+        ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
+      }];
+    };
+
+    services.nginx = lib.mkIf cfg.configureNginx {
+      enable = true;
+      virtualHosts."${cfg.localDomain}" = {
+        root = lib.mkForce "${cfg.package}/share/castopod/public";
+
+        extraConfig = ''
+          try_files $uri $uri/ /index.php?$args;
+          index index.php index.html;
+        '';
+
+        locations."^~ /${cfg.settings."media.root"}/" = {
+          root = cfg.settings."media.storage";
+          extraConfig = ''
+            add_header Access-Control-Allow-Origin "*";
+            expires max;
+            access_log off;
+          '';
+        };
+
+        locations."~ \.php$" = {
+          fastcgiParams = {
+            SERVER_NAME = "$host";
+          };
+          extraConfig = ''
+            fastcgi_intercept_errors on;
+            fastcgi_index index.php;
+            fastcgi_pass unix:${fpm.socket};
+            try_files $uri =404;
+            fastcgi_read_timeout 3600;
+            fastcgi_send_timeout 3600;
+          '';
+        };
+      };
+    };
+
+    users.users.${user} = lib.mapAttrs (name: lib.mkDefault) {
+      description = "Castopod user";
+      isSystemUser = true;
+      group = config.services.nginx.group;
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/gonic.nix b/nixpkgs/nixos/modules/services/audio/gonic.nix
index 65cf10f2c4b4..66daeb60b503 100644
--- a/nixpkgs/nixos/modules/services/audio/gonic.nix
+++ b/nixpkgs/nixos/modules/services/audio/gonic.nix
@@ -57,6 +57,7 @@ in
         ReadWritePaths = "";
         BindReadOnlyPaths = [
           # gonic can access scrobbling services
+          "-/etc/resolv.conf"
           "-/etc/ssl/certs/ca-certificates.crt"
           builtins.storeDir
           cfg.settings.podcast-path
diff --git a/nixpkgs/nixos/modules/services/audio/wyoming/openwakeword.nix b/nixpkgs/nixos/modules/services/audio/wyoming/openwakeword.nix
new file mode 100644
index 000000000000..e1993407dad1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/wyoming/openwakeword.nix
@@ -0,0 +1,157 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+let
+  cfg = config.services.wyoming.openwakeword;
+
+  inherit (lib)
+    concatMapStringsSep
+    escapeShellArgs
+    mkOption
+    mdDoc
+    mkEnableOption
+    mkIf
+    mkPackageOptionMD
+    types
+    ;
+
+  inherit (builtins)
+    toString
+    ;
+
+  models = [
+    # wyoming_openwakeword/models/*.tflite
+    "alexa"
+    "hey_jarvis"
+    "hey_mycroft"
+    "hey_rhasspy"
+    "ok_nabu"
+  ];
+
+in
+
+{
+  meta.buildDocsInSandbox = false;
+
+  options.services.wyoming.openwakeword = with types; {
+    enable = mkEnableOption (mdDoc "Wyoming openWakeWord server");
+
+    package = mkPackageOptionMD pkgs "wyoming-openwakeword" { };
+
+    uri = mkOption {
+      type = strMatching "^(tcp|unix)://.*$";
+      default = "tcp://0.0.0.0:10400";
+      example = "tcp://192.0.2.1:5000";
+      description = mdDoc ''
+        URI to bind the wyoming server to.
+      '';
+    };
+
+    models = mkOption {
+      type = listOf (enum models);
+      default = models;
+      description = mdDoc ''
+        List of wake word models that should be made available.
+      '';
+    };
+
+    preloadModels = mkOption {
+      type = listOf (enum models);
+      default = [
+        "ok_nabu"
+      ];
+      description = mdDoc ''
+        List of wake word models to preload after startup.
+      '';
+    };
+
+    threshold = mkOption {
+      type = float;
+      default = 0.5;
+      description = mdDoc ''
+        Activation threshold (0-1), where higher means fewer activations.
+
+        See trigger level for the relationship between activations and
+        wake word detections.
+      '';
+      apply = toString;
+    };
+
+    triggerLevel = mkOption {
+      type = int;
+      default = 1;
+      description = mdDoc ''
+        Number of activations before a detection is registered.
+
+        A higher trigger level means fewer detections.
+      '';
+      apply = toString;
+    };
+
+    extraArgs = mkOption {
+      type = listOf str;
+      default = [ ];
+      description = mdDoc ''
+        Extra arguments to pass to the server commandline.
+      '';
+      apply = escapeShellArgs;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services."wyoming-openwakeword" = {
+      description = "Wyoming openWakeWord server";
+      after = [
+        "network-online.target"
+      ];
+      wantedBy = [
+        "multi-user.target"
+      ];
+      serviceConfig = {
+        DynamicUser = true;
+        User = "wyoming-openwakeword";
+        # https://github.com/home-assistant/addons/blob/master/openwakeword/rootfs/etc/s6-overlay/s6-rc.d/openwakeword/run
+        ExecStart = ''
+          ${cfg.package}/bin/wyoming-openwakeword \
+            --uri ${cfg.uri} \
+            ${concatMapStringsSep " " (model: "--model ${model}") cfg.models} \
+            ${concatMapStringsSep " " (model: "--preload-model ${model}") cfg.preloadModels} \
+            --threshold ${cfg.threshold} \
+            --trigger-level ${cfg.triggerLevel} ${cfg.extraArgs}
+        '';
+        CapabilityBoundingSet = "";
+        DeviceAllow = "";
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectProc = "invisible";
+        ProcSubset = "pid";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          "AF_UNIX"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RuntimeDirectory = "wyoming-openwakeword";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+        UMask = "0077";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
index 3b44f097ab79..28887f8e2ad5 100644
--- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
@@ -84,8 +84,8 @@ let
       backupScript = mkBackupScript backupJobName cfg;
     in nameValuePair backupJobName {
       description = "BorgBackup job ${name}";
-      path = with pkgs; [
-        borgbackup openssh
+      path =  [
+        config.services.borgbackup.package pkgs.openssh
       ];
       script = "exec " + optionalString cfg.inhibitsSleep ''\
         ${pkgs.systemd}/bin/systemd-inhibit \
@@ -137,7 +137,7 @@ let
     '');
 
   mkBorgWrapper = name: cfg: mkWrapperDrv {
-    original = "${pkgs.borgbackup}/bin/borg";
+    original = getExe config.services.borgbackup.package;
     name = "borg-job-${name}";
     set = { BORG_REPO = cfg.repo; } // (mkPassEnv cfg) // cfg.environment;
   };
@@ -231,6 +231,8 @@ in {
 
   ###### interface
 
+  options.services.borgbackup.package = mkPackageOptionMD pkgs "borgbackup" { };
+
   options.services.borgbackup.jobs = mkOption {
     description = lib.mdDoc ''
       Deduplicating backups using BorgBackup.
@@ -769,6 +771,7 @@ in {
 
       users = mkMerge (mapAttrsToList mkUsersConfig repos);
 
-      environment.systemPackages = with pkgs; [ borgbackup ] ++ (mapAttrsToList mkBorgWrapper jobs);
+      environment.systemPackages =
+        [ config.services.borgbackup.package ] ++ (mapAttrsToList mkBorgWrapper jobs);
     });
 }
diff --git a/nixpkgs/nixos/modules/services/backup/borgmatic.nix b/nixpkgs/nixos/modules/services/backup/borgmatic.nix
index 5ee036e68c7b..d3ba7628e85d 100644
--- a/nixpkgs/nixos/modules/services/backup/borgmatic.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgmatic.nix
@@ -6,32 +6,50 @@ let
   cfg = config.services.borgmatic;
   settingsFormat = pkgs.formats.yaml { };
 
+  repository = with types; submodule {
+    options = {
+      path = mkOption {
+        type = str;
+        description = mdDoc ''
+          Path to the repository
+        '';
+      };
+      label = mkOption {
+        type = str;
+        description = mdDoc ''
+          Label to the repository
+        '';
+      };
+    };
+  };
   cfgType = with types; submodule {
     freeformType = settingsFormat.type;
-    options.location = {
+    options = {
       source_directories = mkOption {
-        type = listOf str;
+        type = nullOr (listOf str);
+        default = null;
         description = mdDoc ''
-          List of source directories to backup (required). Globs and
-          tildes are expanded.
+          List of source directories and files to backup. Globs and tildes are
+          expanded. Do not backslash spaces in path names.
         '';
-        example = [ "/home" "/etc" "/var/log/syslog*" ];
+        example = [ "/home" "/etc" "/var/log/syslog*" "/home/user/path with spaces" ];
       };
       repositories = mkOption {
-        type = listOf str;
+        type = nullOr (listOf repository);
+        default = null;
         description = mdDoc ''
-          Paths to local or remote repositories (required). Tildes are
-          expanded. Multiple repositories are backed up to in
-          sequence. Borg placeholders can be used. See the output of
-          "borg help placeholders" for details. See ssh_command for
-          SSH options like identity file or port. If systemd service
-          is used, then add local repository paths in the systemd
-          service file to the ReadWritePaths list.
+          A required list of local or remote repositories with paths and
+          optional labels (which can be used with the --repository flag to
+          select a repository). Tildes are expanded. Multiple repositories are
+          backed up to in sequence. Borg placeholders can be used. See the
+          output of "borg help placeholders" for details. See ssh_command for
+          SSH options like identity file or port. If systemd service is used,
+          then add local repository paths in the systemd service file to the
+          ReadWritePaths list.
         '';
         example = [
-          "ssh://user@backupserver/./sourcehostname.borg"
-          "ssh://user@backupserver/./{fqdn}"
-          "/var/local/backups/local.borg"
+          { path="ssh://user@backupserver/./sourcehostname.borg"; label="backupserver"; }
+          { path="/mnt/backup"; label="local"; }
         ];
       };
     };
@@ -62,6 +80,13 @@ in
 
   config = mkIf cfg.enable {
 
+    warnings = []
+      ++ optional (cfg.settings != null && cfg.settings.location != null)
+        "`services.borgmatic.settings.location` is deprecated, please move your options out of sections to the global scope"
+      ++ optional (catAttrs "location" (attrValues cfg.configurations) != [])
+        "`services.borgmatic.configurations.<name>.location` is deprecated, please move your options out of sections to the global scope"
+    ;
+
     environment.systemPackages = [ pkgs.borgmatic ];
 
     environment.etc = (optionalAttrs (cfg.settings != null) { "borgmatic/config.yaml".source = cfgfile; }) //
diff --git a/nixpkgs/nixos/modules/services/backup/btrbk.nix b/nixpkgs/nixos/modules/services/backup/btrbk.nix
index b838c174553d..9b7f1566eb1e 100644
--- a/nixpkgs/nixos/modules/services/backup/btrbk.nix
+++ b/nixpkgs/nixos/modules/services/backup/btrbk.nix
@@ -166,7 +166,7 @@ in
             { command = "${pkgs.coreutils}/bin/mkdir"; options = [ "NOPASSWD" ]; }
             { command = "${pkgs.coreutils}/bin/readlink"; options = [ "NOPASSWD" ]; }
             # for ssh, they are not the same than the one hard coded in ${pkgs.btrbk}
-            { command = "/run/current-system/bin/btrfs"; options = [ "NOPASSWD" ]; }
+            { command = "/run/current-system/sw/bin/btrfs"; options = [ "NOPASSWD" ]; }
             { command = "/run/current-system/sw/bin/mkdir"; options = [ "NOPASSWD" ]; }
             { command = "/run/current-system/sw/bin/readlink"; options = [ "NOPASSWD" ]; }
             ];
@@ -182,7 +182,7 @@ in
             (doasCmdNoPass "${pkgs.coreutils}/bin/mkdir")
             (doasCmdNoPass "${pkgs.coreutils}/bin/readlink")
             # for ssh, they are not the same than the one hard coded in ${pkgs.btrbk}
-            (doasCmdNoPass "/run/current-system/bin/btrfs")
+            (doasCmdNoPass "/run/current-system/sw/bin/btrfs")
             (doasCmdNoPass "/run/current-system/sw/bin/mkdir")
             (doasCmdNoPass "/run/current-system/sw/bin/readlink")
 
diff --git a/nixpkgs/nixos/modules/services/backup/znapzend.nix b/nixpkgs/nixos/modules/services/backup/znapzend.nix
index 76f147c18aff..2ebe8ad2f69a 100644
--- a/nixpkgs/nixos/modules/services/backup/znapzend.nix
+++ b/nixpkgs/nixos/modules/services/backup/znapzend.nix
@@ -359,14 +359,14 @@ in
       };
 
       features.oracleMode = mkEnableOption (lib.mdDoc ''
-        Destroy snapshots one by one instead of using one long argument list.
+        destroying snapshots one by one instead of using one long argument list.
         If source and destination are out of sync for a long time, you may have
         so many snapshots to destroy that the argument gets is too long and the
-        command fails.
+        command fails
       '');
       features.recvu = mkEnableOption (lib.mdDoc ''
         recvu feature which uses `-u` on the receiving end to keep the destination
-        filesystem unmounted.
+        filesystem unmounted
       '');
       features.compressed = mkEnableOption (lib.mdDoc ''
         compressed feature which adds the options `-Lce` to
@@ -377,7 +377,7 @@ in
         support and -e is for embedded data support. see
         {manpage}`znapzend(1)`
         and {manpage}`zfs(8)`
-        for more info.
+        for more info
       '');
       features.sendRaw = mkEnableOption (lib.mdDoc ''
         sendRaw feature which adds the options `-w` to the
@@ -386,25 +386,25 @@ in
         backup that can't be read without the encryption key/passphrase, useful
         when the remote isn't fully trusted or not physically secure. This
         option must be used consistently, raw incrementals cannot be based on
-        non-raw snapshots and vice versa.
+        non-raw snapshots and vice versa
       '');
       features.skipIntermediates = mkEnableOption (lib.mdDoc ''
-        Enable the skipIntermediates feature to send a single increment
+        the skipIntermediates feature to send a single increment
         between latest common snapshot and the newly made one. It may skip
         several source snaps if the destination was offline for some time, and
         it should skip snapshots not managed by znapzend. Normally for online
         destinations, the new snapshot is sent as soon as it is created on the
-        source, so there are no automatic increments to skip.
+        source, so there are no automatic increments to skip
       '');
       features.lowmemRecurse = mkEnableOption (lib.mdDoc ''
         use lowmemRecurse on systems where you have too many datasets, so a
         recursive listing of attributes to find backup plans exhausts the
         memory available to {command}`znapzend`: instead, go the slower
         way to first list all impacted dataset names, and then query their
-        configs one by one.
+        configs one by one
       '');
       features.zfsGetType = mkEnableOption (lib.mdDoc ''
-        use zfsGetType if your {command}`zfs get` supports a
+        using zfsGetType if your {command}`zfs get` supports a
         `-t` argument for filtering by dataset type at all AND
         lists properties for snapshots by default when recursing, so that there
         is too much data to process while searching for backup plans.
@@ -412,7 +412,7 @@ in
         `--recursive` search for backup plans can literally
         differ by hundreds of times (depending on the amount of snapshots in
         that dataset tree... and a decent backup plan will ensure you have a lot
-        of those), so you would benefit from requesting this feature.
+        of those), so you would benefit from requesting this feature
       '');
     };
   };
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixpkgs/nixos/modules/services/continuous-integration/buildbot/master.nix
index b4b997201c8f..9a89745055f0 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -15,6 +15,7 @@ let
 
   defaultMasterCfg = pkgs.writeText "master.cfg" ''
     from buildbot.plugins import *
+    ${cfg.extraImports}
     factory = util.BuildFactory()
     c = BuildmasterConfig = dict(
      workers       = [${concatStringsSep "," cfg.workers}],
@@ -28,6 +29,7 @@ let
      schedulers    = [ ${concatStringsSep "," cfg.schedulers} ],
      builders      = [ ${concatStringsSep "," cfg.builders} ],
      services      = [ ${concatStringsSep "," cfg.reporters} ],
+     configurators = [ ${concatStringsSep "," cfg.configurators} ],
     )
     for step in [ ${concatStringsSep "," cfg.factorySteps} ]:
       factory.addStep(step)
@@ -79,6 +81,15 @@ in {
         ];
       };
 
+      configurators = mkOption {
+        type = types.listOf types.str;
+        description = lib.mdDoc "Configurator Steps, see https://docs.buildbot.net/latest/manual/configuration/configurators.html";
+        default = [];
+        example = [
+          "util.JanitorConfigurator(logHorizon=timedelta(weeks=4), hour=12, dayOfWeek=6)"
+        ];
+      };
+
       enable = mkOption {
         type = types.bool;
         default = false;
@@ -91,6 +102,13 @@ in {
         default = "c['buildbotNetUsageData'] = None";
       };
 
+      extraImports = mkOption {
+        type = types.str;
+        description = lib.mdDoc "Extra python imports to prepend to master.cfg";
+        default = "";
+        example = "from buildbot.process.project import Project";
+      };
+
       masterCfg = mkOption {
         type = types.path;
         description = lib.mdDoc "Optionally pass master.cfg path. Other options in this configuration will be ignored.";
diff --git a/nixpkgs/nixos/modules/services/databases/cassandra.nix b/nixpkgs/nixos/modules/services/databases/cassandra.nix
index e26acb88d8c8..cd816ffaf0dd 100644
--- a/nixpkgs/nixos/modules/services/databases/cassandra.nix
+++ b/nixpkgs/nixos/modules/services/databases/cassandra.nix
@@ -122,7 +122,7 @@ in
   options.services.cassandra = {
 
     enable = mkEnableOption (lib.mdDoc ''
-      Apache Cassandra – Scalable and highly available database.
+      Apache Cassandra – Scalable and highly available database
     '');
 
     clusterName = mkOption {
diff --git a/nixpkgs/nixos/modules/services/databases/ferretdb.nix b/nixpkgs/nixos/modules/services/databases/ferretdb.nix
new file mode 100644
index 000000000000..45f822d64691
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/databases/ferretdb.nix
@@ -0,0 +1,79 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ferretdb;
+in
+{
+
+  meta.maintainers = with lib.maintainers; [ julienmalka camillemndn ];
+
+  options = {
+    services.ferretdb = {
+      enable = mkEnableOption "FerretDB, an Open Source MongoDB alternative";
+
+      package = mkOption {
+        type = types.package;
+        example = literalExpression "pkgs.ferretdb";
+        default = pkgs.ferretdb;
+        defaultText = "pkgs.ferretdb";
+        description = "FerretDB package to use.";
+      };
+
+      settings = lib.mkOption {
+        type =
+          lib.types.submodule { freeformType = with lib.types; attrsOf str; };
+        example = {
+          FERRETDB_LOG_LEVEL = "warn";
+          FERRETDB_MODE = "normal";
+        };
+        description = ''
+          Additional configuration for FerretDB, see
+          <https://docs.ferretdb.io/flags/>
+          for supported values.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable
+    {
+
+      services.ferretdb.settings = {
+        FERRETDB_HANDLER = lib.mkDefault "sqlite";
+        FERRETDB_SQLITE_URL = lib.mkDefault "file:/var/lib/ferretdb/";
+      };
+
+      systemd.services.ferretdb = {
+        description = "FerretDB";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        environment = cfg.settings;
+        serviceConfig = {
+          Type = "simple";
+          StateDirectory = "ferretdb";
+          WorkingDirectory = "/var/lib/ferretdb";
+          ExecStart = "${cfg.package}/bin/ferretdb";
+          Restart = "on-failure";
+          ProtectHome = true;
+          ProtectSystem = "strict";
+          PrivateTmp = true;
+          PrivateDevices = true;
+          ProtectHostname = true;
+          ProtectClock = true;
+          ProtectKernelTunables = true;
+          ProtectKernelModules = true;
+          ProtectKernelLogs = true;
+          ProtectControlGroups = true;
+          NoNewPrivileges = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          RemoveIPC = true;
+          PrivateMounts = true;
+          DynamicUser = true;
+        };
+      };
+    };
+}
+
diff --git a/nixpkgs/nixos/modules/services/databases/pgmanage.nix b/nixpkgs/nixos/modules/services/databases/pgmanage.nix
index cbf988d596f4..12c8253ab49c 100644
--- a/nixpkgs/nixos/modules/services/databases/pgmanage.nix
+++ b/nixpkgs/nixos/modules/services/databases/pgmanage.nix
@@ -187,7 +187,7 @@ in {
       serviceConfig = {
         User         = pgmanage;
         Group        = pgmanage;
-        ExecStart    = "${pkgs.pgmanage}/sbin/pgmanage -c ${confFile}" +
+        ExecStart    = "${cfg.package}/sbin/pgmanage -c ${confFile}" +
                        optionalString cfg.localOnly " --local-only=true";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.nix b/nixpkgs/nixos/modules/services/databases/postgresql.nix
index 0acaf0fd00a6..2d4ef0563182 100644
--- a/nixpkgs/nixos/modules/services/databases/postgresql.nix
+++ b/nixpkgs/nixos/modules/services/databases/postgresql.nix
@@ -106,12 +106,14 @@ in
       identMap = mkOption {
         type = types.lines;
         default = "";
+        example = ''
+          map-name-0 system-username-0 database-username-0
+          map-name-1 system-username-1 database-username-1
+        '';
         description = lib.mdDoc ''
           Defines the mapping from system users to database users.
 
-          The general form is:
-
-          map-name system-username database-username
+          See the [auth doc](https://postgresql.org/docs/current/auth-username-maps.html).
         '';
       };
 
@@ -128,6 +130,11 @@ in
       initialScript = mkOption {
         type = types.nullOr types.path;
         default = null;
+        example = literalExpression ''
+          pkgs.writeText "init-sql-script" '''
+            alter user postgres with password 'myPassword';
+          ''';'';
+
         description = lib.mdDoc ''
           A file containing SQL statements to execute on first startup.
         '';
@@ -451,7 +458,8 @@ in
 
     services.postgresql.package = let
         mkThrow = ver: throw "postgresql_${ver} was removed, please upgrade your postgresql version.";
-        base = if versionAtLeast config.system.stateVersion "22.05" then pkgs.postgresql_14
+        base = if versionAtLeast config.system.stateVersion "23.11" then pkgs.postgresql_15
+            else if versionAtLeast config.system.stateVersion "22.05" then pkgs.postgresql_14
             else if versionAtLeast config.system.stateVersion "21.11" then pkgs.postgresql_13
             else if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11
             else if versionAtLeast config.system.stateVersion "17.09" then mkThrow "9_6"
@@ -464,13 +472,16 @@ in
 
     services.postgresql.dataDir = mkDefault "/var/lib/postgresql/${cfg.package.psqlSchema}";
 
-    services.postgresql.authentication = mkAfter
+    services.postgresql.authentication = mkMerge [
+      (mkBefore "# Generated file; do not edit!")
+      (mkAfter
       ''
-        # Generated file; do not edit!
+        # default value of services.postgresql.authentication
         local all all              peer
         host  all all 127.0.0.1/32 md5
         host  all all ::1/128      md5
-      '';
+      '')
+    ];
 
     users.users.postgres =
       { name = "postgres";
diff --git a/nixpkgs/nixos/modules/services/databases/redis.nix b/nixpkgs/nixos/modules/services/databases/redis.nix
index 1464f4487e39..86b295dadf49 100644
--- a/nixpkgs/nixos/modules/services/databases/redis.nix
+++ b/nixpkgs/nixos/modules/services/databases/redis.nix
@@ -75,7 +75,7 @@ in {
               Note that the NixOS module for Redis disables kernel support
               for Transparent Huge Pages (THP),
               because this features causes major performance problems for Redis,
-              e.g. (https://redis.io/topics/latency).
+              e.g. (https://redis.io/topics/latency)
             '');
 
             user = mkOption {
diff --git a/nixpkgs/nixos/modules/services/databases/surrealdb.nix b/nixpkgs/nixos/modules/services/databases/surrealdb.nix
index 28bd97cd731e..e1a1faed1f8f 100644
--- a/nixpkgs/nixos/modules/services/databases/surrealdb.nix
+++ b/nixpkgs/nixos/modules/services/databases/surrealdb.nix
@@ -8,7 +8,7 @@ in {
 
   options = {
     services.surrealdb = {
-      enable = mkEnableOption (lib.mdDoc "A scalable, distributed, collaborative, document-graph database, for the realtime web ");
+      enable = mkEnableOption (lib.mdDoc "SurrealDB, a scalable, distributed, collaborative, document-graph database, for the realtime web");
 
       package = mkOption {
         default = pkgs.surrealdb;
diff --git a/nixpkgs/nixos/modules/services/desktops/deepin/app-services.nix b/nixpkgs/nixos/modules/services/desktops/deepin/app-services.nix
index 6f9932e48733..4592bc7bb340 100644
--- a/nixpkgs/nixos/modules/services/desktops/deepin/app-services.nix
+++ b/nixpkgs/nixos/modules/services/desktops/deepin/app-services.nix
@@ -14,7 +14,7 @@ with lib;
 
     services.deepin.app-services = {
 
-      enable = mkEnableOption (lib.mdDoc "Service collection of DDE applications, including dconfig-center");
+      enable = mkEnableOption (lib.mdDoc "service collection of DDE applications, including dconfig-center");
 
     };
 
diff --git a/nixpkgs/nixos/modules/services/desktops/deepin/dde-api.nix b/nixpkgs/nixos/modules/services/desktops/deepin/dde-api.nix
index 472d9860c108..459876febf21 100644
--- a/nixpkgs/nixos/modules/services/desktops/deepin/dde-api.nix
+++ b/nixpkgs/nixos/modules/services/desktops/deepin/dde-api.nix
@@ -15,8 +15,8 @@ with lib;
     services.deepin.dde-api = {
 
       enable = mkEnableOption (lib.mdDoc ''
-        Provides some dbus interfaces that is used for screen zone detecting,
-        thumbnail generating, and sound playing in Deepin Desktop Environment.
+        some dbus interfaces that is used for screen zone detecting,
+        thumbnail generating, and sound playing in Deepin Desktop Environment
       '');
 
     };
diff --git a/nixpkgs/nixos/modules/services/desktops/deepin/dde-daemon.nix b/nixpkgs/nixos/modules/services/desktops/deepin/dde-daemon.nix
index 9377f523ebf9..356d323bcbdf 100644
--- a/nixpkgs/nixos/modules/services/desktops/deepin/dde-daemon.nix
+++ b/nixpkgs/nixos/modules/services/desktops/deepin/dde-daemon.nix
@@ -14,7 +14,7 @@ with lib;
 
     services.deepin.dde-daemon = {
 
-      enable = mkEnableOption (lib.mdDoc "Daemon for handling the deepin session settings");
+      enable = mkEnableOption (lib.mdDoc "daemon for handling the deepin session settings");
 
     };
 
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome/gnome-browser-connector.nix b/nixpkgs/nixos/modules/services/desktops/gnome/gnome-browser-connector.nix
index 5d4ddce94220..d18e303891e4 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome/gnome-browser-connector.nix
+++ b/nixpkgs/nixos/modules/services/desktops/gnome/gnome-browser-connector.nix
@@ -24,8 +24,8 @@ in
 
   options = {
     services.gnome.gnome-browser-connector.enable = mkEnableOption (mdDoc ''
-      Native host connector for the GNOME Shell browser extension, a DBus service
-      allowing to install GNOME Shell extensions from a web browser.
+      native host connector for the GNOME Shell browser extension, a DBus service
+      allowing to install GNOME Shell extensions from a web browser
     '');
   };
 
@@ -42,6 +42,6 @@ in
 
     services.dbus.packages = [ pkgs.gnome-browser-connector ];
 
-    nixpkgs.config.firefox.enableGnomeExtensions = true;
+    programs.firefox.wrapperConfig.enableGnomeExtensions = true;
   };
 }
diff --git a/nixpkgs/nixos/modules/services/games/xonotic.nix b/nixpkgs/nixos/modules/services/games/xonotic.nix
new file mode 100644
index 000000000000..c84347ddc981
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/games/xonotic.nix
@@ -0,0 +1,198 @@
+{ config
+, pkgs
+, lib
+, ...
+}:
+
+let
+  cfg = config.services.xonotic;
+
+  serverCfg = pkgs.writeText "xonotic-server.cfg" (
+    toString cfg.prependConfig
+      + "\n"
+      + builtins.concatStringsSep "\n" (
+        lib.mapAttrsToList (key: option:
+          let
+            escape = s: lib.escape [ "\"" ] s;
+            quote = s: "\"${s}\"";
+
+            toValue = x: quote (escape (toString x));
+
+            value = (if lib.isList option then
+              builtins.concatStringsSep
+                " "
+                (builtins.map (x: toValue x) option)
+            else
+              toValue option
+            );
+          in
+          "${key} ${value}"
+        ) cfg.settings
+      )
+      + "\n"
+      + toString cfg.appendConfig
+  );
+in
+
+{
+  options.services.xonotic = {
+    enable = lib.mkEnableOption (lib.mdDoc "Xonotic dedicated server");
+
+    package = lib.mkPackageOption pkgs "xonotic-dedicated" {};
+
+    openFirewall = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Open the firewall for TCP and UDP on the specified port.
+      '';
+    };
+
+    dataDir = lib.mkOption {
+      type = lib.types.path;
+      readOnly = true;
+      default = "/var/lib/xonotic";
+      description = lib.mdDoc ''
+        Data directory.
+      '';
+    };
+
+    settings = lib.mkOption {
+      description = lib.mdDoc ''
+        Generates the `server.cfg` file. Refer to [upstream's example][0] for
+        details.
+
+        [0]: https://gitlab.com/xonotic/xonotic/-/blob/master/server/server.cfg
+      '';
+      default = {};
+      type = lib.types.submodule {
+        freeformType = with lib.types; let
+          scalars = oneOf [ singleLineStr int float ];
+        in
+        attrsOf (oneOf [ scalars (nonEmptyListOf scalars) ]);
+
+        options.sv_public = lib.mkOption {
+          type = lib.types.int;
+          default = 0;
+          example = [ (-1) 1 ];
+          description = lib.mdDoc ''
+            Controls whether the server will be publicly listed.
+          '';
+        };
+
+        options.hostname = lib.mkOption {
+          type = lib.types.singleLineStr;
+          default = "Xonotic $g_xonoticversion Server";
+          description = lib.mdDoc ''
+            The name that will appear in the server list. `$g_xonoticversion`
+            gets replaced with the current version.
+          '';
+        };
+
+        options.sv_motd = lib.mkOption {
+          type = lib.types.singleLineStr;
+          default = "";
+          description = lib.mdDoc ''
+            Text displayed when players join the server.
+          '';
+        };
+
+        options.sv_termsofservice_url = lib.mkOption {
+          type = lib.types.singleLineStr;
+          default = "";
+          description = lib.mdDoc ''
+            URL for the Terms of Service for playing on your server.
+          '';
+        };
+
+        options.maxplayers = lib.mkOption {
+          type = lib.types.int;
+          default = 16;
+          description = lib.mdDoc ''
+            Number of player slots on the server, including spectators.
+          '';
+        };
+
+        options.net_address = lib.mkOption {
+          type = lib.types.singleLineStr;
+          default = "0.0.0.0";
+          description = lib.mdDoc ''
+            The address Xonotic will listen on.
+          '';
+        };
+
+        options.port = lib.mkOption {
+          type = lib.types.port;
+          default = 26000;
+          description = lib.mdDoc ''
+            The port Xonotic will listen on.
+          '';
+        };
+      };
+    };
+
+    # Still useful even though we're using RFC 42 settings because *some* keys
+    # can be repeated.
+    appendConfig = lib.mkOption {
+      type = with lib.types; nullOr lines;
+      default = null;
+      description = lib.mdDoc ''
+        Literal text to insert at the end of `server.cfg`.
+      '';
+    };
+
+    # Certain changes need to happen at the beginning of the file.
+    prependConfig = lib.mkOption {
+      type = with lib.types; nullOr lines;
+      default = null;
+      description = lib.mdDoc ''
+        Literal text to insert at the start of `server.cfg`.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.xonotic = {
+      description = "Xonotic server";
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        # Required or else it tries to write the lock file into the nix store
+        HOME = cfg.dataDir;
+      };
+
+      serviceConfig = {
+        DynamicUser = true;
+        User = "xonotic";
+        StateDirectory = "xonotic";
+        ExecStart = "${cfg.package}/bin/xonotic-dedicated";
+
+        # Symlink the configuration from the nix store to where Xonotic actually
+        # looks for it
+        ExecStartPre = [
+          "${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/.xonotic/data"
+          ''
+            ${pkgs.coreutils}/bin/ln -sf ${serverCfg} \
+              ${cfg.dataDir}/.xonotic/data/server.cfg
+          ''
+        ];
+
+        # Cargo-culted from search results about writing Xonotic systemd units
+        ExecReload = "${pkgs.util-linux}/bin/kill -HUP $MAINPID";
+
+        Restart = "on-failure";
+        RestartSec = 10;
+        StartLimitBurst = 5;
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
+      cfg.settings.port
+    ];
+    networking.firewall.allowedUDPPorts = lib.mkIf cfg.openFirewall [
+      cfg.settings.port
+    ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ CobaltCause ];
+}
diff --git a/nixpkgs/nixos/modules/services/hardware/kanata.nix b/nixpkgs/nixos/modules/services/hardware/kanata.nix
index 7d544050130b..aac20c6c760e 100644
--- a/nixpkgs/nixos/modules/services/hardware/kanata.nix
+++ b/nixpkgs/nixos/modules/services/hardware/kanata.nix
@@ -9,8 +9,14 @@ let
     options = {
       devices = mkOption {
         type = types.listOf types.str;
+        default = [ ];
         example = [ "/dev/input/by-id/usb-0000_0000-event-kbd" ];
-        description = mdDoc "Paths to keyboard devices.";
+        description = mdDoc ''
+          Paths to keyboard devices.
+
+          An empty list, the default value, lets kanata detect which
+          input devices are keyboards and intercept them all.
+        '';
       };
       config = mkOption {
         type = types.lines;
@@ -162,6 +168,14 @@ in
   };
 
   config = mkIf cfg.enable {
+    warnings =
+      let
+        keyboardsWithEmptyDevices = filterAttrs (name: keyboard: keyboard.devices == [ ]) cfg.keyboards;
+        existEmptyDevices = length (attrNames keyboardsWithEmptyDevices) > 0;
+        moreThanOneKeyboard = length (attrNames cfg.keyboards) > 1;
+      in
+      optional (existEmptyDevices && moreThanOneKeyboard) "One device can only be intercepted by one kanata instance.  Setting services.kanata.keyboards.${head (attrNames keyboardsWithEmptyDevices)}.devices = [ ] and using more than one services.kanata.keyboards may cause a race condition.";
+
     hardware.uinput.enable = true;
 
     systemd.services = mapAttrs' mkService cfg.keyboards;
diff --git a/nixpkgs/nixos/modules/services/hardware/keyd.nix b/nixpkgs/nixos/modules/services/hardware/keyd.nix
index ead2f456a202..724e9b956847 100644
--- a/nixpkgs/nixos/modules/services/hardware/keyd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/keyd.nix
@@ -2,7 +2,6 @@
 with lib;
 let
   cfg = config.services.keyd;
-  settingsFormat = pkgs.formats.ini { };
 
   keyboardOptions = { ... }: {
     options = {
@@ -16,7 +15,7 @@ let
       };
 
       settings = mkOption {
-        type = settingsFormat.type;
+        type = (pkgs.formats.ini { }).type;
         default = { };
         example = {
           main = {
@@ -37,6 +36,20 @@ let
           See <https://github.com/rvaiya/keyd> how to configure.
         '';
       };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        example = ''
+          [control+shift]
+          h = left
+        '';
+        description = lib.mdDoc ''
+          Extra configuration that is appended to the end of the file.
+          **Do not** write `ids` section here, use a separate option for it.
+          You can use this option to define compound layers that must always be defined after the layer they are comprised.
+        '';
+      };
     };
   };
 in
@@ -85,15 +98,12 @@ in
     environment.etc = mapAttrs'
       (name: options:
         nameValuePair "keyd/${name}.conf" {
-          source = pkgs.runCommand "${name}.conf"
-            {
-              ids = ''
-                [ids]
-                ${concatStringsSep "\n" options.ids}
-              '';
-              passAsFile = [ "ids" ];
-            } ''
-            cat $idsPath <(echo) ${settingsFormat.generate "keyd-${name}.conf" options.settings} >$out
+          text = ''
+            [ids]
+            ${concatStringsSep "\n" options.ids}
+
+            ${generators.toINI {} options.settings}
+            ${options.extraConfig}
           '';
         })
       cfg.keyboards;
diff --git a/nixpkgs/nixos/modules/services/hardware/supergfxd.nix b/nixpkgs/nixos/modules/services/hardware/supergfxd.nix
index bd82775e8246..f7af993d7238 100644
--- a/nixpkgs/nixos/modules/services/hardware/supergfxd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/supergfxd.nix
@@ -7,7 +7,7 @@ in
 {
   options = {
     services.supergfxd = {
-      enable = lib.mkEnableOption (lib.mdDoc "Enable the supergfxd service");
+      enable = lib.mkEnableOption (lib.mdDoc "the supergfxd service");
 
       settings = lib.mkOption {
         type = lib.types.nullOr json.type;
diff --git a/nixpkgs/nixos/modules/services/hardware/tlp.nix b/nixpkgs/nixos/modules/services/hardware/tlp.nix
index d2cc7c661c69..cad510e571cb 100644
--- a/nixpkgs/nixos/modules/services/hardware/tlp.nix
+++ b/nixpkgs/nixos/modules/services/hardware/tlp.nix
@@ -65,7 +65,7 @@ in
       "tlp.conf".text = (mkTlpConfig cfg.settings) + cfg.extraConfig;
     } // optionalAttrs enableRDW {
       "NetworkManager/dispatcher.d/99tlp-rdw-nm".source =
-        "${tlp}/etc/NetworkManager/dispatcher.d/99tlp-rdw-nm";
+        "${tlp}/usr/lib/NetworkManager/dispatcher.d/99tlp-rdw-nm";
     };
 
     environment.systemPackages = [ tlp ];
diff --git a/nixpkgs/nixos/modules/services/hardware/tuxedo-rs.nix b/nixpkgs/nixos/modules/services/hardware/tuxedo-rs.nix
new file mode 100644
index 000000000000..0daccfef3a53
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/hardware/tuxedo-rs.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.tuxedo-rs;
+
+in
+{
+  options = {
+    hardware.tuxedo-rs = {
+      enable = mkEnableOption (lib.mdDoc "Rust utilities for interacting with hardware from TUXEDO Computers");
+
+      tailor-gui.enable = mkEnableOption (lib.mdDoc "tailor-gui, an alternative to TUXEDO Control Center, written in Rust");
+    };
+  };
+
+  config = mkIf cfg.enable (mkMerge [
+    {
+      hardware.tuxedo-keyboard.enable = true;
+
+      systemd = {
+        services.tailord = {
+          enable = true;
+          description = "Tuxedo Tailor hardware control service";
+          after = [ "systemd-logind.service" ];
+          wantedBy = [ "multi-user.target" ];
+
+          serviceConfig = {
+            Type = "dbus";
+            BusName = "com.tux.Tailor";
+            ExecStart = "${pkgs.tuxedo-rs}/bin/tailord";
+            Environment = "RUST_BACKTRACE=1";
+            Restart = "on-failure";
+          };
+        };
+      };
+
+      services.dbus.packages = [ pkgs.tuxedo-rs ];
+
+      environment.systemPackages = [ pkgs.tuxedo-rs ];
+    }
+    (mkIf cfg.tailor-gui.enable {
+      environment.systemPackages = [ pkgs.tailor-gui ];
+    })
+  ]);
+
+  meta.maintainers = with maintainers; [ mrcjkb ];
+}
diff --git a/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix b/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix
index 0b8b1d719418..99bac86a8e9a 100644
--- a/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixpkgs/nixos/modules/services/home-automation/home-assistant.nix
@@ -457,10 +457,12 @@ in {
           "inkbird"
           "keymitt_ble"
           "led_ble"
+          "medcom_ble"
           "melnor"
           "moat"
           "mopeka"
           "oralb"
+          "private_ble_device"
           "qingping"
           "rapt_ble"
           "ruuvi_gateway"
@@ -586,11 +588,12 @@ in {
           "~@privileged"
         ] ++ optionals (any useComponent componentsUsingPing) [
           "capset"
+          "setuid"
         ];
         UMask = "0077";
       };
       path = [
-        "/run/wrappers" # needed for ping
+        pkgs.unixtools.ping # needed for ping
       ];
     };
 
diff --git a/nixpkgs/nixos/modules/services/logging/ulogd.nix b/nixpkgs/nixos/modules/services/logging/ulogd.nix
index 065032b531c6..05c9797bb28b 100644
--- a/nixpkgs/nixos/modules/services/logging/ulogd.nix
+++ b/nixpkgs/nixos/modules/services/logging/ulogd.nix
@@ -3,7 +3,7 @@
 with lib;
 let
   cfg = config.services.ulogd;
-  settingsFormat = pkgs.formats.ini { };
+  settingsFormat = pkgs.formats.ini { listsAsDuplicateKeys = true; };
   settingsFile = settingsFormat.generate "ulogd.conf" cfg.settings;
 in {
   options = {
@@ -12,22 +12,34 @@ in {
 
       settings = mkOption {
         example = {
-          global.stack = "stack=log1:NFLOG,base1:BASE,pcap1:PCAP";
+          global.stack = [
+            "log1:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,emu1:LOGEMU"
+            "log1:NFLOG,base1:BASE,pcap1:PCAP"
+          ];
+
           log1.group = 2;
+
           pcap1 = {
+            sync = 1;
             file = "/var/log/ulogd.pcap";
+          };
+
+          emu1 = {
             sync = 1;
+            file = "/var/log/ulogd_pkts.log";
           };
         };
         type = settingsFormat.type;
         default = { };
-        description = lib.mdDoc "Configuration for ulogd. See {file}`/share/doc/ulogd/` in `pkgs.ulogd.doc`.";
+        description = lib.mdDoc
+          "Configuration for ulogd. See {file}`/share/doc/ulogd/` in `pkgs.ulogd.doc`.";
       };
 
       logLevel = mkOption {
         type = types.enum [ 1 3 5 7 8 ];
         default = 5;
-        description = lib.mdDoc "Log level (1 = debug, 3 = info, 5 = notice, 7 = error, 8 = fatal)";
+        description = lib.mdDoc
+          "Log level (1 = debug, 3 = info, 5 = notice, 7 = error, 8 = fatal)";
       };
     };
   };
@@ -40,7 +52,10 @@ in {
       before = [ "network-pre.target" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.ulogd}/bin/ulogd -c ${settingsFile} --verbose --loglevel ${toString cfg.logLevel}";
+        ExecStart =
+          "${pkgs.ulogd}/bin/ulogd -c ${settingsFile} --verbose --loglevel ${
+            toString cfg.logLevel
+          }";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/mail/dovecot.nix b/nixpkgs/nixos/modules/services/mail/dovecot.nix
index 21bafd859c3c..abbb2f32e6cc 100644
--- a/nixpkgs/nixos/modules/services/mail/dovecot.nix
+++ b/nixpkgs/nixos/modules/services/mail/dovecot.nix
@@ -302,7 +302,7 @@ in
 
     enablePAM = mkEnableOption (lib.mdDoc "creating a own Dovecot PAM service and configure PAM user logins") // { default = true; };
 
-    enableDHE = mkEnableOption (lib.mdDoc "enable ssl_dh and generation of primes for the key exchange") // { default = true; };
+    enableDHE = mkEnableOption (lib.mdDoc "ssl_dh and generation of primes for the key exchange") // { default = true; };
 
     sieveScripts = mkOption {
       type = types.attrsOf types.path;
diff --git a/nixpkgs/nixos/modules/services/mail/mailman.nix b/nixpkgs/nixos/modules/services/mail/mailman.nix
index 6cfa74de9b9f..646addc5dad2 100644
--- a/nixpkgs/nixos/modules/services/mail/mailman.nix
+++ b/nixpkgs/nixos/modules/services/mail/mailman.nix
@@ -260,7 +260,7 @@ in {
       };
 
       serve = {
-        enable = mkEnableOption (lib.mdDoc "Automatic nginx and uwsgi setup for mailman-web");
+        enable = mkEnableOption (lib.mdDoc "automatic nginx and uwsgi setup for mailman-web");
 
         virtualRoot = mkOption {
           default = "/";
@@ -314,7 +314,7 @@ in {
         queue_dir = "$var_dir/queue";
         template_dir = "$var_dir/templates";
         log_dir = "/var/log/mailman";
-        lock_dir = "$var_dir/lock";
+        lock_dir = "/run/mailman/lock";
         etc_dir = "/etc";
         pid_file = "/run/mailman/master.pid";
       };
@@ -588,7 +588,7 @@ in {
           # Since the mailman-web settings.py obstinately creates a logs
           # dir in the cwd, change to the (writable) runtime directory before
           # starting uwsgi.
-          ExecStart = "${pkgs.coreutils}/bin/env -C $RUNTIME_DIRECTORY ${pkgs.uwsgi.override { plugins = ["python3"]; }}/bin/uwsgi --json ${uwsgiConfigFile}";
+          ExecStart = "${pkgs.coreutils}/bin/env -C $RUNTIME_DIRECTORY ${pkgs.uwsgi.override { plugins = ["python3"]; python3 = webEnv.python; }}/bin/uwsgi --json ${uwsgiConfigFile}";
           User = cfg.webUser;
           Group = "mailman";
           RuntimeDirectory = "mailman-uwsgi";
@@ -640,7 +640,7 @@ in {
   };
 
   meta = {
-    maintainers = with lib.maintainers; [ lheckemann qyliss ma27 ];
+    maintainers = with lib.maintainers; [ lheckemann qyliss ];
     doc = ./mailman.md;
   };
 
diff --git a/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix b/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix
index 9bf4de3317cc..7e464d6ed589 100644
--- a/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix
+++ b/nixpkgs/nixos/modules/services/matrix/matrix-sliding-sync.nix
@@ -7,7 +7,7 @@ in
   options.services.matrix-synapse.sliding-sync = {
     enable = lib.mkEnableOption (lib.mdDoc "sliding sync");
 
-    package = lib.mkPackageOption pkgs "matrix-sliding-sync" { };
+    package = lib.mkPackageOptionMD pkgs "matrix-sliding-sync" { };
 
     settings = lib.mkOption {
       type = lib.types.submodule {
@@ -44,7 +44,7 @@ in
         };
       };
       default = { };
-      description = ''
+      description = lib.mdDoc ''
         Freeform environment variables passed to the sliding sync proxy.
         Refer to <https://github.com/matrix-org/sliding-sync#setup> for all supported values.
       '';
diff --git a/nixpkgs/nixos/modules/services/matrix/mjolnir.nix b/nixpkgs/nixos/modules/services/matrix/mjolnir.nix
index 0824be663340..4e9a915c23c7 100644
--- a/nixpkgs/nixos/modules/services/matrix/mjolnir.nix
+++ b/nixpkgs/nixos/modules/services/matrix/mjolnir.nix
@@ -96,8 +96,8 @@ in
       type = types.submodule {
         options = {
           enable = mkEnableOption (lib.mdDoc ''
-            If true, accessToken is ignored and the username/password below will be
-            used instead. The access token of the bot will be stored in the dataPath.
+            ignoring the accessToken. If true, accessToken is ignored and the username/password below will be
+            used instead. The access token of the bot will be stored in the dataPath
           '');
 
           username = mkOption {
diff --git a/nixpkgs/nixos/modules/services/matrix/synapse.md b/nixpkgs/nixos/modules/services/matrix/synapse.md
index 1d22805b472f..58be24204fcf 100644
--- a/nixpkgs/nixos/modules/services/matrix/synapse.md
+++ b/nixpkgs/nixos/modules/services/matrix/synapse.md
@@ -31,7 +31,7 @@ let
   clientConfig."m.homeserver".base_url = baseUrl;
   serverConfig."m.server" = "${fqdn}:443";
   mkWellKnown = data: ''
-    add_header Content-Type application/json;
+    default_type application/json;
     add_header Access-Control-Allow-Origin *;
     return 200 '${builtins.toJSON data}';
   '';
diff --git a/nixpkgs/nixos/modules/services/matrix/synapse.nix b/nixpkgs/nixos/modules/services/matrix/synapse.nix
index 5cce36f41e50..b38b35361cf0 100644
--- a/nixpkgs/nixos/modules/services/matrix/synapse.nix
+++ b/nixpkgs/nixos/modules/services/matrix/synapse.nix
@@ -12,7 +12,9 @@ let
 
   usePostgresql = cfg.settings.database.name == "psycopg2";
   hasLocalPostgresDB = let args = cfg.settings.database.args; in
-    usePostgresql && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]));
+    usePostgresql
+    && (!(args ? host) || (elem args.host [ "localhost" "127.0.0.1" "::1" ]))
+    && config.services.postgresql.enable;
   hasWorkers = cfg.workers != { };
 
   listenerSupportsResource = resource: listener:
@@ -70,13 +72,12 @@ let
     inherit (cfg) plugins;
   };
 
-  logConfig = logName: {
+  defaultCommonLogConfig = {
     version = 1;
     formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s";
     handlers.journal = {
       class = "systemd.journal.JournalHandler";
       formatter = "journal_fmt";
-      SYSLOG_IDENTIFIER = logName;
     };
     root = {
       level = "INFO";
@@ -84,33 +85,27 @@ let
     };
     disable_existing_loggers = false;
   };
+
+  defaultCommonLogConfigText = generators.toPretty { } defaultCommonLogConfig;
+
   logConfigText = logName:
-    let
-      expr = ''
-        {
-          version = 1;
-          formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s";
-          handlers.journal = {
-            class = "systemd.journal.JournalHandler";
-            formatter = "journal_fmt";
-            SYSLOG_IDENTIFIER = "${logName}";
-          };
-          root = {
-            level = "INFO";
-            handlers = [ "journal" ];
-          };
-          disable_existing_loggers = false;
-        };
-      '';
-    in
     lib.literalMD ''
       Path to a yaml file generated from this Nix expression:
 
       ```
-      ${expr}
+      ${generators.toPretty { } (
+        recursiveUpdate defaultCommonLogConfig { handlers.journal.SYSLOG_IDENTIFIER = logName; }
+      )}
       ```
     '';
-  genLogConfigFile = logName: format.generate "synapse-log-${logName}.yaml" (logConfig logName);
+
+  genLogConfigFile = logName: format.generate
+    "synapse-log-${logName}.yaml"
+    (cfg.log // optionalAttrs (cfg.log?handlers.journal) {
+      handlers.journal = cfg.log.handlers.journal // {
+        SYSLOG_IDENTIFIER = logName;
+      };
+    });
 in {
 
   imports = [
@@ -394,6 +389,49 @@ in {
         '';
       };
 
+      log = mkOption {
+        type = types.attrsOf format.type;
+        defaultText = literalExpression defaultCommonLogConfigText;
+        description = mdDoc ''
+          Default configuration for the loggers used by `matrix-synapse` and its workers.
+          The defaults are added with the default priority which means that
+          these will be merged with additional declarations. These additional
+          declarations also take precedence over the defaults when declared
+          with at least normal priority. For instance
+          the log-level for synapse and its workers can be changed like this:
+
+          ```nix
+          { lib, ... }: {
+            services.matrix-synapse.log.root.level = "WARNING";
+          }
+          ```
+
+          And another field can be added like this:
+
+          ```nix
+          {
+            services.matrix-synapse.log = {
+              loggers."synapse.http.matrixfederationclient".level = "DEBUG";
+            };
+          }
+          ```
+
+          Additionally, the field `handlers.journal.SYSLOG_IDENTIFIER` will be added to
+          each log config, i.e.
+          * `synapse` for `matrix-synapse.service`
+          * `synapse-<worker name>` for `matrix-synapse-worker-<worker name>.service`
+
+          This is only done if this option has a `handlers.journal` field declared.
+
+          To discard all settings declared by this option for each worker and synapse,
+          `lib.mkForce` can be used.
+
+          To discard all settings declared by this option for a single worker or synapse only,
+          [](#opt-services.matrix-synapse.workers._name_.worker_log_config) or
+          [](#opt-services.matrix-synapse.settings.log_config) can be used.
+        '';
+      };
+
       settings = mkOption {
         default = { };
         description = mdDoc ''
@@ -945,23 +983,6 @@ in {
         '';
       }
       {
-        assertion = hasLocalPostgresDB -> config.services.postgresql.enable;
-        message = ''
-          Cannot deploy matrix-synapse with a configuration for a local postgresql database
-            and a missing postgresql service. Since 20.03 it's mandatory to manually configure the
-            database (please read the thread in https://github.com/NixOS/nixpkgs/pull/80447 for
-            further reference).
-
-            If you
-            - try to deploy a fresh synapse, you need to configure the database yourself. An example
-              for this can be found in <nixpkgs/nixos/tests/matrix/synapse.nix>
-            - update your existing matrix-synapse instance, you simply need to add `services.postgresql.enable = true`
-              to your configuration.
-
-          For further information about this update, please read the release-notes of 20.03 carefully.
-        '';
-      }
-      {
         assertion = hasWorkers -> cfg.settings.redis.enabled;
         message = ''
           Workers for matrix-synapse require configuring a redis instance. This can be done
@@ -1008,6 +1029,8 @@ in {
     # default them, so they are additive
     services.matrix-synapse.extras = defaultExtras;
 
+    services.matrix-synapse.log = mapAttrsRecursive (const mkDefault) defaultCommonLogConfig;
+
     users.users.matrix-synapse = {
       group = "matrix-synapse";
       home = cfg.dataDir;
@@ -1022,7 +1045,7 @@ in {
 
     systemd.targets.matrix-synapse = lib.mkIf hasWorkers {
       description = "Synapse Matrix parent target";
-      after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
+      after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
       wantedBy = [ "multi-user.target" ];
     };
 
@@ -1034,9 +1057,11 @@ in {
             partOf = [ "matrix-synapse.target" ];
             wantedBy = [ "matrix-synapse.target" ];
             unitConfig.ReloadPropagatedFrom = "matrix-synapse.target";
+            requires = optional hasLocalPostgresDB "postgresql.service";
           }
           else {
-            after = [ "network.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
+            after = [ "network-online.target" ] ++ optional hasLocalPostgresDB "postgresql.service";
+            requires = optional hasLocalPostgresDB "postgresql.service";
             wantedBy = [ "multi-user.target" ];
           };
         baseServiceConfig = {
@@ -1070,7 +1095,7 @@ in {
             ProtectKernelTunables = true;
             ProtectProc = "invisible";
             ProtectSystem = "strict";
-            ReadWritePaths = [ cfg.dataDir ];
+            ReadWritePaths = [ cfg.dataDir cfg.settings.media_store_path ];
             RemoveIPC = true;
             RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
             RestrictNamespaces = true;
diff --git a/nixpkgs/nixos/modules/services/misc/dysnomia.nix b/nixpkgs/nixos/modules/services/misc/dysnomia.nix
index 0f92265ccbea..129345e38106 100644
--- a/nixpkgs/nixos/modules/services/misc/dysnomia.nix
+++ b/nixpkgs/nixos/modules/services/misc/dysnomia.nix
@@ -223,7 +223,7 @@ in
       ejabberdUser = config.services.ejabberd.user;
     }; }
     // lib.optionalAttrs (config.services.mysql.enable) { mysql-database = {
-        mysqlPort = config.services.mysql.port;
+        mysqlPort = config.services.mysql.settings.mysqld.port;
         mysqlSocket = "/run/mysqld/mysqld.sock";
       } // lib.optionalAttrs cfg.enableAuthentication {
         mysqlUsername = "root";
diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix
index f6ef2bb91910..3f690f85d623 100644
--- a/nixpkgs/nixos/modules/services/misc/gitea.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitea.nix
@@ -246,6 +246,13 @@ in
         description = lib.mdDoc "Path to a file containing the SMTP password.";
       };
 
+      metricsTokenFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "/var/lib/secrets/gitea/metrics_token";
+        description = lib.mdDoc "Path to a file containing the metrics authentication token.";
+      };
+
       settings = mkOption {
         default = {};
         description = lib.mdDoc ''
@@ -433,6 +440,10 @@ in
         PASSWD = "#mailerpass#";
       };
 
+      metrics = mkIf (cfg.metricsTokenFile != null) {
+        TOKEN = "#metricstoken#";
+      };
+
       oauth2 = {
         JWT_SECRET = "#oauth2jwtsecret#";
       };
@@ -559,6 +570,10 @@ in
             ${lib.optionalString (cfg.mailerPasswordFile != null) ''
               ${replaceSecretBin} '#mailerpass#' '${cfg.mailerPasswordFile}' '${runConfig}'
             ''}
+
+            ${lib.optionalString (cfg.metricsTokenFile != null) ''
+              ${replaceSecretBin} '#metricstoken#' '${cfg.metricsTokenFile}' '${runConfig}'
+            ''}
             chmod u-w '${runConfig}'
           }
           (umask 027; gitea_setup)
diff --git a/nixpkgs/nixos/modules/services/misc/gollum.nix b/nixpkgs/nixos/modules/services/misc/gollum.nix
index d607e92e5ec9..b73528abaf65 100644
--- a/nixpkgs/nixos/modules/services/misc/gollum.nix
+++ b/nixpkgs/nixos/modules/services/misc/gollum.nix
@@ -154,5 +154,5 @@ in
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ erictapen bbenno joscha ];
+  meta.maintainers = with lib.maintainers; [ erictapen bbenno ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/gpsd.nix b/nixpkgs/nixos/modules/services/misc/gpsd.nix
index ce0f9bb3ba28..5d2e806181df 100644
--- a/nixpkgs/nixos/modules/services/misc/gpsd.nix
+++ b/nixpkgs/nixos/modules/services/misc/gpsd.nix
@@ -92,6 +92,16 @@ in {
         '';
       };
 
+      extraArgs = mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        example = [ "-r" "-s" "19200" ];
+        description = lib.mdDoc ''
+          A list of extra command line arguments to pass to gpsd.
+          Check gpsd(8) mangpage for possible arguments.
+        '';
+      };
+
     };
 
   };
@@ -117,12 +127,14 @@ in {
         Type = "forking";
         ExecStart = let
           devices = utils.escapeSystemdExecArgs cfg.devices;
+          extraArgs = utils.escapeSystemdExecArgs cfg.extraArgs;
         in ''
           ${pkgs.gpsd}/sbin/gpsd -D "${toString cfg.debugLevel}"  \
             -S "${toString cfg.port}"                             \
             ${optionalString cfg.readonly "-b"}                   \
             ${optionalString cfg.nowait "-n"}                     \
             ${optionalString cfg.listenany "-G"}                  \
+            ${extraArgs}                                          \
             ${devices}
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix
index 67a217c994e4..9eb2fdb46593 100644
--- a/nixpkgs/nixos/modules/services/misc/klipper.nix
+++ b/nixpkgs/nixos/modules/services/misc/klipper.nix
@@ -111,11 +111,11 @@ in
           (submodule {
             options = {
               enable = mkEnableOption (lib.mdDoc ''
-                building of firmware for manual flashing.
+                building of firmware for manual flashing
               '');
               enableKlipperFlash = mkEnableOption (lib.mdDoc ''
                 flashings scripts for firmware. This will add `klipper-flash-$mcu` scripts to your environment which can be called to flash the firmware.
-                Please check the configs at [klipper](https://github.com/Klipper3d/klipper/tree/master/config) whether your board supports flashing via `make flash`.
+                Please check the configs at [klipper](https://github.com/Klipper3d/klipper/tree/master/config) whether your board supports flashing via `make flash`
               '');
               serial = mkOption {
                 type = types.nullOr path;
diff --git a/nixpkgs/nixos/modules/services/misc/mbpfan.nix b/nixpkgs/nixos/modules/services/misc/mbpfan.nix
index e75c35254143..8f64fb2d9c52 100644
--- a/nixpkgs/nixos/modules/services/misc/mbpfan.nix
+++ b/nixpkgs/nixos/modules/services/misc/mbpfan.nix
@@ -26,7 +26,7 @@ in {
 
     aggressive = mkOption {
       type = types.bool;
-      default = false;
+      default = true;
       description = lib.mdDoc "If true, favors higher default fan speeds.";
     };
 
@@ -38,17 +38,20 @@ in {
 
         options.general.low_temp = mkOption {
           type = types.int;
-          default = 63;
+          default = (if cfg.aggressive then 55 else 63);
+          defaultText = literalExpression "55";
           description = lib.mdDoc "If temperature is below this, fans will run at minimum speed.";
         };
         options.general.high_temp = mkOption {
           type = types.int;
-          default = 66;
+          default = (if cfg.aggressive then 58 else 66);
+          defaultText = literalExpression "58";
           description = lib.mdDoc "If temperature is above this, fan speed will gradually increase.";
         };
         options.general.max_temp = mkOption {
           type = types.int;
-          default = 86;
+          default = (if cfg.aggressive then 78 else 86);
+          defaultText = literalExpression "78";
           description = lib.mdDoc "If temperature is above this, fans will run at maximum speed.";
         };
         options.general.polling_interval = mkOption {
@@ -70,13 +73,6 @@ in {
   ];
 
   config = mkIf cfg.enable {
-    services.mbpfan.settings = mkIf cfg.aggressive {
-      general.min_fan1_speed = mkDefault 2000;
-      general.low_temp = mkDefault 55;
-      general.high_temp = mkDefault 58;
-      general.max_temp = mkDefault 70;
-    };
-
     boot.kernelModules = [ "coretemp" "applesmc" ];
     environment.systemPackages = [ cfg.package ];
     environment.etc."mbpfan.conf".source = settingsFile;
@@ -86,6 +82,7 @@ in {
       wantedBy = [ "sysinit.target" ];
       after = [ "syslog.target" "sysinit.target" ];
       restartTriggers = [ config.environment.etc."mbpfan.conf".source ];
+
       serviceConfig = {
         Type = "simple";
         ExecStart = "${cfg.package}/bin/mbpfan -f${verbose}";
diff --git a/nixpkgs/nixos/modules/services/misc/moonraker.nix b/nixpkgs/nixos/modules/services/misc/moonraker.nix
index 7e306d718e08..797e145c47a6 100644
--- a/nixpkgs/nixos/modules/services/misc/moonraker.nix
+++ b/nixpkgs/nixos/modules/services/misc/moonraker.nix
@@ -1,8 +1,8 @@
 { config, lib, options, pkgs, ... }:
 with lib;
 let
-  pkg = pkgs.moonraker;
   cfg = config.services.moonraker;
+  pkg = cfg.package;
   opt = options.services.moonraker;
   format = pkgs.formats.ini {
     # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
@@ -18,6 +18,14 @@ in {
     services.moonraker = {
       enable = mkEnableOption (lib.mdDoc "Moonraker, an API web server for Klipper");
 
+      package = mkOption {
+        type = with types; nullOr package;
+        default = pkgs.moonraker;
+        defaultText = literalExpression "pkgs.moonraker";
+        example = literalExpression "pkgs.moonraker.override { useGpiod = true; }";
+        description = lib.mdDoc "Moonraker package to use";
+      };
+
       klipperSocket = mkOption {
         type = types.path;
         default = config.services.klipper.apiSocket;
diff --git a/nixpkgs/nixos/modules/services/misc/packagekit.nix b/nixpkgs/nixos/modules/services/misc/packagekit.nix
index f3e6bf50e9b2..5a0d314d25cd 100644
--- a/nixpkgs/nixos/modules/services/misc/packagekit.nix
+++ b/nixpkgs/nixos/modules/services/misc/packagekit.nix
@@ -40,9 +40,9 @@ in
 
   options.services.packagekit = {
     enable = mkEnableOption (lib.mdDoc ''
-      PackageKit provides a cross-platform D-Bus abstraction layer for
+      PackageKit, a cross-platform D-Bus abstraction layer for
       installing software. Software utilizing PackageKit can install
-      software regardless of the package manager.
+      software regardless of the package manager
     '');
 
     settings = mkOption {
diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix
index 74a3b49ac9a6..9b8bd62809c5 100644
--- a/nixpkgs/nixos/modules/services/misc/paperless.nix
+++ b/nixpkgs/nixos/modules/services/misc/paperless.nix
@@ -36,18 +36,7 @@ let
 
   # Secure the services
   defaultServiceConfig = {
-    TemporaryFileSystem = "/:ro";
-    BindReadOnlyPaths = [
-      "/nix/store"
-      "-/etc/resolv.conf"
-      "-/etc/nsswitch.conf"
-      "-/etc/hosts"
-      "-/etc/localtime"
-      "-/etc/ssl/certs"
-      "-/etc/static/ssl/certs"
-      "-/run/postgresql"
-    ] ++ (optional enableRedis redisServer.unixSocket);
-    BindPaths = [
+    ReadWritePaths = [
       cfg.consumptionDir
       cfg.dataDir
       cfg.mediaDir
@@ -66,11 +55,9 @@ let
     PrivateUsers = true;
     ProtectClock = true;
     # Breaks if the home dir of the user is in /home
-    # Also does not add much value in combination with the TemporaryFileSystem.
     # ProtectHome = true;
     ProtectHostname = true;
-    # Would re-mount paths ignored by temporary root
-    #ProtectSystem = "strict";
+    ProtectSystem = "strict";
     ProtectControlGroups = true;
     ProtectKernelLogs = true;
     ProtectKernelModules = true;
@@ -319,17 +306,6 @@ in
         Type = "oneshot";
         # Enable internet access
         PrivateNetwork = false;
-        # Restrict write access
-        BindPaths = [];
-        BindReadOnlyPaths = [
-          "/nix/store"
-          "-/etc/resolv.conf"
-          "-/etc/nsswitch.conf"
-          "-/etc/ssl/certs"
-          "-/etc/static/ssl/certs"
-          "-/etc/hosts"
-          "-/etc/localtime"
-        ];
         ExecStart = let pythonWithNltk = pkg.python.withPackages (ps: [ ps.nltk ]); in ''
           ${pythonWithNltk}/bin/python -m nltk.downloader -d '${nltkDir}' punkt snowball_data stopwords
         '';
diff --git a/nixpkgs/nixos/modules/services/misc/rshim.nix b/nixpkgs/nixos/modules/services/misc/rshim.nix
index 0fef2cc228c9..706cf9136b00 100644
--- a/nixpkgs/nixos/modules/services/misc/rshim.nix
+++ b/nixpkgs/nixos/modules/services/misc/rshim.nix
@@ -12,7 +12,7 @@ let
 in
 {
   options.services.rshim = {
-    enable = lib.mkEnableOption (lib.mdDoc "User-space rshim driver for the BlueField SoC");
+    enable = lib.mkEnableOption (lib.mdDoc "user-space rshim driver for the BlueField SoC");
 
     package = lib.mkPackageOptionMD pkgs "rshim-user-space" { };
 
diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
index 580a009a0ad3..bee971662972 100644
--- a/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
@@ -438,7 +438,7 @@ in
         };
 
         options."lists.sr.ht" = commonServiceSettings "lists" // {
-          allow-new-lists = mkEnableOption (lib.mdDoc "Allow creation of new lists");
+          allow-new-lists = mkEnableOption (lib.mdDoc "creation of new lists");
           notify-from = mkOption {
             description = lib.mdDoc "Outgoing email for notifications generated by users.";
             type = types.str;
diff --git a/nixpkgs/nixos/modules/services/misc/tp-auto-kbbl.nix b/nixpkgs/nixos/modules/services/misc/tp-auto-kbbl.nix
index 8d92d3d93677..1076c814e86c 100644
--- a/nixpkgs/nixos/modules/services/misc/tp-auto-kbbl.nix
+++ b/nixpkgs/nixos/modules/services/misc/tp-auto-kbbl.nix
@@ -9,7 +9,7 @@ in {
 
   options = {
     services.tp-auto-kbbl = {
-      enable = mkEnableOption (lib.mdDoc "Auto toggle keyboard back-lighting on Thinkpads (and maybe other laptops) for Linux");
+      enable = mkEnableOption (lib.mdDoc "auto toggle keyboard back-lighting on Thinkpads (and maybe other laptops) for Linux");
 
       package = mkOption {
         type = types.package;
diff --git a/nixpkgs/nixos/modules/services/misc/xmr-stak.nix b/nixpkgs/nixos/modules/services/misc/xmr-stak.nix
index 6e123cf0380c..54efae48d5d2 100644
--- a/nixpkgs/nixos/modules/services/misc/xmr-stak.nix
+++ b/nixpkgs/nixos/modules/services/misc/xmr-stak.nix
@@ -7,7 +7,7 @@ let
   cfg = config.services.xmr-stak;
 
   pkg = pkgs.xmr-stak.override {
-    inherit (cfg) openclSupport cudaSupport;
+    inherit (cfg) openclSupport;
   };
 
 in
@@ -17,7 +17,6 @@ in
     services.xmr-stak = {
       enable = mkEnableOption (lib.mdDoc "xmr-stak miner");
       openclSupport = mkEnableOption (lib.mdDoc "support for OpenCL (AMD/ATI graphics cards)");
-      cudaSupport = mkEnableOption (lib.mdDoc "support for CUDA (NVidia graphics cards)");
 
       extraArgs = mkOption {
         type = types.listOf types.str;
@@ -64,15 +63,12 @@ in
       wantedBy = [ "multi-user.target" ];
       bindsTo = [ "network-online.target" ];
       after = [ "network-online.target" ];
-      environment = mkIf cfg.cudaSupport {
-        LD_LIBRARY_PATH = "${pkgs.linuxPackages_latest.nvidia_x11}/lib";
-      };
 
       preStart = concatStrings (flip mapAttrsToList cfg.configFiles (fn: content: ''
         ln -sf '${pkgs.writeText "xmr-stak-${fn}" content}' '${fn}'
       ''));
 
-      serviceConfig = let rootRequired = cfg.openclSupport || cfg.cudaSupport; in {
+      serviceConfig = let rootRequired = cfg.openclSupport; in {
         ExecStart = "${pkg}/bin/xmr-stak ${concatStringsSep " " cfg.extraArgs}";
         # xmr-stak generates cpu and/or gpu configuration files
         WorkingDirectory = "/tmp";
diff --git a/nixpkgs/nixos/modules/services/misc/zoneminder.nix b/nixpkgs/nixos/modules/services/misc/zoneminder.nix
index b2e4e760d828..fca03b2ad4e1 100644
--- a/nixpkgs/nixos/modules/services/misc/zoneminder.nix
+++ b/nixpkgs/nixos/modules/services/misc/zoneminder.nix
@@ -67,14 +67,14 @@ in {
   options = {
     services.zoneminder = with lib; {
       enable = lib.mkEnableOption (lib.mdDoc ''
-        ZoneMinder
+        ZoneMinder.
 
         If you intend to run the database locally, you should set
         `config.services.zoneminder.database.createLocally` to true. Otherwise,
         when set to `false` (the default), you will have to create the database
         and database user as well as populate the database yourself.
         Additionally, you will need to run `zmupdate.pl` yourself when
-        upgrading to a newer version.
+        upgrading to a newer version
       '');
 
       webserver = mkOption {
diff --git a/nixpkgs/nixos/modules/services/monitoring/librenms.nix b/nixpkgs/nixos/modules/services/monitoring/librenms.nix
new file mode 100644
index 000000000000..08a46754e0e8
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/librenms.nix
@@ -0,0 +1,624 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.librenms;
+  settingsFormat = pkgs.formats.json {};
+  configJson = settingsFormat.generate "librenms-config.json" cfg.settings;
+
+  package = pkgs.librenms.override {
+    logDir = cfg.logDir;
+    dataDir = cfg.dataDir;
+  };
+
+  phpOptions = ''
+    log_errors = on
+    post_max_size = 100M
+    upload_max_filesize = 100M
+    date.timezone = "${config.time.timeZone}"
+  '';
+  phpIni = pkgs.runCommand "php.ini" {
+    inherit (package) phpPackage;
+    inherit phpOptions;
+    preferLocalBuild = true;
+    passAsFile = [ "phpOptions" ];
+  } ''
+    cat $phpPackage/etc/php.ini $phpOptionsPath > $out
+  '';
+
+  artisanWrapper = pkgs.writeShellScriptBin "librenms-artisan" ''
+    cd ${package}
+    sudo=exec
+    if [[ "$USER" != ${cfg.user} ]]; then
+      sudo='exec /run/wrappers/bin/sudo -u ${cfg.user}'
+    fi
+    $sudo ${package}/artisan $*
+  '';
+
+  lnmsWrapper = pkgs.writeShellScriptBin "lnms" ''
+    cd ${package}
+    exec ${package}/lnms $*
+  '';
+
+  configFile = pkgs.writeText "config.php" ''
+    <?php
+    $new_config = json_decode(file_get_contents("${cfg.dataDir}/config.json"), true);
+    $config = ($config == null) ? $new_config : array_merge($config, $new_config);
+
+    ${lib.optionalString (cfg.extraConfig != null) cfg.extraConfig}
+  '';
+
+in {
+  options.services.librenms = with lib; {
+    enable = mkEnableOption "LibreNMS network monitoring system";
+
+    user = mkOption {
+      type = types.str;
+      default = "librenms";
+      description = ''
+        Name of the LibreNMS user.
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "librenms";
+      description = ''
+        Name of the LibreNMS group.
+      '';
+    };
+
+    hostname = mkOption {
+      type = types.str;
+      default = config.networking.fqdnOrHostName;
+      defaultText = literalExpression "config.networking.fqdnOrHostName";
+      description = ''
+        The hostname to serve LibreNMS on.
+      '';
+    };
+
+    pollerThreads = mkOption {
+      type = types.int;
+      default = 16;
+      description = ''
+        Amount of threads of the cron-poller.
+      '';
+    };
+
+    enableOneMinutePolling = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables the [1-Minute Polling](https://docs.librenms.org/Support/1-Minute-Polling/).
+        Changing this option will automatically convert your existing rrd files.
+      '';
+    };
+
+    useDistributedPollers = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables (distributed pollers)[https://docs.librenms.org/Extensions/Distributed-Poller/]
+        for this LibreNMS instance. This will enable a local `rrdcached` and `memcached` server.
+
+        To use this feature, make sure to configure your firewall that the distributed pollers
+        can reach the local `mysql`, `rrdcached` and `memcached` ports.
+      '';
+    };
+
+    distributedPoller = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Configure this LibreNMS instance as a (distributed poller)[https://docs.librenms.org/Extensions/Distributed-Poller/].
+          This will disable all web features and just configure the poller features.
+          Use the `mysql` database of your main LibreNMS instance in the database settings.
+        '';
+      };
+
+      name = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Custom name of this poller.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "0";
+        example = "1,2";
+        description = ''
+          Group(s) of this poller.
+        '';
+      };
+
+      distributedBilling = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable distributed billing on this poller.
+        '';
+      };
+
+      memcachedHost = mkOption {
+        type = types.str;
+        description = ''
+          Hostname or IP of the `memcached` server.
+        '';
+      };
+
+      memcachedPort = mkOption {
+        type = types.port;
+        default = 11211;
+        description = ''
+          Port of the `memcached` server.
+        '';
+      };
+
+      rrdcachedHost = mkOption {
+        type = types.str;
+        description = ''
+          Hostname or IP of the `rrdcached` server.
+        '';
+      };
+
+      rrdcachedPort = mkOption {
+        type = types.port;
+        default = 42217;
+        description = ''
+          Port of the `memcached` server.
+        '';
+      };
+    };
+
+    poolConfig = mkOption {
+      type = with types; attrsOf (oneOf [ str int bool ]);
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = 32;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 2;
+        "pm.max_spare_servers" = 4;
+        "pm.max_requests" = 500;
+      };
+      description = ''
+        Options for the LibreNMS PHP pool. See the documentation on `php-fpm.conf`
+        for details on configuration directives.
+      '';
+    };
+
+    nginx = mkOption {
+      type = types.submodule (
+        recursiveUpdate
+          (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {}
+      );
+      default = { };
+      example = literalExpression ''
+        {
+          serverAliases = [
+            "librenms.''${config.networking.domain}"
+          ];
+          # To enable encryption and let let's encrypt take care of certificate
+          forceSSL = true;
+          enableACME = true;
+          # To set the LibreNMS virtualHost as the default virtualHost;
+          default = true;
+        }
+      '';
+      description = ''
+        With this option, you can customize the nginx virtualHost settings.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.path;
+      default = "/var/lib/librenms";
+      description = ''
+        Path of the LibreNMS state directory.
+      '';
+    };
+
+    logDir = mkOption {
+      type = types.path;
+      default = "/var/log/librenms";
+      description = ''
+        Path of the LibreNMS logging directory.
+      '';
+    };
+
+    database = {
+      createLocally = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to create a local database automatically.
+        '';
+      };
+
+      host = mkOption {
+        default = "localhost";
+        description = ''
+          Hostname or IP of the MySQL/MariaDB server.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 3306;
+        description = ''
+          Port of the MySQL/MariaDB server.
+        '';
+      };
+
+      database = mkOption {
+        type = types.str;
+        default = "librenms";
+        description = ''
+          Name of the database on the MySQL/MariaDB server.
+        '';
+      };
+
+      username = mkOption {
+        type = types.str;
+        default = "librenms";
+        description = ''
+          Name of the user on the MySQL/MariaDB server.
+        '';
+      };
+
+      passwordFile = mkOption {
+        type = types.path;
+        example = "/run/secrets/mysql.pass";
+        description = ''
+          A file containing the password for the user of the MySQL/MariaDB server.
+          Must be readable for the LibreNMS user.
+        '';
+      };
+    };
+
+    environmentFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        File containing env-vars to be substituted into the final config. Useful for secrets.
+        Does not apply to settings defined in `extraConfig`.
+      '';
+    };
+
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+        options = {};
+      };
+      description = ''
+        Attrset of the LibreNMS configuration.
+        See https://docs.librenms.org/Support/Configuration/ for reference.
+        All possible options are listed [here](https://github.com/librenms/librenms/blob/master/misc/config_definitions.json).
+        See https://docs.librenms.org/Extensions/Authentication/ for setting other authentication methods.
+      '';
+      default = { };
+      example = {
+        base_url = "/librenms/";
+        top_devices = true;
+        top_ports = false;
+      };
+    };
+
+    extraConfig = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Additional config for LibreNMS that will be appended to the `config.php`. See
+        https://github.com/librenms/librenms/blob/master/misc/config_definitions.json
+        for possible options. Useful if you want to use PHP-Functions in your config.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = config.time.timeZone != null;
+        message = "You must set `time.timeZone` to use the LibreNMS module.";
+      }
+      {
+        assertion = cfg.database.createLocally -> cfg.database.host == "localhost";
+        message = "The database host must be \"localhost\" if services.librenms.database.createLocally is set to true.";
+      }
+      {
+        assertion = !(cfg.useDistributedPollers && cfg.distributedPoller.enable);
+        message = "The LibreNMS instance can't be a distributed poller and a full instance at the same time.";
+      }
+    ];
+
+    users.users.${cfg.user} = {
+      group = "${cfg.group}";
+      isSystemUser = true;
+    };
+
+    users.groups.${cfg.group} = { };
+
+    services.librenms.settings = {
+      # basic configs
+      "user" = cfg.user;
+      "own_hostname" = cfg.hostname;
+      "base_url" = lib.mkDefault "/";
+      "auth_mechanism" = lib.mkDefault "mysql";
+
+      # disable auto update function (won't work with NixOS)
+      "update" = false;
+
+      # enable fast ping by default
+      "ping_rrd_step" = 60;
+
+      # one minute polling
+      "rrd.step" = if cfg.enableOneMinutePolling then 60 else 300;
+      "rrd.heartbeat" = if cfg.enableOneMinutePolling then 120 else 600;
+    } // (lib.optionalAttrs cfg.distributedPoller.enable {
+      "distributed_poller" = true;
+      "distributed_poller_name" = lib.mkIf (cfg.distributedPoller.name != null) cfg.distributedPoller.name;
+      "distributed_poller_group" = cfg.distributedPoller.group;
+      "distributed_billing" = cfg.distributedPoller.distributedBilling;
+      "distributed_poller_memcached_host" = cfg.distributedPoller.memcachedHost;
+      "distributed_poller_memcached_port" = cfg.distributedPoller.memcachedPort;
+      "rrdcached" = "${cfg.distributedPoller.rrdcachedHost}:${toString cfg.distributedPoller.rrdcachedPort}";
+    }) // (lib.optionalAttrs cfg.useDistributedPollers {
+      "distributed_poller" = true;
+      # still enable a local poller with distributed polling
+      "distributed_poller_group" = lib.mkDefault "0";
+      "distributed_billing" = lib.mkDefault true;
+      "distributed_poller_memcached_host" = "localhost";
+      "distributed_poller_memcached_port" = 11211;
+      "rrdcached" = "localhost:42217";
+    });
+
+    services.memcached = lib.mkIf cfg.useDistributedPollers {
+      enable = true;
+      listen = "0.0.0.0";
+    };
+
+    systemd.services.rrdcached = lib.mkIf cfg.useDistributedPollers {
+      description = "rrdcached";
+      after = [ "librenms-setup.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        User = cfg.user;
+        Group = cfg.group;
+        LimitNOFILE = 16384;
+        RuntimeDirectory = "rrdcached";
+        PidFile = "/run/rrdcached/rrdcached.pid";
+        # rrdcached params from https://docs.librenms.org/Extensions/Distributed-Poller/#config-sample
+        ExecStart = "${pkgs.rrdtool}/bin/rrdcached -l 0:42217 -R -j ${cfg.dataDir}/rrdcached-journal/ -F -b ${cfg.dataDir}/rrd -B -w 1800 -z 900 -p /run/rrdcached/rrdcached.pid";
+      };
+    };
+
+    services.mysql = lib.mkIf cfg.database.createLocally {
+      enable = true;
+      package = lib.mkDefault pkgs.mariadb;
+      settings.mysqld = {
+        innodb_file_per_table = 1;
+        lower_case_table_names = 0;
+      } // (lib.optionalAttrs cfg.useDistributedPollers {
+        bind-address = "0.0.0.0";
+      });
+      ensureDatabases = [ cfg.database.database ];
+      ensureUsers = [
+        {
+          name = cfg.database.username;
+          ensurePermissions = {
+            "${cfg.database.database}.*" = "ALL PRIVILEGES";
+          };
+        }
+      ];
+      initialScript = lib.mkIf cfg.useDistributedPollers (pkgs.writeText "mysql-librenms-init" ''
+        CREATE USER IF NOT EXISTS '${cfg.database.username}'@'%';
+        GRANT ALL PRIVILEGES ON ${cfg.database.database}.* TO '${cfg.database.username}'@'%';
+      '');
+    };
+
+    services.nginx = lib.mkIf (!cfg.distributedPoller.enable) {
+      enable = true;
+      virtualHosts."${cfg.hostname}" = lib.mkMerge [
+        cfg.nginx
+        {
+          root = lib.mkForce "${package}/html";
+          locations."/" = {
+            index = "index.php";
+            tryFiles = "$uri $uri/ /index.php?$query_string";
+          };
+          locations."~ .php$".extraConfig = ''
+            fastcgi_pass unix:${config.services.phpfpm.pools."librenms".socket};
+            fastcgi_split_path_info ^(.+\.php)(/.+)$;
+          '';
+        }
+      ];
+    };
+
+    services.phpfpm.pools.librenms = lib.mkIf (!cfg.distributedPoller.enable) {
+      user = cfg.user;
+      group = cfg.group;
+      inherit (package) phpPackage;
+      inherit phpOptions;
+      settings = {
+        "listen.mode" = "0660";
+        "listen.owner" = config.services.nginx.user;
+        "listen.group" = config.services.nginx.group;
+      } // cfg.poolConfig;
+    };
+
+    systemd.services.librenms-scheduler = {
+      description = "LibreNMS Scheduler";
+      path = [ pkgs.unixtools.whereis ];
+      serviceConfig = {
+        Type = "oneshot";
+        WorkingDirectory = package;
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${artisanWrapper}/bin/librenms-artisan schedule:run";
+      };
+    };
+
+    systemd.timers.librenms-scheduler = {
+      description = "LibreNMS Scheduler";
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = "minutely";
+        AccuracySec = "1second";
+      };
+    };
+
+    systemd.services.librenms-setup = {
+      description = "Preparation tasks for LibreNMS";
+      before = [ "phpfpm-librenms.service" ];
+      after = [ "systemd-tmpfiles-setup.service" ]
+        ++ (lib.optional (cfg.database.host == "localhost") "mysql.service");
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ package configFile ];
+      path = [ pkgs.mariadb pkgs.unixtools.whereis pkgs.gnused ];
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStartPre = lib.mkIf cfg.database.createLocally [ "!${pkgs.writeShellScript "librenms-db-init" ''
+          DB_PASSWORD=$(cat ${cfg.database.passwordFile} | tr -d '\n')
+          echo "ALTER USER '${cfg.database.username}'@'localhost' IDENTIFIED BY '$DB_PASSWORD';" | ${pkgs.mariadb}/bin/mysql
+          ${lib.optionalString cfg.useDistributedPollers ''
+            echo "ALTER USER '${cfg.database.username}'@'%' IDENTIFIED BY '$DB_PASSWORD';" | ${pkgs.mariadb}/bin/mysql
+          ''}
+        ''}"];
+      };
+      script = ''
+        set -euo pipefail
+
+        # config setup
+        ln -sf ${configFile} ${cfg.dataDir}/config.php
+        ${pkgs.envsubst}/bin/envsubst -i ${configJson} -o ${cfg.dataDir}/config.json
+        export PHPRC=${phpIni}
+
+        if [[ ! -s ${cfg.dataDir}/.env ]]; then
+          # init .env file
+          echo "APP_KEY=" > ${cfg.dataDir}/.env
+          ${artisanWrapper}/bin/librenms-artisan key:generate --ansi
+          ${artisanWrapper}/bin/librenms-artisan webpush:vapid
+          echo "" >> ${cfg.dataDir}/.env
+          echo -n "NODE_ID=" >> ${cfg.dataDir}/.env
+          ${package.phpPackage}/bin/php -r "echo uniqid();" >> ${cfg.dataDir}/.env
+          echo "" >> ${cfg.dataDir}/.env
+        else
+          # .env file already exists --> only update database and cache config
+          ${pkgs.gnused}/bin/sed -i /^DB_/d ${cfg.dataDir}/.env
+          ${pkgs.gnused}/bin/sed -i /^CACHE_DRIVER/d ${cfg.dataDir}/.env
+        fi
+        ${lib.optionalString (cfg.useDistributedPollers || cfg.distributedPoller.enable) ''
+          echo "CACHE_DRIVER=memcached" >> ${cfg.dataDir}/.env
+        ''}
+        echo "DB_HOST=${cfg.database.host}" >> ${cfg.dataDir}/.env
+        echo "DB_PORT=${toString cfg.database.port}" >> ${cfg.dataDir}/.env
+        echo "DB_DATABASE=${cfg.database.database}" >> ${cfg.dataDir}/.env
+        echo "DB_USERNAME=${cfg.database.username}" >> ${cfg.dataDir}/.env
+        echo -n "DB_PASSWORD=" >> ${cfg.dataDir}/.env
+        cat ${cfg.database.passwordFile} >> ${cfg.dataDir}/.env
+
+        # clear cache after update
+        OLD_VERSION=$(cat ${cfg.dataDir}/version)
+        if [[ $OLD_VERSION != "${package.version}" ]]; then
+          rm -r ${cfg.dataDir}/cache/*
+          echo "${package.version}" > ${cfg.dataDir}/version
+        fi
+
+        # convert rrd files when the oneMinutePolling option is changed
+        OLD_ENABLED=$(cat ${cfg.dataDir}/one_minute_enabled)
+        if [[ $OLD_ENABLED != "${lib.boolToString cfg.enableOneMinutePolling}" ]]; then
+          ${package}/scripts/rrdstep.php -h all
+          echo "${lib.boolToString cfg.enableOneMinutePolling}" > ${cfg.dataDir}/one_minute_enabled
+        fi
+
+        # migrate db
+        ${artisanWrapper}/bin/librenms-artisan migrate --force --no-interaction
+      '';
+    };
+
+    programs.mtr.enable = true;
+
+    services.logrotate = {
+      enable = true;
+      settings."${cfg.logDir}/librenms.log" = {
+        su = "${cfg.user} ${cfg.group}";
+        create = "0640 ${cfg.user} ${cfg.group}";
+        rotate = 6;
+        frequency = "weekly";
+        compress = true;
+        delaycompress = true;
+        missingok = true;
+        notifempty = true;
+      };
+    };
+
+    services.cron = {
+      enable = true;
+      systemCronJobs = let
+        env = "PHPRC=${phpIni}";
+      in [
+        # based on crontab provided by LibreNMS
+        "33 */6 * * * ${cfg.user} ${env} ${package}/cronic ${package}/discovery-wrapper.py 1"
+        "*/5 * * * * ${cfg.user} ${env} ${package}/discovery.php -h new >> /dev/null 2>&1"
+
+        "${if cfg.enableOneMinutePolling then "*" else "*/5"} * * * * ${cfg.user} ${env} ${package}/cronic ${package}/poller-wrapper.py ${toString cfg.pollerThreads}"
+        "* * * * * ${cfg.user} ${env} ${package}/alerts.php >> /dev/null 2>&1"
+
+        "*/5 * * * * ${cfg.user} ${env} ${package}/poll-billing.php >> /dev/null 2>&1"
+        "01 * * * * ${cfg.user} ${env} ${package}/billing-calculate.php >> /dev/null 2>&1"
+        "*/5 * * * * ${cfg.user} ${env} ${package}/check-services.php >> /dev/null 2>&1"
+
+        # extra: fast ping
+        "* * * * * ${cfg.user} ${env} ${package}/ping.php >> /dev/null 2>&1"
+
+        # daily.sh tasks are split to exclude update
+        "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh cleanup >> /dev/null 2>&1"
+        "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh notifications >> /dev/null 2>&1"
+        "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh peeringdb >> /dev/null 2>&1"
+        "19 0 * * * ${cfg.user} ${env} ${package}/daily.sh mac_oui >> /dev/null 2>&1"
+      ];
+    };
+
+    security.wrappers = {
+      fping = {
+        setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${pkgs.fping}/bin/fping";
+      };
+    };
+
+    environment.systemPackages = [ artisanWrapper lnmsWrapper ];
+
+    systemd.tmpfiles.rules = [
+      "d ${cfg.logDir}                               0750 ${cfg.user} ${cfg.group} - -"
+      "f ${cfg.logDir}/librenms.log                  0640 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}                              0750 ${cfg.user} ${cfg.group} - -"
+      "f ${cfg.dataDir}/.env                         0600 ${cfg.user} ${cfg.group} - -"
+      "f ${cfg.dataDir}/version                      0600 ${cfg.user} ${cfg.group} - -"
+      "f ${cfg.dataDir}/one_minute_enabled           0600 ${cfg.user} ${cfg.group} - -"
+      "f ${cfg.dataDir}/config.json                  0600 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage                      0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/app                  0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/debugbar             0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/framework            0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/framework/cache      0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/framework/sessions   0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/framework/views      0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/storage/logs                 0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/rrd                          0700 ${cfg.user} ${cfg.group} - -"
+      "d ${cfg.dataDir}/cache                        0700 ${cfg.user} ${cfg.group} - -"
+    ] ++ lib.optionals cfg.useDistributedPollers [
+      "d ${cfg.dataDir}/rrdcached-journal            0700 ${cfg.user} ${cfg.group} - -"
+    ];
+
+  };
+
+  meta.maintainers = lib.teams.wdz.members;
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/mackerel-agent.nix b/nixpkgs/nixos/modules/services/monitoring/mackerel-agent.nix
index 67dc1bc19edd..62a7858500f2 100644
--- a/nixpkgs/nixos/modules/services/monitoring/mackerel-agent.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/mackerel-agent.nix
@@ -11,10 +11,10 @@ in {
 
     # the upstream package runs as root, but doesn't seem to be strictly
     # necessary for basic functionality
-    runAsRoot = mkEnableOption (lib.mdDoc "Whether to run as root");
+    runAsRoot = mkEnableOption (lib.mdDoc "running as root");
 
     autoRetirement = mkEnableOption (lib.mdDoc ''
-      Whether to automatically retire the host upon OS shutdown.
+      retiring the host upon OS shutdown
     '');
 
     apiKeyFile = mkOption {
@@ -59,7 +59,7 @@ in {
         };
 
         options.diagnostic =
-          mkEnableOption (lib.mdDoc "Collect memory usage for the agent itself");
+          mkEnableOption (lib.mdDoc "collecting memory usage for the agent itself");
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix
index 987f17c2c6e6..5fb543ec6195 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -8,7 +8,7 @@ let
 
   checkedConfig = file:
     if cfg.checkConfig then
-      pkgs.runCommand "checked-config" { buildInputs = [ cfg.package ]; } ''
+      pkgs.runCommand "checked-config" { nativeBuildInputs = [ cfg.package ]; } ''
         ln -s ${file} $out
         amtool check-config $out
       '' else file;
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
index 19ee3ae6f7da..a38855ccd408 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
@@ -31,7 +31,7 @@ let
     if checkConfigEnabled then
       pkgs.runCommandLocal
         "${name}-${replaceStrings [" "] [""] what}-checked"
-        { buildInputs = [ cfg.package.cli ]; } ''
+        { nativeBuildInputs = [ cfg.package.cli ]; } ''
         ln -s ${file} $out
         promtool ${what} $out
       '' else file;
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
index 66aff30b5ed1..305f235054be 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -37,6 +37,7 @@ let
     "fritzbox"
     "graphite"
     "idrac"
+    "imap-mailstat"
     "influxdb"
     "ipmi"
     "json"
@@ -58,6 +59,7 @@ let
     "nut"
     "openldap"
     "openvpn"
+    "pgbouncer"
     "php-fpm"
     "pihole"
     "postfix"
@@ -68,6 +70,7 @@ let
     "redis"
     "rspamd"
     "rtl_433"
+    "sabnzbd"
     "scaphandre"
     "script"
     "shelly"
@@ -312,6 +315,25 @@ in
           'services.prometheus.exporters.nextcloud.tokenFile'
       '';
     } {
+      assertion =  cfg.pgbouncer.enable -> (
+        (cfg.pgbouncer.connectionStringFile != null || cfg.pgbouncer.connectionString != "")
+      );
+        message = ''
+          PgBouncer exporter needs either connectionStringFile or connectionString configured"
+        '';
+    } {
+      assertion = cfg.pgbouncer.enable -> (
+        config.services.pgbouncer.ignoreStartupParameters != null && builtins.match ".*extra_float_digits.*" config.services.pgbouncer.ignoreStartupParameters != null
+        );
+        message = ''
+          Prometheus PgBouncer exporter requires including `extra_float_digits` in services.pgbouncer.ignoreStartupParameters
+
+          Example:
+          services.pgbouncer.ignoreStartupParameters = extra_float_digits;
+
+          See https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration
+        '';
+    } {
       assertion = cfg.sql.enable -> (
         (cfg.sql.configFile == null) != (cfg.sql.configuration == null)
       );
@@ -349,12 +371,24 @@ in
         `openFirewall' is set to `true'!
       '';
     })) ++ config.services.prometheus.exporters.assertions;
-    warnings = [(mkIf (config.services.prometheus.exporters.idrac.enable && config.services.prometheus.exporters.idrac.configurationPath != null) ''
-        Configuration file in `services.prometheus.exporters.idrac.configurationPath` may override
-        `services.prometheus.exporters.idrac.listenAddress` and/or `services.prometheus.exporters.idrac.port`.
-        Consider using `services.prometheus.exporters.idrac.configuration` instead.
-      ''
-    )] ++ config.services.prometheus.exporters.warnings;
+    warnings = [
+      (mkIf (config.services.prometheus.exporters.idrac.enable && config.services.prometheus.exporters.idrac.configurationPath != null) ''
+          Configuration file in `services.prometheus.exporters.idrac.configurationPath` may override
+          `services.prometheus.exporters.idrac.listenAddress` and/or `services.prometheus.exporters.idrac.port`.
+          Consider using `services.prometheus.exporters.idrac.configuration` instead.
+        ''
+      )
+      (mkIf
+        (cfg.pgbouncer.enable && cfg.pgbouncer.connectionString != "") ''
+          config.services.prometheus.exporters.pgbouncer.connectionString is insecure. Use connectionStringFile instead.
+        ''
+      )
+      (mkIf
+        (cfg.pgbouncer.enable && config.services.pgbouncer.authType != "any") ''
+          Admin user (with password or passwordless) MUST exist in the services.pgbouncer.authFile if authType other than any is used.
+        ''
+      )
+    ] ++ 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/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
index 407bff1d62de..ce2c391de523 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -25,7 +25,7 @@ let
   checkConfig = file:
     pkgs.runCommand "checked-blackbox-exporter.conf" {
       preferLocalBuild = true;
-      buildInputs = [ pkgs.buildPackages.prometheus-blackbox-exporter ];
+      nativeBuildInputs = [ pkgs.buildPackages.prometheus-blackbox-exporter ];
     } ''
       ln -s ${coerceConfigFile file} $out
       blackbox_exporter --config.check --config.file $out
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix
new file mode 100644
index 000000000000..c5024a258e71
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.imap-mailstat;
+  valueToString = value:
+    if (builtins.typeOf value == "string") then "\"${value}\""
+    else (
+      if (builtins.typeOf value == "int") then "${toString value}"
+      else (
+        if (builtins.typeOf value == "bool") then (if value then "true" else "false")
+        else "XXX ${toString value}"
+      )
+    );
+  createConfigFile = accounts:
+    # unfortunately on toTOML yet
+    # https://github.com/NixOS/nix/issues/3929
+    pkgs.writeText "imap-mailstat-exporter.conf" ''
+      ${concatStrings (attrValues (mapAttrs (name: config: "[[Accounts]]\nname = \"${name}\"\n${concatStrings (attrValues (mapAttrs (k: v: "${k} = ${valueToString v}\n") config))}") accounts))}
+    '';
+  mkOpt = type: description: mkOption {
+    type = types.nullOr type;
+    default = null;
+    description = lib.mdDoc description;
+  };
+  accountOptions.options = {
+    mailaddress = mkOpt types.str "Your email address (at the moment used as login name)";
+    username = mkOpt types.str "If empty string mailaddress value is used";
+    password = mkOpt types.str "";
+    serveraddress = mkOpt types.str "mailserver name or address";
+    serverport = mkOpt types.int "imap port number (at the moment only tls connection is supported)";
+    starttls = mkOpt types.bool "set to true for using STARTTLS to start a TLS connection";
+  };
+in
+{
+  port = 8081;
+  extraOpts = {
+    oldestUnseenDate = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Enable metric with timestamp of oldest unseen mail
+      '';
+    };
+    accounts = mkOption {
+      type = types.attrsOf (types.submodule accountOptions);
+      default = {};
+      description = lib.mdDoc ''
+        Accounts to monitor
+      '';
+    };
+    configurationFile = mkOption {
+      type = types.path;
+      example = "/path/to/config-file";
+      description = lib.mdDoc ''
+        File containing the configuration
+      '';
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-imap-mailstat-exporter}/bin/imap-mailstat-exporter \
+          -config ${createConfigFile cfg.accounts} \
+          ${optionalString cfg.oldestUnseenDate "-oldestunseendate"} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix
new file mode 100644
index 000000000000..9e55cadae523
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix
@@ -0,0 +1,145 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.pgbouncer;
+in
+{
+  port = 9127;
+  extraOpts = {
+
+    telemetryPath = mkOption {
+      type = types.str;
+      default = "/metrics";
+      description = lib.mdDoc ''
+        Path under which to expose metrics.
+      '';
+    };
+
+    connectionString = mkOption {
+      type = types.str;
+      default = "";
+      example = "postgres://admin:@localhost:6432/pgbouncer?sslmode=require";
+      description = lib.mdDoc ''
+        Connection string for accessing pgBouncer.
+
+        NOTE: You MUST keep pgbouncer as database name (special internal db)!!!
+
+        NOTE: Admin user (with password or passwordless) MUST exist
+        in the services.pgbouncer.authFile if authType other than any is used.
+
+        WARNING: this secret is stored in the world-readable Nix store!
+        Use {option}`connectionStringFile` instead.
+      '';
+    };
+
+    connectionStringFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/keys/pgBouncer-connection-string";
+      description = lib.mdDoc ''
+        File that contains pgBouncer connection string in format:
+        postgres://admin:@localhost:6432/pgbouncer?sslmode=require
+
+        NOTE: You MUST keep pgbouncer as database name (special internal db)!!!
+
+        NOTE: Admin user (with password or passwordless) MUST exist
+        in the services.pgbouncer.authFile if authType other than any is used.
+
+        {option}`connectionStringFile` takes precedence over {option}`connectionString`
+      '';
+    };
+
+    pidFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = lib.mdDoc ''
+        Path to PgBouncer pid file.
+
+        If provided, the standard process metrics get exported for the PgBouncer
+        process, prefixed with 'pgbouncer_process_...'. The pgbouncer_process exporter
+        needs to have read access to files owned by the PgBouncer process. Depends on
+        the availability of /proc.
+
+        https://prometheus.io/docs/instrumenting/writing_clientlibs/#process-metrics.
+
+      '';
+    };
+
+    webSystemdSocket = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        Use systemd socket activation listeners instead of port listeners (Linux only).
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum ["debug" "info" "warn" "error" ];
+      default = "info";
+      description = lib.mdDoc ''
+        Only log messages with the given severity or above.
+      '';
+    };
+
+    logFormat = mkOption {
+      type = types.enum ["logfmt" "json"];
+      default = "logfmt";
+      description = lib.mdDoc ''
+        Output format of log messages. One of: [logfmt, json]
+      '';
+    };
+
+    webConfigFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        Path to configuration file that can enable TLS or authentication.
+      '';
+    };
+
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      description = lib.mdDoc ''
+        Extra commandline options when launching Prometheus.
+      '';
+    };
+
+  };
+
+  serviceOpts = {
+    after = [ "pgbouncer.service" ];
+      serviceConfig = let
+      startScript = pkgs.writeShellScriptBin "pgbouncer-start" "${concatStringsSep " " ([
+            "${pkgs.prometheus-pgbouncer-exporter}/bin/pgbouncer_exporter"
+            "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}"
+            "--pgBouncer.connectionString ${if cfg.connectionStringFile != null then
+            "$(head -n1 ${cfg.connectionStringFile})" else "${escapeShellArg cfg.connectionString}"}"
+          ]
+            ++ optionals (cfg.telemetryPath != null) [
+            "--web.telemetry-path ${escapeShellArg cfg.telemetryPath}"
+          ]
+            ++ optionals (cfg.pidFile != null) [
+            "--pgBouncer.pid-file= ${escapeShellArg cfg.pidFile}"
+          ]
+            ++ optionals (cfg.logLevel != null) [
+            "--log.level ${escapeShellArg cfg.logLevel}"
+          ]
+            ++ optionals (cfg.logFormat != null) [
+            "--log.format ${escapeShellArg cfg.logFormat}"
+          ]
+            ++ optionals (cfg.webSystemdSocket != false) [
+            "--web.systemd-socket ${escapeShellArg cfg.webSystemdSocket}"
+          ]
+            ++ optionals (cfg.webConfigFile != null) [
+            "--web.config.file ${escapeShellArg cfg.webConfigFile}"
+          ]
+            ++ cfg.extraFlags)}";
+      in
+      {
+        ExecStart = "${startScript}/bin/pgbouncer-start";
+      };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
new file mode 100644
index 000000000000..411277494013
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
@@ -0,0 +1,47 @@
+{ config, lib, pkgs, options }:
+
+let
+  inherit (lib) mkOption types;
+  cfg = config.services.prometheus.exporters.sabnzbd;
+in
+{
+  port = 9387;
+
+  extraOpts = {
+    servers = mkOption {
+      description = "List of sabnzbd servers to connect to.";
+      type = types.listOf (types.submodule {
+        options = {
+          baseUrl = mkOption {
+            type = types.str;
+            description = "Base URL of the sabnzbd server.";
+            example = "http://localhost:8080/sabnzbd";
+          };
+          apiKeyFile = mkOption {
+            type = types.str;
+            description = "File containing the API key.";
+            example = "/run/secrets/sabnzbd_apikey";
+          };
+        };
+      });
+    };
+  };
+
+  serviceOpts =
+    let
+      servers = lib.zipAttrs cfg.servers;
+      apiKeys = lib.concatStringsSep "," (builtins.map (file: "$(cat ${file})") servers.apiKeyFile);
+    in
+    {
+      environment = {
+        METRICS_PORT = toString cfg.port;
+        METRICS_ADDR = cfg.listenAddress;
+        SABNZBD_BASEURLS = lib.concatStringsSep "," servers.baseUrl;
+      };
+
+      script = ''
+        export SABNZBD_APIKEYS="${apiKeys}"
+        exec ${lib.getExe pkgs.prometheus-sabnzbd-exporter}
+      '';
+    };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
index c98dcd9f64bf..9b7590314936 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
@@ -11,7 +11,7 @@ in {
     ({ options.warnings = options.warnings; options.assertions = options.assertions; })
   ];
   extraOpts = {
-    verbose = mkEnableOption (lib.mdDoc "Verbose logging mode for prometheus-wireguard-exporter");
+    verbose = mkEnableOption (lib.mdDoc "verbose logging mode for prometheus-wireguard-exporter");
 
     wireguardConfig = mkOption {
       type = with types; nullOr (either path str);
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix b/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix
index 5a355f3441d8..bc746bed31f2 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/kubo.nix
@@ -203,10 +203,8 @@ in
               default = [
                 "/ip4/0.0.0.0/tcp/4001"
                 "/ip6/::/tcp/4001"
-                "/ip4/0.0.0.0/udp/4001/quic"
                 "/ip4/0.0.0.0/udp/4001/quic-v1"
                 "/ip4/0.0.0.0/udp/4001/quic-v1/webtransport"
-                "/ip6/::/udp/4001/quic"
                 "/ip6/::/udp/4001/quic-v1"
                 "/ip6/::/udp/4001/quic-v1/webtransport"
               ];
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/openafs/server.nix b/nixpkgs/nixos/modules/services/network-filesystems/openafs/server.nix
index ad0fd7835670..fbaa7cfc1929 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/openafs/server.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/openafs/server.nix
@@ -177,13 +177,13 @@ in {
 
         backup = {
           enable = mkEnableOption (lib.mdDoc ''
-            Backup server role. When using OpenAFS built-in buserver, use in conjunction with the
+            the backup server role. When using OpenAFS built-in buserver, use in conjunction with the
             `database` role to maintain the Backup
             Database. Normally only used in conjunction with tape storage
             or IBM's Tivoli Storage Manager.
 
             For a modern backup server, enable this role and see
-            {option}`enableFabs`.
+            {option}`enableFabs`
           '');
 
           enableFabs = mkEnableOption (lib.mdDoc ''
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix b/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix
index e20e7975ebaa..085b64e4c040 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix
@@ -192,7 +192,7 @@ in {
     # orangefs daemon will run as user
     users.users.orangefs = {
       isSystemUser = true;
-      group = "orangfs";
+      group = "orangefs";
     };
     users.groups.orangefs = {};
 
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/samba.nix b/nixpkgs/nixos/modules/services/network-filesystems/samba.nix
index 1310a374abd0..0b22302c0b6d 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/samba.nix
@@ -39,7 +39,7 @@ let
   daemonService = appName: args:
     { description = "Samba Service Daemon ${appName}";
 
-      after = [ (mkIf (cfg.enableNmbd && "${appName}" == "smbd") "samba-nmbd.service") ];
+      after = [ (mkIf (cfg.enableNmbd && "${appName}" == "smbd") "samba-nmbd.service") "network.target" ];
       requiredBy = [ "samba.target" ];
       partOf = [ "samba.target" ];
 
diff --git a/nixpkgs/nixos/modules/services/networking/connman.nix b/nixpkgs/nixos/modules/services/networking/connman.nix
index 498991419579..c626945ccd0c 100644
--- a/nixpkgs/nixos/modules/services/networking/connman.nix
+++ b/nixpkgs/nixos/modules/services/networking/connman.nix
@@ -1,55 +1,59 @@
 { config, lib, pkgs, ... }:
 
-with pkgs;
-with lib;
-
 let
   cfg = config.services.connman;
   configFile = pkgs.writeText "connman.conf" ''
     [General]
-    NetworkInterfaceBlacklist=${concatStringsSep "," cfg.networkInterfaceBlacklist}
+    NetworkInterfaceBlacklist=${lib.concatStringsSep "," cfg.networkInterfaceBlacklist}
 
     ${cfg.extraConfig}
   '';
   enableIwd = cfg.wifi.backend == "iwd";
 in {
+  meta.maintainers = with lib.maintainers; [ AndersonTorres ];
 
   imports = [
-    (mkRenamedOptionModule [ "networking" "connman" ] [ "services" "connman" ])
+    (lib.mkRenamedOptionModule [ "networking" "connman" ] [ "services" "connman" ])
   ];
 
   ###### interface
 
   options = {
-
     services.connman = {
-
-      enable = mkOption {
-        type = types.bool;
+      enable = lib.mkOption {
+        type = lib.types.bool;
         default = false;
         description = lib.mdDoc ''
           Whether to use ConnMan for managing your network connections.
         '';
       };
 
-      enableVPN = mkOption {
-        type = types.bool;
+      package = lib.mkOption {
+        type = lib.types.package;
+        description = lib.mdDoc "The connman package / build flavor";
+        default = pkgs.connman;
+        defaultText = lib.literalExpression "pkgs.connman";
+        example = lib.literalExpression "pkgs.connmanFull";
+      };
+
+      enableVPN = lib.mkOption {
+        type = lib.types.bool;
         default = true;
         description = lib.mdDoc ''
           Whether to enable ConnMan VPN service.
         '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
+      extraConfig = lib.mkOption {
+        type = lib.types.lines;
         default = "";
         description = lib.mdDoc ''
           Configuration lines appended to the generated connman configuration file.
         '';
       };
 
-      networkInterfaceBlacklist = mkOption {
-        type = with types; listOf str;
+      networkInterfaceBlacklist = lib.mkOption {
+        type = with lib.types; listOf str;
         default = [ "vmnet" "vboxnet" "virbr" "ifb" "ve" ];
         description = lib.mdDoc ''
           Default blacklisted interfaces, this includes NixOS containers interfaces (ve).
@@ -57,8 +61,8 @@ in {
       };
 
       wifi = {
-        backend = mkOption {
-          type = types.enum [ "wpa_supplicant" "iwd" ];
+        backend = lib.mkOption {
+          type = lib.types.enum [ "wpa_supplicant" "iwd" ];
           default = "wpa_supplicant";
           description = lib.mdDoc ''
             Specify the Wi-Fi backend used.
@@ -67,31 +71,20 @@ in {
         };
       };
 
-      extraFlags = mkOption {
-        type = with types; listOf str;
+      extraFlags = lib.mkOption {
+        type = with lib.types; listOf str;
         default = [ ];
         example = [ "--nodnsproxy" ];
         description = lib.mdDoc ''
           Extra flags to pass to connmand
         '';
       };
-
-      package = mkOption {
-        type = types.package;
-        description = lib.mdDoc "The connman package / build flavor";
-        default = connman;
-        defaultText = literalExpression "pkgs.connman";
-        example = literalExpression "pkgs.connmanFull";
-      };
-
     };
-
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable {
-
+  config = lib.mkIf cfg.enable {
     assertions = [{
       assertion = !config.networking.useDHCP;
       message = "You can not use services.connman with networking.useDHCP";
@@ -107,8 +100,8 @@ in {
     systemd.services.connman = {
       description = "Connection service";
       wantedBy = [ "multi-user.target" ];
-      after = [ "syslog.target" ] ++ optional enableIwd "iwd.service";
-      requires = optional enableIwd "iwd.service";
+      after = [ "syslog.target" ] ++ lib.optional enableIwd "iwd.service";
+      requires = lib.optional enableIwd "iwd.service";
       serviceConfig = {
         Type = "dbus";
         BusName = "net.connman";
@@ -117,13 +110,13 @@ in {
           "${cfg.package}/sbin/connmand"
           "--config=${configFile}"
           "--nodaemon"
-        ] ++ optional enableIwd "--wifi=iwd_agent"
+        ] ++ lib.optional enableIwd "--wifi=iwd_agent"
           ++ cfg.extraFlags);
         StandardOutput = "null";
       };
     };
 
-    systemd.services.connman-vpn = mkIf cfg.enableVPN {
+    systemd.services.connman-vpn = lib.mkIf cfg.enableVPN {
       description = "ConnMan VPN service";
       wantedBy = [ "multi-user.target" ];
       after = [ "syslog.target" ];
@@ -136,7 +129,7 @@ in {
       };
     };
 
-    systemd.services.net-connman-vpn = mkIf cfg.enableVPN {
+    systemd.services.net-connman-vpn = lib.mkIf cfg.enableVPN {
       description = "D-BUS Service";
       serviceConfig = {
         Name = "net.connman.vpn";
@@ -150,9 +143,9 @@ in {
     networking = {
       useDHCP = false;
       wireless = {
-        enable = mkIf (!enableIwd) true;
+        enable = lib.mkIf (!enableIwd) true;
         dbusControlled = true;
-        iwd = mkIf enableIwd {
+        iwd = lib.mkIf enableIwd {
           enable = true;
         };
       };
diff --git a/nixpkgs/nixos/modules/services/networking/coredns.nix b/nixpkgs/nixos/modules/services/networking/coredns.nix
index f928cdf96143..f1fe7b2f1241 100644
--- a/nixpkgs/nixos/modules/services/networking/coredns.nix
+++ b/nixpkgs/nixos/modules/services/networking/coredns.nix
@@ -29,6 +29,13 @@ in {
       type = types.package;
       description = lib.mdDoc "Coredns package to use.";
     };
+
+    extraArgs = mkOption {
+      default = [];
+      example = [ "-dns.port=53" ];
+      type = types.listOf types.str;
+      description = lib.mdDoc "Extra arguments to pass to coredns.";
+    };
   };
 
   config = mkIf cfg.enable {
@@ -44,7 +51,7 @@ in {
         AmbientCapabilities = "cap_net_bind_service";
         NoNewPrivileges = true;
         DynamicUser = true;
-        ExecStart = "${getBin cfg.package}/bin/coredns -conf=${configFile}";
+        ExecStart = "${getBin cfg.package}/bin/coredns -conf=${configFile} ${lib.escapeShellArgs cfg.extraArgs}";
         ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR1 $MAINPID";
         Restart = "on-failure";
       };
diff --git a/nixpkgs/nixos/modules/services/networking/create_ap.nix b/nixpkgs/nixos/modules/services/networking/create_ap.nix
index e772cf21ec57..994aa6d36d2a 100644
--- a/nixpkgs/nixos/modules/services/networking/create_ap.nix
+++ b/nixpkgs/nixos/modules/services/networking/create_ap.nix
@@ -8,7 +8,7 @@ let
 in {
   options = {
     services.create_ap = {
-      enable = mkEnableOption (lib.mdDoc "setup wifi hotspots using create_ap");
+      enable = mkEnableOption (lib.mdDoc "setting up wifi hotspots using create_ap");
       settings = mkOption {
         type = with types; attrsOf (oneOf [ int bool str ]);
         default = {};
diff --git a/nixpkgs/nixos/modules/services/networking/dae.nix b/nixpkgs/nixos/modules/services/networking/dae.nix
index 42ed3c7f8d4a..cf3fead19be5 100644
--- a/nixpkgs/nixos/modules/services/networking/dae.nix
+++ b/nixpkgs/nixos/modules/services/networking/dae.nix
@@ -14,10 +14,11 @@ in
   options = {
     services.dae = with lib;{
       enable = mkEnableOption
-        (mdDoc "A Linux high-performance transparent proxy solution based on eBPF");
+        (mdDoc "dae, a Linux high-performance transparent proxy solution based on eBPF");
 
       package = mkPackageOptionMD pkgs "dae" { };
 
+
       assets = mkOption {
         type = with types;(listOf path);
         default = with pkgs; [ v2ray-geoip v2ray-domain-list-community ];
@@ -45,9 +46,9 @@ in
       openFirewall = mkOption {
         type = with types; submodule {
           options = {
-            enable = mkEnableOption "enable";
+            enable = mkEnableOption (mdDoc "opening {option}`port` in the firewall");
             port = mkOption {
-              type = types.int;
+              type = types.port;
               description = ''
                 Port to be opened. Consist with field `tproxy_port` in config file.
               '';
@@ -70,8 +71,8 @@ in
       };
 
       configFile = mkOption {
-        type = types.path;
-        default = "/etc/dae/config.dae";
+        type = with types; (nullOr path);
+        default = null;
         example = "/path/to/your/config.dae";
         description = mdDoc ''
           The path of dae config file, end with `.dae`.
@@ -79,12 +80,10 @@ in
       };
 
       config = mkOption {
-        type = types.str;
-        default = ''
-          global{}
-          routing{}
-        '';
+        type = with types; (nullOr str);
+        default = null;
         description = mdDoc ''
+          WARNING: This option will expose store your config unencrypted world-readable in the nix store.
           Config text for dae.
 
           See <https://github.com/daeuniverse/dae/blob/main/example.dae>.
@@ -92,7 +91,7 @@ in
       };
 
       disableTxChecksumIpGeneric =
-        mkEnableOption (mdDoc "See <https://github.com/daeuniverse/dae/issues/43>");
+        mkEnableOption "" // { description = mdDoc "See <https://github.com/daeuniverse/dae/issues/43>"; };
 
     };
   };
@@ -103,11 +102,6 @@ in
       environment.systemPackages = [ cfg.package ];
       systemd.packages = [ cfg.package ];
 
-      environment.etc."dae/config.dae" = {
-        mode = "0400";
-        source = pkgs.writeText "config.dae" cfg.config;
-      };
-
       networking = lib.mkIf cfg.openFirewall.enable {
         firewall =
           let portToOpen = cfg.openFirewall.port;
@@ -121,20 +115,27 @@ in
       systemd.services.dae =
         let
           daeBin = lib.getExe cfg.package;
-          TxChecksumIpGenericWorkaround = with lib;(getExe pkgs.writeShellApplication {
-            name = "disable-tx-checksum-ip-generic";
-            text = with pkgs; ''
-              iface=$(${iproute2}/bin/ip route | ${lib.getExe gawk} '/default/ {print $5}')
-              ${lib.getExe ethtool} -K "$iface" tx-checksum-ip-generic off
-            '';
-          });
+
+          configPath =
+            if cfg.configFile != null
+            then cfg.configFile else pkgs.writeText "config.dae" cfg.config;
+
+          TxChecksumIpGenericWorkaround = with lib;
+            (getExe pkgs.writeShellApplication {
+              name = "disable-tx-checksum-ip-generic";
+              text = with pkgs; ''
+                iface=$(${iproute2}/bin/ip route | ${lib.getExe gawk} '/default/ {print $5}')
+                ${lib.getExe ethtool} -K "$iface" tx-checksum-ip-generic off
+              '';
+            });
         in
         {
           wantedBy = [ "multi-user.target" ];
           serviceConfig = {
-            ExecStartPre = [ "" "${daeBin} validate -c ${cfg.configFile}" ]
+            LoadCredential = [ "config.dae:${configPath}" ];
+            ExecStartPre = [ "" "${daeBin} validate -c \${CREDENTIALS_DIRECTORY}/config.dae" ]
               ++ (with lib; optional cfg.disableTxChecksumIpGeneric TxChecksumIpGenericWorkaround);
-            ExecStart = [ "" "${daeBin} run --disable-timestamp -c ${cfg.configFile}" ];
+            ExecStart = [ "" "${daeBin} run --disable-timestamp -c \${CREDENTIALS_DIRECTORY}/config.dae" ];
             Environment = "DAE_LOCATION_ASSET=${cfg.assetsPath}";
           };
         };
@@ -149,13 +150,21 @@ in
         }
 
         {
-          assertion = !((config.services.dae.config != "global{}\nrouting{}\n")
-            && (config.services.dae.configFile != "/etc/dae/config.dae"));
+          assertion = !((config.services.dae.config != null)
+            && (config.services.dae.configFile != null));
           message = ''
             Option `config` and `configFile` could not be set
             at the same time.
           '';
         }
+
+        {
+          assertion = !((config.services.dae.config == null)
+            && (config.services.dae.configFile == null));
+          message = ''
+            Either `config` or `configFile` should be set.
+          '';
+        }
       ];
     };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/deconz.nix b/nixpkgs/nixos/modules/services/networking/deconz.nix
new file mode 100644
index 000000000000..05b724708777
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/deconz.nix
@@ -0,0 +1,125 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.deconz;
+  name = "deconz";
+  stateDir = "/var/lib/${name}";
+  # ref. upstream deconz.service
+  capabilities =
+    lib.optionals (cfg.httpPort < 1024 || cfg.wsPort < 1024) [ "CAP_NET_BIND_SERVICE" ]
+    ++ lib.optionals (cfg.allowRebootSystem) [ "CAP_SYS_BOOT" ]
+    ++ lib.optionals (cfg.allowRestartService) [ "CAP_KILL" ]
+    ++ lib.optionals (cfg.allowSetSystemTime) [ "CAP_SYS_TIME" ];
+in
+{
+  options.services.deconz = {
+
+    enable = lib.mkEnableOption "deCONZ, a Zigbee gateway for use with ConBee hardware (https://phoscon.de/en/conbee2)";
+
+    package = lib.mkOption {
+      type = lib.types.package;
+      default = pkgs.deconz;
+      defaultText = lib.literalExpression "pkgs.deconz";
+      description = "Which deCONZ package to use.";
+    };
+
+    device = lib.mkOption {
+      type = lib.types.nullOr lib.types.str;
+      default = null;
+      description = ''
+        Force deCONZ to use a specific USB device (e.g. /dev/ttyACM0). By
+        default it does a search.
+      '';
+    };
+
+    listenAddress = lib.mkOption {
+      type = lib.types.str;
+      default = "127.0.0.1";
+      description = ''
+        Pin deCONZ to the network interface specified through the provided IP
+        address. This applies for the webserver as well as the websocket
+        notifications.
+      '';
+    };
+
+    httpPort = lib.mkOption {
+      type = lib.types.port;
+      default = 80;
+      description = "TCP port for the web server.";
+    };
+
+    wsPort = lib.mkOption {
+      type = lib.types.port;
+      default = 443;
+      description = "TCP port for the WebSocket.";
+    };
+
+    openFirewall = lib.mkEnableOption "opening up the service ports in the firewall";
+
+    allowRebootSystem = lib.mkEnableOption "rebooting the system";
+
+    allowRestartService = lib.mkEnableOption "killing/restarting processes";
+
+    allowSetSystemTime = lib.mkEnableOption "setting the system time";
+
+    extraArgs = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      example = [
+        "--dbg-info=1"
+        "--dbg-err=2"
+      ];
+      description = ''
+        Extra command line arguments for deCONZ, see
+        https://github.com/dresden-elektronik/deconz-rest-plugin/wiki/deCONZ-command-line-parameters.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
+      cfg.httpPort
+      cfg.wsPort
+    ];
+
+    services.udev.packages = [ cfg.package ];
+
+    systemd.services.deconz = {
+      description = "deCONZ Zigbee gateway";
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        # The service puts a nix store path reference in here, and that path can
+        # be garbage collected. Ensure the file gets "refreshed" on every start.
+        rm -f ${stateDir}/.local/share/dresden-elektronik/deCONZ/zcldb.txt
+      '';
+      environment = {
+        HOME = stateDir;
+        XDG_RUNTIME_DIR = "/run/${name}";
+      };
+      serviceConfig = {
+        ExecStart =
+          "${lib.getExe cfg.package}"
+          + " -platform minimal"
+          + " --http-listen=${cfg.listenAddress}"
+          + " --http-port=${toString cfg.httpPort}"
+          + " --ws-port=${toString cfg.wsPort}"
+          + " --auto-connect=1"
+          + (lib.optionalString (cfg.device != null) " --dev=${cfg.device}")
+          + " " + (lib.escapeShellArgs cfg.extraArgs);
+        Restart = "on-failure";
+        AmbientCapabilities = capabilities;
+        CapabilityBoundingSet = capabilities;
+        UMask = "0027";
+        DynamicUser = true;
+        RuntimeDirectory = name;
+        RuntimeDirectoryMode = "0700";
+        StateDirectory = name;
+        WorkingDirectory = stateDir;
+        # For access to /dev/ttyACM0 (ConBee).
+        SupplementaryGroups = [ "dialout" ];
+        ProtectHome = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
index 4886654e8c03..14bbe334e50d 100644
--- a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
+++ b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.dnsmasq;
-  dnsmasq = pkgs.dnsmasq;
+  dnsmasq = cfg.package;
   stateDir = "/var/lib/dnsmasq";
 
   # True values are just put as `name` instead of `name=true`, and false values
@@ -53,6 +53,8 @@ in
         '';
       };
 
+      package = mkPackageOptionMD pkgs "dnsmasq" {};
+
       resolveLocalQueries = mkOption {
         type = types.bool;
         default = true;
diff --git a/nixpkgs/nixos/modules/services/networking/firefox-syncserver.md b/nixpkgs/nixos/modules/services/networking/firefox-syncserver.md
index 3ee863343ece..4d8777d204bb 100644
--- a/nixpkgs/nixos/modules/services/networking/firefox-syncserver.md
+++ b/nixpkgs/nixos/modules/services/networking/firefox-syncserver.md
@@ -45,7 +45,7 @@ this instance, and `url`, which holds the URL under which the sync server can be
 accessed. The `url` can be configured automatically when using nginx.
 
 Options that affect the surroundings of the sync server are `enableNginx`,
-`enableTLS` and `hostnam`. If `enableNginx` is set the sync server module will
+`enableTLS` and `hostname`. If `enableNginx` is set the sync server module will
 automatically add an nginx virtual host to the system using `hostname` as the
 domain and set `url` accordingly. If `enableTLS` is set the module will also
 enable ACME certificates on the new virtual host and force all connections to
diff --git a/nixpkgs/nixos/modules/services/networking/firefox-syncserver.nix b/nixpkgs/nixos/modules/services/networking/firefox-syncserver.nix
index 42924d7f6993..71eb2f537acc 100644
--- a/nixpkgs/nixos/modules/services/networking/firefox-syncserver.nix
+++ b/nixpkgs/nixos/modules/services/networking/firefox-syncserver.nix
@@ -224,10 +224,12 @@ in
           Settings for the sync server. These take priority over values computed
           from NixOS options.
 
-          See the doc comments on the `Settings` structs in
-          <https://github.com/mozilla-services/syncstorage-rs/blob/master/syncstorage/src/settings.rs>
+          See the example config in
+          <https://github.com/mozilla-services/syncstorage-rs/blob/master/config/local.example.toml>
+          and the doc comments on the `Settings` structs in
+          <https://github.com/mozilla-services/syncstorage-rs/blob/master/syncstorage-settings/src/lib.rs>
           and
-          <https://github.com/mozilla-services/syncstorage-rs/blob/master/syncstorage/src/tokenserver/settings.rs>
+          <https://github.com/mozilla-services/syncstorage-rs/blob/master/tokenserver-settings/src/lib.rs>
           for available options.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/networking/go-neb.nix b/nixpkgs/nixos/modules/services/networking/go-neb.nix
index b65bb5f548ee..78d24ecf17d9 100644
--- a/nixpkgs/nixos/modules/services/networking/go-neb.nix
+++ b/nixpkgs/nixos/modules/services/networking/go-neb.nix
@@ -9,7 +9,7 @@ let
   configFile = settingsFormat.generate "config.yaml" cfg.config;
 in {
   options.services.go-neb = {
-    enable = mkEnableOption (lib.mdDoc "Extensible matrix bot written in Go");
+    enable = mkEnableOption (lib.mdDoc "an extensible matrix bot written in Go");
 
     bindAddress = mkOption {
       type = types.str;
diff --git a/nixpkgs/nixos/modules/services/networking/hostapd.nix b/nixpkgs/nixos/modules/services/networking/hostapd.nix
index 4ec066c2ec97..ffb154463053 100644
--- a/nixpkgs/nixos/modules/services/networking/hostapd.nix
+++ b/nixpkgs/nixos/modules/services/networking/hostapd.nix
@@ -116,10 +116,10 @@ in {
   options = {
     services.hostapd = {
       enable = mkEnableOption (mdDoc ''
-        Whether to enable hostapd. hostapd is a user space daemon for access point and
+        hostapd, a user space daemon for access point and
         authentication servers. It implements IEEE 802.11 access point management,
         IEEE 802.1X/WPA/WPA2/EAP Authenticators, RADIUS client, EAP server, and RADIUS
-        authentication server.
+        authentication server
       '');
 
       package = mkPackageOption pkgs "hostapd" {};
diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
index 82c144236f3b..49b2bef90a5f 100644
--- a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
+++ b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix
@@ -272,18 +272,18 @@ in
     };
 
     faxcron.enable.spoolInit = mkEnableOption (lib.mdDoc ''
-      Purge old files from the spooling area with
+      purging old files from the spooling area with
       {file}`faxcron`
-      each time the spooling area is initialized.
+      each time the spooling area is initialized
     '');
     faxcron.enable.frequency = mkOption {
       type = nullOr nonEmptyStr;
       default = null;
       example = "daily";
       description = lib.mdDoc ''
-        Purge old files from the spooling area with
+        purging old files from the spooling area with
         {file}`faxcron` with the given frequency
-        (see systemd.time(7)).
+        (see systemd.time(7))
       '';
     };
     faxcron.infoDays = mkOption {
diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix
index c940324ad096..f872daf05b8f 100644
--- a/nixpkgs/nixos/modules/services/networking/i2pd.nix
+++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix
@@ -265,7 +265,7 @@ in
         '';
       };
 
-      logCLFTime = mkEnableOption (lib.mdDoc "Full CLF-formatted date and time to log");
+      logCLFTime = mkEnableOption (lib.mdDoc "full CLF-formatted date and time to log");
 
       address = mkOption {
         type = with types; nullOr str;
@@ -456,7 +456,7 @@ in
         '';
       };
 
-      trust.enable = mkEnableOption (lib.mdDoc "Explicit trust options");
+      trust.enable = mkEnableOption (lib.mdDoc "explicit trust options");
 
       trust.family = mkOption {
         type = with types; nullOr str;
@@ -474,7 +474,7 @@ in
         '';
       };
 
-      trust.hidden = mkEnableOption (lib.mdDoc "Router concealment");
+      trust.hidden = mkEnableOption (lib.mdDoc "router concealment");
 
       websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666;
 
@@ -552,7 +552,7 @@ in
 
       proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
 
-        auth = mkEnableOption (lib.mdDoc "Webconsole authentication");
+        auth = mkEnableOption (lib.mdDoc "webconsole authentication");
 
         user = mkOption {
           type = types.str;
diff --git a/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix b/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
index d2865a660ead..9c71a988f29c 100644
--- a/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
+++ b/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
@@ -7,7 +7,7 @@ in
     enable = mkEnableOption (lib.mdDoc "the openiscsi iscsi daemon");
     enableAutoLoginOut = mkEnableOption (lib.mdDoc ''
       automatic login and logout of all automatic targets.
-      You probably do not want this.
+      You probably do not want this
     '');
     discoverPortal = mkOption {
       type = nullOr str;
diff --git a/nixpkgs/nixos/modules/services/networking/knot.nix b/nixpkgs/nixos/modules/services/networking/knot.nix
index e97195d82919..4f6ac945cf97 100644
--- a/nixpkgs/nixos/modules/services/networking/knot.nix
+++ b/nixpkgs/nixos/modules/services/networking/knot.nix
@@ -5,10 +5,114 @@ with lib;
 let
   cfg = config.services.knot;
 
-  configFile = pkgs.writeTextFile {
+  yamlConfig = let
+    result = assert secsCheck; nix2yaml cfg.settings;
+
+    secAllow = n: hasPrefix "mod-" n || elem n [
+      "module"
+      "server" "xdp" "control"
+      "log"
+      "statistics" "database"
+      "keystore" "key" "remote" "remotes" "acl" "submission" "policy"
+      "template"
+      "zone"
+      "include"
+    ];
+    secsCheck = let
+      secsBad = filter (n: !secAllow n) (attrNames cfg.settings);
+    in if secsBad == [] then true else throw
+      ("services.knot.settings contains unknown sections: " + toString secsBad);
+
+    nix2yaml = nix_def: concatStrings (
+        # We output the config section in the upstream-mandated order.
+        # Ordering is important due to forward-references not being allowed.
+        # See definition of conf_export and 'const yp_item_t conf_schema'
+        # upstream for reference.  Last updated for 3.3.
+        # When changing the set of sections, also update secAllow above.
+        [ (sec_list_fa "id" nix_def "module") ]
+        ++ map (sec_plain nix_def)
+          [ "server" "xdp" "control" ]
+        ++ [ (sec_list_fa "target" nix_def "log") ]
+        ++ map (sec_plain nix_def)
+          [  "statistics" "database" ]
+        ++ map (sec_list_fa "id" nix_def)
+          [ "keystore" "key" "remote" "remotes" "acl" "submission" "policy" ]
+
+        # Export module sections before the template section.
+        ++ map (sec_list_fa "id" nix_def) (filter (hasPrefix "mod-") (attrNames nix_def))
+
+        ++ [ (sec_list_fa "id" nix_def "template") ]
+        ++ [ (sec_list_fa "domain" nix_def "zone") ]
+        ++ [ (sec_plain nix_def "include") ]
+      );
+
+    # A plain section contains directly attributes (we don't really check that ATM).
+    sec_plain = nix_def: sec_name: if !hasAttr sec_name nix_def then "" else
+      n2y "" { ${sec_name} = nix_def.${sec_name}; };
+
+    # This section contains a list of attribute sets.  In each of the sets
+    # there's an attribute (`fa_name`, typically "id") that must exist and come first.
+    # Alternatively we support using attribute sets instead of lists; example diff:
+    # -template = [ { id = "default"; /* other attributes */ }   { id = "foo"; } ]
+    # +template = { default = {       /* those attributes */ };  foo = { };      }
+    sec_list_fa = fa_name: nix_def: sec_name: if !hasAttr sec_name nix_def then "" else
+      let
+        elem2yaml = fa_val: other_attrs:
+          "  - " + n2y "" { ${fa_name} = fa_val; }
+          + "    " + n2y "    " other_attrs
+          + "\n";
+        sec = nix_def.${sec_name};
+      in
+        sec_name + ":\n" +
+          (if isList sec
+            then flip concatMapStrings sec
+              (elem: elem2yaml elem.${fa_name} (removeAttrs elem [ fa_name ]))
+            else concatStrings (mapAttrsToList elem2yaml sec)
+          );
+
+    # This convertor doesn't care about ordering of attributes.
+    # TODO: it could probably be simplified even more, now that it's not
+    # to be used directly, but we might want some other tweaks, too.
+    n2y = indent: val:
+      if doRecurse val then concatStringsSep "\n${indent}"
+        (mapAttrsToList
+          # This is a bit wacky - set directly under a set would start on bad indent,
+          # so we start those on a new line, but not other types of attribute values.
+          (aname: aval: "${aname}:${if doRecurse aval then "\n${indent}  " else " "}"
+            + n2y (indent + "  ") aval)
+          val
+        )
+        + "\n"
+        else
+      /*
+      if isList val && stringLength indent < 4 then concatMapStrings
+        (elem: "\n${indent}- " + n2y (indent + "  ") elem)
+        val
+        else
+      */
+      if isList val /* and long indent */ then
+        "[ " + concatMapStringsSep ", " quoteString val + " ]" else
+      if isBool val then (if val then "on" else "off") else
+      quoteString val;
+
+    # We don't want paths like ./my-zone.txt be converted to plain strings.
+    quoteString = s: ''"${if builtins.typeOf s == "path" then s else toString s}"'';
+    # We don't want to walk the insides of derivation attributes.
+    doRecurse = val: isAttrs val && !isDerivation val;
+
+  in result;
+
+  configFile = if cfg.settingsFile != null then
+    # Note: with extraConfig, the 23.05 compat code did include keyFiles from settingsFile.
+    assert cfg.settings == {} && (cfg.keyFiles == [] || cfg.extraConfig != null);
+    cfg.settingsFile
+  else
+    mkConfigFile yamlConfig;
+
+  mkConfigFile = configString: pkgs.writeTextFile {
     name = "knot.conf";
-    text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" +
-           cfg.extraConfig;
+    text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + configString;
+    # TODO: maybe we could do some checks even when private keys complicate this?
     checkPhase = lib.optionalString (cfg.keyFiles == []) ''
       ${cfg.package}/bin/knotc --config=$out conf-check
     '';
@@ -60,11 +164,21 @@ in {
         '';
       };
 
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
+      settings = mkOption {
+        type = types.attrs;
+        default = {};
+        description = lib.mdDoc ''
+          Extra configuration as nix values.
+        '';
+      };
+
+      settingsFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
         description = lib.mdDoc ''
-          Extra lines to be added verbatim to knot.conf
+          As alternative to ``settings``, you can provide whole configuration
+          directly in the almost-YAML format of Knot DNS.
+          You might want to utilize ``pkgs.writeText "knot.conf" "longConfigString"`` for this.
         '';
       };
 
@@ -78,6 +192,12 @@ in {
       };
     };
   };
+  imports = [
+    # Compatibility with NixOS 23.05.
+    (mkChangedOptionModule [ "services" "knot" "extraConfig" ] [ "services" "knot" "settingsFile" ]
+      (config: mkConfigFile config.services.knot.extraConfig)
+    )
+  ];
 
   config = mkIf config.services.knot.enable {
     users.groups.knot = {};
@@ -87,6 +207,8 @@ in {
       description = "Knot daemon user";
     };
 
+    environment.etc."knot/knot.conf".source = configFile; # just for user's convenience
+
     systemd.services.knot = {
       unitConfig.Documentation = "man:knotd(8) man:knot.conf(5) man:knotc(8) https://www.knot-dns.cz/docs/${cfg.package.version}/html/";
       description = cfg.package.meta.description;
diff --git a/nixpkgs/nixos/modules/services/networking/mtr-exporter.nix b/nixpkgs/nixos/modules/services/networking/mtr-exporter.nix
index 43ebbbe96d05..af694c3e736b 100644
--- a/nixpkgs/nixos/modules/services/networking/mtr-exporter.nix
+++ b/nixpkgs/nixos/modules/services/networking/mtr-exporter.nix
@@ -2,63 +2,114 @@
 
 let
   inherit (lib)
-    maintainers types mkEnableOption mkOption mkIf
-    literalExpression escapeShellArg escapeShellArgs;
+    maintainers types literalExpression
+    escapeShellArg escapeShellArgs
+    mkEnableOption mkOption mkRemovedOptionModule mkIf mdDoc
+    optionalString concatMapStrings concatStringsSep;
+
   cfg = config.services.mtr-exporter;
+
+  jobsConfig = pkgs.writeText "mtr-exporter.conf" (concatMapStrings (job: ''
+    ${job.name} -- ${job.schedule} -- ${concatStringsSep " " job.flags} ${job.address}
+  '') cfg.jobs);
 in {
+  imports = [
+    (mkRemovedOptionModule [ "services" "mtr-exporter" "target" ] "Use services.mtr-exporter.jobs instead.")
+    (mkRemovedOptionModule [ "services" "mtr-exporter" "mtrFlags" ] "Use services.mtr-exporter.jobs.<job>.flags instead.")
+  ];
+
   options = {
     services = {
       mtr-exporter = {
-        enable = mkEnableOption (lib.mdDoc "a Prometheus exporter for MTR");
+        enable = mkEnableOption (mdDoc "a Prometheus exporter for MTR");
 
-        target = mkOption {
+        address = mkOption {
           type = types.str;
-          example = "example.org";
-          description = lib.mdDoc "Target to check using MTR.";
-        };
-
-        interval = mkOption {
-          type = types.int;
-          default = 60;
-          description = lib.mdDoc "Interval between MTR checks in seconds.";
+          default = "127.0.0.1";
+          description = lib.mdDoc "Listen address for MTR exporter.";
         };
 
         port = mkOption {
           type = types.port;
           default = 8080;
-          description = lib.mdDoc "Listen port for MTR exporter.";
+          description = mdDoc "Listen port for MTR exporter.";
         };
 
-        address = mkOption {
-          type = types.str;
-          default = "127.0.0.1";
-          description = lib.mdDoc "Listen address for MTR exporter.";
+        extraFlags = mkOption {
+          type = types.listOf types.str;
+          default = [];
+          example = ["-flag.deprecatedMetrics"];
+          description = mdDoc ''
+            Extra command line options to pass to MTR exporter.
+          '';
         };
 
-        mtrFlags = mkOption {
-          type = with types; listOf str;
-          default = [];
-          example = ["-G1"];
-          description = lib.mdDoc "Additional flags to pass to MTR.";
+        package = mkOption {
+          type = types.package;
+          default = pkgs.mtr-exporter;
+          defaultText = literalExpression "pkgs.mtr-exporter";
+          description = mdDoc "The MTR exporter package to use.";
+        };
+
+        mtrPackage = mkOption {
+          type = types.package;
+          default = pkgs.mtr;
+          defaultText = literalExpression "pkgs.mtr";
+          description = mdDoc "The MTR package to use.";
+        };
+
+        jobs = mkOption {
+          description = mdDoc "List of MTR jobs. Will be added to /etc/mtr-exporter.conf";
+          type = types.nonEmptyListOf (types.submodule {
+            options = {
+              name = mkOption {
+                type = types.str;
+                description = mdDoc "Name of ICMP pinging job.";
+              };
+
+              address = mkOption {
+                type = types.str;
+                example = "host.example.org:1234";
+                description = mdDoc "Target address for MTR client.";
+              };
+
+              schedule = mkOption {
+                type = types.str;
+                default = "@every 60s";
+                example = "@hourly";
+                description = mdDoc "Schedule of MTR checks. Also accepts Cron format.";
+              };
+
+              flags = mkOption {
+                type = with types; listOf str;
+                default = [];
+                example = ["-G1"];
+                description = mdDoc "Additional flags to pass to MTR.";
+              };
+            };
+          });
         };
       };
     };
   };
 
   config = mkIf cfg.enable {
+    environment.etc."mtr-exporter.conf" = {
+      source = jobsConfig;
+    };
+
     systemd.services.mtr-exporter = {
-      script = ''
-        exec ${pkgs.mtr-exporter}/bin/mtr-exporter \
-          -mtr ${pkgs.mtr}/bin/mtr \
-          -schedule '@every ${toString cfg.interval}s' \
-          -bind ${escapeShellArg cfg.address}:${toString cfg.port} \
-          -- \
-          ${escapeShellArgs (cfg.mtrFlags ++ [ cfg.target ])}
-      '';
       wantedBy = [ "multi-user.target" ];
       requires = [ "network.target" ];
       after = [ "network.target" ];
       serviceConfig = {
+        ExecStart = ''
+          ${cfg.package}/bin/mtr-exporter \
+            -mtr '${cfg.mtrPackage}/bin/mtr' \
+            -bind ${escapeShellArg "${cfg.address}:${toString cfg.port}"} \
+            -jobs '${jobsConfig}' \
+            ${escapeShellArgs cfg.extraFlags}
+        '';
         Restart = "on-failure";
         # Hardening
         CapabilityBoundingSet = [ "" ];
diff --git a/nixpkgs/nixos/modules/services/networking/nar-serve.nix b/nixpkgs/nixos/modules/services/networking/nar-serve.nix
index beee53c8a242..b8b76120e44f 100644
--- a/nixpkgs/nixos/modules/services/networking/nar-serve.nix
+++ b/nixpkgs/nixos/modules/services/networking/nar-serve.nix
@@ -10,7 +10,7 @@ in
   };
   options = {
     services.nar-serve = {
-      enable = mkEnableOption (lib.mdDoc "Serve NAR file contents via HTTP");
+      enable = mkEnableOption (lib.mdDoc "serving NAR file contents via HTTP");
 
       port = mkOption {
         type = types.port;
diff --git a/nixpkgs/nixos/modules/services/networking/netclient.nix b/nixpkgs/nixos/modules/services/networking/netclient.nix
new file mode 100644
index 000000000000..124735fd716a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/netclient.nix
@@ -0,0 +1,27 @@
+{ config, pkgs, lib, ... }:
+let
+  cfg = config.services.netclient;
+in
+{
+  meta.maintainers = with lib.maintainers; [ wexder ];
+
+  options.services.netclient = {
+    enable = lib.mkEnableOption (lib.mdDoc "Netclient Daemon");
+    package = lib.mkPackageOptionMD pkgs "netclient" { };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    systemd.services.netclient = {
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      description = "Netclient Daemon";
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${lib.getExe cfg.package} daemon";
+        Restart = "on-failure";
+        RestartSec = "15s";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix
index a0afdb452752..424d005dc0b5 100644
--- a/nixpkgs/nixos/modules/services/networking/nftables.nix
+++ b/nixpkgs/nixos/modules/services/networking/nftables.nix
@@ -103,7 +103,7 @@ in
       '';
     };
 
-    networking.nftables.flushRuleset = mkEnableOption (lib.mdDoc "Flush the entire ruleset on each reload.");
+    networking.nftables.flushRuleset = mkEnableOption (lib.mdDoc "flushing the entire ruleset on each reload");
 
     networking.nftables.extraDeletions = mkOption {
       type = types.lines;
diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix
index 40648c724812..8054f01d705f 100644
--- a/nixpkgs/nixos/modules/services/networking/searx.nix
+++ b/nixpkgs/nixos/modules/services/networking/searx.nix
@@ -43,12 +43,8 @@ in
       [ "services" "searx" "settingsFile" ])
   ];
 
-  ###### interface
-
   options = {
-
     services.searx = {
-
       enable = mkOption {
         type = types.bool;
         default = false;
@@ -149,8 +145,8 @@ in
 
       package = mkOption {
         type = types.package;
-        default = pkgs.searx;
-        defaultText = literalExpression "pkgs.searx";
+        default = pkgs.searxng;
+        defaultText = literalExpression "pkgs.searxng";
         description = lib.mdDoc "searx package to use.";
       };
 
@@ -190,21 +186,7 @@ in
 
   };
 
-
-  ###### implementation
-
   config = mkIf cfg.enable {
-    assertions = [
-      {
-        assertion = (cfg.limiterSettings != { }) -> cfg.package.pname == "searxng";
-        message = "services.searx.limiterSettings requires services.searx.package to be searxng.";
-      }
-      {
-        assertion = cfg.redisCreateLocally -> cfg.package.pname == "searxng";
-        message = "services.searx.redisCreateLocally requires services.searx.package to be searxng.";
-      }
-    ];
-
     environment.systemPackages = [ cfg.package ];
 
     users.users.searx =
@@ -245,10 +227,10 @@ in
       };
     };
 
-    systemd.services.uwsgi = mkIf (cfg.runInUwsgi)
-      { requires = [ "searx-init.service" ];
-        after = [ "searx-init.service" ];
-      };
+    systemd.services.uwsgi = mkIf cfg.runInUwsgi {
+      requires = [ "searx-init.service" ];
+      after = [ "searx-init.service" ];
+    };
 
     services.searx.settings = {
       # merge NixOS settings with defaults settings.yml
@@ -256,7 +238,7 @@ in
       redis.url = lib.mkIf cfg.redisCreateLocally "unix://${config.services.redis.servers.searx.unixSocket}";
     };
 
-    services.uwsgi = mkIf (cfg.runInUwsgi) {
+    services.uwsgi = mkIf cfg.runInUwsgi {
       enable = true;
       plugins = [ "python3" ];
 
@@ -270,6 +252,7 @@ in
         enable-threads = true;
         module = "searx.webapp";
         env = [
+          # TODO: drop this as it is only required for searx
           "SEARX_SETTINGS_PATH=${cfg.settingsFile}"
           # searxng compatibility https://github.com/searxng/searxng/issues/1519
           "SEARXNG_SETTINGS_PATH=${cfg.settingsFile}"
diff --git a/nixpkgs/nixos/modules/services/networking/snowflake-proxy.nix b/nixpkgs/nixos/modules/services/networking/snowflake-proxy.nix
index ca015ed9d44b..19b68f1e20ba 100644
--- a/nixpkgs/nixos/modules/services/networking/snowflake-proxy.nix
+++ b/nixpkgs/nixos/modules/services/networking/snowflake-proxy.nix
@@ -8,7 +8,7 @@ in
 {
   options = {
     services.snowflake-proxy = {
-      enable = mkEnableOption (lib.mdDoc "System to defeat internet censorship");
+      enable = mkEnableOption (lib.mdDoc "snowflake-proxy, a system to defeat internet censorship");
 
       broker = mkOption {
         description = lib.mdDoc "Broker URL (default \"https://snowflake-broker.torproject.net/\")";
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index 942317f8da74..1946e9e79e98 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -27,13 +27,11 @@ let
       mkValueString = mkValueStringSshd;
     } " ";});
 
-  configFile = settingsFormat.generate "config" cfg.settings;
-  sshconf = pkgs.runCommand "sshd.conf-validated" { nativeBuildInputs = [ validationPackage ]; } ''
+  configFile = settingsFormat.generate "sshd.conf-settings" cfg.settings;
+  sshconf = pkgs.runCommand "sshd.conf-final" { } ''
     cat ${configFile} - >$out <<EOL
     ${cfg.extraConfig}
     EOL
-
-    sshd -G -f $out
   '';
 
   cfg  = config.services.openssh;
@@ -76,6 +74,19 @@ let
       };
     };
 
+    options.openssh.authorizedPrincipals = mkOption {
+      type = with types; listOf types.singleLineStr;
+      default = [];
+      description = mdDoc ''
+        A list of verbatim principal names that should be added to the user's
+        authorized principals.
+      '';
+      example = [
+        "example@host"
+        "foo@bar"
+      ];
+    };
+
   };
 
   authKeysFiles = let
@@ -91,6 +102,16 @@ let
     ));
   in listToAttrs (map mkAuthKeyFile usersWithKeys);
 
+  authPrincipalsFiles = let
+    mkAuthPrincipalsFile = u: nameValuePair "ssh/authorized_principals.d/${u.name}" {
+      mode = "0444";
+      text = concatStringsSep "\n" u.openssh.authorizedPrincipals;
+    };
+    usersWithPrincipals = attrValues (flip filterAttrs config.users.users (n: u:
+      length u.openssh.authorizedPrincipals != 0
+    ));
+  in listToAttrs (map mkAuthPrincipalsFile usersWithPrincipals);
+
 in
 
 {
@@ -287,6 +308,14 @@ in
         type = types.submodule ({name, ...}: {
           freeformType = settingsFormat.type;
           options = {
+            AuthorizedPrincipalsFile = mkOption {
+              type = types.str;
+              default = "none"; # upstream default
+              description = lib.mdDoc ''
+                Specifies a file that lists principal names that are accepted for certificate authentication. The default
+                is `"none"`, i.e. not to use	a principals file.
+              '';
+            };
             LogLevel = mkOption {
               type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ];
               default = "INFO"; # upstream default
@@ -448,7 +477,7 @@ in
     services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli";
     services.openssh.sftpServerExecutable = mkDefault "${cfgc.package}/libexec/sftp-server";
 
-    environment.etc = authKeysFiles //
+    environment.etc = authKeysFiles // authPrincipalsFiles //
       { "ssh/moduli".source = cfg.moduliFile;
         "ssh/sshd_config".source = sshconf;
       };
@@ -545,6 +574,8 @@ in
     services.openssh.authorizedKeysFiles =
       [ "%h/.ssh/authorized_keys" "/etc/ssh/authorized_keys.d/%u" ];
 
+    services.openssh.settings.AuthorizedPrincipalsFile = mkIf (authPrincipalsFiles != {}) "/etc/ssh/authorized_principals.d/%u";
+
     services.openssh.extraConfig = mkOrder 0
       ''
         UsePAM yes
@@ -578,6 +609,21 @@ in
         '')}
       '';
 
+    system.checks = [
+      (pkgs.runCommand "check-sshd-config"
+        {
+          nativeBuildInputs = [ validationPackage ];
+        } ''
+        ${concatMapStringsSep "\n"
+          (lport: "sshd -G -T -C lport=${toString lport} -f ${sshconf} > /dev/null")
+          cfg.ports}
+        ${concatMapStringsSep "\n"
+          (la: "sshd -G -T -C ${escapeShellArg "laddr=${la.addr},lport=${toString la.port}"} -f ${sshconf} > /dev/null")
+          cfg.listenAddresses}
+        touch $out
+      '')
+    ];
+
     assertions = [{ assertion = if cfg.settings.X11Forwarding then cfgc.setXAuthLocation else true;
                     message = "cannot enable X11 forwarding without setting xauth location";}
                   (let
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
index 346b50700c79..c4b2d0e80f9b 100644
--- a/nixpkgs/nixos/modules/services/networking/syncthing.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -36,17 +36,15 @@ let
     # be careful not to leak secrets in the filesystem or in process listings
     umask 0077
 
-    # get the api key by parsing the config.xml
-    while
-        ! ${pkgs.libxml2}/bin/xmllint \
-            --xpath 'string(configuration/gui/apikey)' \
-            ${cfg.configDir}/config.xml \
-            >"$RUNTIME_DIRECTORY/api_key"
-    do sleep 1; done
-
-    (printf "X-API-Key: "; cat "$RUNTIME_DIRECTORY/api_key") >"$RUNTIME_DIRECTORY/headers"
-
     curl() {
+        # get the api key by parsing the config.xml
+        while
+            ! ${pkgs.libxml2}/bin/xmllint \
+                --xpath 'string(configuration/gui/apikey)' \
+                ${cfg.configDir}/config.xml \
+                >"$RUNTIME_DIRECTORY/api_key"
+        do sleep 1; done
+        (printf "X-API-Key: "; cat "$RUNTIME_DIRECTORY/api_key") >"$RUNTIME_DIRECTORY/headers"
         ${pkgs.curl}/bin/curl -sSLk -H "@$RUNTIME_DIRECTORY/headers" \
             --retry 1000 --retry-delay 1 --retry-all-errors \
             "$@"
@@ -100,13 +98,13 @@ let
       the Nix configured list of IDs
       */
       + lib.optionalString s.override ''
-        old_conf_${conf_type}_ids="$(curl -X GET ${s.baseAddress} | ${jq} --raw-output '.[].${s.GET_IdAttrName}')"
-        for id in ''${old_conf_${conf_type}_ids}; do
-          if echo ${lib.concatStringsSep " " s.new_conf_IDs} | grep -q $id; then
-            continue
-          else
-            curl -X DELETE ${s.baseAddress}/$id
-          fi
+        stale_${conf_type}_ids="$(curl -X GET ${s.baseAddress} | ${jq} \
+          --argjson new_ids ${lib.escapeShellArg (builtins.toJSON s.new_conf_IDs)} \
+          --raw-output \
+          '[.[].${s.GET_IdAttrName}] - $new_ids | .[]'
+        )"
+        for id in ''${stale_${conf_type}_ids}; do
+          curl -X DELETE ${s.baseAddress}/$id
         done
       ''
     ))
diff --git a/nixpkgs/nixos/modules/services/networking/tinyproxy.nix b/nixpkgs/nixos/modules/services/networking/tinyproxy.nix
new file mode 100644
index 000000000000..9bcd8bfd814b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/tinyproxy.nix
@@ -0,0 +1,103 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.tinyproxy;
+  mkValueStringTinyproxy = with lib; v:
+        if true  ==         v then "yes"
+        else if false ==    v then "no"
+        else generators.mkValueStringDefault {} v;
+  mkKeyValueTinyproxy = {
+    mkValueString ? mkValueStringDefault {}
+  }: sep: k: v:
+    if null     ==  v then ""
+    else "${lib.strings.escape [sep] k}${sep}${mkValueString v}";
+
+  settingsFormat = (pkgs.formats.keyValue {
+      mkKeyValue = mkKeyValueTinyproxy {
+        mkValueString = mkValueStringTinyproxy;
+      } " ";
+      listsAsDuplicateKeys= true;
+  });
+  configFile = settingsFormat.generate "tinyproxy.conf" cfg.settings;
+
+in
+{
+
+  options = {
+    services.tinyproxy = {
+      enable = mkEnableOption (lib.mdDoc "Tinyproxy daemon");
+      package = mkPackageOptionMD pkgs "tinyproxy" {};
+      settings = mkOption {
+        description = lib.mdDoc "Configuration for [tinyproxy](https://tinyproxy.github.io/).";
+        default = { };
+        example = literalExpression ''{
+          Port 8888;
+          Listen 127.0.0.1;
+          Timeout 600;
+          Allow 127.0.0.1;
+          Anonymous = ['"Host"' '"Authorization"'];
+          ReversePath = '"/example/" "http://www.example.com/"';
+        }'';
+        type = types.submodule ({name, ...}: {
+          freeformType = settingsFormat.type;
+          options = {
+            Listen = mkOption {
+              type = types.str;
+              default = "127.0.0.1";
+              description = lib.mdDoc ''
+              Specify which address to listen to.
+              '';
+            };
+            Port = mkOption {
+              type = types.int;
+              default = 8888;
+              description = lib.mdDoc ''
+              Specify which port to listen to.
+              '';
+            };
+            Anonymous = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = lib.mdDoc ''
+              If an `Anonymous` keyword is present, then anonymous proxying is enabled. The headers listed with `Anonymous` are allowed through, while all others are denied. If no Anonymous keyword is present, then all headers are allowed through. You must include quotes around the headers.
+              '';
+            };
+            Filter = mkOption {
+              type = types.nullOr types.path;
+              default = null;
+              description = lib.mdDoc ''
+              Tinyproxy supports filtering of web sites based on URLs or domains. This option specifies the location of the file containing the filter rules, one rule per line.
+              '';
+            };
+          };
+        });
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    systemd.services.tinyproxy = {
+      description = "TinyProxy daemon";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = "tinyproxy";
+        Group = "tinyproxy";
+        Type = "simple";
+        ExecStart = "${getExe pkgs.tinyproxy} -d -c ${configFile}";
+        ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
+        KillSignal = "SIGINT";
+        TimeoutStopSec = "30s";
+        Restart = "on-failure";
+      };
+    };
+
+    users.users.tinyproxy = {
+        group = "tinyproxy";
+        isSystemUser = true;
+    };
+    users.groups.tinyproxy = {};
+  };
+  meta.maintainers = with maintainers; [ tcheronneau ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 3579d67aa54b..37a739f41d48 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -5,6 +5,10 @@ let
   stateDir = "/var/lib/unifi";
   cmd = ''
     @${cfg.jrePackage}/bin/java java \
+        ${optionalString (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16")
+        "--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED "
+        + "--add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED "
+        + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED"} \
         ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \
         ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \
         -jar ${stateDir}/lib/ace.jar
@@ -24,8 +28,8 @@ in
 
     services.unifi.jrePackage = mkOption {
       type = types.package;
-      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'';
+      default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.5") then pkgs.jdk17_headless else 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.5") then pkgs.jdk17_headless else 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/nixpkgs/nixos/modules/services/networking/wg-quick.nix b/nixpkgs/nixos/modules/services/networking/wg-quick.nix
index 34210580f538..68e0e06d0469 100644
--- a/nixpkgs/nixos/modules/services/networking/wg-quick.nix
+++ b/nixpkgs/nixos/modules/services/networking/wg-quick.nix
@@ -17,6 +17,8 @@ let
         type = with types; nullOr str;
         description = lib.mdDoc ''
           wg-quick .conf file, describing the interface.
+          Using this option can be a useful means of configuring WireGuard if
+          one has an existing .conf file.
           This overrides any other configuration interface configuration options.
           See wg-quick manpage for more details.
         '';
diff --git a/nixpkgs/nixos/modules/services/networking/wireguard.nix b/nixpkgs/nixos/modules/services/networking/wireguard.nix
index 21473388d76e..d4099be12a27 100644
--- a/nixpkgs/nixos/modules/services/networking/wireguard.nix
+++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix
@@ -164,6 +164,15 @@ let
           of the wireguard network has to be adjusted as well.
         '';
       };
+
+      metric = mkOption {
+        default = null;
+        type = with types; nullOr int;
+        example = 700;
+        description = lib.mdDoc ''
+          Set the metric of routes related to this Wireguard interface.
+        '';
+      };
     };
 
   };
@@ -395,7 +404,7 @@ let
             optionalString interfaceCfg.allowedIPsAsRoutes
               (concatMapStringsSep "\n"
                 (allowedIP:
-                  ''${ip} route replace "${allowedIP}" dev "${interfaceName}" table "${interfaceCfg.table}"''
+                  ''${ip} route replace "${allowedIP}" dev "${interfaceName}" table "${interfaceCfg.table}" ${optionalString (interfaceCfg.metric != null) "metric ${toString interfaceCfg.metric}"}''
                 ) peer.allowedIPs);
         in ''
           ${wg_setup}
diff --git a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
index 55a6002d61af..56d81fb04013 100644
--- a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
+++ b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix
@@ -116,11 +116,18 @@ in
       };
 
       persistentKeys = mkEnableOption (lib.mdDoc ''
-        If enabled then keys will be generated once and Yggdrasil
+        persistent keys. If enabled then keys will be generated once and Yggdrasil
         will retain the same IPv6 address when the service is
-        restarted. Keys are stored at ${keysPath}.
+        restarted. Keys are stored at ${keysPath}
       '');
 
+      extraArgs = mkOption {
+        type = listOf str;
+        default = [ ];
+        example = [ "-loglevel" "info" ];
+        description = lib.mdDoc "Extra command line arguments.";
+      };
+
     };
   };
 
@@ -181,7 +188,7 @@ in
             "${binYggdrasil} -genconf") + " > /run/yggdrasil/yggdrasil.conf"}
 
           # start yggdrasil
-          ${binYggdrasil} -useconffile /run/yggdrasil/yggdrasil.conf
+          ${binYggdrasil} -useconffile /run/yggdrasil/yggdrasil.conf ${lib.strings.escapeShellArgs cfg.extraArgs}
         '';
 
         serviceConfig = {
diff --git a/nixpkgs/nixos/modules/services/search/typesense.nix b/nixpkgs/nixos/modules/services/search/typesense.nix
index 856c3cad22df..c158d04fea23 100644
--- a/nixpkgs/nixos/modules/services/search/typesense.nix
+++ b/nixpkgs/nixos/modules/services/search/typesense.nix
@@ -83,12 +83,12 @@ in {
         Group = "typesense";
 
         StateDirectory = "typesense";
-        StateDirectoryMode = "0700";
+        StateDirectoryMode = "0750";
 
         # Hardening
         CapabilityBoundingSet = "";
         LockPersonality = true;
-        MemoryDenyWriteExecute = true;
+        # MemoryDenyWriteExecute = true; needed since 0.25.1
         NoNewPrivileges = true;
         PrivateUsers = true;
         PrivateTmp = true;
diff --git a/nixpkgs/nixos/modules/services/security/fail2ban.nix b/nixpkgs/nixos/modules/services/security/fail2ban.nix
index 9393fa751288..7059284850a5 100644
--- a/nixpkgs/nixos/modules/services/security/fail2ban.nix
+++ b/nixpkgs/nixos/modules/services/security/fail2ban.nix
@@ -393,7 +393,7 @@ in
           )
         ) // {
           # Miscellaneous options
-          inherit (cfg) banaction maxretry;
+          inherit (cfg) banaction maxretry bantime;
           ignoreip = ''127.0.0.1/8 ${optionalString config.networking.enableIPv6 "::1"} ${concatStringsSep " " cfg.ignoreIP}'';
           backend = "systemd";
           # Actions
diff --git a/nixpkgs/nixos/modules/services/security/opensnitch.nix b/nixpkgs/nixos/modules/services/security/opensnitch.nix
index 013aeb16756c..97ac3a72804c 100644
--- a/nixpkgs/nixos/modules/services/security/opensnitch.nix
+++ b/nixpkgs/nixos/modules/services/security/opensnitch.nix
@@ -172,7 +172,7 @@ in {
         ln -sf '${file}' "${local}"
       '') rules}
 
-      if [ ! -f /etc/opensnitch-system-fw.json ]; then
+      if [ ! -f /etc/opensnitchd/system-fw.json ]; then
         cp "${pkgs.opensnitch}/etc/opensnitchd/system-fw.json" "/etc/opensnitchd/system-fw.json"
       fi
     '');
diff --git a/nixpkgs/nixos/modules/services/security/tang.nix b/nixpkgs/nixos/modules/services/security/tang.nix
new file mode 100644
index 000000000000..9cb0a22fca42
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/security/tang.nix
@@ -0,0 +1,95 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.tang;
+in
+{
+  options.services.tang = {
+    enable = mkEnableOption "tang";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.tang;
+      defaultText = literalExpression "pkgs.tang";
+      description = mdDoc "The tang package to use.";
+    };
+
+    listenStream = mkOption {
+      type = with types; listOf str;
+      default = [ "7654" ];
+      example = [ "198.168.100.1:7654" "[2001:db8::1]:7654" "7654" ];
+      description = mdDoc ''
+        Addresses and/or ports on which tang should listen.
+        For detailed syntax see ListenStream in {manpage}`systemd.socket(5)`.
+      '';
+    };
+
+    ipAddressAllow = mkOption {
+      example = [ "192.168.1.0/24" ];
+      type = types.listOf types.str;
+      description = ''
+        Whitelist a list of address prefixes.
+        Preferably, internal addresses should be used.
+      '';
+    };
+
+  };
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services."tangd@" = {
+      description = "Tang server";
+      path = [ cfg.package ];
+      serviceConfig = {
+        StandardInput = "socket";
+        StandardOutput = "socket";
+        StandardError = "journal";
+        DynamicUser = true;
+        StateDirectory = "tang";
+        RuntimeDirectory = "tang";
+        StateDirectoryMode = "700";
+        UMask = "0077";
+        CapabilityBoundingSet = [ "" ];
+        ExecStart = "${cfg.package}/libexec/tangd %S/tang";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        DeviceAllow = [ "/dev/stdin" ];
+        RestrictAddressFamilies = [ "AF_UNIX" ];
+        DevicePolicy = "strict";
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+        IPAddressDeny = "any";
+        IPAddressAllow = cfg.ipAddressAllow;
+      };
+    };
+
+    systemd.sockets.tangd = {
+      description = "Tang server";
+      wantedBy = [ "sockets.target" ];
+      socketConfig = {
+        ListenStream = cfg.listenStream;
+        Accept = "yes";
+        IPAddressDeny = "any";
+        IPAddressAllow = cfg.ipAddressAllow;
+      };
+    };
+  };
+  meta.maintainers = with lib.maintainers; [ jfroche julienmalka ];
+}
diff --git a/nixpkgs/nixos/modules/services/security/usbguard.nix b/nixpkgs/nixos/modules/services/security/usbguard.nix
index 9b158bb9d18c..071e69975143 100644
--- a/nixpkgs/nixos/modules/services/security/usbguard.nix
+++ b/nixpkgs/nixos/modules/services/security/usbguard.nix
@@ -7,10 +7,8 @@ let
   # valid policy options
   policy = (types.enum [ "allow" "block" "reject" "keep" "apply-policy" ]);
 
-  defaultRuleFile = "/var/lib/usbguard/rules.conf";
-
   # decide what file to use for rules
-  ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else defaultRuleFile;
+  ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else cfg.ruleFile;
 
   daemonConf = ''
     # generated by nixos/modules/services/security/usbguard.nix
@@ -51,6 +49,19 @@ in
         '';
       };
 
+      ruleFile = mkOption {
+        type = types.nullOr types.path;
+        default = "/var/lib/usbguard/rules.conf";
+        example = "/run/secrets/usbguard-rules";
+        description = lib.mdDoc ''
+          This tells the USBGuard daemon which file to load as policy rule set.
+
+          The file can be changed manually or via the IPC interface assuming it has the right file permissions.
+
+          For more details see {manpage}`usbguard-rules.conf(5)`.
+        '';
+
+      };
       rules = mkOption {
         type = types.nullOr types.lines;
         default = null;
@@ -63,8 +74,7 @@ in
           be changed by the IPC interface.
 
           If you do not set this option, the USBGuard daemon will load
-          it's policy rule set from `${defaultRuleFile}`.
-          This file can be changed manually or via the IPC interface.
+          it's policy rule set from the option configured in `services.usbguard.ruleFile`.
 
           Running `usbguard generate-policy` as root will
           generate a config for your currently plugged in devices.
@@ -248,7 +258,6 @@ in
       '';
   };
   imports = [
-    (mkRemovedOptionModule [ "services" "usbguard" "ruleFile" ] "The usbguard module now uses ${defaultRuleFile} as ruleFile. Alternatively, use services.usbguard.rules to configure rules.")
     (mkRemovedOptionModule [ "services" "usbguard" "IPCAccessControlFiles" ] "The usbguard module now hardcodes IPCAccessControlFiles to /var/lib/usbguard/IPCAccessControl.d.")
     (mkRemovedOptionModule [ "services" "usbguard" "auditFilePath" ] "Removed usbguard module audit log files. Audit logs can be found in the systemd journal.")
     (mkRenamedOptionModule [ "services" "usbguard" "implictPolicyTarget" ] [ "services" "usbguard" "implicitPolicyTarget" ])
diff --git a/nixpkgs/nixos/modules/services/system/earlyoom.nix b/nixpkgs/nixos/modules/services/system/earlyoom.nix
index 3f501d453460..38805eba2ca1 100644
--- a/nixpkgs/nixos/modules/services/system/earlyoom.nix
+++ b/nixpkgs/nixos/modules/services/system/earlyoom.nix
@@ -11,7 +11,7 @@ let
 in
 {
   options.services.earlyoom = {
-    enable = mkEnableOption (lib.mdDoc "Early out of memory killing");
+    enable = mkEnableOption (lib.mdDoc "early out of memory killing");
 
     freeMemThreshold = mkOption {
       type = types.ints.between 1 100;
diff --git a/nixpkgs/nixos/modules/services/system/systembus-notify.nix b/nixpkgs/nixos/modules/services/system/systembus-notify.nix
index 269197b3997e..f79879fa1360 100644
--- a/nixpkgs/nixos/modules/services/system/systembus-notify.nix
+++ b/nixpkgs/nixos/modules/services/system/systembus-notify.nix
@@ -13,7 +13,7 @@ in
 
       WARNING: enabling this option (while convenient) should *not* be done on a
       machine where you do not trust the other users as it allows any other
-      local user to DoS your session by spamming notifications.
+      local user to DoS your session by spamming notifications
     '');
   };
 
diff --git a/nixpkgs/nixos/modules/services/torrent/flexget.nix b/nixpkgs/nixos/modules/services/torrent/flexget.nix
index 1b971838b32e..5cd7ae6ad7db 100644
--- a/nixpkgs/nixos/modules/services/torrent/flexget.nix
+++ b/nixpkgs/nixos/modules/services/torrent/flexget.nix
@@ -14,7 +14,7 @@ let
 in {
   options = {
     services.flexget = {
-      enable = mkEnableOption (lib.mdDoc "Run FlexGet Daemon");
+      enable = mkEnableOption (lib.mdDoc "FlexGet daemon");
 
       package = mkPackageOptionMD pkgs "flexget" {};
 
diff --git a/nixpkgs/nixos/modules/services/torrent/transmission.nix b/nixpkgs/nixos/modules/services/torrent/transmission.nix
index 752ab91fe631..b98cb5283a1a 100644
--- a/nixpkgs/nixos/modules/services/torrent/transmission.nix
+++ b/nixpkgs/nixos/modules/services/torrent/transmission.nix
@@ -182,7 +182,7 @@ in
         example = "770";
         description = lib.mdDoc ''
           If not `null`, is used as the permissions
-          set by `systemd.activationScripts.transmission-daemon`
+          set by `system.activationScripts.transmission-daemon`
           on the directories [](#opt-services.transmission.settings.download-dir),
           [](#opt-services.transmission.settings.incomplete-dir).
           and [](#opt-services.transmission.settings.watch-dir).
diff --git a/nixpkgs/nixos/modules/services/video/mediamtx.nix b/nixpkgs/nixos/modules/services/video/mediamtx.nix
index c3abd9cdcc5c..50f8e8810278 100644
--- a/nixpkgs/nixos/modules/services/video/mediamtx.nix
+++ b/nixpkgs/nixos/modules/services/video/mediamtx.nix
@@ -40,7 +40,7 @@ in
       };
 
       allowVideoAccess = lib.mkEnableOption (lib.mdDoc ''
-        Enable access to video devices like cameras on the system.
+        access to video devices like cameras on the system
       '');
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/audiobookshelf.nix b/nixpkgs/nixos/modules/services/web-apps/audiobookshelf.nix
new file mode 100644
index 000000000000..84dffc5f9d3c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/audiobookshelf.nix
@@ -0,0 +1,90 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.audiobookshelf;
+in
+{
+  options = {
+    services.audiobookshelf = {
+      enable = mkEnableOption "Audiobookshelf, self-hosted audiobook and podcast server.";
+
+      package = mkPackageOption pkgs "audiobookshelf" { };
+
+      dataDir = mkOption {
+        description = "Path to Audiobookshelf config and metadata inside of /var/lib.";
+        default = "audiobookshelf";
+        type = types.str;
+      };
+
+      host = mkOption {
+        description = "The host Audiobookshelf binds to.";
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        type = types.str;
+      };
+
+      port = mkOption {
+        description = "The TCP port Audiobookshelf will listen on.";
+        default = 8000;
+        type = types.port;
+      };
+
+      user = mkOption {
+        description = "User account under which Audiobookshelf runs.";
+        default = "audiobookshelf";
+        type = types.str;
+      };
+
+      group = mkOption {
+        description = "Group under which Audiobookshelf runs.";
+        default = "audiobookshelf";
+        type = types.str;
+      };
+
+      openFirewall = mkOption {
+        description = "Open ports in the firewall for the Audiobookshelf web interface.";
+        default = false;
+        type = types.bool;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.audiobookshelf = {
+      description = "Audiobookshelf is a self-hosted audiobook and podcast server";
+
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        User = cfg.user;
+        Group = cfg.group;
+        StateDirectory = cfg.dataDir;
+        WorkingDirectory = "/var/lib/${cfg.dataDir}";
+        ExecStart = "${cfg.package}/bin/audiobookshelf --host ${cfg.host} --port ${toString cfg.port}";
+        Restart = "on-failure";
+      };
+    };
+
+    users.users = mkIf (cfg.user == "audiobookshelf") {
+      audiobookshelf = {
+        isSystemUser = true;
+        group = cfg.group;
+        home = "/var/lib/${cfg.dataDir}";
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "audiobookshelf") {
+      audiobookshelf = { };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+  };
+
+  meta.maintainers = with maintainers; [ wietsedv ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix b/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
index da2cf93d7f1c..5519d6967a12 100644
--- a/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/cloudlog.nix
@@ -69,7 +69,7 @@ let
 in
 {
   options.services.cloudlog = with types; {
-    enable = mkEnableOption (mdDoc "Whether to enable Cloudlog");
+    enable = mkEnableOption (mdDoc "Cloudlog");
     dataDir = mkOption {
       type = str;
       default = "/var/lib/cloudlog";
diff --git a/nixpkgs/nixos/modules/services/web-apps/dex.nix b/nixpkgs/nixos/modules/services/web-apps/dex.nix
index bd041db007a1..0c4a71c6dfe4 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dex.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dex.nix
@@ -108,8 +108,7 @@ in
         ProtectClock = true;
         ProtectHome = true;
         ProtectHostname = true;
-        # Would re-mount paths ignored by temporary root
-        #ProtectSystem = "strict";
+        ProtectSystem = "strict";
         ProtectControlGroups = true;
         ProtectKernelLogs = true;
         ProtectKernelModules = true;
@@ -121,9 +120,7 @@ in
         RestrictSUIDSGID = true;
         SystemCallArchitectures = "native";
         SystemCallFilter = [ "@system-service" "~@privileged @setuid @keyring" ];
-        TemporaryFileSystem = "/:ro";
-        # Does not work well with the temporary root
-        #UMask = "0066";
+        UMask = "0066";
       } // optionalAttrs (cfg.environmentFile != null) {
         EnvironmentFile = cfg.environmentFile;
       };
diff --git a/nixpkgs/nixos/modules/services/web-apps/grocy.nix b/nixpkgs/nixos/modules/services/web-apps/grocy.nix
index 4d1084e295ff..fe40a3c20941 100644
--- a/nixpkgs/nixos/modules/services/web-apps/grocy.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/grocy.nix
@@ -8,6 +8,8 @@ in {
   options.services.grocy = {
     enable = mkEnableOption (lib.mdDoc "grocy");
 
+    package = mkPackageOptionMD pkgs "grocy" { };
+
     hostName = mkOption {
       type = types.str;
       description = lib.mdDoc ''
@@ -143,7 +145,7 @@ in {
     services.nginx = {
       enable = true;
       virtualHosts."${cfg.hostName}" = mkMerge [
-        { root = "${pkgs.grocy}/public";
+        { root = "${cfg.package}/public";
           locations."/".extraConfig = ''
             rewrite ^ /index.php;
           '';
diff --git a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
index bfa5fd5aff25..1a66f077b09d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/hedgedoc.nix
@@ -1,7 +1,7 @@
 { config, lib, pkgs, ... }:
 
 let
-  inherit (lib) literalExpression mdDoc mkEnableOption mkIf mkOption mkPackageOptionMD mkRenamedOptionModule types versionAtLeast;
+  inherit (lib) mkOption types mdDoc literalExpression;
 
   cfg = config.services.hedgedoc;
 
@@ -9,990 +9,189 @@ let
   # versionAtLeast statement remains set to 21.03 for backwards compatibility.
   # See https://github.com/NixOS/nixpkgs/pull/108899 and
   # https://github.com/NixOS/rfcs/blob/master/rfcs/0080-nixos-release-schedule.md.
-  name = if versionAtLeast config.system.stateVersion "21.03"
-    then "hedgedoc"
-    else "codimd";
+  name = if lib.versionAtLeast config.system.stateVersion "21.03" then
+    "hedgedoc"
+  else
+    "codimd";
 
-  settingsFormat = pkgs.formats.json {};
-
-  prettyJSON = conf:
-    pkgs.runCommandLocal "hedgedoc-config.json" {
-      nativeBuildInputs = [ pkgs.jq ];
-    } ''
-      jq '{production:del(.[]|nulls)|del(.[][]?|nulls)}' \
-        < ${settingsFormat.generate "hedgedoc-ugly.json" cfg.settings} \
-        > $out
-    '';
+  settingsFormat = pkgs.formats.json { };
 in
 {
+  meta.maintainers = with lib.maintainers; [ SuperSandro2000 h7x4 ];
+
   imports = [
-    (mkRenamedOptionModule [ "services" "codimd" ] [ "services" "hedgedoc" ])
-    (mkRenamedOptionModule
-      [ "services" "hedgedoc" "configuration" ] [ "services" "hedgedoc" "settings" ])
+    (lib.mkRenamedOptionModule [ "services" "codimd" ] [ "services" "hedgedoc" ])
+    (lib.mkRenamedOptionModule [ "services" "hedgedoc" "configuration" ] [ "services" "hedgedoc" "settings" ])
+    (lib.mkRenamedOptionModule [ "services" "hedgedoc" "groups" ] [ "users" "users" "hedgedoc" "extraGroups" ])
+    (lib.mkRemovedOptionModule [ "services" "hedgedoc" "workDir" ] ''
+      This option has been removed in favor of systemd managing the state directory.
+
+      If you have set this option without specifying `services.settings.uploadsDir`,
+      please move these files to `/var/lib/hedgedoc/uploads`, or set the option to point
+      at the correct location.
+    '')
   ];
 
   options.services.hedgedoc = {
-    package = mkPackageOptionMD pkgs "hedgedoc" { };
-    enable = mkEnableOption (lib.mdDoc "the HedgeDoc Markdown Editor");
+    package = lib.mkPackageOptionMD pkgs "hedgedoc" { };
+    enable = lib.mkEnableOption (mdDoc "the HedgeDoc Markdown Editor");
 
-    groups = mkOption {
-      type = types.listOf types.str;
-      default = [];
-      description = lib.mdDoc ''
-        Groups to which the service user should be added.
-      '';
-    };
-
-    workDir = mkOption {
-      type = types.path;
-      default = "/var/lib/${name}";
-      description = lib.mdDoc ''
-        Working directory for the HedgeDoc service.
-      '';
-    };
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+        options = {
+          domain = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            example = "hedgedoc.org";
+            description = mdDoc ''
+              Domain to use for website.
 
-    settings = let options = {
-      debug = mkEnableOption (lib.mdDoc "debug mode");
-      domain = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "hedgedoc.org";
-        description = lib.mdDoc ''
-          Domain name for the HedgeDoc instance.
-        '';
-      };
-      urlPath = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/url/path/to/hedgedoc";
-        description = lib.mdDoc ''
-          Path under which HedgeDoc is accessible.
-        '';
-      };
-      host = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = lib.mdDoc ''
-          Address to listen on.
-        '';
-      };
-      port = mkOption {
-        type = types.port;
-        default = 3000;
-        example = 80;
-        description = lib.mdDoc ''
-          Port to listen on.
-        '';
-      };
-      path = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/run/hedgedoc.sock";
-        description = lib.mdDoc ''
-          Specify where a UNIX domain socket should be placed.
-        '';
-      };
-      allowOrigin = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "localhost" "hedgedoc.org" ];
-        description = lib.mdDoc ''
-          List of domains to whitelist.
-        '';
-      };
-      useSSL = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable to use SSL server. This will also enable
-          {option}`protocolUseSSL`.
-        '';
-      };
-      enableStatsApi = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enables or disables the /status and /metrics endpoint.
-        '';
-      };
-      hsts = {
-        enable = mkOption {
-          type = types.bool;
-          default = true;
-          description = lib.mdDoc ''
-            Whether to enable HSTS if HTTPS is also enabled.
-          '';
-        };
-        maxAgeSeconds = mkOption {
-          type = types.int;
-          default = 31536000;
-          description = lib.mdDoc ''
-            Max duration for clients to keep the HSTS status.
-          '';
-        };
-        includeSubdomains = mkOption {
-          type = types.bool;
-          default = true;
-          description = lib.mdDoc ''
-            Whether to include subdomains in HSTS.
-          '';
-        };
-        preload = mkOption {
-          type = types.bool;
-          default = true;
-          description = lib.mdDoc ''
-            Whether to allow preloading of the site's HSTS status.
-          '';
-        };
-      };
-      csp = mkOption {
-        type = types.nullOr types.attrs;
-        default = null;
-        example = literalExpression ''
-          {
-            enable = true;
-            directives = {
-              scriptSrc = "trustworthy.scripts.example.com";
-            };
-            upgradeInsecureRequest = "auto";
-            addDefaults = true;
-          }
-        '';
-        description = lib.mdDoc ''
-          Specify the Content Security Policy which is passed to Helmet.
-          For configuration details see <https://helmetjs.github.io/docs/csp/>.
-        '';
-      };
-      protocolUseSSL = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable to use TLS for resource paths.
-          This only applies when {option}`domain` is set.
-        '';
-      };
-      urlAddPort = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable to add the port to callback URLs.
-          This only applies when {option}`domain` is set
-          and only for ports other than 80 and 443.
-        '';
-      };
-      useCDN = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to use CDN resources or not.
-        '';
-      };
-      allowAnonymous = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to allow anonymous usage.
-        '';
-      };
-      allowAnonymousEdits = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to allow guests to edit existing notes with the `freely` permission,
-          when {option}`allowAnonymous` is enabled.
-        '';
-      };
-      allowFreeURL = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to allow note creation by accessing a nonexistent note URL.
-        '';
-      };
-      requireFreeURLAuthentication = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Whether to require authentication for FreeURL mode style note creation.
-        '';
-      };
-      defaultPermission = mkOption {
-        type = types.enum [ "freely" "editable" "limited" "locked" "private" ];
-        default = "editable";
-        description = lib.mdDoc ''
-          Default permissions for notes.
-          This only applies for signed-in users.
-        '';
-      };
-      dbURL = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = ''
-          postgres://user:pass@host:5432/dbname
-        '';
-        description = lib.mdDoc ''
-          Specify which database to use.
-          HedgeDoc supports mysql, postgres, sqlite and mssql.
-          See [
-          https://sequelize.readthedocs.io/en/v3/](https://sequelize.readthedocs.io/en/v3/) for more information.
-          Note: This option overrides {option}`db`.
-        '';
-      };
-      db = mkOption {
-        type = types.attrs;
-        default = {};
-        example = literalExpression ''
-          {
-            dialect = "sqlite";
-            storage = "/var/lib/${name}/db.${name}.sqlite";
-          }
-        '';
-        description = lib.mdDoc ''
-          Specify the configuration for sequelize.
-          HedgeDoc supports mysql, postgres, sqlite and mssql.
-          See [
-          https://sequelize.readthedocs.io/en/v3/](https://sequelize.readthedocs.io/en/v3/) for more information.
-          Note: This option overrides {option}`db`.
-        '';
-      };
-      sslKeyPath= mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/var/lib/hedgedoc/hedgedoc.key";
-        description = lib.mdDoc ''
-          Path to the SSL key. Needed when {option}`useSSL` is enabled.
-        '';
-      };
-      sslCertPath = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/var/lib/hedgedoc/hedgedoc.crt";
-        description = lib.mdDoc ''
-          Path to the SSL cert. Needed when {option}`useSSL` is enabled.
-        '';
-      };
-      sslCAPath = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "/var/lib/hedgedoc/ca.crt" ];
-        description = lib.mdDoc ''
-          SSL ca chain. Needed when {option}`useSSL` is enabled.
-        '';
-      };
-      dhParamPath = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        example = "/var/lib/hedgedoc/dhparam.pem";
-        description = lib.mdDoc ''
-          Path to the SSL dh params. Needed when {option}`useSSL` is enabled.
-        '';
-      };
-      tmpPath = mkOption {
-        type = types.str;
-        default = "/tmp";
-        description = lib.mdDoc ''
-          Path to the temp directory HedgeDoc should use.
-          Note that {option}`serviceConfig.PrivateTmp` is enabled for
-          the HedgeDoc systemd service by default.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      defaultNotePath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/default.md";
-        defaultText = literalExpression "\"\${cfg.package}/public/default.md\"";
-        description = lib.mdDoc ''
-          Path to the default Note file.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      docsPath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/docs";
-        defaultText = literalExpression "\"\${cfg.package}/public/docs\"";
-        description = lib.mdDoc ''
-          Path to the docs directory.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      indexPath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/views/index.ejs";
-        defaultText = literalExpression "\"\${cfg.package}/public/views/index.ejs\"";
-        description = lib.mdDoc ''
-          Path to the index template file.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      hackmdPath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/views/hackmd.ejs";
-        defaultText = literalExpression "\"\${cfg.package}/public/views/hackmd.ejs\"";
-        description = lib.mdDoc ''
-          Path to the hackmd template file.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      errorPath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/views/error.ejs";
-        defaultText = literalExpression "\"\${cfg.package}/public/views/error.ejs\"";
-        description = lib.mdDoc ''
-          Path to the error template file.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      prettyPath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/views/pretty.ejs";
-        defaultText = literalExpression "\"\${cfg.package}/public/views/pretty.ejs\"";
-        description = lib.mdDoc ''
-          Path to the pretty template file.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      slidePath = mkOption {
-        type = types.nullOr types.str;
-        default = "${cfg.package}/public/views/slide.hbs";
-        defaultText = literalExpression "\"\${cfg.package}/public/views/slide.hbs\"";
-        description = lib.mdDoc ''
-          Path to the slide template file.
-          (Non-canonical paths are relative to HedgeDoc's base directory)
-        '';
-      };
-      uploadsPath = mkOption {
-        type = types.str;
-        default = "${cfg.workDir}/uploads";
-        defaultText = literalExpression "\"\${cfg.workDir}/uploads\"";
-        description = lib.mdDoc ''
-          Path under which uploaded files are saved.
-        '';
-      };
-      sessionName = mkOption {
-        type = types.str;
-        default = "connect.sid";
-        description = lib.mdDoc ''
-          Specify the name of the session cookie.
-        '';
-      };
-      sessionSecret = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = lib.mdDoc ''
-          Specify the secret used to sign the session cookie.
-          If unset, one will be generated on startup.
-        '';
-      };
-      sessionLife = mkOption {
-        type = types.int;
-        default = 1209600000;
-        description = lib.mdDoc ''
-          Session life time in milliseconds.
-        '';
-      };
-      heartbeatInterval = mkOption {
-        type = types.int;
-        default = 5000;
-        description = lib.mdDoc ''
-          Specify the socket.io heartbeat interval.
-        '';
-      };
-      heartbeatTimeout = mkOption {
-        type = types.int;
-        default = 10000;
-        description = lib.mdDoc ''
-          Specify the socket.io heartbeat timeout.
-        '';
-      };
-      documentMaxLength = mkOption {
-        type = types.int;
-        default = 100000;
-        description = lib.mdDoc ''
-          Specify the maximum document length.
-        '';
-      };
-      email = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to enable email sign-in.
-        '';
-      };
-      allowEmailRegister = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to enable email registration.
-        '';
-      };
-      allowGravatar = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to use gravatar as profile picture source.
-        '';
-      };
-      imageUploadType = mkOption {
-        type = types.enum [ "imgur" "s3" "minio" "filesystem" ];
-        default = "filesystem";
-        description = lib.mdDoc ''
-          Specify where to upload images.
-        '';
-      };
-      minio = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            accessKey = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Minio access key.
-              '';
-            };
-            secretKey = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Minio secret key.
-              '';
-            };
-            endPoint = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Minio endpoint.
-              '';
-            };
-            port = mkOption {
-              type = types.port;
-              default = 9000;
-              description = lib.mdDoc ''
-                Minio listen port.
-              '';
-            };
-            secure = mkOption {
-              type = types.bool;
-              default = true;
-              description = lib.mdDoc ''
-                Whether to use HTTPS for Minio.
-              '';
-            };
+              This is useful if you are trying to run hedgedoc behind
+              a reverse proxy.
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the minio third-party integration.";
-      };
-      s3 = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            accessKeyId = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                AWS access key id.
-              '';
-            };
-            secretAccessKey = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                AWS access key.
-              '';
-            };
-            region = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                AWS S3 region.
-              '';
-            };
-          };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the s3 third-party integration.";
-      };
-      s3bucket = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = lib.mdDoc ''
-          Specify the bucket name for upload types `s3` and `minio`.
-        '';
-      };
-      allowPDFExport = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Whether to enable PDF exports.
-        '';
-      };
-      imgur.clientId = mkOption {
-        type = types.nullOr types.str;
-        default = null;
-        description = lib.mdDoc ''
-          Imgur API client ID.
-        '';
-      };
-      azure = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            connectionString = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Azure Blob Storage connection string.
-              '';
-            };
-            container = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Azure Blob Storage container name.
-                It will be created if non-existent.
-              '';
-            };
-          };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the azure third-party integration.";
-      };
-      oauth2 = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            authorizationURL = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Specify the OAuth authorization URL.
-              '';
-            };
-            tokenURL = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Specify the OAuth token URL.
-              '';
-            };
-            baseURL = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the OAuth base URL.
-              '';
-            };
-            userProfileURL = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the OAuth userprofile URL.
-              '';
-            };
-            userProfileUsernameAttr = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the name of the attribute for the username from the claim.
-              '';
-            };
-            userProfileDisplayNameAttr = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the name of the attribute for the display name from the claim.
-              '';
-            };
-            userProfileEmailAttr = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the name of the attribute for the email from the claim.
-              '';
-            };
-            scope = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the OAuth scope.
-              '';
-            };
-            providerName = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the name to be displayed for this strategy.
-              '';
-            };
-            rolesClaim = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the role claim name.
-              '';
-            };
-            accessRole = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify role which should be included in the ID token roles claim to grant access
-              '';
-            };
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Specify the OAuth client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = with types; nullOr str;
-              default = null;
-              description = lib.mdDoc ''
-                Specify the OAuth client secret.
-              '';
-            };
+          urlPath = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            example = "hedgedoc";
+            description = mdDoc ''
+              URL path for the website.
+
+              This is useful if you are hosting hedgedoc on a path like
+              `www.example.com/hedgedoc`
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the OAuth integration.";
-      };
-      facebook = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Facebook API client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Facebook API client secret.
-              '';
-            };
+          host = mkOption {
+            type = with types; nullOr str;
+            default = "localhost";
+            description = mdDoc ''
+              Address to listen on.
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the facebook third-party integration";
-      };
-      twitter = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            consumerKey = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Twitter API consumer key.
-              '';
-            };
-            consumerSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Twitter API consumer secret.
-              '';
-            };
+          port = mkOption {
+            type = types.port;
+            default = 3000;
+            example = 80;
+            description = mdDoc ''
+              Port to listen on.
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the Twitter third-party integration.";
-      };
-      github = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                GitHub API client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Github API client secret.
-              '';
-            };
+          path = mkOption {
+            type = with types; nullOr path;
+            default = null;
+            example = "/run/hedgedoc/hedgedoc.sock";
+            description = mdDoc ''
+              Path to UNIX domain socket to listen on
+
+              ::: {.note}
+                If specified, {option}`host` and {option}`port` will be ignored.
+              :::
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the GitHub third-party integration.";
-      };
-      gitlab = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            baseURL = mkOption {
-              type = types.str;
-              default = "";
-              description = lib.mdDoc ''
-                GitLab API authentication endpoint.
-                Only needed for other endpoints than gitlab.com.
-              '';
-            };
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                GitLab API client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                GitLab API client secret.
-              '';
-            };
-            scope = mkOption {
-              type = types.enum [ "api" "read_user" ];
-              default = "api";
-              description = lib.mdDoc ''
-                GitLab API requested scope.
-                GitLab snippet import/export requires api scope.
-              '';
-            };
+          protocolUseSSL = mkOption {
+            type = types.bool;
+            default = false;
+            example = true;
+            description = mdDoc ''
+              Use `https://` for all links.
+
+              This is useful if you are trying to run hedgedoc behind
+              a reverse proxy.
+
+              ::: {.note}
+                Only applied if {option}`domain` is set.
+              :::
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the GitLab third-party integration.";
-      };
-      mattermost = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            baseURL = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Mattermost authentication endpoint.
-              '';
-            };
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Mattermost API client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Mattermost API client secret.
-              '';
-            };
+          allowOrigin = mkOption {
+            type = with types; listOf str;
+            default = with cfg.settings; [ host ] ++ lib.optionals (domain != null) [ domain ];
+            defaultText = literalExpression ''
+              with config.services.hedgedoc.settings; [ host ] ++ lib.optionals (domain != null) [ domain ]
+            '';
+            example = [ "localhost" "hedgedoc.org" ];
+            description = mdDoc ''
+              List of domains to whitelist.
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the Mattermost third-party integration.";
-      };
-      dropbox = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Dropbox API client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Dropbox API client secret.
-              '';
-            };
-            appKey = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Dropbox app key.
-              '';
-            };
+          db = mkOption {
+            type = types.attrs;
+            default = {
+              dialect = "sqlite";
+              storage = "/var/lib/${name}/db.sqlite";
+            };
+            defaultText = literalExpression ''
+              {
+                dialect = "sqlite";
+                storage = "/var/lib/hedgedoc/db.sqlite";
+              }
+            '';
+            example = literalExpression ''
+              db = {
+                username = "hedgedoc";
+                database = "hedgedoc";
+                host = "localhost:5432";
+                # or via socket
+                # host = "/run/postgresql";
+                dialect = "postgresql";
+              };
+            '';
+            description = mdDoc ''
+              Specify the configuration for sequelize.
+              HedgeDoc supports `mysql`, `postgres`, `sqlite` and `mssql`.
+              See <https://sequelize.readthedocs.io/en/v3/>
+              for more information.
+
+              ::: {.note}
+                The relevant parts will be overriden if you set {option}`dbURL`.
+              :::
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the Dropbox third-party integration.";
-      };
-      google = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            clientID = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Google API client ID.
-              '';
-            };
-            clientSecret = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Google API client secret.
-              '';
-            };
+          useSSL = mkOption {
+            type = types.bool;
+            default = false;
+            description = mdDoc ''
+              Enable to use SSL server.
+
+              ::: {.note}
+                This will also enable {option}`protocolUseSSL`.
+
+                It will also require you to set the following:
+
+                - {option}`sslKeyPath`
+                - {option}`sslCertPath`
+                - {option}`sslCAPath`
+                - {option}`dhParamPath`
+              :::
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the Google third-party integration.";
-      };
-      ldap = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            providerName = mkOption {
-              type = types.str;
-              default = "";
-              description = lib.mdDoc ''
-                Optional name to be displayed at login form, indicating the LDAP provider.
-              '';
-            };
-            url = mkOption {
-              type = types.str;
-              example = "ldap://localhost";
-              description = lib.mdDoc ''
-                URL of LDAP server.
-              '';
-            };
-            bindDn = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Bind DN for LDAP access.
-              '';
-            };
-            bindCredentials = mkOption {
-              type = types.str;
-              description = lib.mdDoc ''
-                Bind credentials for LDAP access.
-              '';
-            };
-            searchBase = mkOption {
-              type = types.str;
-              example = "o=users,dc=example,dc=com";
-              description = lib.mdDoc ''
-                LDAP directory to begin search from.
-              '';
-            };
-            searchFilter = mkOption {
-              type = types.str;
-              example = "(uid={{username}})";
-              description = lib.mdDoc ''
-                LDAP filter to search with.
-              '';
-            };
-            searchAttributes = mkOption {
-              type = types.nullOr (types.listOf types.str);
-              default = null;
-              example = [ "displayName" "mail" ];
-              description = lib.mdDoc ''
-                LDAP attributes to search with.
-              '';
-            };
-            userNameField = mkOption {
-              type = types.str;
-              default = "";
-              description = lib.mdDoc ''
-                LDAP field which is used as the username on HedgeDoc.
-                By default {option}`useridField` is used.
-              '';
-            };
-            useridField = mkOption {
-              type = types.str;
-              example = "uid";
-              description = lib.mdDoc ''
-                LDAP field which is a unique identifier for users on HedgeDoc.
-              '';
-            };
-            tlsca = mkOption {
-              type = types.str;
-              default = "/etc/ssl/certs/ca-certificates.crt";
-              example = "server-cert.pem,root.pem";
-              description = lib.mdDoc ''
-                Root CA for LDAP TLS in PEM format.
-              '';
-            };
+          uploadsPath = mkOption {
+            type = types.path;
+            default = "/var/lib/${name}/uploads";
+            defaultText = "/var/lib/hedgedoc/uploads";
+            description = mdDoc ''
+              Directory for storing uploaded images.
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the LDAP integration.";
-      };
-      saml = mkOption {
-        type = types.nullOr (types.submodule {
-          options = {
-            idpSsoUrl = mkOption {
-              type = types.str;
-              example = "https://idp.example.com/sso";
-              description = lib.mdDoc ''
-                IdP authentication endpoint.
-              '';
-            };
-            idpCert = mkOption {
-              type = types.path;
-              example = "/path/to/cert.pem";
-              description = lib.mdDoc ''
-                Path to IdP certificate file in PEM format.
-              '';
-            };
-            issuer = mkOption {
-              type = types.str;
-              default = "";
-              description = lib.mdDoc ''
-                Optional identity of the service provider.
-                This defaults to the server URL.
-              '';
-            };
-            identifierFormat = mkOption {
-              type = types.str;
-              default = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress";
-              description = lib.mdDoc ''
-                Optional name identifier format.
-              '';
-            };
-            groupAttribute = mkOption {
-              type = types.str;
-              default = "";
-              example = "memberOf";
-              description = lib.mdDoc ''
-                Optional attribute name for group list.
-              '';
-            };
-            externalGroups = mkOption {
-              type = types.listOf types.str;
-              default = [];
-              example = [ "Temporary-staff" "External-users" ];
-              description = lib.mdDoc ''
-                Excluded group names.
-              '';
-            };
-            requiredGroups = mkOption {
-              type = types.listOf types.str;
-              default = [];
-              example = [ "Hedgedoc-Users" ];
-              description = lib.mdDoc ''
-                Required group names.
-              '';
-            };
-            providerName = mkOption {
-              type = types.str;
-              default = "";
-              example = "My institution";
-              description = lib.mdDoc ''
-                Optional name to be displayed at login form indicating the SAML provider.
-              '';
-            };
-            attribute = {
-              id = mkOption {
-                type = types.str;
-                default = "";
-                description = lib.mdDoc ''
-                  Attribute map for `id`.
-                  Defaults to `NameID` of SAML response.
-                '';
-              };
-              username = mkOption {
-                type = types.str;
-                default = "";
-                description = lib.mdDoc ''
-                  Attribute map for `username`.
-                  Defaults to `NameID` of SAML response.
-                '';
-              };
-              email = mkOption {
-                type = types.str;
-                default = "";
-                description = lib.mdDoc ''
-                  Attribute map for `email`.
-                  Defaults to `NameID` of SAML response if
-                  {option}`identifierFormat` has
-                  the default value.
-                '';
-              };
-            };
+
+          # Declared because we change the default to false.
+          allowGravatar = mkOption {
+            type = types.bool;
+            default = false;
+            example = true;
+            description = mdDoc ''
+              Whether to enable [Libravatar](https://wiki.libravatar.org/) as
+              profile picture source on your instance.
+
+              Despite the naming of the setting, Hedgedoc replaced Gravatar
+              with Libravatar in [CodiMD 1.4.0](https://hedgedoc.org/releases/1.4.0/)
+            '';
           };
-        });
-        default = null;
-        description = lib.mdDoc "Configure the SAML integration.";
-      };
-    }; in lib.mkOption {
-      type = lib.types.submodule {
-        freeformType = settingsFormat.type;
-        inherit options;
+        };
       };
-      description = lib.mdDoc ''
+
+      description = mdDoc ''
         HedgeDoc configuration, see
         <https://docs.hedgedoc.org/configuration/>
         for documentation.
@@ -1003,7 +202,7 @@ in
       type = with types; nullOr path;
       default = null;
       example = "/var/lib/hedgedoc/hedgedoc.env";
-      description = lib.mdDoc ''
+      description = mdDoc ''
         Environment file as defined in {manpage}`systemd.exec(5)`.
 
         Secrets may be passed to the service without adding them to the world-readable
@@ -1028,45 +227,94 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
-    assertions = [
-      { assertion = cfg.settings.db == {} -> (
-          cfg.settings.dbURL != "" && cfg.settings.dbURL != null
-        );
-        message = "Database configuration for HedgeDoc missing."; }
-    ];
-    users.groups.${name} = {};
+  config = lib.mkIf cfg.enable {
+    users.groups.${name} = { };
     users.users.${name} = {
       description = "HedgeDoc service user";
       group = name;
-      extraGroups = cfg.groups;
-      home = cfg.workDir;
-      createHome = true;
       isSystemUser = true;
     };
 
+    services.hedgedoc.settings = {
+      defaultNotePath = lib.mkDefault "${cfg.package}/public/default.md";
+      docsPath = lib.mkDefault "${cfg.package}/public/docs";
+      viewPath = lib.mkDefault "${cfg.package}/public/views";
+    };
+
     systemd.services.hedgedoc = {
       description = "HedgeDoc Service";
+      documentation = [ "https://docs.hedgedoc.org/" ];
       wantedBy = [ "multi-user.target" ];
       after = [ "networking.target" ];
-      preStart = ''
-        ${pkgs.envsubst}/bin/envsubst \
-          -o ${cfg.workDir}/config.json \
-          -i ${prettyJSON cfg.settings}
-        mkdir -p ${cfg.settings.uploadsPath}
-      '';
+      preStart =
+        let
+          configFile = settingsFormat.generate "hedgedoc-config.json" {
+            production = cfg.settings;
+          };
+        in
+        ''
+          ${pkgs.envsubst}/bin/envsubst \
+            -o /run/${name}/config.json \
+            -i ${configFile}
+          ${pkgs.coreutils}/bin/mkdir -p ${cfg.settings.uploadsPath}
+        '';
       serviceConfig = {
-        WorkingDirectory = cfg.workDir;
-        StateDirectory = [ cfg.workDir cfg.settings.uploadsPath ];
-        ExecStart = "${lib.getExe cfg.package}";
-        EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+        User = name;
+        Group = name;
+
+        Restart = "always";
+        ExecStart = "${cfg.package}/bin/hedgedoc";
+        RuntimeDirectory = [ name ];
+        StateDirectory = [ name ];
+        WorkingDirectory = "/run/${name}";
+        ReadWritePaths = [
+          "-${cfg.settings.uploadsPath}"
+        ] ++ lib.optionals (cfg.settings.db ? "storage") [ "-${cfg.settings.db.storage}" ];
+        EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
         Environment = [
-          "CMD_CONFIG_FILE=${cfg.workDir}/config.json"
+          "CMD_CONFIG_FILE=/run/${name}/config.json"
           "NODE_ENV=production"
         ];
-        Restart = "always";
-        User = name;
+
+        # Hardening
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
         PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          # Required for connecting to database sockets,
+          # and listening to unix socket at `cfg.settings.path`
+          "AF_UNIX"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SocketBindAllow = lib.mkIf (cfg.settings.path == null) cfg.settings.port;
+        SocketBindDeny = "any";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged @obsolete"
+          "@pkey"
+        ];
+        UMask = "0007";
       };
     };
   };
diff --git a/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix b/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix
index 0fc283ff5219..be8ecc645e59 100644
--- a/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/hledger-web.nix
@@ -7,7 +7,7 @@ in {
 
     enable = mkEnableOption (lib.mdDoc "hledger-web service");
 
-    serveApi = mkEnableOption (lib.mdDoc "Serve only the JSON web API, without the web UI");
+    serveApi = mkEnableOption (lib.mdDoc "serving only the JSON web API, without the web UI");
 
     host = mkOption {
       type = types.str;
diff --git a/nixpkgs/nixos/modules/services/web-apps/honk.nix b/nixpkgs/nixos/modules/services/web-apps/honk.nix
index e8718774575b..d47b17e54ffb 100644
--- a/nixpkgs/nixos/modules/services/web-apps/honk.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/honk.nix
@@ -116,7 +116,7 @@ in
       unitConfig = {
         ConditionPathExists = [
           # Skip this service if the database already exists
-          "!$STATE_DIRECTORY/honk.db"
+          "!%S/honk/honk.db"
         ];
       };
     };
diff --git a/nixpkgs/nixos/modules/services/web-apps/isso.nix b/nixpkgs/nixos/modules/services/web-apps/isso.nix
index 1a852ec352f2..6cb2d9ec785e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/isso.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/isso.nix
@@ -12,11 +12,11 @@ in {
   options = {
     services.isso = {
       enable = mkEnableOption (lib.mdDoc ''
-        A commenting server similar to Disqus.
+        isso, a commenting server similar to Disqus.
 
         Note: The application's author suppose to run isso behind a reverse proxy.
         The embedded solution offered by NixOS is also only suitable for small installations
-        below 20 requests per second.
+        below 20 requests per second
       '');
 
       settings = mkOption {
diff --git a/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix b/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix
index 3825b03c2449..21416be35877 100644
--- a/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -105,9 +105,9 @@ in
         type = bool;
         default = true;
         description = lib.mdDoc ''
-          Whether to enable Jitsi Videobridge instance and configure it to connect to Prosody.
+          Jitsi Videobridge instance and configure it to connect to Prosody.
 
-          Additional configuration is possible with {option}`services.jitsi-videobridge`.
+          Additional configuration is possible with {option}`services.jitsi-videobridge`
         '';
       };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
index 21c587694c6e..c5fb03766899 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mediawiki.nix
@@ -8,7 +8,12 @@ let
   cfg = config.services.mediawiki;
   fpm = config.services.phpfpm.pools.mediawiki;
   user = "mediawiki";
-  group = if cfg.webserver == "apache" then config.services.httpd.group else "mediawiki";
+  group =
+    if cfg.webserver == "apache" then
+      config.services.httpd.group
+    else if cfg.webserver == "nginx" then
+      config.services.nginx.group
+    else "mediawiki";
 
   cacheDir = "/var/cache/mediawiki";
   stateDir = "/var/lib/mediawiki";
@@ -71,7 +76,7 @@ let
       ## For more information on customizing the URLs
       ## (like /w/index.php/Page_title to /wiki/Page_title) please see:
       ## https://www.mediawiki.org/wiki/Manual:Short_URL
-      $wgScriptPath = "";
+      $wgScriptPath = "${lib.optionalString (cfg.webserver == "nginx") "/w"}";
 
       ## The protocol and server name to use in fully-qualified URLs
       $wgServer = "${cfg.url}";
@@ -79,6 +84,11 @@ let
       ## The URL path to static resources (images, scripts, etc.)
       $wgResourceBasePath = $wgScriptPath;
 
+      ${lib.optionalString (cfg.webserver == "nginx") ''
+        $wgArticlePath = "/wiki/$1";
+        $wgUsePathInfo = true;
+      ''}
+
       ## The URL path to the logo.  Make sure you change this from the default,
       ## or else you'll overwrite your logo when you upgrade!
       $wgLogo = "$wgResourceBasePath/resources/assets/wiki.png";
@@ -175,6 +185,7 @@ let
       ${cfg.extraConfig}
   '';
 
+  withTrailingSlash = str: if lib.hasSuffix "/" str then str else "${str}/";
 in
 {
   # interface
@@ -209,8 +220,14 @@ in
 
       url = mkOption {
         type = types.str;
-        default = if cfg.webserver == "apache" then
+        default =
+          if cfg.webserver == "apache" then
             "${if cfg.httpd.virtualHost.addSSL || cfg.httpd.virtualHost.forceSSL || cfg.httpd.virtualHost.onlySSL then "https" else "http"}://${cfg.httpd.virtualHost.hostName}"
+          else if cfg.webserver == "nginx" then
+            let
+              hasSSL = host: host.forceSSL || host.addSSL;
+            in
+            "${if hasSSL config.services.nginx.virtualHosts.${cfg.nginx.hostName} then "https" else "http"}://${cfg.nginx.hostName}"
           else
             "http://localhost";
         defaultText = literalExpression ''
@@ -286,7 +303,7 @@ in
       };
 
       webserver = mkOption {
-        type = types.enum [ "apache" "none" ];
+        type = types.enum [ "apache" "none" "nginx" ];
         default = "apache";
         description = lib.mdDoc "Webserver to use.";
       };
@@ -368,6 +385,16 @@ in
         };
       };
 
+      nginx.hostName = mkOption {
+        type = types.str;
+        example = literalExpression ''wiki.example.com'';
+        default = "localhost";
+        description = lib.mdDoc ''
+          The hostname to use for the nginx virtual host.
+          This is used to generate the nginx configuration.
+        '';
+      };
+
       httpd.virtualHost = mkOption {
         type = types.submodule (import ../web-servers/apache-httpd/vhost-options.nix);
         example = literalExpression ''
@@ -469,6 +496,9 @@ in
       settings = (if (cfg.webserver == "apache") then {
         "listen.owner" = config.services.httpd.user;
         "listen.group" = config.services.httpd.group;
+      } else if (cfg.webserver == "nginx") then {
+        "listen.owner" = config.services.nginx.user;
+        "listen.group" = config.services.nginx.group;
       } else {
         "listen.owner" = user;
         "listen.group" = group;
@@ -503,6 +533,62 @@ in
         }
       ];
     };
+    # inspired by https://www.mediawiki.org/wiki/Manual:Short_URL/Nginx
+    services.nginx = lib.mkIf (cfg.webserver == "nginx") {
+      enable = true;
+      virtualHosts.${config.services.mediawiki.nginx.hostName} = {
+        root = "${pkg}/share/mediawiki";
+        locations = {
+          "~ ^/w/(index|load|api|thumb|opensearch_desc|rest|img_auth)\\.php$".extraConfig = ''
+            rewrite ^/w/(.*) /$1 break;
+            include ${config.services.nginx.package}/conf/fastcgi_params;
+            fastcgi_index index.php;
+            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+            fastcgi_pass unix:${config.services.phpfpm.pools.mediawiki.socket};
+          '';
+          "/w/images/".alias = withTrailingSlash cfg.uploadsDir;
+          # Deny access to deleted images folder
+          "/w/images/deleted".extraConfig = ''
+            deny all;
+          '';
+          # MediaWiki assets (usually images)
+          "~ ^/w/resources/(assets|lib|src)" = {
+            tryFiles = "$uri =404";
+            extraConfig = ''
+              add_header Cache-Control "public";
+              expires 7d;
+            '';
+          };
+          # Assets, scripts and styles from skins and extensions
+          "~ ^/w/(skins|extensions)/.+\\.(css|js|gif|jpg|jpeg|png|svg|wasm|ttf|woff|woff2)$" = {
+            tryFiles = "$uri =404";
+            extraConfig = ''
+              add_header Cache-Control "public";
+              expires 7d;
+            '';
+          };
+
+          # Handling for Mediawiki REST API, see [[mw:API:REST_API]]
+          "/w/rest.php".tryFiles = "$uri $uri/ /rest.php?$query_string";
+
+          # Handling for the article path (pretty URLs)
+          "/wiki/".extraConfig = ''
+            rewrite ^/wiki/(?<pagename>.*)$ /w/index.php;
+          '';
+
+          # Explicit access to the root website, redirect to main page (adapt as needed)
+          "= /".extraConfig = ''
+            return 301 /wiki/Main_Page;
+          '';
+
+          # Every other entry point will be disallowed.
+          # Add specific rules for other entry points/images as needed above this
+          "/".extraConfig = ''
+             return 404;
+          '';
+        };
+      };
+    };
 
     systemd.tmpfiles.rules = [
       "d '${stateDir}' 0750 ${user} ${group} - -"
diff --git a/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix b/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix
index cb864321ef27..652dc8840252 100644
--- a/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/meme-bingo-web.nix
@@ -8,9 +8,9 @@ in {
   options = {
     services.meme-bingo-web = {
       enable = mkEnableOption (mdDoc ''
-        A web app for the meme bingo, rendered entirely on the web server and made interactive with forms.
+        a web app for the meme bingo, rendered entirely on the web server and made interactive with forms.
 
-        Note: The application's author suppose to run meme-bingo-web behind a reverse proxy for SSL and HTTP/3.
+        Note: The application's author suppose to run meme-bingo-web behind a reverse proxy for SSL and HTTP/3
       '');
 
       package = mkOption {
diff --git a/nixpkgs/nixos/modules/services/web-apps/microbin.nix b/nixpkgs/nixos/modules/services/web-apps/microbin.nix
new file mode 100644
index 000000000000..233bfac6e699
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/microbin.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.microbin;
+in
+{
+  options.services.microbin = {
+    enable = lib.mkEnableOption (lib.mdDoc "MicroBin is a super tiny, feature rich, configurable paste bin web application");
+
+    package = lib.mkPackageOption pkgs "microbin" { };
+
+    settings = lib.mkOption {
+      type = lib.types.submodule { freeformType = with lib.types; attrsOf (oneOf [ bool int str ]); };
+      default = { };
+      example = {
+        MICROBIN_PORT = 8080;
+        MICROBIN_HIDE_LOGO = false;
+      };
+      description = lib.mdDoc ''
+        Additional configuration for MicroBin, see
+        <https://microbin.eu/docs/installation-and-configuration/configuration/>
+        for supported values.
+
+        For secrets use passwordFile option instead.
+      '';
+    };
+
+    dataDir = lib.mkOption {
+      type = lib.types.str;
+      default = "/var/lib/microbin";
+      description = lib.mdDoc "Default data folder for MicroBin.";
+    };
+
+    passwordFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      example = "/run/secrets/microbin.env";
+      description = lib.mdDoc ''
+        Path to file containing environment variables.
+        Useful for passing down secrets.
+        Variables that can be considered secrets are:
+         - MICROBIN_BASIC_AUTH_USERNAME
+         - MICROBIN_BASIC_AUTH_PASSWORD
+         - MICROBIN_ADMIN_USERNAME
+         - MICROBIN_ADMIN_PASSWORD
+         - MICROBIN_UPLOADER_PASSWORD
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.microbin.settings = with lib; {
+      MICROBIN_BIND = mkDefault "0.0.0.0";
+      MICROBIN_DISABLE_TELEMETRY = mkDefault true;
+      MICROBIN_LIST_SERVER = mkDefault false;
+      MICROBIN_PORT = mkDefault "8080";
+    };
+
+    systemd.services.microbin = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment = lib.mapAttrs (_: v: if lib.isBool v then lib.boolToString v else toString v) cfg.settings;
+      serviceConfig = {
+        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+        DevicePolicy = "closed";
+        DynamicUser = true;
+        EnvironmentFile = lib.optional (cfg.passwordFile != null) cfg.passwordFile;
+        ExecStart = "${cfg.package}/bin/microbin";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ReadWritePaths = cfg.dataDir;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        StateDirectory = "microbin";
+        SystemCallArchitectures = [ "native" ];
+        SystemCallFilter = [ "@system-service" ];
+        WorkingDirectory = cfg.dataDir;
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ surfaceflinger ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/netbox.nix b/nixpkgs/nixos/modules/services/web-apps/netbox.nix
index 6d89ffc2a7b7..8ba1852848e5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/netbox.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/netbox.nix
@@ -74,9 +74,18 @@ in {
 
     package = lib.mkOption {
       type = lib.types.package;
-      default = if lib.versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3;
+      default =
+        if lib.versionAtLeast config.system.stateVersion "23.11"
+        then pkgs.netbox_3_6
+        else if lib.versionAtLeast config.system.stateVersion "23.05"
+        then pkgs.netbox_3_5
+        else pkgs.netbox_3_3;
       defaultText = lib.literalExpression ''
-        if versionAtLeast config.system.stateVersion "23.05" then pkgs.netbox else pkgs.netbox_3_3;
+        if lib.versionAtLeast config.system.stateVersion "23.11"
+        then pkgs.netbox_3_6
+        else if lib.versionAtLeast config.system.stateVersion "23.05"
+        then pkgs.netbox_3_5
+        else pkgs.netbox_3_3;
       '';
       description = lib.mdDoc ''
         NetBox package to use.
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.md b/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
index cbd7b5b3d066..a25bed30e47f 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.md
@@ -119,13 +119,7 @@ Auto updates for Nextcloud apps can be enabled using
   - **Server-side encryption.**
     Nextcloud supports [server-side encryption (SSE)](https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html).
     This is not an end-to-end encryption, but can be used to encrypt files that will be persisted
-    to external storage such as S3. Please note that this won't work anymore when using OpenSSL 3
-    for PHP's openssl extension and **Nextcloud 25 or older** because this is implemented using the
-    legacy cipher RC4. For Nextcloud26 this isn't relevant anymore, because Nextcloud has an RC4 implementation
-    written in native PHP and thus doesn't need `ext-openssl` for that anymore.
-    If [](#opt-system.stateVersion) is *above* `22.05`,
-    this is disabled by default. To turn it on again and for further information please refer to
-    [](#opt-services.nextcloud.enableBrokenCiphersForSSE).
+    to external storage such as S3.
 
 ## Using an alternative webserver as reverse-proxy (e.g. `httpd`) {#module-services-nextcloud-httpd}
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index e0a7e7d4859c..f9713cac47e9 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -27,13 +27,7 @@ let
 
   phpPackage = cfg.phpPackage.buildEnv {
     extensions = { enabled, all }:
-      (with all;
-        # disable default openssl extension
-        (lib.filter (e: e.pname != "php-openssl") enabled)
-        # use OpenSSL 1.1 for RC4 Nextcloud encryption if user
-        # has acknowledged the brokenness of the ciphers (RC4).
-        # TODO: remove when https://github.com/nextcloud/server/issues/32003 is fixed.
-        ++ (if cfg.enableBrokenCiphersForSSE then [ cfg.phpPackage.extensions.openssl-legacy ] else [ cfg.phpPackage.extensions.openssl ])
+      (with all; enabled
         ++ optional cfg.enableImagemagick imagick
         # Optionally enabled depending on caching settings
         ++ optional cfg.caching.apcu apcu
@@ -66,6 +60,9 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.config.dbtype == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.config.dbtype == "pgsql";
 
+  # https://github.com/nextcloud/documentation/pull/11179
+  ocmProviderIsNotAStaticDirAnymore = versionAtLeast cfg.package.version "27.1.2";
+
 in {
 
   imports = [
@@ -87,6 +84,10 @@ in {
       Further details about this can be found in the `Nextcloud`-section of the NixOS-manual
       (which can be opened e.g. by running `nixos-help`).
     '')
+    (mkRemovedOptionModule [ "services" "nextcloud" "enableBrokenCiphersForSSE" ] ''
+      This option has no effect since there's no supported Nextcloud version packaged here
+      using OpenSSL for RC4 SSE.
+    '')
     (mkRemovedOptionModule [ "services" "nextcloud" "disableImagemagick" ] ''
       Use services.nextcloud.enableImagemagick instead.
     '')
@@ -95,39 +96,6 @@ in {
   options.services.nextcloud = {
     enable = mkEnableOption (lib.mdDoc "nextcloud");
 
-    enableBrokenCiphersForSSE = mkOption {
-      type = types.bool;
-      default = versionOlder stateVersion "22.11";
-      defaultText = literalExpression "versionOlder system.stateVersion \"22.11\"";
-      description = lib.mdDoc ''
-        This option enables using the OpenSSL PHP extension linked against OpenSSL 1.1
-        rather than latest OpenSSL (≥ 3), this is not recommended unless you need
-        it for server-side encryption (SSE). SSE uses the legacy RC4 cipher which is
-        considered broken for several years now. See also [RFC7465](https://datatracker.ietf.org/doc/html/rfc7465).
-
-        This cipher has been disabled in OpenSSL ≥ 3 and requires
-        a specific legacy profile to re-enable it.
-
-        If you deploy Nextcloud using OpenSSL ≥ 3 for PHP and have
-        server-side encryption configured, you will not be able to access
-        your files anymore. Enabling this option can restore access to your files.
-        Upon testing we didn't encounter any data corruption when turning
-        this on and off again, but this cannot be guaranteed for
-        each Nextcloud installation.
-
-        It is `true` by default for systems with a [](#opt-system.stateVersion) below
-        `22.11` to make sure that existing installations won't break on update. On newer
-        NixOS systems you have to explicitly enable it on your own.
-
-        Please note that this only provides additional value when using
-        external storage such as S3 since it's not an end-to-end encryption.
-        If this is not the case,
-        it is advised to [disable server-side encryption](https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption) and set this to `false`.
-
-        In the future, Nextcloud may move to AES-256-GCM, by then,
-        this option will be removed.
-      '';
-    };
     hostName = mkOption {
       type = types.str;
       description = lib.mdDoc "FQDN for the nextcloud instance.";
@@ -225,7 +193,7 @@ in {
     package = mkOption {
       type = types.package;
       description = lib.mdDoc "Which package to use for the Nextcloud instance.";
-      relatedPackages = [ "nextcloud25" "nextcloud26" "nextcloud27" ];
+      relatedPackages = [ "nextcloud26" "nextcloud27" ];
     };
     phpPackage = mkOption {
       type = types.package;
@@ -740,28 +708,7 @@ in {
         '')
         ++ (optional (versionOlder cfg.package.version "25") (upgradeWarning 24 "22.11"))
         ++ (optional (versionOlder cfg.package.version "26") (upgradeWarning 25 "23.05"))
-        ++ (optional (versionOlder cfg.package.version "27") (upgradeWarning 26 "23.11"))
-        ++ (optional cfg.enableBrokenCiphersForSSE ''
-          You're using PHP's openssl extension built against OpenSSL 1.1 for Nextcloud.
-          This is only necessary if you're using Nextcloud's server-side encryption.
-          Please keep in mind that it's using the broken RC4 cipher.
-
-          If you don't use that feature, you can switch to OpenSSL 3 and get
-          rid of this warning by declaring
-
-            services.nextcloud.enableBrokenCiphersForSSE = false;
-
-          If you need to use server-side encryption you can ignore this warning.
-          Otherwise you'd have to disable server-side encryption first in order
-          to be able to safely disable this option and get rid of this warning.
-          See <https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption> on how to achieve this.
-
-          For more context, here is the implementing pull request: https://github.com/NixOS/nixpkgs/pull/198470
-        '')
-        ++ (optional (cfg.enableBrokenCiphersForSSE && versionAtLeast cfg.package.version "26") ''
-          Nextcloud26 supports RC4 without requiring legacy OpenSSL, so
-          `services.nextcloud.enableBrokenCiphersForSSE` can be set to `false`.
-        '');
+        ++ (optional (versionOlder cfg.package.version "27") (upgradeWarning 26 "23.11"));
 
       services.nextcloud.package = with pkgs;
         mkDefault (
@@ -1136,10 +1083,6 @@ in {
               }
             '';
           };
-          "/" = {
-            priority = 900;
-            extraConfig = "rewrite ^ /index.php;";
-          };
           "~ ^/store-apps" = {
             priority = 201;
             extraConfig = "root ${cfg.home};";
@@ -1164,15 +1107,23 @@ in {
               try_files $uri $uri/ =404;
             '';
           };
-          "~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)".extraConfig = ''
-            return 404;
-          '';
-          "~ ^/(?:\\.(?!well-known)|autotest|occ|issue|indie|db_|console)".extraConfig = ''
-            return 404;
-          '';
-          "~ ^\\/(?:index|remote|public|cron|core\\/ajax\\/update|status|ocs\\/v[12]|updater\\/.+|oc[ms]-provider\\/.+|.+\\/richdocumentscode\\/proxy)\\.php(?:$|\\/)" = {
+          "~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)" = {
+            priority = 450;
+            extraConfig = ''
+              return 404;
+            '';
+          };
+          "~ ^/(?:\\.|autotest|occ|issue|indie|db_|console)" = {
+            priority = 450;
+            extraConfig = ''
+              return 404;
+            '';
+          };
+          "~ \\.php(?:$|/)" = {
             priority = 500;
             extraConfig = ''
+              # legacy support (i.e. static files and directories in cfg.package)
+              rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[s${optionalString (!ocmProviderIsNotAStaticDirAnymore) "m"}]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
               include ${config.services.nginx.package}/conf/fastcgi.conf;
               fastcgi_split_path_info ^(.+?\.php)(\\/.*)$;
               set $path_info $fastcgi_path_info;
@@ -1188,19 +1139,30 @@ in {
               fastcgi_read_timeout ${builtins.toString cfg.fastcgiTimeout}s;
             '';
           };
-          "~ \\.(?:css|js|woff2?|svg|gif|map)$".extraConfig = ''
+          "~ \\.(?:css|js|mjs|svg|gif|png|jpg|jpeg|ico|wasm|tflite|map|html|ttf|bcmap|mp4|webm)$".extraConfig = ''
             try_files $uri /index.php$request_uri;
             expires 6M;
             access_log off;
+            location ~ \.wasm$ {
+              default_type application/wasm;
+            }
           '';
-          "~ ^\\/(?:updater|ocs-provider|ocm-provider)(?:$|\\/)".extraConfig = ''
+          "~ ^\\/(?:updater|ocs-provider${optionalString (!ocmProviderIsNotAStaticDirAnymore) "|ocm-provider"})(?:$|\\/)".extraConfig = ''
             try_files $uri/ =404;
             index index.php;
           '';
-          "~ \\.(?:png|html|ttf|ico|jpg|jpeg|bcmap|mp4|webm)$".extraConfig = ''
-            try_files $uri /index.php$request_uri;
-            access_log off;
-          '';
+          "/remote" = {
+            priority = 1500;
+            extraConfig = ''
+              return 301 /remote.php$request_uri;
+            '';
+          };
+          "/" = {
+            priority = 1600;
+            extraConfig = ''
+              try_files $uri $uri/ /index.php$request_uri;
+            '';
+          };
         };
         extraConfig = ''
           index index.php index.html /index.php$request_uri;
diff --git a/nixpkgs/nixos/modules/services/web-apps/outline.nix b/nixpkgs/nixos/modules/services/web-apps/outline.nix
index 1d8298963e6d..0e3bd07c1fc1 100644
--- a/nixpkgs/nixos/modules/services/web-apps/outline.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/outline.nix
@@ -117,13 +117,14 @@ in
     storage = lib.mkOption {
       description = lib.mdDoc ''
         To support uploading of images for avatars and document attachments an
-        s3-compatible storage must be provided. AWS S3 is recommended for
+        s3-compatible storage can be provided. AWS S3 is recommended for
         redundancy however if you want to keep all file storage local an
         alternative such as [minio](https://github.com/minio/minio)
         can be used.
+        Local filesystem storage can also be used.
 
-        A more detailed guide on setting up S3 is available
-        [here](https://wiki.generaloutline.com/share/125de1cc-9ff6-424b-8415-0d58c809a40f).
+        A more detailed guide on setting up storage is available
+        [here](https://docs.getoutline.com/s/hosting/doc/file-storage-N4M0T6Ypu7).
       '';
       example = lib.literalExpression ''
         {
@@ -136,6 +137,19 @@ in
       '';
       type = lib.types.submodule {
         options = {
+          storageType = lib.mkOption {
+            type = lib.types.enum [ "local" "s3" ];
+            description = lib.mdDoc "File storage type, it can be local or s3.";
+            default = "s3";
+          };
+          localRootDir = lib.mkOption {
+            type = lib.types.str;
+            description = lib.mdDoc ''
+              If `storageType` is `local`, this sets the parent directory
+              under which all attachments/images go.
+            '';
+            default = "/var/lib/outline/data";
+          };
           accessKey = lib.mkOption {
             type = lib.types.str;
             description = lib.mdDoc "S3 access key.";
@@ -557,7 +571,10 @@ in
     systemd.tmpfiles.rules = [
       "f ${cfg.secretKeyFile} 0600 ${cfg.user} ${cfg.group} -"
       "f ${cfg.utilsSecretFile} 0600 ${cfg.user} ${cfg.group} -"
-      "f ${cfg.storage.secretKeyFile} 0600 ${cfg.user} ${cfg.group} -"
+      (if (cfg.storage.storageType == "s3") then
+        "f ${cfg.storage.secretKeyFile} 0600 ${cfg.user} ${cfg.group} -"
+      else
+        "d ${cfg.storage.localRootDir} 0700 ${cfg.user} ${cfg.group} - -")
     ];
 
     services.postgresql = lib.mkIf (cfg.databaseUrl == "local") {
@@ -599,14 +616,6 @@ in
           URL = cfg.publicUrl;
           PORT = builtins.toString cfg.port;
 
-          AWS_ACCESS_KEY_ID = cfg.storage.accessKey;
-          AWS_REGION = cfg.storage.region;
-          AWS_S3_UPLOAD_BUCKET_URL = cfg.storage.uploadBucketUrl;
-          AWS_S3_UPLOAD_BUCKET_NAME = cfg.storage.uploadBucketName;
-          AWS_S3_UPLOAD_MAX_SIZE = builtins.toString cfg.storage.uploadMaxSize;
-          AWS_S3_FORCE_PATH_STYLE = builtins.toString cfg.storage.forcePathStyle;
-          AWS_S3_ACL = cfg.storage.acl;
-
           CDN_URL = cfg.cdnUrl;
           FORCE_HTTPS = builtins.toString cfg.forceHttps;
           ENABLE_UPDATES = builtins.toString cfg.enableUpdateCheck;
@@ -622,8 +631,21 @@ in
           RATE_LIMITER_ENABLED = builtins.toString cfg.rateLimiter.enable;
           RATE_LIMITER_REQUESTS = builtins.toString cfg.rateLimiter.requests;
           RATE_LIMITER_DURATION_WINDOW = builtins.toString cfg.rateLimiter.durationWindow;
+
+          FILE_STORAGE = cfg.storage.storageType;
+          FILE_STORAGE_UPLOAD_MAX_SIZE = builtins.toString cfg.storage.uploadMaxSize;
+          FILE_STORAGE_LOCAL_ROOT_DIR = cfg.storage.localRootDir;
         }
 
+        (lib.mkIf (cfg.storage.storageType == "s3") {
+          AWS_ACCESS_KEY_ID = cfg.storage.accessKey;
+          AWS_REGION = cfg.storage.region;
+          AWS_S3_UPLOAD_BUCKET_URL = cfg.storage.uploadBucketUrl;
+          AWS_S3_UPLOAD_BUCKET_NAME = cfg.storage.uploadBucketName;
+          AWS_S3_FORCE_PATH_STYLE = builtins.toString cfg.storage.forcePathStyle;
+          AWS_S3_ACL = cfg.storage.acl;
+        })
+
         (lib.mkIf (cfg.slackAuthentication != null) {
           SLACK_CLIENT_ID = cfg.slackAuthentication.clientId;
         })
@@ -676,7 +698,9 @@ in
       script = ''
         export SECRET_KEY="$(head -n1 ${lib.escapeShellArg cfg.secretKeyFile})"
         export UTILS_SECRET="$(head -n1 ${lib.escapeShellArg cfg.utilsSecretFile})"
-        export AWS_SECRET_ACCESS_KEY="$(head -n1 ${lib.escapeShellArg cfg.storage.secretKeyFile})"
+        ${lib.optionalString (cfg.storage.storageType == "s3") ''
+          export AWS_SECRET_ACCESS_KEY="$(head -n1 ${lib.escapeShellArg cfg.storage.secretKeyFile})"
+        ''}
         ${lib.optionalString (cfg.slackAuthentication != null) ''
           export SLACK_CLIENT_SECRET="$(head -n1 ${lib.escapeShellArg cfg.slackAuthentication.secretFile})"
         ''}
diff --git a/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix b/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
index 641a3644614f..7012df6dffbf 100644
--- a/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/peering-manager.nix
@@ -2,40 +2,15 @@
 
 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}
-    '';
+  pythonFmt = pkgs.formats.pythonVars {};
+  settingsFile = pythonFmt.generate "peering-manager-settings.py" cfg.settings;
+  extraConfigFile = pkgs.writeTextFile {
+    name = "peering-manager-extraConfig.py";
+    text = cfg.extraConfig;
   };
+  configFile = pkgs.concatText "configuration.py" [ settingsFile extraConfigFile ];
+
   pkg = (pkgs.peering-manager.overrideAttrs (old: {
     postInstall = ''
       ln -s ${configFile} $out/opt/peering-manager/peering_manager/configuration.py
@@ -60,7 +35,15 @@ in {
         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.
+        See this [example](https://github.com/peering-manager/contrib/blob/main/nginx.conf on how to configure this.
+      '';
+    };
+
+    enableScheduledTasks = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Set up [scheduled tasks](https://peering-manager.readthedocs.io/en/stable/setup/8-scheduled-tasks/)
       '';
     };
 
@@ -106,6 +89,30 @@ in {
       '';
     };
 
+    settings = lib.mkOption {
+      description = lib.mdDoc ''
+        Configuration options to set in `configuration.py`.
+        See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options.
+      '';
+
+      default = { };
+
+      type = lib.types.submodule {
+        freeformType = pythonFmt.type;
+
+        options = {
+          ALLOWED_HOSTS = lib.mkOption {
+            type = with lib.types; listOf str;
+            default = ["*"];
+            description = lib.mdDoc ''
+              A list of valid fully-qualified domain names (FQDNs) and/or IP
+              addresses that can be used to reach the peering manager service.
+            '';
+          };
+        };
+      };
+    };
+
     extraConfig = mkOption {
       type = types.lines;
       default = "";
@@ -135,7 +142,39 @@ in {
   };
 
   config = lib.mkIf cfg.enable {
-    services.peering-manager.plugins = lib.mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
+    services.peering-manager = {
+      settings = {
+        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;
+          };
+        };
+      };
+
+      extraConfig = ''
+        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()
+      '';
+
+      plugins = lib.mkIf cfg.enableLdap (ps: [ ps.django-auth-ldap ]);
+    };
 
     system.build.peeringManagerPkg = pkg;
 
@@ -163,32 +202,30 @@ in {
     };
 
     systemd.services = let
-      defaultServiceConfig = {
-        WorkingDirectory = "/var/lib/peering-manager";
-        User = "peering-manager";
-        Group = "peering-manager";
-        StateDirectory = "peering-manager";
-        StateDirectoryMode = "0750";
-        Restart = "on-failure";
+      defaults = {
+        environment = {
+          PYTHONPATH = pkg.pythonPath;
+        };
+        serviceConfig = {
+          WorkingDirectory = "/var/lib/peering-manager";
+          User = "peering-manager";
+          Group = "peering-manager";
+          StateDirectory = "peering-manager";
+          StateDirectoryMode = "0750";
+          Restart = "on-failure";
+        };
       };
     in {
-      peering-manager-migration = {
+      peering-manager-migration = lib.recursiveUpdate defaults {
         description = "Peering Manager migrations";
         wantedBy = [ "peering-manager.target" ];
-
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
-        };
-
-        serviceConfig = defaultServiceConfig // {
+        serviceConfig = {
           Type = "oneshot";
-          ExecStart = ''
-            ${pkg}/bin/peering-manager migrate
-          '';
+          ExecStart = "${pkg}/bin/peering-manager migrate";
         };
       };
 
-      peering-manager = {
+      peering-manager = lib.recursiveUpdate defaults {
         description = "Peering Manager WSGI Service";
         wantedBy = [ "peering-manager.target" ];
         after = [ "peering-manager-migration.service" ];
@@ -197,11 +234,7 @@ in {
           ${pkg}/bin/peering-manager remove_stale_contenttypes --no-input
         '';
 
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
-        };
-
-        serviceConfig = defaultServiceConfig // {
+        serviceConfig = {
           ExecStart = ''
             ${pkg.python.pkgs.gunicorn}/bin/gunicorn peering_manager.wsgi \
               --bind ${cfg.listenAddress}:${toString cfg.port} \
@@ -210,45 +243,92 @@ in {
         };
       };
 
-      peering-manager-rq = {
+      peering-manager-rq = lib.recursiveUpdate defaults {
         description = "Peering Manager Request Queue Worker";
         wantedBy = [ "peering-manager.target" ];
         after = [ "peering-manager.service" ];
+        serviceConfig.ExecStart = "${pkg}/bin/peering-manager rqworker high default low";
+      };
 
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
+      peering-manager-housekeeping = lib.recursiveUpdate defaults {
+        description = "Peering Manager housekeeping job";
+        after = [ "peering-manager.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkg}/bin/peering-manager housekeeping";
         };
+      };
 
-        serviceConfig = defaultServiceConfig // {
-          ExecStart = ''
-            ${pkg}/bin/peering-manager rqworker high default low
-          '';
+      peering-manager-peeringdb-sync = lib.recursiveUpdate defaults {
+        description = "PeeringDB sync";
+        after = [ "peering-manager.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkg}/bin/peering-manager peeringdb_sync";
         };
       };
 
-      peering-manager-housekeeping = {
-        description = "Peering Manager housekeeping job";
+      peering-manager-prefix-fetch = lib.recursiveUpdate defaults {
+        description = "Fetch IRR AS-SET prefixes";
         after = [ "peering-manager.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkg}/bin/peering-manager grab_prefixes";
+        };
+      };
 
-        environment = {
-          PYTHONPATH = pkg.pythonPath;
+      peering-manager-configuration-deployment = lib.recursiveUpdate defaults {
+        description = "Push configuration to routers";
+        after = [ "peering-manager.service" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkg}/bin/peering-manager configure_routers";
         };
+      };
 
-        serviceConfig = defaultServiceConfig // {
+      peering-manager-session-poll = lib.recursiveUpdate defaults {
+        description = "Poll peering sessions from routers";
+        after = [ "peering-manager.service" ];
+        serviceConfig = {
           Type = "oneshot";
-          ExecStart = ''
-            ${pkg}/bin/peering-manager housekeeping
-          '';
+          ExecStart = "${pkg}/bin/peering-manager poll_bgp_sessions --all";
         };
       };
     };
 
-    systemd.timers.peering-manager-housekeeping = {
-      description = "Run Peering Manager housekeeping job";
-      wantedBy = [ "timers.target" ];
+    systemd.timers = {
+      peering-manager-housekeeping = {
+        description = "Run Peering Manager housekeeping job";
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnCalendar = "daily";
+      };
+
+      peering-manager-peeringdb-sync = {
+        enable = lib.mkDefault cfg.enableScheduledTasks;
+        description = "Sync PeeringDB at 2:30";
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnCalendar = "02:30:00";
+      };
+
+      peering-manager-prefix-fetch = {
+        enable = lib.mkDefault cfg.enableScheduledTasks;
+        description = "Fetch IRR AS-SET prefixes at 4:30";
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnCalendar = "04:30:00";
+      };
+
+      peering-manager-configuration-deployment = {
+        enable = lib.mkDefault cfg.enableScheduledTasks;
+        description = "Push router configuration every hour 5 minutes before full hour";
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnCalendar = "*:55:00";
+      };
 
-      timerConfig = {
-        OnCalendar = "daily";
+      peering-manager-session-poll = {
+        enable = lib.mkDefault cfg.enableScheduledTasks;
+        description = "Poll peering sessions from routers every hour";
+        wantedBy = [ "timers.target" ];
+        timerConfig.OnCalendar = "*:00:00";
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/phylactery.nix b/nixpkgs/nixos/modules/services/web-apps/phylactery.nix
index 4801bd203b48..723b38ee75d9 100644
--- a/nixpkgs/nixos/modules/services/web-apps/phylactery.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/phylactery.nix
@@ -4,7 +4,7 @@ with lib;
 let cfg = config.services.phylactery;
 in {
   options.services.phylactery = {
-    enable = mkEnableOption (lib.mdDoc "Whether to enable Phylactery server");
+    enable = mkEnableOption (lib.mdDoc "Phylactery server");
 
     host = mkOption {
       type = types.str;
diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
index e2d5cdc4f7c7..e5deb6cf511f 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
@@ -296,6 +296,6 @@ in {
     ];
   };
 
-  meta.maintainers = with maintainers; [ ma27 ];
+  meta.maintainers = with maintainers; [ ];
   meta.doc = ./plausible.md;
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/rimgo.nix b/nixpkgs/nixos/modules/services/web-apps/rimgo.nix
new file mode 100644
index 000000000000..4d35473fda31
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/rimgo.nix
@@ -0,0 +1,107 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  cfg = config.services.rimgo;
+  inherit (lib)
+    mkOption
+    mkEnableOption
+    mkPackageOption
+    mkDefault
+    mkIf
+    types
+    literalExpression
+    optionalString
+    getExe
+    mapAttrs
+  ;
+in
+{
+  options.services.rimgo = {
+    enable = mkEnableOption "rimgo";
+    package = mkPackageOption pkgs "rimgo" { };
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = with types; attrsOf str;
+        options = {
+          PORT = mkOption {
+            type = types.port;
+            default = 3000;
+            example = 69420;
+            description = "The port to use.";
+          };
+          ADDRESS = mkOption {
+            type = types.str;
+            default = "127.0.0.1";
+            example = "1.1.1.1";
+            description = "The address to listen on.";
+          };
+        };
+      };
+      example = literalExpression ''
+        {
+          PORT = 69420;
+          FORCE_WEBP = "1";
+        }
+      '';
+      description = ''
+        Settings for rimgo, see [the official documentation](https://rimgo.codeberg.page/docs/usage/configuration/) for supported options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.rimgo = {
+      description = "Rimgo";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = mapAttrs (_: toString) cfg.settings;
+      serviceConfig = {
+        ExecStart = getExe cfg.package;
+        AmbientCapabilities = mkIf (cfg.settings.PORT < 1024) [
+          "CAP_NET_BIND_SERVICE"
+        ];
+        DynamicUser = true;
+        Restart = "on-failure";
+        RestartSec = "5s";
+        CapabilityBoundingSet = [
+          (optionalString (cfg.settings.PORT < 1024) "CAP_NET_BIND_SERVICE")
+        ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateUsers = cfg.settings.PORT >= 1024;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+        UMask = "0077";
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ quantenzitrone ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
index e861a4185194..9cba5cb4fa9e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
@@ -30,7 +30,7 @@ let
 in {
   options.services.snipe-it = {
 
-    enable = mkEnableOption (lib.mdDoc "A free open source IT asset/license management system");
+    enable = mkEnableOption (lib.mdDoc "snipe-it, a free open source IT asset/license management system");
 
     user = mkOption {
       default = "snipeit";
diff --git a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
index d4c987da1144..5d2e775d4521 100644
--- a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
@@ -34,7 +34,7 @@ let
       # copy additional plugin(s), theme(s) and language(s)
       ${concatStringsSep "\n" (mapAttrsToList (name: theme: "cp -r ${theme} $out/share/wordpress/wp-content/themes/${name}") cfg.themes)}
       ${concatStringsSep "\n" (mapAttrsToList (name: plugin: "cp -r ${plugin} $out/share/wordpress/wp-content/plugins/${name}") cfg.plugins)}
-      ${concatMapStringsSep "\n" (language: "cp -r ${language} $out/share/wordpress/wp-content/languages/") cfg.languages}
+      ${concatMapStringsSep "\n" (language: "cp -r ${language}/* $out/share/wordpress/wp-content/languages/") cfg.languages}
     '';
   };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/writefreely.nix b/nixpkgs/nixos/modules/services/web-apps/writefreely.nix
index a7671aa717f4..f92afa9276e3 100644
--- a/nixpkgs/nixos/modules/services/web-apps/writefreely.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/writefreely.nix
@@ -120,7 +120,7 @@ let
     withConfigFile ''
       query () {
         local result=$(${sqlite}/bin/sqlite3 \
-          '${cfg.stateDir}/${settings.database.filename}'
+          '${cfg.stateDir}/${settings.database.filename}' \
           "$1" \
         )
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/zitadel.nix b/nixpkgs/nixos/modules/services/web-apps/zitadel.nix
new file mode 100644
index 000000000000..99b0a0bc56f6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/zitadel.nix
@@ -0,0 +1,223 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.zitadel;
+
+  settingsFormat = pkgs.formats.yaml { };
+in
+{
+  options.services.zitadel =
+    let inherit (lib) mkEnableOption mkOption mkPackageOption types;
+    in {
+      enable = mkEnableOption "ZITADEL, a user and identity access management platform";
+
+      package = mkPackageOption pkgs "ZITADEL" { default = [ "zitadel" ]; };
+
+      user = mkOption {
+        type = types.str;
+        default = "zitadel";
+        description = "The user to run ZITADEL under.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "zitadel";
+        description = "The group to run ZITADEL under.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to open the port specified in `listenPort` in the firewall.
+        '';
+      };
+
+      masterKeyFile = mkOption {
+        type = types.path;
+        description = ''
+          Path to a file containing a master encryption key for ZITADEL. The
+          key must be 32 bytes.
+        '';
+      };
+
+      tlsMode = mkOption {
+        type = types.enum [ "external" "enabled" "disabled" ];
+        default = "external";
+        example = "enabled";
+        description = ''
+          The TLS mode to use. Options are:
+
+          - enabled: ZITADEL accepts HTTPS connections directly. You must
+            configure TLS if this option is selected.
+          - external: ZITADEL forces HTTPS connections, with TLS terminated at a
+            reverse proxy.
+          - disabled: ZITADEL accepts HTTP connections only. Should only be used
+            for testing.
+        '';
+      };
+
+      settings = mkOption {
+        type = lib.types.submodule {
+          freeformType = settingsFormat.type;
+
+          options = {
+            Port = mkOption {
+              type = types.port;
+              default = 8080;
+              description = "The port that ZITADEL listens on.";
+            };
+
+            TLS = {
+              KeyPath = mkOption {
+                type = types.nullOr types.path;
+                default = null;
+                description = "Path to the TLS certificate private key.";
+              };
+              Key = mkOption {
+                type = types.nullOr types.str;
+                default = null;
+                description = ''
+                  The TLS certificate private key, as a base64-encoded string.
+
+                  Note that the contents of this option will be added to the Nix
+                  store as world-readable plain text. Set
+                  [KeyPath](#opt-services.zitadel.settings.TLS.KeyPath) instead
+                  if this is undesired.
+                '';
+              };
+              CertPath = mkOption {
+                type = types.nullOr types.path;
+                default = null;
+                description = "Path to the TLS certificate.";
+              };
+              Cert = mkOption {
+                type = types.nullOr types.str;
+                default = null;
+                description = ''
+                  The TLS certificate, as a base64-encoded string.
+
+                  Note that the contents of this option will be added to the Nix
+                  store as world-readable plain text. Set
+                  [CertPath](#opt-services.zitadel.settings.TLS.CertPath) instead
+                  if this is undesired.
+                '';
+              };
+            };
+          };
+        };
+        default = { };
+        example = lib.literalExpression ''
+          {
+            Port = 8123;
+            ExternalDomain = "example.com";
+            TLS = {
+              CertPath = "/path/to/cert.pem";
+              KeyPath = "/path/to/cert.key";
+            };
+            Database.cockroach.Host = "db.example.com";
+          };
+        '';
+        description = ''
+          Contents of the runtime configuration file. See
+          https://zitadel.com/docs/self-hosting/manage/configure for more
+          details.
+        '';
+      };
+
+      extraSettingsPaths = mkOption {
+        type = types.listOf types.path;
+        default = [ ];
+        description = ''
+          A list of paths to extra settings files. These will override the
+          values set in [settings](#opt-services.zitadel.settings). Useful if
+          you want to keep sensitive secrets out of the Nix store.
+        '';
+      };
+
+      steps = mkOption {
+        type = settingsFormat.type;
+        default = { };
+        example = lib.literalExpression ''
+          {
+            FirstInstance = {
+              InstanceName = "Example";
+              Org.Human = {
+                UserName = "foobar";
+                FirstName = "Foo";
+                LastName = "Bar";
+              };
+            };
+          }
+        '';
+        description = ''
+          Contents of the database initialization config file. See
+          https://zitadel.com/docs/self-hosting/manage/configure for more
+          details.
+        '';
+      };
+
+      extraStepsPaths = mkOption {
+        type = types.listOf types.path;
+        default = [ ];
+        description = ''
+          A list of paths to extra steps files. These will override the values
+          set in [steps](#opt-services.zitadel.steps). Useful if you want to
+          keep sensitive secrets out of the Nix store.
+        '';
+      };
+    };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [{
+      assertion = cfg.tlsMode == "enabled"
+        -> ((cfg.settings.TLS.Key != null || cfg.settings.TLS.KeyPath != null)
+        && (cfg.settings.TLS.Cert != null || cfg.settings.TLS.CertPath
+        != null));
+      message = ''
+        A TLS certificate and key must be configured in
+        services.zitadel.settings.TLS if services.zitadel.tlsMode is enabled.
+      '';
+    }];
+
+    networking.firewall.allowedTCPPorts =
+      lib.mkIf cfg.openFirewall [ cfg.settings.Port ];
+
+    systemd.services.zitadel =
+      let
+        configFile = settingsFormat.generate "config.yaml" cfg.settings;
+        stepsFile = settingsFormat.generate "steps.yaml" cfg.steps;
+
+        args = lib.cli.toGNUCommandLineShell { } {
+          config = cfg.extraSettingsPaths ++ [ configFile ];
+          steps = cfg.extraStepsPaths ++ [ stepsFile ];
+          masterkeyFile = cfg.masterKeyFile;
+          inherit (cfg) tlsMode;
+        };
+      in
+      {
+        description = "ZITADEL identity access management";
+        path = [ cfg.package ];
+        wantedBy = [ "multi-user.target" ];
+
+        script = ''
+          zitadel start-from-init ${args}
+        '';
+
+        serviceConfig = {
+          Type = "simple";
+          User = cfg.user;
+          Group = cfg.group;
+          Restart = "on-failure";
+        };
+      };
+
+    users.users.zitadel = lib.mkIf (cfg.user == "zitadel") {
+      isSystemUser = true;
+      group = cfg.group;
+    };
+    users.groups.zitadel = lib.mkIf (cfg.group == "zitadel") { };
+  };
+
+  meta.maintainers = with lib.maintainers; [ Sorixelle ];
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/garage.nix b/nixpkgs/nixos/modules/services/web-servers/garage.nix
index 8b5734b5a2ce..731d5315f23a 100644
--- a/nixpkgs/nixos/modules/services/web-servers/garage.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/garage.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.garage;
-  toml = pkgs.formats.toml {};
+  toml = pkgs.formats.toml { };
   configFile = toml.generate "garage.toml" cfg.settings;
 in
 {
@@ -19,12 +19,18 @@ in
     extraEnvironment = mkOption {
       type = types.attrsOf types.str;
       description = lib.mdDoc "Extra environment variables to pass to the Garage server.";
-      default = {};
-      example = { RUST_BACKTRACE="yes"; };
+      default = { };
+      example = { RUST_BACKTRACE = "yes"; };
+    };
+
+    environmentFile = mkOption {
+      type = types.nullOr types.path;
+      description = lib.mdDoc "File containing environment variables to be passed to the Garage server.";
+      default = null;
     };
 
     logLevel = mkOption {
-      type = types.enum (["info" "debug" "trace"]);
+      type = types.enum ([ "info" "debug" "trace" ]);
       default = "info";
       example = "debug";
       description = lib.mdDoc "Garage log level, see <https://garagehq.deuxfleurs.fr/documentation/quick-start/#launching-the-garage-server> for examples.";
@@ -59,12 +65,8 @@ in
     };
 
     package = mkOption {
-      # TODO: when 23.05 is released and if Garage 0.9 is the default, put a stateVersion check.
-      default = if versionAtLeast config.system.stateVersion "23.05" then pkgs.garage_0_8
-                else pkgs.garage_0_7;
-      defaultText = literalExpression "pkgs.garage_0_7";
       type = types.package;
-      description = lib.mdDoc "Garage package to use, if you are upgrading from a major version, please read NixOS and Garage release notes for upgrade instructions.";
+      description = lib.mdDoc "Garage package to use, needs to be set explicitly. If you are upgrading from a major version, please read NixOS and Garage release notes for upgrade instructions.";
     };
   };
 
@@ -80,7 +82,7 @@ in
       after = [ "network.target" "network-online.target" ];
       wants = [ "network.target" "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
-      restartTriggers = [ configFile ];
+      restartTriggers = [ configFile ] ++ (lib.optional (cfg.environmentFile != null) cfg.environmentFile);
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/garage server";
 
@@ -88,6 +90,7 @@ in
         DynamicUser = lib.mkDefault true;
         ProtectHome = true;
         NoNewPrivileges = true;
+        EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
       };
       environment = {
         RUST_LOG = lib.mkDefault "garage=${cfg.logLevel}";
diff --git a/nixpkgs/nixos/modules/services/web-servers/keter/default.nix b/nixpkgs/nixos/modules/services/web-servers/keter/default.nix
index 3916c486475d..0cd9c30cea14 100644
--- a/nixpkgs/nixos/modules/services/web-servers/keter/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/keter/default.nix
@@ -16,7 +16,7 @@ in
   options.services.keter = {
     enable = lib.mkEnableOption (lib.mdDoc ''keter, a web app deployment manager.
 Note that this module only support loading of webapps:
-Keep an old app running and swap the ports when the new one is booted.
+Keep an old app running and swap the ports when the new one is booted
 '');
 
     root = lib.mkOption {
diff --git a/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix
index 0438e12e7da8..729a633a36cc 100644
--- a/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/lighttpd/default.nix
@@ -253,6 +253,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig.ExecStart = "${cfg.package}/sbin/lighttpd -D -f ${configFile}";
+      serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR1 $MAINPID";
       # SIGINT => graceful shutdown
       serviceConfig.KillSignal = "SIGINT";
     };
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
index 7a7fb4061eea..955d6e19064e 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -146,6 +146,10 @@ let
     error_log ${cfg.logError};
     daemon off;
 
+    ${optionalString cfg.enableQuicBPF ''
+      quic_bpf on;
+    ''}
+
     ${cfg.config}
 
     ${optionalString (cfg.eventsConfig != "" || cfg.config == "") ''
@@ -783,6 +787,19 @@ in
         '';
       };
 
+      enableQuicBPF = mkOption {
+        default = false;
+        type = types.bool;
+        description = lib.mdDoc ''
+          Enables routing of QUIC packets using eBPF. When enabled, this allows
+          to support QUIC connection migration. The directive is only supported
+          on Linux 5.7+.
+          Note that enabling this option will make nginx run with extended
+          capabilities that are usually limited to processes running as root
+          namely `CAP_SYS_ADMIN` and `CAP_NET_ADMIN`.
+        '';
+      };
+
       user = mkOption {
         type = types.str;
         default = "nginx";
@@ -1126,6 +1143,14 @@ in
       }
 
       {
+        assertion = cfg.package.pname != "nginxQuic" -> !(cfg.enableQuicBPF);
+        message = ''
+          services.nginx.enableQuicBPF requires using nginxQuic package,
+          which can be achieved by setting `services.nginx.package = pkgs.nginxQuic;`.
+        '';
+      }
+
+      {
         assertion = cfg.package.pname != "nginxQuic" -> all (host: !host.quic) (attrValues virtualHosts);
         message = ''
           services.nginx.service.virtualHosts.<name>.quic requires using nginxQuic package,
@@ -1224,8 +1249,8 @@ in
         # New file permissions
         UMask = "0027"; # 0640 / 0750
         # Capabilities
-        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
-        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ] ++ optionals cfg.enableQuicBPF [ "CAP_SYS_ADMIN" "CAP_NET_ADMIN" ];
+        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ] ++ optionals cfg.enableQuicBPF [ "CAP_SYS_ADMIN" "CAP_NET_ADMIN" ];
         # Security
         NoNewPrivileges = true;
         # Sandboxing (sorted by occurrence in https://www.freedesktop.org/software/systemd/man/systemd.exec.html)
@@ -1250,6 +1275,7 @@ in
         # System Call Filtering
         SystemCallArchitectures = "native";
         SystemCallFilter = [ "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" ]
+          ++ optional cfg.enableQuicBPF [ "bpf" ]
           ++ optionals ((cfg.package != pkgs.tengine) && (cfg.package != pkgs.openresty) && (!lib.any (mod: (mod.disableIPC or false)) cfg.package.modules)) [ "~@ipc" ];
       };
     };
@@ -1314,6 +1340,11 @@ in
       nginx.gid = config.ids.gids.nginx;
     };
 
+    # do not delete the default temp directories created upon nginx startup
+    systemd.tmpfiles.rules = [
+      "X /tmp/systemd-private-%b-nginx.service-*/tmp/nginx_*"
+    ];
+
     services.logrotate.settings.nginx = mapAttrs (_: mkDefault) {
       files = "/var/log/nginx/*.log";
       frequency = "weekly";
diff --git a/nixpkgs/nixos/modules/services/web-servers/rustus.nix b/nixpkgs/nixos/modules/services/web-servers/rustus.nix
index 878d790e3666..6d3b2e6a65d9 100644
--- a/nixpkgs/nixos/modules/services/web-servers/rustus.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/rustus.nix
@@ -8,7 +8,7 @@ in
 
   options.services.rustus = {
 
-    enable = mkEnableOption (lib.mdDoc "TUS protocol implementation in Rust.");
+    enable = mkEnableOption (lib.mdDoc "TUS protocol implementation in Rust");
 
     host = mkOption {
       type = types.str;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index bb42c52b69ca..f1e4d9304021 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -221,7 +221,7 @@ in
 
       # Default Fonts
       fonts.packages = with pkgs; [
-        source-code-pro # Default monospace font in 3.32
+        dejavu_fonts # Default monospace font in LMDE 6+
         ubuntu_font_family # required for default theme
       ];
     })
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix
index b2369e2426f8..28d751305892 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/deepin.nix
@@ -15,7 +15,7 @@ in
   options = {
 
     services.xserver.desktopManager.deepin = {
-      enable = mkEnableOption (lib.mdDoc "Enable Deepin desktop manager");
+      enable = mkEnableOption (lib.mdDoc "Deepin desktop manager");
       extraGSettingsOverrides = mkOption {
         default = "";
         type = types.lines;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 1512b5fdf8a0..d4b2a50cb8af 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -90,7 +90,7 @@ in
         };
     };
 
-    environment.etc."X11/xkb".source = xcfg.xkbDir;
+    environment.etc."X11/xkb".source = xcfg.xkb.dir;
 
     fonts.packages = [ pkgs.dejavu_fonts pkgs.ubuntu_font_family ];
 
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
index fecdd86deb86..8b5daf83de1d 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -229,7 +229,7 @@ in
         panelModulePackages = mkOption {
           default = [ pkgs.gnome.gnome-applets ];
           defaultText = literalExpression "[ pkgs.gnome.gnome-applets ]";
-          type = types.listOf types.path;
+          type = types.listOf types.package;
           description = lib.mdDoc ''
             Packages containing modules that should be made available to `gnome-panel` (usually for applets).
 
@@ -282,9 +282,6 @@ in
 
       # Override GSettings schemas
       environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
-
-       # If gnome is installed, build vim for gtk3 too.
-      nixpkgs.config.vim.gui = "gtk3";
     })
 
     (mkIf flashbackEnabled {
@@ -297,8 +294,7 @@ in
           map
             (wm:
               pkgs.gnome.gnome-flashback.mkSessionForWm {
-                inherit (wm) wmName wmLabel wmCommand enableGnomePanel;
-                inherit (cfg.flashback) panelModulePackages;
+                inherit (wm) wmName wmLabel wmCommand;
               }
             ) flashbackWms;
 
@@ -310,10 +306,16 @@ in
         gnome-flashback
       ] ++ map gnome-flashback.mkSystemdTargetForWm flashbackWms;
 
-      # gnome-panel needs these for menu applet
-      environment.sessionVariables.XDG_DATA_DIRS = [ "${pkgs.gnome.gnome-flashback}/share" ];
-      # TODO: switch to sessionVariables (resolve conflict)
-      environment.variables.XDG_CONFIG_DIRS = [ "${pkgs.gnome.gnome-flashback}/etc/xdg" ];
+      environment.systemPackages = with pkgs.gnome; [
+        gnome-flashback
+        (gnome-panel-with-modules.override {
+          panelModulePackages = cfg.flashback.panelModulePackages;
+        })
+      ]
+      # For /share/applications/${wmName}.desktop
+      ++ (map (wm: gnome-flashback.mkWmApplication { inherit (wm) wmName wmLabel wmCommand; }) flashbackWms)
+      # For /share/gnome-session/sessions/gnome-flashback-${wmName}.session
+      ++ (map (wm: gnome-flashback.mkGnomeSession { inherit (wm) wmName wmLabel enableGnomePanel; }) flashbackWms);
     })
 
     (mkIf serviceCfg.core-os-services.enable {
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 282a34f6b011..285d0a181931 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -309,7 +309,7 @@ in
         "/share"
       ];
 
-      environment.etc."X11/xkb".source = xcfg.xkbDir;
+      environment.etc."X11/xkb".source = xcfg.xkb.dir;
 
       environment.sessionVariables = {
         PLASMA_USE_QT_SCALING = mkIf cfg.useQtScaling "1";
@@ -379,7 +379,7 @@ in
       # Update the start menu for each user that is currently logged in
       system.userActivationScripts.plasmaSetup = activationScript;
 
-      nixpkgs.config.firefox.enablePlasmaBrowserIntegration = true;
+      programs.firefox.wrapperConfig.enablePlasmaBrowserIntegration = true;
     })
 
     (mkIf (cfg.kwinrc != {}) {
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix
index 8d6bfa98a7e4..dede7680ecb3 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix
@@ -61,7 +61,7 @@ in
 
     services.xserver.displayManager.lightdm.greeters.gtk.enable = false;
 
-    nixpkgs.config.lightdm-tiny-greeter.conf =
+    services.xserver.displayManager.lightdm.greeter =
     let
       configHeader = ''
         #include <gtk/gtk.h>
@@ -69,13 +69,11 @@ in
         static const char *pass_text = "${cfg.label.pass}";
         static const char *session = "${dmcfg.defaultSession}";
       '';
+      config = optionalString (cfg.extraConfig != "") (configHeader + cfg.extraConfig);
+      package = pkgs.lightdm-tiny-greeter.override { conf = config; };
     in
-      optionalString (cfg.extraConfig != "")
-        (configHeader + cfg.extraConfig);
-
-    services.xserver.displayManager.lightdm.greeter =
       mkDefault {
-        package = pkgs.lightdm-tiny-greeter.xgreeters;
+        package = package.xgreeters;
         name = "lightdm-tiny-greeter";
       };
 
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
index 47e60236eaeb..6ca7a4425f89 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
@@ -33,6 +33,8 @@ let
 
       # Implementation is done via pkgs/applications/display-managers/sddm/sddm-default-session.patch
       DefaultSession = optionalString (dmcfg.defaultSession != null) "${dmcfg.defaultSession}.desktop";
+
+      DisplayServer = if cfg.wayland.enable then "wayland" else "x11";
     };
 
     Theme = {
@@ -62,6 +64,7 @@ let
     Wayland = {
       EnableHiDPI = cfg.enableHidpi;
       SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
+      CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
     };
   } // lib.optionalAttrs dmcfg.autoLogin.enable {
     Autologin = {
@@ -184,6 +187,32 @@ in
           '';
         };
       };
+
+      # Experimental Wayland support
+      wayland = {
+        enable = mkEnableOption "experimental Wayland support";
+
+        compositorCommand = mkOption {
+          type = types.str;
+          internal = true;
+
+          # This is basically the upstream default, but with Weston referenced by full path
+          # and the configuration generated from NixOS options.
+          default = let westonIni = (pkgs.formats.ini {}).generate "weston.ini" {
+              libinput = {
+                enable-tap = xcfg.libinput.mouse.tapping;
+                left-handed = xcfg.libinput.mouse.leftHanded;
+              };
+              keyboard = {
+                keymap_model = xcfg.xkb.model;
+                keymap_layout = xcfg.xkb.layout;
+                keymap_variant = xcfg.xkb.variant;
+                keymap_options = xcfg.xkb.options;
+              };
+            }; in "${pkgs.weston}/bin/weston --shell=fullscreen-shell.so -c ${westonIni}";
+          description = lib.mdDoc "Command used to start the selected compositor";
+        };
+      };
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/x11/extra-layouts.nix b/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
index 1f48713a68dd..3941f50b7550 100644
--- a/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
+++ b/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
@@ -121,11 +121,11 @@ in
     environment.sessionVariables = {
       # runtime override supported by multiple libraries e. g. libxkbcommon
       # https://xkbcommon.org/doc/current/group__include-path.html
-      XKB_CONFIG_ROOT = config.services.xserver.xkbDir;
+      XKB_CONFIG_ROOT = config.services.xserver.xkb.dir;
     };
 
     services.xserver = {
-      xkbDir = "${xkb_patched}/etc/X11/xkb";
+      xkb.dir = "${xkb_patched}/etc/X11/xkb";
       exportConfiguration = config.services.xserver.displayManager.startx.enable
         || config.services.xserver.displayManager.sx.enable;
     };
diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix
index c2e6da4b453b..4a8f2f61caaf 100644
--- a/nixpkgs/nixos/modules/services/x11/xserver.nix
+++ b/nixpkgs/nixos/modules/services/x11/xserver.nix
@@ -175,6 +175,31 @@ in
         "Use services.xserver.fontPath instead of useXFS")
       (mkRemovedOptionModule [ "services" "xserver" "useGlamor" ]
         "Option services.xserver.useGlamor was removed because it is unnecessary. Drivers that uses Glamor will use it automatically.")
+      (lib.mkRenamedOptionModuleWith {
+        sinceRelease = 2311;
+        from = [ "services" "xserver" "layout" ];
+        to = [ "services" "xserver" "xkb" "layout" ];
+      })
+      (lib.mkRenamedOptionModuleWith {
+        sinceRelease = 2311;
+        from = [ "services" "xserver" "xkbModel" ];
+        to = [ "services" "xserver" "xkb" "model" ];
+      })
+      (lib.mkRenamedOptionModuleWith {
+        sinceRelease = 2311;
+        from = [ "services" "xserver" "xkbOptions" ];
+        to = [ "services" "xserver" "xkb" "options" ];
+      })
+      (lib.mkRenamedOptionModuleWith {
+        sinceRelease = 2311;
+        from = [ "services" "xserver" "xkbVariant" ];
+        to = [ "services" "xserver" "xkb" "variant" ];
+      })
+      (lib.mkRenamedOptionModuleWith {
+        sinceRelease = 2311;
+        from = [ "services" "xserver" "xkbDir" ];
+        to = [ "services" "xserver" "xkb" "dir" ];
+      })
     ];
 
 
@@ -339,48 +364,50 @@ in
         '';
       };
 
-      layout = mkOption {
-        type = types.str;
-        default = "us";
-        description = lib.mdDoc ''
-          Keyboard layout, or multiple keyboard layouts separated by commas.
-        '';
-      };
+      xkb = {
+        layout = mkOption {
+          type = types.str;
+          default = "us";
+          description = lib.mdDoc ''
+            X keyboard layout, or multiple keyboard layouts separated by commas.
+          '';
+        };
 
-      xkbModel = mkOption {
-        type = types.str;
-        default = "pc104";
-        example = "presario";
-        description = lib.mdDoc ''
-          Keyboard model.
-        '';
-      };
+        model = mkOption {
+          type = types.str;
+          default = "pc104";
+          example = "presario";
+          description = lib.mdDoc ''
+            X keyboard model.
+          '';
+        };
 
-      xkbOptions = mkOption {
-        type = types.commas;
-        default = "terminate:ctrl_alt_bksp";
-        example = "grp:caps_toggle,grp_led:scroll";
-        description = lib.mdDoc ''
-          X keyboard options; layout switching goes here.
-        '';
-      };
+        options = mkOption {
+          type = types.commas;
+          default = "terminate:ctrl_alt_bksp";
+          example = "grp:caps_toggle,grp_led:scroll";
+          description = lib.mdDoc ''
+            X keyboard options; layout switching goes here.
+          '';
+        };
 
-      xkbVariant = mkOption {
-        type = types.str;
-        default = "";
-        example = "colemak";
-        description = lib.mdDoc ''
-          X keyboard variant.
-        '';
-      };
+        variant = mkOption {
+          type = types.str;
+          default = "";
+          example = "colemak";
+          description = lib.mdDoc ''
+            X keyboard variant.
+          '';
+        };
 
-      xkbDir = mkOption {
-        type = types.path;
-        default = "${pkgs.xkeyboard_config}/etc/X11/xkb";
-        defaultText = literalExpression ''"''${pkgs.xkeyboard_config}/etc/X11/xkb"'';
-        description = lib.mdDoc ''
-          Path used for -xkbdir xserver parameter.
-        '';
+        dir = mkOption {
+          type = types.path;
+          default = "${pkgs.xkeyboard_config}/etc/X11/xkb";
+          defaultText = literalExpression ''"''${pkgs.xkeyboard_config}/etc/X11/xkb"'';
+          description = lib.mdDoc ''
+            Path used for -xkbdir xserver parameter.
+          '';
+        };
       };
 
       config = mkOption {
@@ -667,7 +694,7 @@ in
         {
           "X11/xorg.conf".source = "${configFile}";
           # -xkbdir command line option does not seems to be passed to xkbcomp.
-          "X11/xkb".source = "${cfg.xkbDir}";
+          "X11/xkb".source = "${cfg.xkb.dir}";
         })
       # localectl looks into 00-keyboard.conf
       //{
@@ -675,10 +702,10 @@ in
             Section "InputClass"
               Identifier "Keyboard catchall"
               MatchIsKeyboard "on"
-              Option "XkbModel" "${cfg.xkbModel}"
-              Option "XkbLayout" "${cfg.layout}"
-              Option "XkbOptions" "${cfg.xkbOptions}"
-              Option "XkbVariant" "${cfg.xkbVariant}"
+              Option "XkbModel" "${cfg.xkb.model}"
+              Option "XkbLayout" "${cfg.xkb.layout}"
+              Option "XkbOptions" "${cfg.xkb.options}"
+              Option "XkbVariant" "${cfg.xkb.variant}"
             EndSection
           '';
         }
@@ -759,7 +786,7 @@ in
 
     services.xserver.displayManager.xserverArgs =
       [ "-config ${configFile}"
-        "-xkbdir" "${cfg.xkbDir}"
+        "-xkbdir" "${cfg.xkb.dir}"
       ] ++ optional (cfg.display != null) ":${toString cfg.display}"
         ++ optional (cfg.tty     != null) "vt${toString cfg.tty}"
         ++ optional (cfg.dpi     != null) "-dpi ${toString cfg.dpi}"
@@ -777,14 +804,14 @@ in
       ];
 
     system.checks = singleton (pkgs.runCommand "xkb-validated" {
-      inherit (cfg) xkbModel layout xkbVariant xkbOptions;
+      inherit (cfg.xkb) model layout variant options;
       nativeBuildInputs = with pkgs.buildPackages; [ xkbvalidate ];
       preferLocalBuild = true;
     } ''
       ${optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
         "export XKB_CONFIG_ROOT=${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
       }
-      xkbvalidate "$xkbModel" "$layout" "$xkbVariant" "$xkbOptions"
+      xkbvalidate "$model" "$layout" "$variant" "$options"
       touch "$out"
     '');