about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-09-08 17:57:14 +0000
committerAlyssa Ross <hi@alyssa.is>2021-09-13 11:31:47 +0000
commitee7984efa14902a2ddd820c937457667a4f40c6a (patch)
treec9c1d046733cefe5e21fdd8a52104175d47b2443 /nixpkgs/nixos/modules/services
parentffc9d4ba381da62fd08b361bacd1e71e2a3d934d (diff)
parentb3c692172e5b5241b028a98e1977f9fb12eeaf42 (diff)
downloadnixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.gz
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.bz2
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.lz
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.xz
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.zst
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.zip
Merge commit 'b3c692172e5b5241b028a98e1977f9fb12eeaf42'
Diffstat (limited to 'nixpkgs/nixos/modules/services')
-rw-r--r--nixpkgs/nixos/modules/services/admin/meshcentral.nix53
-rw-r--r--nixpkgs/nixos/modules/services/amqp/rabbitmq.nix40
-rw-r--r--nixpkgs/nixos/modules/services/audio/hqplayerd.nix142
-rw-r--r--nixpkgs/nixos/modules/services/audio/navidrome.nix71
-rw-r--r--nixpkgs/nixos/modules/services/audio/networkaudiod.nix19
-rw-r--r--nixpkgs/nixos/modules/services/audio/roon-bridge.nix14
-rw-r--r--nixpkgs/nixos/modules/services/audio/roon-server.nix14
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgbackup.nix2
-rw-r--r--nixpkgs/nixos/modules/services/backup/postgresql-backup.nix45
-rw-r--r--nixpkgs/nixos/modules/services/backup/sanoid.nix221
-rw-r--r--nixpkgs/nixos/modules/services/backup/syncoid.nix524
-rw-r--r--nixpkgs/nixos/modules/services/backup/znapzend.nix2
-rw-r--r--nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix4
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix54
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix11
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix4
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix2
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix3
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix12
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix8
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/influxdb2.nix53
-rw-r--r--nixpkgs/nixos/modules/services/databases/postgresql.nix3
-rw-r--r--nixpkgs/nixos/modules/services/databases/redis.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/victoriametrics.nix8
-rw-r--r--nixpkgs/nixos/modules/services/desktops/bamf.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/geoclue2.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json46
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json12
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json1
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json1
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/tumbler.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/zeitgeist.nix4
-rw-r--r--nixpkgs/nixos/modules/services/development/distccd.nix155
-rw-r--r--nixpkgs/nixos/modules/services/development/hoogle.nix2
-rw-r--r--nixpkgs/nixos/modules/services/hardware/sane.nix27
-rw-r--r--nixpkgs/nixos/modules/services/mail/dovecot.nix5
-rw-r--r--nixpkgs/nixos/modules/services/mail/nullmailer.nix2
-rw-r--r--nixpkgs/nixos/modules/services/mail/postfix.nix4
-rw-r--r--nixpkgs/nixos/modules/services/mail/postfixadmin.nix199
-rw-r--r--nixpkgs/nixos/modules/services/mail/rspamd.nix5
-rw-r--r--nixpkgs/nixos/modules/services/misc/airsonic.nix23
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitea.nix14
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitlab.nix6
-rw-r--r--nixpkgs/nixos/modules/services/misc/home-assistant.nix3
-rw-r--r--nixpkgs/nixos/modules/services/misc/klipper.nix11
-rw-r--r--nixpkgs/nixos/modules/services/misc/libreddit.nix66
-rw-r--r--nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/matrix-synapse.nix5
-rw-r--r--nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/moonraker.nix135
-rw-r--r--nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix120
-rw-r--r--nixpkgs/nixos/modules/services/misc/nitter.nix351
-rw-r--r--nixpkgs/nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/nzbget.nix54
-rw-r--r--nixpkgs/nixos/modules/services/misc/octoprint.nix3
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless-ng.nix308
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless.nix183
-rw-r--r--nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/uhub.nix238
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/grafana.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/nagios.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix1
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix34
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/thanos.nix2
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix114
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix100
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml65
-rw-r--r--nixpkgs/nixos/modules/services/networking/autossh.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/avahi-daemon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/bird.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/cjdns.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/connman.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/epmd.nix19
-rw-r--r--nixpkgs/nixos/modules/services/networking/firewall.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/i2pd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/iwd.nix32
-rw-r--r--nixpkgs/nixos/modules/services/networking/kea.nix16
-rw-r--r--nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/nats.nix159
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix13
-rw-r--r--nixpkgs/nixos/modules/services/networking/nftables.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/chrony.nix31
-rw-r--r--nixpkgs/nixos/modules/services/networking/nylon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/quicktun.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/shout.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/soju.nix113
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncplay.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix656
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinc.nix7
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix35
-rw-r--r--nixpkgs/nixos/modules/services/networking/v2ray.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/wakeonlan.nix16
-rw-r--r--nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix296
-rw-r--r--nixpkgs/nixos/modules/services/networking/zeronet.nix4
-rw-r--r--nixpkgs/nixos/modules/services/search/elasticsearch.nix20
-rw-r--r--nixpkgs/nixos/modules/services/system/uptimed.nix10
-rw-r--r--nixpkgs/nixos/modules/services/torrent/deluge.nix9
-rw-r--r--nixpkgs/nixos/modules/services/video/replay-sorcery.nix70
-rw-r--r--nixpkgs/nixos/modules/services/wayland/cage.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.xml13
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/fluidd.nix64
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/isso.nix69
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/keycloak.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/miniflux.nix23
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/moodle.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix3
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.xml124
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/node-red.nix148
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/tt-rss.nix181
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix13
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix21
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/caddy/default.nix (renamed from nixpkgs/nixos/modules/services/web-servers/caddy.nix)93
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix28
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/minio.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix43
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix19
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix7
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix3
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix7
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix6
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix2
-rwxr-xr-xnixpkgs/nixos/modules/services/x11/display-managers/set-session.py3
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sx.nix37
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix16
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix41
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/xserver.nix9
141 files changed, 4773 insertions, 1449 deletions
diff --git a/nixpkgs/nixos/modules/services/admin/meshcentral.nix b/nixpkgs/nixos/modules/services/admin/meshcentral.nix
new file mode 100644
index 000000000000..ae7b6edda7d5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/admin/meshcentral.nix
@@ -0,0 +1,53 @@
+{ config, pkgs, lib, ... }:
+let
+  cfg = config.services.meshcentral;
+  configFormat = pkgs.formats.json {};
+  configFile = configFormat.generate "meshcentral-config.json" cfg.settings;
+in with lib; {
+  options.services.meshcentral = with types; {
+    enable = mkEnableOption "MeshCentral computer management server";
+    package = mkOption {
+      description = "MeshCentral package to use. Replacing this may be necessary to add dependencies for extra functionality.";
+      type = types.package;
+      default = pkgs.meshcentral;
+      defaultText = "pkgs.meshcentral";
+    };
+    settings = mkOption {
+      description = ''
+        Settings for MeshCentral. Refer to upstream documentation for details:
+
+        <itemizedlist>
+          <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json">JSON Schema definition</link></para></listitem>
+          <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config.json">simple sample configuration</link></para></listitem>
+          <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config-advanced.json">complex sample configuration</link></para></listitem>
+          <listitem><para><link xlink:href="https://www.meshcommander.com/meshcentral2">Old homepage) with documentation link</link></para></listitem>
+        </itemizedlist>
+      '';
+      type = types.submodule {
+        freeformType = configFormat.type;
+      };
+      example = {
+        settings = {
+          WANonly = true;
+          Cert = "meshcentral.example.com";
+          TlsOffload = "10.0.0.2,fd42::2";
+          Port = 4430;
+        };
+        domains."".certUrl = "https://meshcentral.example.com/";
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    services.meshcentral.settings.settings.autoBackup.backupPath = lib.mkDefault "/var/lib/meshcentral/backups";
+    systemd.services.meshcentral = {
+      wantedBy = ["multi-user.target"];
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/meshcentral --datapath /var/lib/meshcentral --configfile ${configFile}";
+        DynamicUser = true;
+        StateDirectory = "meshcentral";
+        CacheDirectory = "meshcentral";
+      };
+    };
+  };
+  meta.maintainers = [ maintainers.lheckemann ];
+}
diff --git a/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix b/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix
index fc8a1bc3c23c..8fdfda9a66d8 100644
--- a/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix
+++ b/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix
@@ -7,12 +7,13 @@ let
 
   inherit (builtins) concatStringsSep;
 
-  config_file_content = lib.generators.toKeyValue {} cfg.configItems;
+  config_file_content = lib.generators.toKeyValue { } cfg.configItems;
   config_file = pkgs.writeText "rabbitmq.conf" config_file_content;
 
   advanced_config_file = pkgs.writeText "advanced.config" cfg.config;
 
-in {
+in
+{
   ###### interface
   options = {
     services.rabbitmq = {
@@ -79,7 +80,7 @@ in {
       };
 
       configItems = mkOption {
-        default = {};
+        default = { };
         type = types.attrsOf types.str;
         example = literalExample ''
           {
@@ -123,16 +124,38 @@ in {
       };
 
       plugins = mkOption {
-        default = [];
+        default = [ ];
         type = types.listOf types.str;
         description = "The names of plugins to enable";
       };
 
       pluginDirs = mkOption {
-        default = [];
+        default = [ ];
         type = types.listOf types.path;
         description = "The list of directories containing external plugins";
       };
+
+      managementPlugin = mkOption {
+        description = "The options to run the management plugin";
+        type = types.submodule {
+          options = {
+            enable = mkOption {
+              default = false;
+              type = types.bool;
+              description = ''
+                Whether to enable the management plugin
+              '';
+            };
+            port = mkOption {
+              default = 15672;
+              type = types.port;
+              description = ''
+                On which port to run the management plugin
+              '';
+            };
+          };
+        };
+      };
     };
   };
 
@@ -157,8 +180,13 @@ in {
 
     services.rabbitmq.configItems = {
       "listeners.tcp.1" = mkDefault "${cfg.listenAddress}:${toString cfg.port}";
+    } // optionalAttrs cfg.managementPlugin.enable {
+      "management.tcp.port" = toString cfg.managementPlugin.port;
+      "management.tcp.ip" = cfg.listenAddress;
     };
 
+    services.rabbitmq.plugins = optional cfg.managementPlugin.enable "rabbitmq_management";
+
     systemd.services.rabbitmq = {
       description = "RabbitMQ Server";
 
@@ -180,7 +208,7 @@ in {
         RABBITMQ_ENABLED_PLUGINS_FILE = pkgs.writeText "enabled_plugins" ''
           [ ${concatStringsSep "," cfg.plugins} ].
         '';
-      } //  optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
+      } // optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
 
       serviceConfig = {
         ExecStart = "${cfg.package}/sbin/rabbitmq-server";
diff --git a/nixpkgs/nixos/modules/services/audio/hqplayerd.nix b/nixpkgs/nixos/modules/services/audio/hqplayerd.nix
new file mode 100644
index 000000000000..d549ac77e0e5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/hqplayerd.nix
@@ -0,0 +1,142 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.hqplayerd;
+  pkg = pkgs.hqplayerd;
+  # XXX: This is hard-coded in the distributed binary, don't try to change it.
+  stateDir = "/var/lib/hqplayer";
+  configDir = "/etc/hqplayer";
+in
+{
+  options = {
+    services.hqplayerd = {
+      enable = mkEnableOption "HQPlayer Embedded";
+
+      auth = {
+        username = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Username used for HQPlayer's WebUI.
+
+            Without this you will need to manually create the credentials after
+            first start by going to http://your.ip/8088/auth
+          '';
+        };
+
+        password = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Password used for HQPlayer's WebUI.
+
+            Without this you will need to manually create the credentials after
+            first start by going to http://your.ip/8088/auth
+          '';
+        };
+      };
+
+      licenseFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to the HQPlayer license key file.
+
+          Without this, the service will run in trial mode and restart every 30
+          minutes.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Opens ports needed for the WebUI and controller API.
+        '';
+      };
+
+      config = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          HQplayer daemon configuration, written to /etc/hqplayer/hqplayerd.xml.
+
+          Refer to ${pkg}/share/doc/hqplayerd/readme.txt for possible values.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.auth.username != null -> cfg.auth.password != null)
+                 && (cfg.auth.password != null -> cfg.auth.username != null);
+        message = "You must set either both services.hqplayer.auth.username and password, or neither.";
+      }
+    ];
+
+    environment = {
+      etc = {
+        "hqplayer/hqplayerd.xml" = mkIf (cfg.config != null) { source = pkgs.writeText "hqplayerd.xml" cfg.config; };
+        "hqplayer/hqplayerd4-key.xml" = mkIf (cfg.licenseFile != null) { source = cfg.licenseFile; };
+        "modules-load.d/taudio2.conf".source = "${pkg}/etc/modules-load.d/taudio2.conf";
+      };
+      systemPackages = [ pkg ];
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 8088 4321 ];
+    };
+
+    services.udev.packages = [ pkg ];
+
+    systemd = {
+      tmpfiles.rules = [
+        "d ${configDir}      0755 hqplayer hqplayer - -"
+        "d ${stateDir}       0755 hqplayer hqplayer - -"
+        "d ${stateDir}/home  0755 hqplayer hqplayer - -"
+      ];
+
+      packages = [ pkg ];
+
+      services.hqplayerd = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "systemd-tmpfiles-setup.service" ];
+
+        environment.HOME = "${stateDir}/home";
+
+        unitConfig.ConditionPathExists = [ configDir stateDir ];
+
+        restartTriggers = optionals (cfg.config != null) [ config.environment.etc."hqplayer/hqplayerd.xml".source ];
+
+        preStart = ''
+          cp -r "${pkg}/var/lib/hqplayer/web" "${stateDir}"
+          chmod -R u+wX "${stateDir}/web"
+
+          if [ ! -f "${configDir}/hqplayerd.xml" ]; then
+            echo "creating initial config file"
+            install -m 0644 "${pkg}/etc/hqplayer/hqplayerd.xml" "${configDir}/hqplayerd.xml"
+          fi
+        '' + optionalString (cfg.auth.username != null && cfg.auth.password != null) ''
+          ${pkg}/bin/hqplayerd -s ${cfg.auth.username} ${cfg.auth.password}
+        '';
+      };
+    };
+
+    users.groups = {
+      hqplayer.gid = config.ids.gids.hqplayer;
+    };
+
+    users.users = {
+      hqplayer = {
+        description = "hqplayer daemon user";
+        extraGroups = [ "audio" ];
+        group = "hqplayer";
+        uid = config.ids.uids.hqplayer;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/navidrome.nix b/nixpkgs/nixos/modules/services/audio/navidrome.nix
new file mode 100644
index 000000000000..c2fe429f9844
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/navidrome.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.navidrome;
+  settingsFormat = pkgs.formats.json {};
+in {
+  options = {
+    services.navidrome = {
+
+      enable = mkEnableOption pkgs.navidrome.meta.description;
+
+      settings = mkOption rec {
+        type = settingsFormat.type;
+        apply = recursiveUpdate default;
+        default = {
+          Address = "127.0.0.1";
+          Port = 4533;
+        };
+        example = {
+          MusicFolder = "/mnt/music";
+        };
+        description = ''
+          Configuration for Navidrome, see <link xlink:href="https://www.navidrome.org/docs/usage/configuration-options/"/> for supported values.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.navidrome = {
+      description = "Navidrome Media Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.navidrome}/bin/navidrome --configfile ${settingsFormat.generate "navidrome.json" cfg.settings}
+        '';
+        DynamicUser = true;
+        StateDirectory = "navidrome";
+        WorkingDirectory = "/var/lib/navidrome";
+        RuntimeDirectory = "navidrome";
+        RootDirectory = "/run/navidrome";
+        ReadWritePaths = "";
+        BindReadOnlyPaths = [
+          builtins.storeDir
+        ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder;
+        CapabilityBoundingSet = "";
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+        RestrictRealtime = true;
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        UMask = "0066";
+        ProtectHostname = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/networkaudiod.nix b/nixpkgs/nixos/modules/services/audio/networkaudiod.nix
new file mode 100644
index 000000000000..265a4e1d95d6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/networkaudiod.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  name = "networkaudiod";
+  cfg = config.services.networkaudiod;
+in {
+  options = {
+    services.networkaudiod = {
+      enable = mkEnableOption "Networkaudiod (NAA)";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.packages = [ pkgs.networkaudiod ];
+    systemd.services.networkaudiod.wantedBy = [ "multi-user.target" ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
index 85273a2039c3..e08f8a4f9e7a 100644
--- a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
+++ b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
@@ -14,9 +14,6 @@ in {
         default = false;
         description = ''
           Open ports in the firewall for the bridge.
-
-          UDP: 9003
-          TCP: 9100 - 9200
         '';
       };
       user = mkOption {
@@ -54,10 +51,15 @@ in {
     };
 
     networking.firewall = mkIf cfg.openFirewall {
-      allowedTCPPortRanges = [
-        { from = 9100; to = 9200; }
-      ];
+      allowedTCPPortRanges = [{ from = 9100; to = 9200; }];
       allowedUDPPorts = [ 9003 ];
+      extraCommands = ''
+        iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type broadcast -j ACCEPT
+      '';
     };
 
 
diff --git a/nixpkgs/nixos/modules/services/audio/roon-server.nix b/nixpkgs/nixos/modules/services/audio/roon-server.nix
index eceb65044c5b..42da5a100170 100644
--- a/nixpkgs/nixos/modules/services/audio/roon-server.nix
+++ b/nixpkgs/nixos/modules/services/audio/roon-server.nix
@@ -14,9 +14,6 @@ in {
         default = false;
         description = ''
           Open ports in the firewall for the server.
-
-          UDP: 9003
-          TCP: 9100 - 9200
         '';
       };
       user = mkOption {
@@ -54,10 +51,15 @@ in {
     };
 
     networking.firewall = mkIf cfg.openFirewall {
-      allowedTCPPortRanges = [
-        { from = 9100; to = 9200; }
-      ];
+      allowedTCPPortRanges = [{ from = 9100; to = 9200; }];
       allowedUDPPorts = [ 9003 ];
+      extraCommands = ''
+        iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type broadcast -j ACCEPT
+      '';
     };
 
 
diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
index 18fb29fd72a5..ccbc7726392d 100644
--- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
@@ -102,7 +102,7 @@ let
   mkWrapperDrv = {
       original, name, set ? {}
     }:
-    pkgs.runCommandNoCC "${name}-wrapper" {
+    pkgs.runCommand "${name}-wrapper" {
       buildInputs = [ pkgs.makeWrapper ];
     } (with lib; ''
       makeWrapper "${original}" "$out/bin/${name}" \
diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
index f658eb756f7b..bcc135005e16 100644
--- a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
@@ -7,28 +7,49 @@ let
   cfg = config.services.postgresqlBackup;
 
   postgresqlBackupService = db: dumpCmd:
-    {
+    let
+      compressSuffixes = {
+        "none" = "";
+        "gzip" = ".gz";
+        "zstd" = ".zstd";
+      };
+      compressSuffix = getAttr cfg.compression compressSuffixes;
+
+      compressCmd = getAttr cfg.compression {
+        "none" = "cat";
+        "gzip" = "${pkgs.gzip}/bin/gzip -c";
+        "zstd" = "${pkgs.zstd}/bin/zstd -c";
+      };
+
+      mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
+      curFile = mkSqlPath "" compressSuffix;
+      prevFile = mkSqlPath ".prev" compressSuffix;
+      prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes);
+      inProgressFile = mkSqlPath ".in-progress" compressSuffix;
+    in {
       enable = true;
 
       description = "Backup of ${db} database(s)";
 
       requires = [ "postgresql.service" ];
 
-      path = [ pkgs.coreutils pkgs.gzip config.services.postgresql.package ];
+      path = [ pkgs.coreutils config.services.postgresql.package ];
 
       script = ''
         set -e -o pipefail
 
         umask 0077 # ensure backup is only readable by postgres user
 
-        if [ -e ${cfg.location}/${db}.sql.gz ]; then
-          mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz
+        if [ -e ${curFile} ]; then
+          rm -f ${toString prevFiles}
+          mv ${curFile} ${prevFile}
         fi
 
-        ${dumpCmd} | \
-          gzip -c > ${cfg.location}/${db}.in-progress.sql.gz
+        ${dumpCmd} \
+          | ${compressCmd} \
+          > ${inProgressFile}
 
-        mv ${cfg.location}/${db}.in-progress.sql.gz ${cfg.location}/${db}.sql.gz
+        mv ${inProgressFile} ${curFile}
       '';
 
       serviceConfig = {
@@ -87,7 +108,7 @@ in {
         default = "/var/backup/postgresql";
         type = types.path;
         description = ''
-          Location to put the gzipped PostgreSQL database dumps.
+          Path of directory where the PostgreSQL database dumps will be placed.
         '';
       };
 
@@ -101,6 +122,14 @@ in {
           when no databases where specified.
         '';
       };
+
+      compression = mkOption {
+        type = types.enum ["none" "gzip" "zstd"];
+        default = "gzip";
+        description = ''
+          The type of compression to use on the generated database dump.
+        '';
+      };
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/backup/sanoid.nix b/nixpkgs/nixos/modules/services/backup/sanoid.nix
index abc4def1c61f..41d0e2e1df68 100644
--- a/nixpkgs/nixos/modules/services/backup/sanoid.nix
+++ b/nixpkgs/nixos/modules/services/backup/sanoid.nix
@@ -52,7 +52,7 @@ let
     use_template = mkOption {
       description = "Names of the templates to use for this dataset.";
       type = types.listOf (types.enum (attrNames cfg.templates));
-      default = [];
+      default = [ ];
     };
     useTemplate = use_template;
 
@@ -70,116 +70,127 @@ let
     processChildrenOnly = process_children_only;
   };
 
-  # Extract pool names from configured datasets
-  pools = unique (map (d: head (builtins.match "([^/]+).*" d)) (attrNames cfg.datasets));
-
-  configFile = let
-    mkValueString = v:
-      if builtins.isList v then concatStringsSep "," v
-      else generators.mkValueStringDefault {} v;
-
-    mkKeyValue = k: v: if v == null then ""
-      else if k == "processChildrenOnly" then ""
-      else if k == "useTemplate" then ""
-      else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
-  in generators.toINI { inherit mkKeyValue; } cfg.settings;
-
-in {
-
-    # Interface
-
-    options.services.sanoid = {
-      enable = mkEnableOption "Sanoid ZFS snapshotting service";
-
-      interval = mkOption {
-        type = types.str;
-        default = "hourly";
-        example = "daily";
-        description = ''
-          Run sanoid at this interval. The default is to run hourly.
-
-          The format is described in
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>.
-        '';
-      };
+  # Extract unique dataset names
+  datasets = unique (attrNames cfg.datasets);
+
+  # Function to build "zfs allow" and "zfs unallow" commands for the
+  # filesystems we've delegated permissions to.
+  buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
+    # Here we explicitly use the booted system to guarantee the stable API needed by ZFS
+    "-+/run/booted-system/sw/bin/zfs"
+    zfsAction
+    "sanoid"
+    (concatStringsSep "," permissions)
+    dataset
+  ];
+
+  configFile =
+    let
+      mkValueString = v:
+        if builtins.isList v then concatStringsSep "," v
+        else generators.mkValueStringDefault { } v;
+
+      mkKeyValue = k: v:
+        if v == null then ""
+        else if k == "processChildrenOnly" then ""
+        else if k == "useTemplate" then ""
+        else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
+    in
+    generators.toINI { inherit mkKeyValue; } cfg.settings;
+
+in
+{
+
+  # Interface
+
+  options.services.sanoid = {
+    enable = mkEnableOption "Sanoid ZFS snapshotting service";
+
+    interval = mkOption {
+      type = types.str;
+      default = "hourly";
+      example = "daily";
+      description = ''
+        Run sanoid at this interval. The default is to run hourly.
+
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
 
-      datasets = mkOption {
-        type = types.attrsOf (types.submodule ({config, options, ...}: {
-          freeformType = datasetSettingsType;
-          options = commonOptions // datasetOptions;
-          config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or {});
-          config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or {});
-        }));
-        default = {};
-        description = "Datasets to snapshot.";
-      };
+    datasets = mkOption {
+      type = types.attrsOf (types.submodule ({ config, options, ... }: {
+        freeformType = datasetSettingsType;
+        options = commonOptions // datasetOptions;
+        config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or { });
+        config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or { });
+      }));
+      default = { };
+      description = "Datasets to snapshot.";
+    };
 
-      templates = mkOption {
-        type = types.attrsOf (types.submodule {
-          freeformType = datasetSettingsType;
-          options = commonOptions;
-        });
-        default = {};
-        description = "Templates for datasets.";
-      };
+    templates = mkOption {
+      type = types.attrsOf (types.submodule {
+        freeformType = datasetSettingsType;
+        options = commonOptions;
+      });
+      default = { };
+      description = "Templates for datasets.";
+    };
 
-      settings = mkOption {
-        type = types.attrsOf datasetSettingsType;
-        description = ''
-          Free-form settings written directly to the config file. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/>
-          for allowed values.
-        '';
-      };
+    settings = mkOption {
+      type = types.attrsOf datasetSettingsType;
+      description = ''
+        Free-form settings written directly to the config file. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/>
+        for allowed values.
+      '';
+    };
 
-      extraArgs = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "--verbose" "--readonly" "--debug" ];
-        description = ''
-          Extra arguments to pass to sanoid. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/>
-          for allowed options.
-        '';
-      };
+    extraArgs = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--verbose" "--readonly" "--debug" ];
+      description = ''
+        Extra arguments to pass to sanoid. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/>
+        for allowed options.
+      '';
     };
+  };
 
-    # Implementation
-
-    config = mkIf cfg.enable {
-      services.sanoid.settings = mkMerge [
-        (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates)
-        (mapAttrs (d: v: v) cfg.datasets)
-      ];
-
-      systemd.services.sanoid = {
-        description = "Sanoid snapshot service";
-        serviceConfig = {
-          ExecStartPre = map (pool: lib.escapeShellArgs [
-            "+/run/booted-system/sw/bin/zfs" "allow"
-            "sanoid" "snapshot,mount,destroy" pool
-          ]) pools;
-          ExecStart = lib.escapeShellArgs ([
-            "${pkgs.sanoid}/bin/sanoid"
-            "--cron"
-            "--configdir" (pkgs.writeTextDir "sanoid.conf" configFile)
-          ] ++ cfg.extraArgs);
-          ExecStopPost = map (pool: lib.escapeShellArgs [
-            "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool
-          ]) pools;
-          User = "sanoid";
-          Group = "sanoid";
-          DynamicUser = true;
-          RuntimeDirectory = "sanoid";
-          CacheDirectory = "sanoid";
-        };
-        # Prevents missing snapshots during DST changes
-        environment.TZ = "UTC";
-        after = [ "zfs.target" ];
-        startAt = cfg.interval;
+  # Implementation
+
+  config = mkIf cfg.enable {
+    services.sanoid.settings = mkMerge [
+      (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates)
+      (mapAttrs (d: v: v) cfg.datasets)
+    ];
+
+    systemd.services.sanoid = {
+      description = "Sanoid snapshot service";
+      serviceConfig = {
+        ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets);
+        ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets);
+        ExecStart = lib.escapeShellArgs ([
+          "${pkgs.sanoid}/bin/sanoid"
+          "--cron"
+          "--configdir"
+          (pkgs.writeTextDir "sanoid.conf" configFile)
+        ] ++ cfg.extraArgs);
+        User = "sanoid";
+        Group = "sanoid";
+        DynamicUser = true;
+        RuntimeDirectory = "sanoid";
+        CacheDirectory = "sanoid";
       };
+      # Prevents missing snapshots during DST changes
+      environment.TZ = "UTC";
+      after = [ "zfs.target" ];
+      startAt = cfg.interval;
     };
+  };
 
-    meta.maintainers = with maintainers; [ lopsided98 ];
-  }
+  meta.maintainers = with maintainers; [ lopsided98 ];
+}
diff --git a/nixpkgs/nixos/modules/services/backup/syncoid.nix b/nixpkgs/nixos/modules/services/backup/syncoid.nix
index b764db1f14e4..3ad8d279a36d 100644
--- a/nixpkgs/nixos/modules/services/backup/syncoid.nix
+++ b/nixpkgs/nixos/modules/services/backup/syncoid.nix
@@ -5,212 +5,366 @@ with lib;
 let
   cfg = config.services.syncoid;
 
-  # Extract pool names of local datasets (ones that don't contain "@") that
-  # have the specified type (either "source" or "target")
-  getPools = type: unique (map (d: head (builtins.match "([^/]+).*" d)) (
-    # Filter local datasets
-    filter (d: !hasInfix "@" d)
-    # Get datasets of the specified type
-    (catAttrs type (attrValues cfg.commands))
-  ));
-in {
-
-    # Interface
-
-    options.services.syncoid = {
-      enable = mkEnableOption "Syncoid ZFS synchronization service";
-
-      interval = mkOption {
-        type = types.str;
-        default = "hourly";
-        example = "*-*-* *:15:00";
-        description = ''
-          Run syncoid at this interval. The default is to run hourly.
-
-          The format is described in
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>.
-        '';
-      };
+  # Extract local dasaset names (so no datasets containing "@")
+  localDatasetName = d: optionals (d != null) (
+    let m = builtins.match "([^/@]+[^@]*)" d; in
+    optionals (m != null) m
+  );
 
-      user = mkOption {
-        type = types.str;
-        default = "syncoid";
-        example = "backup";
-        description = ''
-          The user for the service. ZFS privilege delegation will be
-          automatically configured for any local pools used by syncoid if this
-          option is set to a user other than root. The user will be given the
-          "hold" and "send" privileges on any pool that has datasets being sent
-          and the "create", "mount", "receive", and "rollback" privileges on
-          any pool that has datasets being received.
-        '';
-      };
+  # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
+  escapeUnitName = name:
+    lib.concatMapStrings (s: if lib.isList s then "-" else s)
+      (builtins.split "[^a-zA-Z0-9_.\\-]+" name);
 
-      group = mkOption {
-        type = types.str;
-        default = "syncoid";
-        example = "backup";
-        description = "The group for the service.";
-      };
+  # Function to build "zfs allow" and "zfs unallow" commands for the
+  # filesystems we've delegated permissions to.
+  buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
+    # Here we explicitly use the booted system to guarantee the stable API needed by ZFS
+    "-+/run/booted-system/sw/bin/zfs"
+    zfsAction
+    cfg.user
+    (concatStringsSep "," permissions)
+    dataset
+  ];
+in
+{
 
-      sshKey = mkOption {
-        type = types.nullOr types.path;
-        # Prevent key from being copied to store
-        apply = mapNullable toString;
-        default = null;
-        description = ''
-          SSH private key file to use to login to the remote system. Can be
-          overridden in individual commands.
-        '';
-      };
+  # Interface
 
-      commonArgs = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "--no-sync-snap" ];
-        description = ''
-          Arguments to add to every syncoid command, unless disabled for that
-          command. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/>
-          for available options.
-        '';
-      };
+  options.services.syncoid = {
+    enable = mkEnableOption "Syncoid ZFS synchronization service";
 
-      commands = mkOption {
-        type = types.attrsOf (types.submodule ({ name, ... }: {
-          options = {
-            source = mkOption {
-              type = types.str;
-              example = "pool/dataset";
-              description = ''
-                Source ZFS dataset. Can be either local or remote. Defaults to
-                the attribute name.
-              '';
-            };
+    interval = mkOption {
+      type = types.str;
+      default = "hourly";
+      example = "*-*-* *:15:00";
+      description = ''
+        Run syncoid at this interval. The default is to run hourly.
 
-            target = mkOption {
-              type = types.str;
-              example = "user@server:pool/dataset";
-              description = ''
-                Target ZFS dataset. Can be either local
-                (<replaceable>pool/dataset</replaceable>) or remote
-                (<replaceable>user@server:pool/dataset</replaceable>).
-              '';
-            };
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
 
-            recursive = mkOption {
-              type = types.bool;
-              default = false;
-              description = ''
-                Whether to also transfer child datasets.
-              '';
-            };
+    user = mkOption {
+      type = types.str;
+      default = "syncoid";
+      example = "backup";
+      description = ''
+        The user for the service. ZFS privilege delegation will be
+        automatically configured for any local pools used by syncoid if this
+        option is set to a user other than root. The user will be given the
+        "hold" and "send" privileges on any pool that has datasets being sent
+        and the "create", "mount", "receive", and "rollback" privileges on
+        any pool that has datasets being received.
+      '';
+    };
 
-            sshKey = mkOption {
-              type = types.nullOr types.path;
-              # Prevent key from being copied to store
-              apply = mapNullable toString;
-              description = ''
-                SSH private key file to use to login to the remote system.
-                Defaults to <option>services.syncoid.sshKey</option> option.
-              '';
-            };
+    group = mkOption {
+      type = types.str;
+      default = "syncoid";
+      example = "backup";
+      description = "The group for the service.";
+    };
 
-            sendOptions = mkOption {
-              type = types.separatedString " ";
-              default = "";
-              example = "Lc e";
-              description = ''
-                Advanced options to pass to zfs send. Options are specified
-                without their leading dashes and separated by spaces.
-              '';
-            };
+    sshKey = mkOption {
+      type = types.nullOr types.path;
+      # Prevent key from being copied to store
+      apply = mapNullable toString;
+      default = null;
+      description = ''
+        SSH private key file to use to login to the remote system. Can be
+        overridden in individual commands.
+      '';
+    };
 
-            recvOptions = mkOption {
-              type = types.separatedString " ";
-              default = "";
-              example = "ux recordsize o compression=lz4";
-              description = ''
-                Advanced options to pass to zfs recv. Options are specified
-                without their leading dashes and separated by spaces.
-              '';
-            };
+    localSourceAllow = mkOption {
+      type = types.listOf types.str;
+      # Permissions snapshot and destroy are in case --no-sync-snap is not used
+      default = [ "bookmark" "hold" "send" "snapshot" "destroy" ];
+      description = ''
+        Permissions granted for the <option>services.syncoid.user</option> user
+        for local source datasets. See
+        <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+        for available permissions.
+      '';
+    };
 
-            useCommonArgs = mkOption {
-              type = types.bool;
-              default = true;
-              description = ''
-                Whether to add the configured common arguments to this command.
-              '';
-            };
+    localTargetAllow = mkOption {
+      type = types.listOf types.str;
+      default = [ "change-key" "compression" "create" "mount" "mountpoint" "receive" "rollback" ];
+      example = [ "create" "mount" "receive" "rollback" ];
+      description = ''
+        Permissions granted for the <option>services.syncoid.user</option> user
+        for local target datasets. See
+        <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+        for available permissions.
+        Make sure to include the <literal>change-key</literal> permission if you send raw encrypted datasets,
+        the <literal>compression</literal> permission if you send raw compressed datasets, and so on.
+        For remote target datasets you'll have to set your remote user permissions by yourself.
+      '';
+    };
 
-            extraArgs = mkOption {
-              type = types.listOf types.str;
-              default = [];
-              example = [ "--sshport 2222" ];
-              description = "Extra syncoid arguments for this command.";
-            };
+    commonArgs = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--no-sync-snap" ];
+      description = ''
+        Arguments to add to every syncoid command, unless disabled for that
+        command. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/>
+        for available options.
+      '';
+    };
+
+    service = mkOption {
+      type = types.attrs;
+      default = { };
+      description = ''
+        Systemd configuration common to all syncoid services.
+      '';
+    };
+
+    commands = mkOption {
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options = {
+          source = mkOption {
+            type = types.str;
+            example = "pool/dataset";
+            description = ''
+              Source ZFS dataset. Can be either local or remote. Defaults to
+              the attribute name.
+            '';
           };
-          config = {
-            source = mkDefault name;
-            sshKey = mkDefault cfg.sshKey;
+
+          target = mkOption {
+            type = types.str;
+            example = "user@server:pool/dataset";
+            description = ''
+              Target ZFS dataset. Can be either local
+              (<replaceable>pool/dataset</replaceable>) or remote
+              (<replaceable>user@server:pool/dataset</replaceable>).
+            '';
           };
-        }));
-        default = {};
-        example = literalExample ''
-          {
-            "pool/test".target = "root@target:pool/test";
-          }
-        '';
-        description = "Syncoid commands to run.";
-      };
-    };
 
-    # Implementation
+          recursive = mkEnableOption ''the transfer of child datasets'';
+
+          sshKey = mkOption {
+            type = types.nullOr types.path;
+            # Prevent key from being copied to store
+            apply = mapNullable toString;
+            description = ''
+              SSH private key file to use to login to the remote system.
+              Defaults to <option>services.syncoid.sshKey</option> option.
+            '';
+          };
 
-    config = mkIf cfg.enable {
-      users =  {
-        users = mkIf (cfg.user == "syncoid") {
-          syncoid = {
-            group = cfg.group;
-            isSystemUser = true;
+          localSourceAllow = mkOption {
+            type = types.listOf types.str;
+            description = ''
+              Permissions granted for the <option>services.syncoid.user</option> user
+              for local source datasets. See
+              <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+              for available permissions.
+              Defaults to <option>services.syncoid.localSourceAllow</option> option.
+            '';
+          };
+
+          localTargetAllow = mkOption {
+            type = types.listOf types.str;
+            description = ''
+              Permissions granted for the <option>services.syncoid.user</option> user
+              for local target datasets. See
+              <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+              for available permissions.
+              Make sure to include the <literal>change-key</literal> permission if you send raw encrypted datasets,
+              the <literal>compression</literal> permission if you send raw compressed datasets, and so on.
+              For remote target datasets you'll have to set your remote user permissions by yourself.
+            '';
+          };
+
+          sendOptions = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            example = "Lc e";
+            description = ''
+              Advanced options to pass to zfs send. Options are specified
+              without their leading dashes and separated by spaces.
+            '';
+          };
+
+          recvOptions = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            example = "ux recordsize o compression=lz4";
+            description = ''
+              Advanced options to pass to zfs recv. Options are specified
+              without their leading dashes and separated by spaces.
+            '';
+          };
+
+          useCommonArgs = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Whether to add the configured common arguments to this command.
+            '';
+          };
+
+          service = mkOption {
+            type = types.attrs;
+            default = { };
+            description = ''
+              Systemd configuration specific to this syncoid service.
+            '';
+          };
+
+          extraArgs = mkOption {
+            type = types.listOf types.str;
+            default = [ ];
+            example = [ "--sshport 2222" ];
+            description = "Extra syncoid arguments for this command.";
           };
         };
-        groups = mkIf (cfg.group == "syncoid") {
-          syncoid = {};
+        config = {
+          source = mkDefault name;
+          sshKey = mkDefault cfg.sshKey;
+          localSourceAllow = mkDefault cfg.localSourceAllow;
+          localTargetAllow = mkDefault cfg.localTargetAllow;
         };
-      };
+      }));
+      default = { };
+      example = literalExample ''
+        {
+          "pool/test".target = "root@target:pool/test";
+        }
+      '';
+      description = "Syncoid commands to run.";
+    };
+  };
+
+  # Implementation
 
-      systemd.services.syncoid = {
-        description = "Syncoid ZFS synchronization service";
-        script = concatMapStringsSep "\n" (c: lib.escapeShellArgs
-          ([ "${pkgs.sanoid}/bin/syncoid" ]
-            ++ (optionals c.useCommonArgs cfg.commonArgs)
-            ++ (optional c.recursive "-r")
-            ++ (optionals (c.sshKey != null) [ "--sshkey" c.sshKey ])
-            ++ c.extraArgs
-            ++ [ "--sendoptions" c.sendOptions
-                 "--recvoptions" c.recvOptions
-                 "--no-privilege-elevation"
-                 c.source c.target
-               ])) (attrValues cfg.commands);
-        after = [ "zfs.target" ];
-        serviceConfig = {
-          ExecStartPre = let
-            allowCmd = permissions: pool: lib.escapeShellArgs [
-              "+/run/booted-system/sw/bin/zfs" "allow"
-              cfg.user (concatStringsSep "," permissions) pool
-            ];
-          in
-            (map (allowCmd [ "hold" "send" "snapshot" "destroy" ]) (getPools "source")) ++
-            (map (allowCmd [ "create" "mount" "receive" "rollback" ]) (getPools "target"));
-          User = cfg.user;
-          Group = cfg.group;
+  config = mkIf cfg.enable {
+    users = {
+      users = mkIf (cfg.user == "syncoid") {
+        syncoid = {
+          group = cfg.group;
+          isSystemUser = true;
+          # For syncoid to be able to create /var/lib/syncoid/.ssh/
+          # and to use custom ssh_config or known_hosts.
+          home = "/var/lib/syncoid";
+          createHome = false;
         };
-        startAt = cfg.interval;
+      };
+      groups = mkIf (cfg.group == "syncoid") {
+        syncoid = { };
       };
     };
 
-    meta.maintainers = with maintainers; [ lopsided98 ];
-  }
+    systemd.services = mapAttrs'
+      (name: c:
+        nameValuePair "syncoid-${escapeUnitName name}" (mkMerge [
+          {
+            description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}";
+            after = [ "zfs.target" ];
+            startAt = cfg.interval;
+            # syncoid may need zpool to get feature@extensible_dataset
+            path = [ "/run/booted-system/sw/bin/" ];
+            serviceConfig = {
+              ExecStartPre =
+                (map (buildAllowCommand "allow" c.localSourceAllow) (localDatasetName c.source)) ++
+                (map (buildAllowCommand "allow" c.localTargetAllow) (localDatasetName c.target));
+              ExecStopPost =
+                (map (buildAllowCommand "unallow" c.localSourceAllow) (localDatasetName c.source)) ++
+                (map (buildAllowCommand "unallow" c.localTargetAllow) (localDatasetName c.target));
+              ExecStart = lib.escapeShellArgs ([ "${pkgs.sanoid}/bin/syncoid" ]
+                ++ optionals c.useCommonArgs cfg.commonArgs
+                ++ optional c.recursive "-r"
+                ++ optionals (c.sshKey != null) [ "--sshkey" c.sshKey ]
+                ++ c.extraArgs
+                ++ [
+                "--sendoptions"
+                c.sendOptions
+                "--recvoptions"
+                c.recvOptions
+                "--no-privilege-elevation"
+                c.source
+                c.target
+              ]);
+              User = cfg.user;
+              Group = cfg.group;
+              StateDirectory = [ "syncoid" ];
+              StateDirectoryMode = "700";
+              # Prevent SSH control sockets of different syncoid services from interfering
+              PrivateTmp = true;
+              # Permissive access to /proc because syncoid
+              # calls ps(1) to detect ongoing `zfs receive`.
+              ProcSubset = "all";
+              ProtectProc = "default";
+
+              # The following options are only for optimizing:
+              # systemd-analyze security | grep syncoid-'*'
+              AmbientCapabilities = "";
+              CapabilityBoundingSet = "";
+              DeviceAllow = [ "/dev/zfs" ];
+              LockPersonality = true;
+              MemoryDenyWriteExecute = true;
+              NoNewPrivileges = true;
+              PrivateDevices = true;
+              PrivateMounts = true;
+              PrivateNetwork = mkDefault false;
+              PrivateUsers = true;
+              ProtectClock = true;
+              ProtectControlGroups = true;
+              ProtectHome = true;
+              ProtectHostname = true;
+              ProtectKernelLogs = true;
+              ProtectKernelModules = true;
+              ProtectKernelTunables = true;
+              ProtectSystem = "strict";
+              RemoveIPC = true;
+              RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+              RestrictNamespaces = true;
+              RestrictRealtime = true;
+              RestrictSUIDSGID = true;
+              RootDirectory = "/run/syncoid/${escapeUnitName name}";
+              RootDirectoryStartOnly = true;
+              BindPaths = [ "/dev/zfs" ];
+              BindReadOnlyPaths = [ builtins.storeDir "/etc" "/run" "/bin/sh" ];
+              # Avoid useless mounting of RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace.
+              InaccessiblePaths = [ "-+/run/syncoid/${escapeUnitName name}" ];
+              MountAPIVFS = true;
+              # Create RootDirectory= in the host's mount namespace.
+              RuntimeDirectory = [ "syncoid/${escapeUnitName name}" ];
+              RuntimeDirectoryMode = "700";
+              SystemCallFilter = [
+                "@system-service"
+                # Groups in @system-service which do not contain a syscall listed by:
+                # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' syncoid …
+                # awk >perf.syscalls -F "," '$1 > 0 {sub("syscalls:sys_enter_","",$3); print $3}' perf.log
+                # systemd-analyze syscall-filter | grep -v -e '#' | sed -e ':loop; /^[^ ]/N; s/\n //; t loop' | grep $(printf ' -e \\<%s\\>' $(cat perf.syscalls)) | cut -f 1 -d ' '
+                "~@aio"
+                "~@chown"
+                "~@keyring"
+                "~@memlock"
+                "~@privileged"
+                "~@resources"
+                "~@setuid"
+                "~@timer"
+              ];
+              SystemCallArchitectures = "native";
+              # This is for BindPaths= and BindReadOnlyPaths=
+              # to allow traversal of directories they create in RootDirectory=.
+              UMask = "0066";
+            };
+          }
+          cfg.service
+          c.service
+        ]))
+      cfg.commands;
+  };
+
+  meta.maintainers = with maintainers; [ julm lopsided98 ];
+}
diff --git a/nixpkgs/nixos/modules/services/backup/znapzend.nix b/nixpkgs/nixos/modules/services/backup/znapzend.nix
index 0ca71b413cee..debb2a397050 100644
--- a/nixpkgs/nixos/modules/services/backup/znapzend.nix
+++ b/nixpkgs/nixos/modules/services/backup/znapzend.nix
@@ -279,7 +279,7 @@ let
     src_plan = plan;
     tsformat = timestampFormat;
     zend_delay = toString sendDelay;
-  } // fold (a: b: a // b) {} (
+  } // foldr (a: b: a // b) {} (
     map mkDestAttrs (builtins.attrValues destinations)
   );
 
diff --git a/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix b/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix
index be3f40f6bd86..6c2df95886e7 100644
--- a/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix
+++ b/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix
@@ -83,8 +83,8 @@ let
       };
 
       syncmode = mkOption {
-        type = types.enum [ "fast" "full" "light" ];
-        default = "fast";
+        type = types.enum [ "snap" "fast" "full" "light" ];
+        default = "snap";
         description = "Blockchain sync mode.";
       };
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix
index 1378b5ccfb7a..821f1aa54604 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix
@@ -84,6 +84,9 @@ in
         Restart = "on-failure";
         RestartSec = 10;
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
     };
 
     services.kubernetes.addonManager.bootstrapAddons = mkIf isRBACEnabled
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix
index 24d86628b211..8f937a13231b 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix
@@ -60,6 +60,45 @@ in {
         sha256 = "02r440xcdsgi137k5lmmvp0z5w5fmk8g9mysq5pnysq1wl8sj6mw";
       };
     };
+
+    corefile = mkOption {
+      description = ''
+        Custom coredns corefile configuration.
+
+        See: <link xlink:href="https://coredns.io/manual/toc/#configuration"/>.
+      '';
+      type = types.str;
+      default = ''
+        .:${toString ports.dns} {
+          errors
+          health :${toString ports.health}
+          kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa {
+            pods insecure
+            fallthrough in-addr.arpa ip6.arpa
+          }
+          prometheus :${toString ports.metrics}
+          forward . /etc/resolv.conf
+          cache 30
+          loop
+          reload
+          loadbalance
+        }'';
+      defaultText = ''
+        .:${toString ports.dns} {
+          errors
+          health :${toString ports.health}
+          kubernetes ''${config.services.kubernetes.addons.dns.clusterDomain} in-addr.arpa ip6.arpa {
+            pods insecure
+            fallthrough in-addr.arpa ip6.arpa
+          }
+          prometheus :${toString ports.metrics}
+          forward . /etc/resolv.conf
+          cache 30
+          loop
+          reload
+          loadbalance
+        }'';
+    };
   };
 
   config = mkIf cfg.enable {
@@ -151,20 +190,7 @@ in {
           namespace = "kube-system";
         };
         data = {
-          Corefile = ".:${toString ports.dns} {
-            errors
-            health :${toString ports.health}
-            kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa {
-              pods insecure
-              fallthrough in-addr.arpa ip6.arpa
-            }
-            prometheus :${toString ports.metrics}
-            forward . /etc/resolv.conf
-            cache 30
-            loop
-            reload
-            loadbalance
-          }";
+          Corefile = cfg.corefile;
         };
       };
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix
index f1531caa7544..2c89310beb5a 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix
@@ -190,12 +190,6 @@ in
       type = nullOr path;
     };
 
-    kubeletHttps = mkOption {
-      description = "Whether to use https for connections to kubelet.";
-      default = true;
-      type = bool;
-    };
-
     preferredAddressTypes = mkOption {
       description = "List of the preferred NodeAddressTypes to use for kubelet connections.";
       type = nullOr str;
@@ -365,7 +359,6 @@ in
                 "--feature-gates=${concatMapStringsSep "," (feature: "${feature}=true") cfg.featureGates}"} \
               ${optionalString (cfg.basicAuthFile != null)
                 "--basic-auth-file=${cfg.basicAuthFile}"} \
-              --kubelet-https=${boolToString cfg.kubeletHttps} \
               ${optionalString (cfg.kubeletClientCaFile != null)
                 "--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"} \
               ${optionalString (cfg.kubeletClientCertFile != null)
@@ -405,6 +398,10 @@ in
             Restart = "on-failure";
             RestartSec = 5;
           };
+
+          unitConfig = {
+            StartLimitIntervalSec = 0;
+          };
         };
 
         services.etcd = {
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix
index 0c81fa9ae492..7128b5f70b1a 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix
@@ -146,6 +146,9 @@ in
         User = "kubernetes";
         Group = "kubernetes";
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
       path = top.path;
     };
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix
index 3f55719027f0..fecea7a15f3d 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix
@@ -58,7 +58,7 @@ in
     services.kubernetes.addonManager.bootstrapAddons = mkIf ((storageBackend == "kubernetes") && (elem "RBAC" top.apiserver.authorizationMode)) {
 
       flannel-cr = {
-        apiVersion = "rbac.authorization.k8s.io/v1beta1";
+        apiVersion = "rbac.authorization.k8s.io/v1";
         kind = "ClusterRole";
         metadata = { name = "flannel"; };
         rules = [{
@@ -79,7 +79,7 @@ in
       };
 
       flannel-crb = {
-        apiVersion = "rbac.authorization.k8s.io/v1beta1";
+        apiVersion = "rbac.authorization.k8s.io/v1";
         kind = "ClusterRoleBinding";
         metadata = { name = "flannel"; };
         roleRef = {
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix
index fcfcc8435477..08f5cdfdf334 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -337,6 +337,9 @@ in
           '';
           WorkingDirectory = top.dataDir;
         };
+        unitConfig = {
+          StartLimitIntervalSec = 0;
+        };
       };
 
       # Allways include cni plugins
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
index d9311d3e3a04..faf951d81574 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
@@ -189,7 +189,7 @@ in
         # manually paste it in place. Just symlink.
         # otherwise, create the target file, ready for users to insert the token
 
-        mkdir -p $(dirname ${certmgrAPITokenPath})
+        mkdir -p "$(dirname "${certmgrAPITokenPath}")"
         if [ -f "${cfsslAPITokenPath}" ]; then
           ln -fs "${cfsslAPITokenPath}" "${certmgrAPITokenPath}"
         else
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix
index 42729f54643b..a92043d52597 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix
@@ -77,6 +77,9 @@ in
         Restart = "on-failure";
         RestartSec = 5;
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
     };
 
     services.kubernetes.proxy.hostname = with config.networking; mkDefault hostName;
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix
index 454c689759df..1b0c22a11426 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix
@@ -79,6 +79,9 @@ in
         Restart = "on-failure";
         RestartSec = 5;
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
     };
 
     services.kubernetes.pki.certs = {
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix
index 9627b723f8f1..f951c1553235 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix
@@ -98,6 +98,14 @@ in
       '';
       default = [ ];
     };
+
+    package = mkOption {
+      type = types.package;
+      description = ''
+        Which github-runner derivation to use.
+      '';
+      default = pkgs.github-runner;
+    };
   };
 
   config = mkIf cfg.enable {
@@ -131,7 +139,7 @@ in
       ] ++ cfg.extraPackages;
 
       serviceConfig = rec {
-        ExecStart = "${pkgs.github-runner}/bin/runsvc.sh";
+        ExecStart = "${cfg.package}/bin/runsvc.sh";
 
         # Does the following, sequentially:
         # - Copy the current and the previous `tokenFile` to the $RUNTIME_DIRECTORY
@@ -208,7 +216,7 @@ in
               if [[ -z "$empty" ]]; then
                 echo "Configuring GitHub Actions Runner"
                 token=$(< "$RUNTIME_DIRECTORY"/${newConfigTokenFilename})
-                RUNNER_ROOT="$STATE_DIRECTORY" ${pkgs.github-runner}/bin/config.sh \
+                RUNNER_ROOT="$STATE_DIRECTORY" ${cfg.package}/bin/config.sh \
                   --unattended \
                   --work "$RUNTIME_DIRECTORY" \
                   --url ${escapeShellArg cfg.url} \
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 2c6d9530a6b8..15c37c2bc76d 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -339,6 +339,9 @@ in
               <literal>CI_SERVER_URL=&lt;CI server URL&gt;</literal>
 
               <literal>REGISTRATION_TOKEN=&lt;registration secret&gt;</literal>
+
+              WARNING: make sure to use quoted absolute path,
+              or it is going to be copied to Nix Store.
             '';
           };
           registrationFlags = mkOption {
@@ -523,7 +526,10 @@ in
     };
   };
   config = mkIf cfg.enable {
-    warnings = optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`.";
+    warnings = (mapAttrsToList
+      (n: v: "services.gitlab-runner.services.${n}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.")
+      (filterAttrs (n: v: isStorePath v.registrationConfigFile) cfg.services))
+    ++ optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`.";
     environment.systemPackages = [ cfg.package ];
     systemd.services.gitlab-runner = {
       description = "Gitlab Runner";
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
index 889688a26853..98ef1e2c691b 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -61,7 +61,7 @@ in {
 
       port = mkOption {
         default = 8080;
-        type = types.int;
+        type = types.port;
         description = ''
           Specifies port number on which the jenkins HTTP interface listens.
           The default is 8080.
diff --git a/nixpkgs/nixos/modules/services/databases/influxdb2.nix b/nixpkgs/nixos/modules/services/databases/influxdb2.nix
new file mode 100644
index 000000000000..df7bac4261b5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/databases/influxdb2.nix
@@ -0,0 +1,53 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  format = pkgs.formats.json { };
+  cfg = config.services.influxdb2;
+  configFile = format.generate "config.json" cfg.settings;
+in
+{
+  options = {
+    services.influxdb2 = {
+      enable = mkEnableOption "the influxdb2 server";
+      package = mkOption {
+        default = pkgs.influxdb2;
+        defaultText = "pkgs.influxdb2";
+        description = "influxdb2 derivation to use.";
+        type = types.package;
+      };
+      settings = mkOption {
+        default = { };
+        description = "configuration options for influxdb2, see https://docs.influxdata.com/influxdb/v2.0/reference/config-options for details.";
+        type = format.type;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = !(builtins.hasAttr "bolt-path" cfg.settings) && !(builtins.hasAttr "engine-path" cfg.settings);
+      message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd";
+    }];
+    systemd.services.influxdb2 = {
+      description = "InfluxDB is an open-source, distributed, time series database";
+      documentation = [ "https://docs.influxdata.com/influxdb/" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        INFLUXD_CONFIG_PATH = "${configFile}";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/influxd --bolt-path \${STATE_DIRECTORY}/influxd.bolt --engine-path \${STATE_DIRECTORY}/engine";
+        StateDirectory = "influxdb2";
+        DynamicUser = true;
+        CapabilityBoundingSet = "";
+        SystemCallFilter = "@system-service";
+        LimitNOFILE = 65536;
+        KillMode = "control-group";
+        Restart = "on-failure";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ nickcao ];
+}
diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.nix b/nixpkgs/nixos/modules/services/databases/postgresql.nix
index effc9182472e..fd4a195787f3 100644
--- a/nixpkgs/nixos/modules/services/databases/postgresql.nix
+++ b/nixpkgs/nixos/modules/services/databases/postgresql.nix
@@ -293,7 +293,8 @@ in
       # Note: when changing the default, make it conditional on
       # ‘system.stateVersion’ to maintain compatibility with existing
       # systems!
-      mkDefault (if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11
+      mkDefault (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 pkgs.postgresql_9_6
             else throw "postgresql_9_5 was removed, please upgrade your postgresql version.");
 
diff --git a/nixpkgs/nixos/modules/services/databases/redis.nix b/nixpkgs/nixos/modules/services/databases/redis.nix
index 9c0740f28c9b..8873f6d00e0b 100644
--- a/nixpkgs/nixos/modules/services/databases/redis.nix
+++ b/nixpkgs/nixos/modules/services/databases/redis.nix
@@ -272,7 +272,7 @@ in {
       }
       (mkIf (cfg.bind != null) { bind = cfg.bind; })
       (mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; })
-      (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${cfg.slaveOf.port}"; })
+      (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; })
       (mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; })
       (mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; })
     ];
diff --git a/nixpkgs/nixos/modules/services/databases/victoriametrics.nix b/nixpkgs/nixos/modules/services/databases/victoriametrics.nix
index 5b09115bb2fb..9e2c79e61a39 100644
--- a/nixpkgs/nixos/modules/services/databases/victoriametrics.nix
+++ b/nixpkgs/nixos/modules/services/databases/victoriametrics.nix
@@ -53,6 +53,14 @@ let cfg = config.services.victoriametrics; in
               -retentionPeriod ${toString cfg.retentionPeriod} \
               ${lib.escapeShellArgs cfg.extraOptions}
         '';
+        # victoriametrics 1.59 with ~7GB of data seems to eventually panic when merging files and then
+        # begins restart-looping forever. Set LimitNOFILE= to a large number to work around this issue.
+        #
+        # panic: FATAL: unrecoverable error when merging small parts in the partition "/var/lib/victoriametrics/data/small/2021_08":
+        # cannot open source part for merging: cannot open values file in stream mode:
+        # cannot open file "/var/lib/victoriametrics/data/small/2021_08/[...]/values.bin":
+        # open /var/lib/victoriametrics/data/small/2021_08/[...]/values.bin: too many open files
+        LimitNOFILE = 1048576;
       };
       wantedBy = [ "multi-user.target" ];
 
diff --git a/nixpkgs/nixos/modules/services/desktops/bamf.nix b/nixpkgs/nixos/modules/services/desktops/bamf.nix
index 37121c219a37..13de3a44328f 100644
--- a/nixpkgs/nixos/modules/services/desktops/bamf.nix
+++ b/nixpkgs/nixos/modules/services/desktops/bamf.nix
@@ -5,8 +5,8 @@
 with lib;
 
 {
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   ###### interface
diff --git a/nixpkgs/nixos/modules/services/desktops/geoclue2.nix b/nixpkgs/nixos/modules/services/desktops/geoclue2.nix
index e9ec787e5ada..cb5c948ecf78 100644
--- a/nixpkgs/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixpkgs/nixos/modules/services/desktops/geoclue2.nix
@@ -266,5 +266,7 @@ in
       } // mapAttrs' appConfigToINICompatible cfg.appConfig);
   };
 
-  meta.maintainers = with lib.maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
+  };
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
index 7c527b292158..46697ece4483 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
@@ -28,6 +28,12 @@
       ]
     },
     {
+      "name": "BAA 100",
+      "no-features": [
+        "hw-volume"
+      ]
+    },
+    {
       "name": "JBL Endurance RUN BT",
       "no-features": [
         "msbc-alt1",
@@ -191,6 +197,46 @@
       ]
     },
     {
+      "sysname": "Linux",
+      "release": "~^5\\.10\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50)($|[^0-9])"
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.10\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.12\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)($|[^0-9])"
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.12\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.13\\.(1|2)($|[^0-9])"
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.13\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.14\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
       "no-features": []
     }
   ]
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json
index e36e04fffcf2..128178bfa027 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json
@@ -24,5 +24,15 @@
       "name": "libpipewire-module-metadata"
     }
   ],
-  "jack.properties": {}
+  "jack.properties": {},
+  "jack.rules": [
+    {
+      "matches": [
+        {}
+      ],
+      "actions": {
+        "update-props": {}
+      }
+    }
+  ]
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json
index 24906e767d6d..4b4e302af387 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json
@@ -59,6 +59,7 @@
     "with-pulseaudio": [
       "with-audio",
       "bluez5",
+      "bluez5-autoswitch",
       "logind",
       "restore-stream",
       "streams-follow-default"
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json
index 17bbbdef1179..3ed994f11145 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json
@@ -37,5 +37,6 @@
       }
     }
   ],
+  "context.exec": [],
   "stream.properties": {}
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
index dbd6c5d87e1a..bc75aa2717a9 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -194,7 +194,7 @@ in {
     };
 
     environment.sessionVariables.LD_LIBRARY_PATH =
-      lib.optional cfg.jack.enable "/run/current-system/sw/lib/pipewire";
+      lib.optional cfg.jack.enable "${cfg.package.jack}/lib";
 
     # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/464#note_723554
     systemd.user.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1";
diff --git a/nixpkgs/nixos/modules/services/desktops/tumbler.nix b/nixpkgs/nixos/modules/services/desktops/tumbler.nix
index 8d9248cb9839..f5341df2f7a4 100644
--- a/nixpkgs/nixos/modules/services/desktops/tumbler.nix
+++ b/nixpkgs/nixos/modules/services/desktops/tumbler.nix
@@ -18,8 +18,8 @@ in
       "")
   ];
 
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   ###### interface
diff --git a/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix b/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix
index fb0218da3045..297fd1d3ff2f 100644
--- a/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix
+++ b/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix
@@ -6,8 +6,8 @@ with lib;
 
 {
 
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   ###### interface
diff --git a/nixpkgs/nixos/modules/services/development/distccd.nix b/nixpkgs/nixos/modules/services/development/distccd.nix
new file mode 100644
index 000000000000..8790ea08d0c1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/development/distccd.nix
@@ -0,0 +1,155 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.distccd;
+in
+{
+  options = {
+    services.distccd = {
+      enable = mkEnableOption "distccd";
+
+      allowedClients = mkOption {
+        type = types.listOf types.str;
+        default = [ "127.0.0.1" ];
+        example = [ "127.0.0.1" "192.168.0.0/24" "10.0.0.0/24" ];
+        description = ''
+          Client IPs which are allowed to connect to distccd in CIDR notation.
+
+          Anyone who can connect to the distccd server can run arbitrary
+          commands on that system as the distcc user, therefore you should use
+          this judiciously.
+        '';
+      };
+
+      jobTimeout = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Maximum duration, in seconds, of a single compilation request.
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.nullOr (types.enum [ "critical" "error" "warning" "notice" "info" "debug" ]);
+        default = "warning";
+        description = ''
+          Set the minimum severity of error that will be included in the log
+          file. Useful if you only want to see error messages rather than an
+          entry for each connection.
+        '';
+      };
+
+      maxJobs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Maximum number of tasks distccd should execute at any time.
+        '';
+      };
+
+
+      nice = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Niceness of the compilation tasks.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Opens the specified TCP port for distcc.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.distcc;
+        example = "pkgs.distcc";
+        description = ''
+          The distcc package to use.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 3632;
+        description = ''
+          The TCP port which distccd will listen on.
+        '';
+      };
+
+      stats = {
+        enable = mkEnableOption "statistics reporting via HTTP server";
+        port = mkOption {
+          type = types.port;
+          default = 3633;
+          description = ''
+            The TCP port which the distccd statistics HTTP server will listen
+            on.
+          '';
+        };
+      };
+
+      zeroconf = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to register via mDNS/DNS-SD
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ]
+        ++ optionals cfg.stats.enable [ cfg.stats.port ];
+    };
+
+    systemd.services.distccd = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      description = "Distributed C, C++ and Objective-C compiler";
+      documentation = [ "man:distccd(1)" ];
+
+      serviceConfig = {
+        User = "distcc";
+        Group = "distcc";
+        # FIXME: I'd love to get rid of `--enable-tcp-insecure` here, but I'm
+        # not sure how I'm supposed to get distccd to "accept" running a binary
+        # (the compiler) that's outside of /usr/lib.
+        ExecStart = pkgs.writeShellScript "start-distccd" ''
+          export PATH="${pkgs.distccMasquerade}/bin"
+          ${cfg.package}/bin/distccd \
+            --no-detach \
+            --daemon \
+            --enable-tcp-insecure \
+            --port ${toString cfg.port} \
+            ${optionalString (cfg.jobTimeout != null) "--job-lifetime ${toString cfg.jobTimeout}"} \
+            ${optionalString (cfg.logLevel != null) "--log-level ${cfg.logLevel}"} \
+            ${optionalString (cfg.maxJobs != null) "--jobs ${toString cfg.maxJobs}"} \
+            ${optionalString (cfg.nice != null) "--nice ${toString cfg.nice}"} \
+            ${optionalString cfg.stats.enable "--stats"} \
+            ${optionalString cfg.stats.enable "--stats-port ${toString cfg.stats.port}"} \
+            ${optionalString cfg.zeroconf "--zeroconf"} \
+            ${concatMapStrings (c: "--allow ${c} ") cfg.allowedClients}
+        '';
+      };
+    };
+
+    users = {
+      groups.distcc.gid = config.ids.gids.distcc;
+      users.distcc = {
+        description = "distccd user";
+        group = "distcc";
+        uid = config.ids.uids.distcc;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/development/hoogle.nix b/nixpkgs/nixos/modules/services/development/hoogle.nix
index 6d6c88b9b2aa..a6693013b73c 100644
--- a/nixpkgs/nixos/modules/services/development/hoogle.nix
+++ b/nixpkgs/nixos/modules/services/development/hoogle.nix
@@ -17,7 +17,7 @@ in {
     enable = mkEnableOption "Haskell documentation server";
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 8080;
       description = ''
         Port number Hoogle will be listening to.
diff --git a/nixpkgs/nixos/modules/services/hardware/sane.nix b/nixpkgs/nixos/modules/services/hardware/sane.nix
index 8c1bde7b4158..ccf726bd182b 100644
--- a/nixpkgs/nixos/modules/services/hardware/sane.nix
+++ b/nixpkgs/nixos/modules/services/hardware/sane.nix
@@ -4,7 +4,10 @@ with lib;
 
 let
 
-  pkg = pkgs.sane-backends;
+  pkg = pkgs.sane-backends.override {
+    scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable;
+    scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package;
+  };
 
   sanedConf = pkgs.writeTextFile {
     name = "saned.conf";
@@ -98,6 +101,28 @@ in
       '';
     };
 
+    hardware.sane.drivers.scanSnap.enable = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        Whether to enable drivers for the Fujitsu ScanSnap scanners.
+
+        The driver files are unfree and extracted from the Windows driver image.
+      '';
+    };
+
+    hardware.sane.drivers.scanSnap.package = mkOption {
+      type = types.package;
+      default = pkgs.sane-drivers.epjitsu;
+      description = ''
+        Epjitsu driver package to use. Useful if you want to extract the driver files yourself.
+
+        The process is described in the <literal>/etc/sane.d/epjitsu.conf</literal> file in
+        the <literal>sane-backends</literal> package.
+      '';
+    };
+
     services.saned.enable = mkOption {
       type = types.bool;
       default = false;
diff --git a/nixpkgs/nixos/modules/services/mail/dovecot.nix b/nixpkgs/nixos/modules/services/mail/dovecot.nix
index 1ccfb357750b..f3500f46e355 100644
--- a/nixpkgs/nixos/modules/services/mail/dovecot.nix
+++ b/nixpkgs/nixos/modules/services/mail/dovecot.nix
@@ -429,6 +429,7 @@ in
 
       startLimitIntervalSec = 60;  # 1 min
       serviceConfig = {
+        Type = "notify";
         ExecStart = "${dovecotPkg}/sbin/dovecot -F";
         ExecReload = "${dovecotPkg}/sbin/doveadm reload";
         Restart = "on-failure";
@@ -468,10 +469,6 @@ in
 
     assertions = [
       {
-        assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != [];
-        message = "dovecot needs at least one of the IMAP or POP3 listeners enabled";
-      }
-      {
         assertion = (cfg.sslServerCert == null) == (cfg.sslServerKey == null)
         && (cfg.sslCACert != null -> !(cfg.sslServerCert == null || cfg.sslServerKey == null));
         message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto";
diff --git a/nixpkgs/nixos/modules/services/mail/nullmailer.nix b/nixpkgs/nixos/modules/services/mail/nullmailer.nix
index 09874ca0ed75..f9c345669978 100644
--- a/nixpkgs/nixos/modules/services/mail/nullmailer.nix
+++ b/nixpkgs/nixos/modules/services/mail/nullmailer.nix
@@ -220,7 +220,7 @@ with lib;
       after = [ "network.target" ];
 
       preStart = ''
-        mkdir -p /var/spool/nullmailer/{queue,tmp}
+        mkdir -p /var/spool/nullmailer/{queue,tmp,failed}
         rm -f /var/spool/nullmailer/trigger && mkfifo -m 660 /var/spool/nullmailer/trigger
       '';
 
diff --git a/nixpkgs/nixos/modules/services/mail/postfix.nix b/nixpkgs/nixos/modules/services/mail/postfix.nix
index 35639e1bbc83..9b0a5bba2feb 100644
--- a/nixpkgs/nixos/modules/services/mail/postfix.nix
+++ b/nixpkgs/nixos/modules/services/mail/postfix.nix
@@ -194,7 +194,7 @@ let
       # We need to handle the last column specially here, because it's
       # open-ended (command + args).
       lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf);
-    in fold foldLine (genList (const 0) (length labels)) lines;
+    in foldr foldLine (genList (const 0) (length labels)) lines;
 
     # Pad a string with spaces from the right (opposite of fixedWidthString).
     pad = width: str: let
@@ -203,7 +203,7 @@ let
     in str + optionalString (padWidth > 0) padding;
 
     # It's + 2 here, because that's the amount of spacing between columns.
-    fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths;
+    fullWidth = foldr (width: acc: acc + width + 2) 0 maxWidths;
 
     formatLine = line: concatStringsSep "  " (zipListsWith pad maxWidths line);
 
diff --git a/nixpkgs/nixos/modules/services/mail/postfixadmin.nix b/nixpkgs/nixos/modules/services/mail/postfixadmin.nix
new file mode 100644
index 000000000000..f5c8efb3076c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/mail/postfixadmin.nix
@@ -0,0 +1,199 @@
+{ lib, config, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.postfixadmin;
+  fpm = config.services.phpfpm.pools.postfixadmin;
+  localDB = cfg.database.host == "localhost";
+  user = if localDB then cfg.database.username else "nginx";
+in
+{
+  options.services.postfixadmin = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable postfixadmin.
+
+        Also enables nginx virtual host management.
+        Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.&lt;name&gt;</literal>.
+        See <xref linkend="opt-services.nginx.virtualHosts"/> for further information.
+      '';
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      example = "postfixadmin.example.com";
+      description = "Hostname to use for the nginx vhost";
+    };
+
+    adminEmail = mkOption {
+      type = types.str;
+      example = "postmaster@example.com";
+      description = ''
+        Defines the Site Admin's email address.
+        This will be used to send emails from to create mailboxes and
+        from Send Email / Broadcast message pages.
+      '';
+    };
+
+    setupPasswordFile = mkOption {
+      type = types.path;
+      description = ''
+        Password file for the admin.
+        Generate with <literal>php -r "echo password_hash('some password here', PASSWORD_DEFAULT);"</literal>
+      '';
+    };
+
+    database = {
+      username = mkOption {
+        type = types.str;
+        default = "postfixadmin";
+        description = ''
+          Username for the postgresql connection.
+          If <literal>database.host</literal> is set to <literal>localhost</literal>, a unix user and group of the same name will be created as well.
+        '';
+      };
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          Host of the postgresql server. If this is not set to
+          <literal>localhost</literal>, you have to create the
+          postgresql user and database yourself, with appropriate
+          permissions.
+        '';
+      };
+      passwordFile = mkOption {
+        type = types.path;
+        description = "Password file for the postgresql connection. Must be readable by user <literal>nginx</literal>.";
+      };
+      dbname = mkOption {
+        type = types.str;
+        default = "postfixadmin";
+        description = "Name of the postgresql database";
+      };
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = "Extra configuration for the postfixadmin instance, see postfixadmin's config.inc.php for available options.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."postfixadmin/config.local.php".text = ''
+      <?php
+
+      $CONF['setup_password'] = file_get_contents('${cfg.setupPasswordFile}');
+
+      $CONF['database_type'] = 'pgsql';
+      $CONF['database_host'] = ${if localDB then "null" else "'${cfg.database.host}'"};
+      ${optionalString localDB "$CONF['database_user'] = '${cfg.database.username}';"}
+      $CONF['database_password'] = ${if localDB then "'dummy'" else "file_get_contents('${cfg.database.passwordFile}')"};
+      $CONF['database_name'] = '${cfg.database.dbname}';
+      $CONF['configured'] = true;
+
+      ${cfg.extraConfig}
+    '';
+
+    systemd.tmpfiles.rules = [ "d /var/cache/postfixadmin/templates_c 700 ${user} ${user}" ];
+
+    services.nginx = {
+      enable = true;
+      virtualHosts = {
+        ${cfg.hostName} = {
+          forceSSL = mkDefault true;
+          enableACME = mkDefault true;
+          locations."/" = {
+            root = "${pkgs.postfixadmin}/public";
+            index = "index.php";
+            extraConfig = ''
+              location ~* \.php$ {
+                fastcgi_split_path_info ^(.+\.php)(/.+)$;
+                fastcgi_pass unix:${fpm.socket};
+                include ${pkgs.nginx}/conf/fastcgi_params;
+                include ${pkgs.nginx}/conf/fastcgi.conf;
+              }
+            '';
+          };
+        };
+      };
+    };
+
+    services.postgresql = mkIf localDB {
+      enable = true;
+      ensureUsers = [ {
+        name = cfg.database.username;
+      } ];
+    };
+    # The postgresql module doesn't currently support concepts like
+    # objects owners and extensions; for now we tack on what's needed
+    # here.
+    systemd.services.postfixadmin-postgres = let pgsql = config.services.postgresql; in mkIf localDB {
+      after = [ "postgresql.service" ];
+      bindsTo = [ "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [
+        pgsql.package
+        pkgs.util-linux
+      ];
+      script = ''
+        set -eu
+
+        PSQL() {
+            psql --port=${toString pgsql.port} "$@"
+        }
+
+        PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.database.dbname}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.database.dbname}" OWNER "${cfg.database.username}"'
+        current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.database.dbname}'")
+        if [[ "$current_owner" != "${cfg.database.username}" ]]; then
+            PSQL -tAc 'ALTER DATABASE "${cfg.database.dbname}" OWNER TO "${cfg.database.username}"'
+            if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" ]]; then
+                echo "Reassigning ownership of database ${cfg.database.dbname} to user ${cfg.database.username} failed on last boot. Failing..."
+                exit 1
+            fi
+            touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}"
+            PSQL "${cfg.database.dbname}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.database.username}\""
+            rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}"
+        fi
+      '';
+
+      serviceConfig = {
+        User = pgsql.superUser;
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+    };
+
+    users.users.${user} = mkIf localDB {
+      group = user;
+      isSystemUser = true;
+      createHome = false;
+    };
+    users.groups.${user} = mkIf localDB {};
+
+    services.phpfpm.pools.postfixadmin = {
+      user = user;
+      phpPackage = pkgs.php74;
+      phpOptions = ''
+        error_log = 'stderr'
+        log_errors = on
+      '';
+      settings = mapAttrs (name: mkDefault) {
+        "listen.owner" = "nginx";
+        "listen.group" = "nginx";
+        "listen.mode" = "0660";
+        "pm" = "dynamic";
+        "pm.max_children" = 75;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 1;
+        "pm.max_spare_servers" = 20;
+        "pm.max_requests" = 500;
+        "catch_workers_output" = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/mail/rspamd.nix b/nixpkgs/nixos/modules/services/mail/rspamd.nix
index 473ddd52357d..c78f464235aa 100644
--- a/nixpkgs/nixos/modules/services/mail/rspamd.nix
+++ b/nixpkgs/nixos/modules/services/mail/rspamd.nix
@@ -371,8 +371,9 @@ in
     };
     services.postfix.config = mkIf cfg.postfix.enable cfg.postfix.config;
 
-    systemd.services.postfix.serviceConfig.SupplementaryGroups =
-      mkIf cfg.postfix.enable [ postfixCfg.group ];
+    systemd.services.postfix = mkIf cfg.postfix.enable {
+      serviceConfig.SupplementaryGroups = [ postfixCfg.group ];
+    };
 
     # Allow users to run 'rspamc' and 'rspamadm'.
     environment.systemPackages = [ pkgs.rspamd ];
diff --git a/nixpkgs/nixos/modules/services/misc/airsonic.nix b/nixpkgs/nixos/modules/services/misc/airsonic.nix
index a572f1f6d6f5..490f6c5a5c06 100644
--- a/nixpkgs/nixos/modules/services/misc/airsonic.nix
+++ b/nixpkgs/nixos/modules/services/misc/airsonic.nix
@@ -82,6 +82,25 @@ in {
         '';
       };
 
+      jre = mkOption {
+        type = types.package;
+        default = pkgs.jre8;
+        defaultText = literalExample "pkgs.jre8";
+        description = ''
+          JRE package to use.
+
+          Airsonic only supports Java 8, airsonic-advanced requires at least
+          Java 11.
+        '';
+      };
+
+      war = mkOption {
+        type = types.path;
+        default = "${pkgs.airsonic}/webapps/airsonic.war";
+        defaultText = "\${pkgs.airsonic}/webapps/airsonic.war";
+        description = "Airsonic war file to use.";
+      };
+
       jvmOptions = mkOption {
         description = ''
           Extra command line options for the JVM running AirSonic.
@@ -118,7 +137,7 @@ in {
       '';
       serviceConfig = {
         ExecStart = ''
-          ${pkgs.jre8}/bin/java -Xmx${toString cfg.maxMemory}m \
+          ${cfg.jre}/bin/java -Xmx${toString cfg.maxMemory}m \
           -Dairsonic.home=${cfg.home} \
           -Dserver.address=${cfg.listenAddress} \
           -Dserver.port=${toString cfg.port} \
@@ -128,7 +147,7 @@ in {
             "-Dserver.use-forward-headers=true"} \
           ${toString cfg.jvmOptions} \
           -verbose:gc \
-          -jar ${pkgs.airsonic}/webapps/airsonic.war
+          -jar ${cfg.war}
         '';
         Restart = "always";
         User = "airsonic";
diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix
index b6c1ca3e61a9..8322b7c09022 100644
--- a/nixpkgs/nixos/modules/services/misc/gitea.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitea.nix
@@ -522,20 +522,16 @@ in
           (umask 027; gitea_setup)
         ''}
 
+        # run migrations/init the database
+        ${gitea}/bin/gitea migrate
+
         # update all hooks' binary paths
-        HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*")
-        if [ "$HOOKS" ]
-        then
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' $HOOKS
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS
-        fi
+        ${gitea}/bin/gitea admin regenerate hooks
 
         # update command option in authorized_keys
         if [ -r ${cfg.stateDir}/.ssh/authorized_keys ]
         then
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' ${cfg.stateDir}/.ssh/authorized_keys
+          ${gitea}/bin/gitea admin regenerate keys
         fi
       '';
 
diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix
index 1514cc0665df..805deeee0c04 100644
--- a/nixpkgs/nixos/modules/services/misc/gitlab.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix
@@ -117,6 +117,7 @@ let
       shared.path = "${cfg.statePath}/shared";
       gitaly.client_path = "${cfg.packages.gitaly}/bin";
       backup = {
+        gitaly_backup_path = "${cfg.packages.gitaly}/bin/gitaly-backup";
         path = cfg.backup.path;
         keep_time = cfg.backup.keepTime;
       } // (optionalAttrs (cfg.backup.uploadOptions != {}) {
@@ -1299,7 +1300,7 @@ in {
         Restart = "on-failure";
         WorkingDirectory = gitlabEnv.HOME;
         ExecStart =
-          "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse "
+          "${cfg.packages.gitlab-workhorse}/bin/workhorse "
           + "-listenUmask 0 "
           + "-listenNetwork unix "
           + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
@@ -1352,9 +1353,8 @@ in {
         procps
         gnupg
       ];
-
       serviceConfig = {
-        Type = "simple";
+        Type = "notify";
         User = cfg.user;
         Group = cfg.group;
         TimeoutSec = "infinity";
diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
index dcd825bba433..73ec3b9a17a2 100644
--- a/nixpkgs/nixos/modules/services/misc/home-assistant.nix
+++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
@@ -78,7 +78,7 @@ in {
 
     port = mkOption {
       default = 8123;
-      type = types.int;
+      type = types.port;
       description = "The port on which to listen.";
     };
 
@@ -285,6 +285,7 @@ in {
           "alarmdecoder"
           "arduino"
           "blackbird"
+          "deconz"
           "dsmr"
           "edl21"
           "elkm1"
diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix
index 4930648ba8e3..e6b9dd234a9b 100644
--- a/nixpkgs/nixos/modules/services/misc/klipper.nix
+++ b/nixpkgs/nixos/modules/services/misc/klipper.nix
@@ -2,7 +2,13 @@
 with lib;
 let
   cfg = config.services.klipper;
-  format = pkgs.formats.ini { mkKeyValue = generators.mkKeyValueDefault {} ":"; };
+  format = pkgs.formats.ini {
+    # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
+    listToValue = l:
+      if builtins.length l == 1 then generators.mkValueStringDefault {} (head l)
+      else lib.concatMapStrings (s: "\n  ${generators.mkValueStringDefault {} s}") l;
+    mkKeyValue = generators.mkKeyValueDefault {} ":";
+  };
 in
 {
   ##### interface
@@ -24,8 +30,7 @@ in
 
       apiSocket = mkOption {
         type = types.nullOr types.path;
-        default = null;
-        example = "/run/klipper/api";
+        default = "/run/klipper/api";
         description = "Path of the API socket to create.";
       };
 
diff --git a/nixpkgs/nixos/modules/services/misc/libreddit.nix b/nixpkgs/nixos/modules/services/misc/libreddit.nix
new file mode 100644
index 000000000000..77b34a856204
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/libreddit.nix
@@ -0,0 +1,66 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+  let
+    cfg = config.services.libreddit;
+
+    args = concatStringsSep " " ([
+      "--port ${toString cfg.port}"
+      "--address ${cfg.address}"
+    ] ++ optional cfg.redirect "--redirect-https");
+
+in
+{
+  options = {
+    services.libreddit = {
+      enable = mkEnableOption "Private front-end for Reddit";
+
+      address = mkOption {
+        default = "0.0.0.0";
+        example = "127.0.0.1";
+        type =  types.str;
+        description = "The address to listen on";
+      };
+
+      port = mkOption {
+        default = 8080;
+        example = 8000;
+        type = types.port;
+        description = "The port to listen on";
+      };
+
+      redirect = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the redirecting to HTTPS";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for the libreddit web interface";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.libreddit = {
+        description = "Private front-end for Reddit";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          ExecStart = "${pkgs.libreddit}/bin/libreddit ${args}";
+          AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+          Restart = "on-failure";
+          RestartSec = "2s";
+        };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix b/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix
index a0a5973d30f2..02627e51c932 100644
--- a/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix
+++ b/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix
@@ -10,7 +10,7 @@ let
 
   jsonType = (pkgs.formats.json {}).type;
 
-  configFile = pkgs.runCommandNoCC "matrix-appservice-irc.yml" {
+  configFile = pkgs.runCommand "matrix-appservice-irc.yml" {
     # Because this program will be run at build time, we need `nativeBuildInputs`
     nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.pyyaml ps.jsonschema ])) ];
     preferLocalBuild = true;
diff --git a/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix b/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix
index 3c734a948198..e150a1aaaad1 100644
--- a/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix
@@ -221,9 +221,10 @@ in {
         default = config.networking.hostName;
         description = ''
           The domain name of the server, with optional explicit port.
-          This is used by remote servers to connect to this server,
-          e.g. matrix.org, localhost:8080, etc.
+          This is used by remote servers to look up the server address.
           This is also the last part of your UserID.
+
+          The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
         '';
       };
       public_baseurl = mkOption {
diff --git a/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix b/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix
index 0ae5797fea04..717cf7936ead 100644
--- a/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix
+++ b/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix
@@ -128,7 +128,7 @@ in {
         # https://github.com/tulir/mautrix-telegram/issues/584
         [ -f ${settingsFile} ] && rm -f ${settingsFile}
         old_umask=$(umask)
-        umask 0277
+        umask 0177
         ${pkgs.envsubst}/bin/envsubst \
           -o ${settingsFile} \
           -i ${settingsFileUnsubstituted}
diff --git a/nixpkgs/nixos/modules/services/misc/moonraker.nix b/nixpkgs/nixos/modules/services/misc/moonraker.nix
new file mode 100644
index 000000000000..de8668a0c066
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/moonraker.nix
@@ -0,0 +1,135 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  pkg = pkgs.moonraker;
+  cfg = config.services.moonraker;
+  format = pkgs.formats.ini {
+    # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
+    listToValue = l:
+      if builtins.length l == 1 then generators.mkValueStringDefault {} (head l)
+      else lib.concatMapStrings (s: "\n  ${generators.mkValueStringDefault {} s}") l;
+    mkKeyValue = generators.mkKeyValueDefault {} ":";
+  };
+in {
+  options = {
+    services.moonraker = {
+      enable = mkEnableOption "Moonraker, an API web server for Klipper";
+
+      klipperSocket = mkOption {
+        type = types.path;
+        default = config.services.klipper.apiSocket;
+        description = "Path to Klipper's API socket.";
+      };
+
+      stateDir = mkOption {
+        type = types.path;
+        default = "/var/lib/moonraker";
+        description = "The directory containing the Moonraker databases.";
+      };
+
+      configDir = mkOption {
+        type = types.path;
+        default = cfg.stateDir + "/config";
+        description = ''
+          The directory containing client-writable configuration files.
+
+          Clients will be able to edit files in this directory via the API. This directory must be writable.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "moonraker";
+        description = "User account under which Moonraker runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "moonraker";
+        description = "Group account under which Moonraker runs.";
+      };
+
+      address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        description = "The IP or host to listen on.";
+      };
+
+      port = mkOption {
+        type = types.ints.unsigned;
+        default = 7125;
+        description = "The port to listen on.";
+      };
+
+      settings = mkOption {
+        type = format.type;
+        default = { };
+        example = {
+          authorization = {
+            trusted_clients = [ "10.0.0.0/24" ];
+            cors_domains = [ "https://app.fluidd.xyz" ];
+          };
+        };
+        description = ''
+          Configuration for Moonraker. See the <link xlink:href="https://moonraker.readthedocs.io/en/latest/configuration/">documentation</link>
+          for supported values.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    warnings = optional (cfg.settings ? update_manager)
+      ''Enabling update_manager is not supported on NixOS and will lead to non-removable warnings in some clients.'';
+
+    users.users = optionalAttrs (cfg.user == "moonraker") {
+      moonraker = {
+        group = cfg.group;
+        uid = config.ids.uids.moonraker;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "moonraker") {
+      moonraker.gid = config.ids.gids.moonraker;
+    };
+
+    environment.etc."moonraker.cfg".source = let
+      forcedConfig = {
+        server = {
+          host = cfg.address;
+          port = cfg.port;
+          klippy_uds_address = cfg.klipperSocket;
+          config_path = cfg.configDir;
+          database_path = "${cfg.stateDir}/database";
+        };
+      };
+      fullConfig = recursiveUpdate cfg.settings forcedConfig;
+    in format.generate "moonraker.cfg" fullConfig;
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
+      "d '${cfg.configDir}' - ${cfg.user} ${cfg.group} - -"
+    ];
+
+    systemd.services.moonraker = {
+      description = "Moonraker, an API web server for Klipper";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ]
+        ++ optional config.services.klipper.enable "klipper.service";
+
+      # Moonraker really wants its own config to be writable...
+      script = ''
+        cp /etc/moonraker.cfg ${cfg.configDir}/moonraker-temp.cfg
+        chmod u+w ${cfg.configDir}/moonraker-temp.cfg
+        exec ${pkg}/bin/moonraker -c ${cfg.configDir}/moonraker-temp.cfg
+      '';
+
+      serviceConfig = {
+        WorkingDirectory = cfg.stateDir;
+        Group = cfg.group;
+        User = cfg.user;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix b/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix
new file mode 100644
index 000000000000..11116f7c3489
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix
@@ -0,0 +1,120 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  dataDir = "/var/lib/mx-puppet-discord";
+  registrationFile = "${dataDir}/discord-registration.yaml";
+  cfg = config.services.mx-puppet-discord;
+  settingsFormat = pkgs.formats.json {};
+  settingsFile = settingsFormat.generate "mx-puppet-discord-config.json" cfg.settings;
+
+in {
+  options = {
+    services.mx-puppet-discord = {
+      enable = mkEnableOption ''
+        mx-puppet-discord is a discord puppeting bridge for matrix.
+        It handles bridging private and group DMs, as well as Guilds (servers)
+      '';
+
+      settings = mkOption rec {
+        apply = recursiveUpdate default;
+        inherit (settingsFormat) type;
+        default = {
+          bridge.port = 8434;
+          presence = {
+            enabled = true;
+            interval = 500;
+          };
+          provisioning.whitelist = [ ];
+          relay.whitelist = [ ];
+
+          # variables are preceded by a colon.
+          namePatterns = {
+            user = ":name";
+            userOverride = ":displayname";
+            room = ":name";
+            group = ":name";
+          };
+
+          #defaults to sqlite but can be configured to use postgresql with
+          #connstring
+          database.filename = "${dataDir}/mx-puppet-discord/database.db";
+          logging = {
+            console = "info";
+            lineDateFormat = "MMM-D HH:mm:ss.SSS";
+          };
+        };
+        example = literalExample ''
+          {
+            bridge = {
+              bindAddress = "localhost";
+              domain = "example.com";
+              homeserverUrl = "https://example.com";
+            };
+
+            provisioning.whitelist = [ "@admin:example.com" ];
+            relay.whitelist = [ "@.*:example.com" ];
+          }
+        '';
+        description = ''
+          <filename>config.yaml</filename> configuration as a Nix attribute set.
+          Configuration options should match those described in
+          <link xlink:href="https://github.com/matrix-discord/mx-puppet-discord/blob/master/sample.config.yaml">
+          sample.config.yaml</link>.
+        '';
+      };
+      serviceDependencies = mkOption {
+        type = with types; listOf str;
+        default = optional config.services.matrix-synapse.enable "matrix-synapse.service";
+        description = ''
+          List of Systemd services to require and wait for when starting the application service.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.mx-puppet-discord = {
+      description = ''
+        mx-puppet-discord is a discord puppeting bridge for matrix.
+        It handles bridging private and group DMs, as well as Guilds (servers).
+      '';
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
+      preStart = ''
+        # generate the appservice's registration file if absent
+        if [ ! -f '${registrationFile}' ]; then
+          ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -r -c ${settingsFile} \
+          -f ${registrationFile}
+        fi
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        Restart = "always";
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        DynamicUser = true;
+        PrivateTmp = true;
+        WorkingDirectory = pkgs.mx-puppet-discord;
+        StateDirectory = baseNameOf dataDir;
+        UMask = 0027;
+
+        ExecStart = ''
+          ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -c ${settingsFile}
+        '';
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ govanify ];
+}
diff --git a/nixpkgs/nixos/modules/services/misc/nitter.nix b/nixpkgs/nixos/modules/services/misc/nitter.nix
new file mode 100644
index 000000000000..301af76c336a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/nitter.nix
@@ -0,0 +1,351 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nitter;
+  configFile = pkgs.writeText "nitter.conf" ''
+    ${generators.toINI {
+      # String values need to be quoted
+      mkKeyValue = generators.mkKeyValueDefault {
+        mkValueString = v:
+          if isString v then "\"" + (strings.escape ["\""] (toString v)) + "\""
+          else generators.mkValueStringDefault {} v;
+      } " = ";
+    } (lib.recursiveUpdate {
+      Server = cfg.server;
+      Cache = cfg.cache;
+      Config = cfg.config // { hmacKey = "@hmac@"; };
+      Preferences = cfg.preferences;
+    } cfg.settings)}
+  '';
+  # `hmac` is a secret used for cryptographic signing of video URLs.
+  # Generate it on first launch, then copy configuration and replace
+  # `@hmac@` with this value.
+  # We are not using sed as it would leak the value in the command line.
+  preStart = pkgs.writers.writePython3 "nitter-prestart" {} ''
+    import os
+    import secrets
+
+    state_dir = os.environ.get("STATE_DIRECTORY")
+    if not os.path.isfile(f"{state_dir}/hmac"):
+        # Generate hmac on first launch
+        hmac = secrets.token_hex(32)
+        with open(f"{state_dir}/hmac", "w") as f:
+            f.write(hmac)
+    else:
+        # Load previously generated hmac
+        with open(f"{state_dir}/hmac", "r") as f:
+            hmac = f.read()
+
+    configFile = "${configFile}"
+    with open(configFile, "r") as f_in:
+        with open(f"{state_dir}/nitter.conf", "w") as f_out:
+            f_out.write(f_in.read().replace("@hmac@", hmac))
+  '';
+in
+{
+  options = {
+    services.nitter = {
+      enable = mkEnableOption "If enabled, start Nitter.";
+
+      server = {
+        address = mkOption {
+          type =  types.str;
+          default = "0.0.0.0";
+          example = "127.0.0.1";
+          description = "The address to listen on.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8080;
+          example = 8000;
+          description = "The port to listen on.";
+        };
+
+        https = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Set secure attribute on cookies. Keep it disabled to enable cookies when not using HTTPS.";
+        };
+
+        httpMaxConnections = mkOption {
+          type = types.int;
+          default = 100;
+          description = "Maximum number of HTTP connections.";
+        };
+
+        staticDir = mkOption {
+          type = types.path;
+          default = "${pkgs.nitter}/share/nitter/public";
+          defaultText = "\${pkgs.nitter}/share/nitter/public";
+          description = "Path to the static files directory.";
+        };
+
+        title = mkOption {
+          type = types.str;
+          default = "nitter";
+          description = "Title of the instance.";
+        };
+
+        hostname = mkOption {
+          type = types.str;
+          default = "localhost";
+          example = "nitter.net";
+          description = "Hostname of the instance.";
+        };
+      };
+
+      cache = {
+        listMinutes = mkOption {
+          type = types.int;
+          default = 240;
+          description = "How long to cache list info (not the tweets, so keep it high).";
+        };
+
+        rssMinutes = mkOption {
+          type = types.int;
+          default = 10;
+          description = "How long to cache RSS queries.";
+        };
+
+        redisHost = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Redis host.";
+        };
+
+        redisPort = mkOption {
+          type = types.port;
+          default = 6379;
+          description = "Redis port.";
+        };
+
+        redisConnections = mkOption {
+          type = types.int;
+          default = 20;
+          description = "Redis connection pool size.";
+        };
+
+        redisMaxConnections = mkOption {
+          type = types.int;
+          default = 30;
+          description = ''
+            Maximum number of connections to Redis.
+
+            New connections are opened when none are available, but if the
+            pool size goes above this, they are closed when released, do not
+            worry about this unless you receive tons of requests per second.
+          '';
+        };
+      };
+
+      config = {
+        base64Media = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Use base64 encoding for proxied media URLs.";
+        };
+
+        tokenCount = mkOption {
+          type = types.int;
+          default = 10;
+          description = ''
+            Minimum amount of usable tokens.
+
+            Tokens are used to authorize API requests, but they expire after
+            ~1 hour, and have a limit of 187 requests. The limit gets reset
+            every 15 minutes, and the pool is filled up so there is always at
+            least tokenCount usable tokens. Only increase this if you receive
+            major bursts all the time.
+          '';
+        };
+      };
+
+      preferences = {
+        replaceTwitter = mkOption {
+          type = types.str;
+          default = "";
+          example = "nitter.net";
+          description = "Replace Twitter links with links to this instance (blank to disable).";
+        };
+
+        replaceYouTube = mkOption {
+          type = types.str;
+          default = "";
+          example = "piped.kavin.rocks";
+          description = "Replace YouTube links with links to this instance (blank to disable).";
+        };
+
+        replaceInstagram = mkOption {
+          type = types.str;
+          default = "";
+          description = "Replace Instagram links with links to this instance (blank to disable).";
+        };
+
+        mp4Playback = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Enable MP4 video playback.";
+        };
+
+        hlsPlayback = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Enable HLS video streaming (requires JavaScript).";
+        };
+
+        proxyVideos = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Proxy video streaming through the server (might be slow).";
+        };
+
+        muteVideos = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Mute videos by default.";
+        };
+
+        autoplayGifs = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Autoplay GIFs.";
+        };
+
+        theme = mkOption {
+          type = types.str;
+          default = "Nitter";
+          description = "Instance theme.";
+        };
+
+        infiniteScroll = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Infinite scrolling (requires JavaScript, experimental!).";
+        };
+
+        stickyProfile = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Make profile sidebar stick to top.";
+        };
+
+        bidiSupport = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Support bidirectional text (makes clicking on tweets harder).";
+        };
+
+        hideTweetStats = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide tweet stats (replies, retweets, likes).";
+        };
+
+        hideBanner = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide profile banner.";
+        };
+
+        hidePins = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide pinned tweets.";
+        };
+
+        hideReplies = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide tweet replies.";
+        };
+      };
+
+      settings = mkOption {
+        type = types.attrs;
+        default = {};
+        description = ''
+          Add settings here to override NixOS module generated settings.
+
+          Check the official repository for the available settings:
+          https://github.com/zedeus/nitter/blob/master/nitter.conf
+        '';
+      };
+
+      redisCreateLocally = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Configure local Redis server for Nitter.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for Nitter web interface.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !cfg.redisCreateLocally || (cfg.cache.redisHost == "localhost" && cfg.cache.redisPort == 6379);
+        message = "When services.nitter.redisCreateLocally is enabled, you need to use localhost:6379 as a cache server.";
+      }
+    ];
+
+    systemd.services.nitter = {
+        description = "Nitter (An alternative Twitter front-end)";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "syslog.target" "network.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          StateDirectory = "nitter";
+          Environment = [ "NITTER_CONF_FILE=/var/lib/nitter/nitter.conf" ];
+          # Some parts of Nitter expect `public` folder in working directory,
+          # see https://github.com/zedeus/nitter/issues/414
+          WorkingDirectory = "${pkgs.nitter}/share/nitter";
+          ExecStart = "${pkgs.nitter}/bin/nitter";
+          ExecStartPre = "${preStart}";
+          AmbientCapabilities = lib.mkIf (cfg.server.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+          Restart = "on-failure";
+          RestartSec = "5s";
+          # Hardening
+          CapabilityBoundingSet = if (cfg.server.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
+          DeviceAllow = [ "" ];
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          # A private user cannot have process capabilities on the host's user
+          # namespace and thus CAP_NET_BIND_SERVICE has no effect.
+          PrivateUsers = (cfg.server.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" "~@resources" ];
+          UMask = "0077";
+        };
+    };
+
+    services.redis = lib.mkIf (cfg.redisCreateLocally) {
+      enable = true;
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.server.port ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
index 133e96da0ec8..70b27b7d3d09 100644
--- a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
@@ -458,7 +458,7 @@ in
                 description = "The flake reference to which <option>from></option> is to be rewritten.";
               };
               flake = mkOption {
-                type = types.unspecified;
+                type = types.nullOr types.attrs;
                 default = null;
                 example = literalExample "nixpkgs";
                 description = ''
diff --git a/nixpkgs/nixos/modules/services/misc/nzbget.nix b/nixpkgs/nixos/modules/services/misc/nzbget.nix
index 715ec891cd68..27c5f2e395f6 100644
--- a/nixpkgs/nixos/modules/services/misc/nzbget.nix
+++ b/nixpkgs/nixos/modules/services/misc/nzbget.nix
@@ -7,24 +7,12 @@ let
   pkg = pkgs.nzbget;
   stateDir = "/var/lib/nzbget";
   configFile = "${stateDir}/nzbget.conf";
-  configOpts = concatStringsSep " " (mapAttrsToList (name: value: "-o ${name}=${value}") nixosOpts);
-
-  nixosOpts = {
-    # allows nzbget to run as a "simple" service
-    OutputMode = "loggable";
-    # use journald for logging
-    WriteLog = "none";
-    ErrorTarget = "screen";
-    WarningTarget = "screen";
-    InfoTarget = "screen";
-    DetailTarget = "screen";
-    # required paths
-    ConfigTemplate = "${pkg}/share/nzbget/nzbget.conf";
-    WebDir = "${pkg}/share/nzbget/webui";
-    # nixos handles package updates
-    UpdateCheck = "none";
-  };
-
+  configOpts = concatStringsSep " " (mapAttrsToList (name: value: "-o ${name}=${escapeShellArg (toStr value)}") cfg.settings);
+  toStr = v:
+    if v == true then "yes"
+    else if v == false then "no"
+    else if isInt v then toString v
+    else v;
 in
 {
   imports = [
@@ -50,12 +38,41 @@ in
         default = "nzbget";
         description = "Group under which NZBGet runs";
       };
+
+      settings = mkOption {
+        type = with types; attrsOf (oneOf [ bool int str ]);
+        default = {};
+        description = ''
+          NZBGet configuration, passed via command line using switch -o. Refer to
+          <link xlink:href="https://github.com/nzbget/nzbget/blob/master/nzbget.conf"/>
+          for details on supported values.
+        '';
+        example = {
+          MainDir = "/data";
+        };
+      };
     };
   };
 
   # implementation
 
   config = mkIf cfg.enable {
+    services.nzbget.settings = {
+      # allows nzbget to run as a "simple" service
+      OutputMode = "loggable";
+      # use journald for logging
+      WriteLog = "none";
+      ErrorTarget = "screen";
+      WarningTarget = "screen";
+      InfoTarget = "screen";
+      DetailTarget = "screen";
+      # required paths
+      ConfigTemplate = "${pkg}/share/nzbget/nzbget.conf";
+      WebDir = "${pkg}/share/nzbget/webui";
+      # nixos handles package updates
+      UpdateCheck = "none";
+    };
+
     systemd.services.nzbget = {
       description = "NZBGet Daemon";
       after = [ "network.target" ];
@@ -64,6 +81,7 @@ in
         unrar
         p7zip
       ];
+
       preStart = ''
         if [ ! -f ${configFile} ]; then
           ${pkgs.coreutils}/bin/install -m 0700 ${pkg}/share/nzbget/nzbget.conf ${configFile}
diff --git a/nixpkgs/nixos/modules/services/misc/octoprint.nix b/nixpkgs/nixos/modules/services/misc/octoprint.nix
index c926d889b37a..7129ac69527f 100644
--- a/nixpkgs/nixos/modules/services/misc/octoprint.nix
+++ b/nixpkgs/nixos/modules/services/misc/octoprint.nix
@@ -122,6 +122,9 @@ in
         ExecStart = "${pluginsEnv}/bin/octoprint serve -b ${cfg.stateDir}";
         User = cfg.user;
         Group = cfg.group;
+        SupplementaryGroups = [
+          "dialout"
+        ];
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/misc/paperless-ng.nix b/nixpkgs/nixos/modules/services/misc/paperless-ng.nix
new file mode 100644
index 000000000000..4b7087e17f96
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/paperless-ng.nix
@@ -0,0 +1,308 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.services.paperless-ng;
+
+  defaultUser = "paperless";
+
+  env = {
+    PAPERLESS_DATA_DIR = cfg.dataDir;
+    PAPERLESS_MEDIA_ROOT = cfg.mediaDir;
+    PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
+    GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}";
+  } // lib.mapAttrs (_: toString) cfg.extraConfig;
+
+  manage = let
+    setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env);
+  in pkgs.writeShellScript "manage" ''
+    ${setupEnv}
+    exec ${cfg.package}/bin/paperless-ng "$@"
+  '';
+
+  # Secure the services
+  defaultServiceConfig = {
+    TemporaryFileSystem = "/:ro";
+    BindReadOnlyPaths = [
+      "/nix/store"
+      "-/etc/resolv.conf"
+      "-/etc/nsswitch.conf"
+      "-/etc/hosts"
+      "-/etc/localtime"
+      "-/run/postgresql"
+    ];
+    BindPaths = [
+      cfg.consumptionDir
+      cfg.dataDir
+      cfg.mediaDir
+    ];
+    CapabilityBoundingSet = "";
+    # ProtectClock adds DeviceAllow=char-rtc r
+    DeviceAllow = "";
+    LockPersonality = true;
+    MemoryDenyWriteExecute = true;
+    NoNewPrivileges = true;
+    PrivateDevices = true;
+    PrivateMounts = true;
+    # Needs to connect to redis
+    # PrivateNetwork = true;
+    PrivateTmp = true;
+    PrivateUsers = true;
+    ProcSubset = "pid";
+    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";
+    ProtectControlGroups = true;
+    ProtectKernelLogs = true;
+    ProtectKernelModules = true;
+    ProtectKernelTunables = true;
+    ProtectProc = "invisible";
+    RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+    RestrictNamespaces = true;
+    RestrictRealtime = true;
+    RestrictSUIDSGID = true;
+    SystemCallArchitectures = "native";
+    SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ];
+    # Does not work well with the temporary root
+    #UMask = "0066";
+  };
+in
+{
+  meta.maintainers = with maintainers; [ earvstedt Flakebi ];
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "paperless"] ''
+      The paperless module has been removed as the upstream project died.
+      Users should migrate to the paperless-ng module (services.paperless-ng).
+      More information can be found in the NixOS 21.11 release notes.
+    '')
+  ];
+
+  options.services.paperless-ng = {
+    enable = mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enable Paperless-ng.
+
+        When started, the Paperless database is automatically created if it doesn't
+        exist and updated if the Paperless package has changed.
+        Both tasks are achieved by running a Django migration.
+
+        A script to manage the Paperless instance (by wrapping Django's manage.py) is linked to
+        <literal>''${dataDir}/paperless-ng-manage</literal>.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.str;
+      default = "/var/lib/paperless";
+      description = "Directory to store the Paperless data.";
+    };
+
+    mediaDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/media";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory to store the Paperless documents.";
+    };
+
+    consumptionDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/consume";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory from which new documents are imported.";
+    };
+
+    consumptionDirIsPublic = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether all users can write to the consumption dir.";
+    };
+
+    passwordFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/keys/paperless-ng-password";
+      description = ''
+        A file containing the superuser password.
+
+        A superuser is required to access the web interface.
+        If unset, you can create a superuser manually by running
+        <literal>''${dataDir}/paperless-ng-manage createsuperuser</literal>.
+
+        The default superuser name is <literal>admin</literal>. To change it, set
+        option <option>extraConfig.PAPERLESS_ADMIN_USER</option>.
+        WARNING: When changing the superuser name after the initial setup, the old superuser
+        will continue to exist.
+
+        To disable login for the web interface, set the following:
+        <literal>extraConfig.PAPERLESS_AUTO_LOGIN_USERNAME = "admin";</literal>.
+        WARNING: Only use this on a trusted system without internet access to Paperless.
+      '';
+    };
+
+    address = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Web interface address.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 28981;
+      description = "Web interface port.";
+    };
+
+    extraConfig = mkOption {
+      type = types.attrs;
+      default = {};
+      description = ''
+        Extra paperless-ng config options.
+
+        See <link xlink:href="https://paperless-ng.readthedocs.io/en/latest/configuration.html">the documentation</link>
+        for available options.
+      '';
+      example = literalExample ''
+        {
+          PAPERLESS_OCR_LANGUAGE = "deu+eng";
+        }
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = "User under which Paperless runs.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.paperless-ng;
+      defaultText = "pkgs.paperless-ng";
+      description = "The Paperless package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # Enable redis if no special url is set
+    services.redis.enable = mkIf (!hasAttr "PAPERLESS_REDIS" env) true;
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      "d '${cfg.mediaDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      (if cfg.consumptionDirIsPublic then
+        "d '${cfg.consumptionDir}' 777 - - - -"
+      else
+        "d '${cfg.consumptionDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      )
+    ];
+
+    systemd.services.paperless-ng-server = {
+      description = "Paperless document server";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = "${cfg.package}/bin/paperless-ng qcluster";
+        Restart = "on-failure";
+      };
+      environment = env;
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "paperless-ng-consumer.service" "paperless-ng-web.service" ];
+
+      preStart = ''
+        ln -sf ${manage} ${cfg.dataDir}/paperless-ng-manage
+
+        # Auto-migrate on first run or if the package has changed
+        versionFile="${cfg.dataDir}/src-version"
+        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
+          ${cfg.package}/bin/paperless-ng migrate
+          echo ${cfg.package} > "$versionFile"
+        fi
+      ''
+      + optionalString (cfg.passwordFile != null) ''
+        export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}"
+        export PAPERLESS_ADMIN_PASSWORD=$(cat "${cfg.dataDir}/superuser-password")
+        superuserState="$PAPERLESS_ADMIN_USER:$PAPERLESS_ADMIN_PASSWORD"
+        superuserStateFile="${cfg.dataDir}/superuser-state"
+
+        if [[ $(cat "$superuserStateFile" 2>/dev/null) != $superuserState ]]; then
+          ${cfg.package}/bin/paperless-ng manage_superuser
+          echo "$superuserState" > "$superuserStateFile"
+        fi
+      '';
+    };
+
+    # Password copying can't be implemented as a privileged preStart script
+    # in 'paperless-ng-server' because 'defaultServiceConfig' limits the filesystem
+    # paths accessible by the service.
+    systemd.services.paperless-ng-copy-password = mkIf (cfg.passwordFile != null) {
+      requiredBy = [ "paperless-ng-server.service" ];
+      before = [ "paperless-ng-server.service" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.coreutils}/bin/install --mode 600 --owner '${cfg.user}' --compare \
+            '${cfg.passwordFile}' '${cfg.dataDir}/superuser-password'
+        '';
+        Type = "oneshot";
+      };
+    };
+
+    systemd.services.paperless-ng-consumer = {
+      description = "Paperless document consumer";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = "${cfg.package}/bin/paperless-ng document_consumer";
+        Restart = "on-failure";
+      };
+      environment = env;
+      # Bind to `paperless-ng-server` so that the consumer never runs
+      # during migrations
+      bindsTo = [ "paperless-ng-server.service" ];
+      after = [ "paperless-ng-server.service" ];
+    };
+
+    systemd.services.paperless-ng-web = {
+      description = "Paperless web server";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = ''
+          ${pkgs.python3Packages.gunicorn}/bin/gunicorn \
+            -c ${cfg.package}/lib/paperless-ng/gunicorn.conf.py paperless.asgi:application
+        '';
+        Restart = "on-failure";
+
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
+        # gunicorn needs setuid
+        SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "@setuid" ];
+      };
+      environment = env // {
+        PATH = mkForce cfg.package.path;
+        PYTHONPATH = "${cfg.package.pythonPath}:${cfg.package}/lib/paperless-ng/src";
+      };
+      # Allow the web interface to access the private /tmp directory of the server.
+      # This is required to support uploading files via the web interface.
+      unitConfig.JoinsNamespaceOf = "paperless-ng-server.service";
+      # Bind to `paperless-ng-server` so that the web server never runs
+      # during migrations
+      bindsTo = [ "paperless-ng-server.service" ];
+      after = [ "paperless-ng-server.service" ];
+    };
+
+    users = optionalAttrs (cfg.user == defaultUser) {
+      users.${defaultUser} = {
+        group = defaultUser;
+        uid = config.ids.uids.paperless;
+        home = cfg.dataDir;
+      };
+
+      groups.${defaultUser} = {
+        gid = config.ids.gids.paperless;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix
deleted file mode 100644
index 43730b80eb2c..000000000000
--- a/nixpkgs/nixos/modules/services/misc/paperless.nix
+++ /dev/null
@@ -1,183 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-let
-  cfg = config.services.paperless;
-
-  defaultUser = "paperless";
-
-  manage = cfg.package.withConfig {
-    config = {
-      PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
-      PAPERLESS_INLINE_DOC = "true";
-      PAPERLESS_DISABLE_LOGIN = "true";
-    } // cfg.extraConfig;
-    inherit (cfg) dataDir ocrLanguages;
-    paperlessPkg = cfg.package;
-  };
-in
-{
-  options.services.paperless = {
-    enable = mkOption {
-      type = lib.types.bool;
-      default = false;
-      description = ''
-        Enable Paperless.
-
-        When started, the Paperless database is automatically created if it doesn't
-        exist and updated if the Paperless package has changed.
-        Both tasks are achieved by running a Django migration.
-      '';
-    };
-
-    dataDir = mkOption {
-      type = types.str;
-      default = "/var/lib/paperless";
-      description = "Directory to store the Paperless data.";
-    };
-
-    consumptionDir = mkOption {
-      type = types.str;
-      default = "${cfg.dataDir}/consume";
-      defaultText = "\${dataDir}/consume";
-      description = "Directory from which new documents are imported.";
-    };
-
-    consumptionDirIsPublic = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Whether all users can write to the consumption dir.";
-    };
-
-    ocrLanguages = mkOption {
-      type = with types; nullOr (listOf str);
-      default = null;
-      description = ''
-        Languages available for OCR via Tesseract, specified as
-        <literal>ISO 639-2/T</literal> language codes.
-        If unset, defaults to all available languages.
-      '';
-      example = [ "eng" "spa" "jpn" ];
-    };
-
-    address = mkOption {
-      type = types.str;
-      default = "localhost";
-      description = "Server listening address.";
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 28981;
-      description = "Server port to listen on.";
-    };
-
-    extraConfig = mkOption {
-      type = types.attrs;
-      default = {};
-      description = ''
-        Extra paperless config options.
-
-        The config values are evaluated as double-quoted Bash string literals.
-
-        See <literal>paperless-src/paperless.conf.example</literal> for available options.
-
-        To enable user authentication, set <literal>PAPERLESS_DISABLE_LOGIN = "false"</literal>
-        and run the shell command <literal>$dataDir/paperless-manage createsuperuser</literal>.
-
-        To define secret options without storing them in /nix/store, use the following pattern:
-        <literal>PAPERLESS_PASSPHRASE = "$(&lt; /etc/my_passphrase_file)"</literal>
-      '';
-      example = literalExample ''
-        {
-          PAPERLESS_OCR_LANGUAGE = "deu";
-        }
-      '';
-    };
-
-    user = mkOption {
-      type = types.str;
-      default = defaultUser;
-      description = "User under which Paperless runs.";
-    };
-
-    package = mkOption {
-      type = types.package;
-      default = pkgs.paperless;
-      defaultText = "pkgs.paperless";
-      description = "The Paperless package to use.";
-    };
-
-    manage = mkOption {
-      type = types.package;
-      readOnly = true;
-      default = manage;
-      description = ''
-        A script to manage the Paperless instance.
-        It wraps Django's manage.py and is also available at
-        <literal>$dataDir/manage-paperless</literal>
-      '';
-    };
-  };
-
-  config = mkIf cfg.enable {
-
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
-    ] ++ (optional cfg.consumptionDirIsPublic
-      "d '${cfg.consumptionDir}' 777 - - - -"
-      # If the consumption dir is not created here, it's automatically created by
-      # 'manage' with the default permissions.
-    );
-
-    systemd.services.paperless-consumer = {
-      description = "Paperless document consumer";
-      serviceConfig = {
-        User = cfg.user;
-        ExecStart = "${manage} document_consumer";
-        Restart = "always";
-      };
-      after = [ "systemd-tmpfiles-setup.service" ];
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        if [[ $(readlink ${cfg.dataDir}/paperless-manage) != ${manage} ]]; then
-          ln -sf ${manage} ${cfg.dataDir}/paperless-manage
-        fi
-
-        ${manage.setupEnv}
-        # Auto-migrate on first run or if the package has changed
-        versionFile="$PAPERLESS_DBDIR/src-version"
-        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
-          python $paperlessSrc/manage.py migrate
-          echo ${cfg.package} > "$versionFile"
-        fi
-      '';
-    };
-
-    systemd.services.paperless-server = {
-      description = "Paperless document server";
-      serviceConfig = {
-        User = cfg.user;
-        ExecStart = "${manage} runserver --noreload ${cfg.address}:${toString cfg.port}";
-        Restart = "always";
-      };
-      # Bind to `paperless-consumer` so that the server never runs
-      # during migrations
-      bindsTo = [ "paperless-consumer.service" ];
-      after = [ "paperless-consumer.service" ];
-      wantedBy = [ "multi-user.target" ];
-    };
-
-    users = optionalAttrs (cfg.user == defaultUser) {
-      users.${defaultUser} = {
-        group = defaultUser;
-        uid = config.ids.uids.paperless;
-        home = cfg.dataDir;
-      };
-
-      groups.${defaultUser} = {
-        gid = config.ids.gids.paperless;
-      };
-    };
-  };
-}
diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix
index a17a1010dbf7..e446f08284f7 100644
--- a/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix
+++ b/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix
@@ -84,7 +84,7 @@ in
             (rev: archs:
               lib.attrsets.mapAttrsToList
                 (arch: image:
-                  pkgs.runCommandNoCC "buildsrht-images" { } ''
+                  pkgs.runCommand "buildsrht-images" { } ''
                     mkdir -p $out/${distro}/${rev}/${arch}
                     ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
                   '')
@@ -97,7 +97,7 @@ in
         "${pkgs.sourcehut.buildsrht}/lib/images"
       ];
     };
-    image_dir = pkgs.runCommandNoCC "builds.sr.ht-worker-images" { } ''
+    image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
       mkdir -p $out/images
       cp -Lr ${image_dir_pre}/* $out/images
     '';
diff --git a/nixpkgs/nixos/modules/services/misc/uhub.nix b/nixpkgs/nixos/modules/services/misc/uhub.nix
index d1b388310280..da2613e6db17 100644
--- a/nixpkgs/nixos/modules/services/misc/uhub.nix
+++ b/nixpkgs/nixos/modules/services/misc/uhub.nix
@@ -3,178 +3,110 @@
 with lib;
 
 let
-
-  cfg = config.services.uhub;
-
-  uhubPkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; };
-
-  pluginConfig = ""
-  + optionalString cfg.plugins.authSqlite.enable ''
-    plugin ${uhubPkg.mod_auth_sqlite}/mod_auth_sqlite.so "file=${cfg.plugins.authSqlite.file}"
-  ''
-  + optionalString cfg.plugins.logging.enable ''
-    plugin ${uhubPkg.mod_logging}/mod_logging.so ${if cfg.plugins.logging.syslog then "syslog=true" else "file=${cfg.plugins.logging.file}"}
-  ''
-  + optionalString cfg.plugins.welcome.enable ''
-    plugin ${uhubPkg.mod_welcome}/mod_welcome.so "motd=${pkgs.writeText "motd.txt"  cfg.plugins.welcome.motd} rules=${pkgs.writeText "rules.txt" cfg.plugins.welcome.rules}"
-  ''
-  + optionalString cfg.plugins.history.enable ''
-    plugin ${uhubPkg.mod_chat_history}/mod_chat_history.so "history_max=${toString cfg.plugins.history.max} history_default=${toString cfg.plugins.history.default} history_connect=${toString cfg.plugins.history.connect}"
-  '';
-
-  uhubConfigFile = pkgs.writeText "uhub.conf" ''
-    file_acl=${pkgs.writeText "users.conf" cfg.aclConfig}
-    file_plugins=${pkgs.writeText "plugins.conf" pluginConfig}
-    server_bind_addr=${cfg.address}
-    server_port=${toString cfg.port}
-    ${lib.optionalString cfg.enableTLS "tls_enable=yes"}
-    ${cfg.hubConfig}
-  '';
-
-in
-
-{
+  settingsFormat = {
+    type = with lib.types; attrsOf (oneOf [ bool int str ]);
+    generate = name: attrs:
+      pkgs.writeText name (lib.strings.concatStringsSep "\n"
+        (lib.attrsets.mapAttrsToList
+          (key: value: "${key}=${builtins.toJSON value}") attrs));
+  };
+in {
   options = {
 
-    services.uhub = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to enable the uhub ADC hub.";
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 1511;
-        description = "TCP port to bind the hub to.";
-      };
-
-      address = mkOption {
-        type = types.str;
-        default = "any";
-        description = "Address to bind the hub to.";
-      };
-
-      enableTLS = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to enable TLS support.";
-      };
+    services.uhub = mkOption {
+      default = { };
+      description = "Uhub ADC hub instances";
+      type = types.attrsOf (types.submodule {
+        options = {
 
-      hubConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = "Contents of uhub configuration file.";
-      };
+          enable = mkEnableOption "hub instance" // { default = true; };
 
-      aclConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = "Contents of user ACL configuration file.";
-      };
-
-      plugins = {
-
-        authSqlite = {
-          enable = mkOption {
+          enableTLS = mkOption {
             type = types.bool;
             default = false;
-            description = "Whether to enable the Sqlite authentication database plugin";
-          };
-          file = mkOption {
-            type = types.path;
-            example = "/var/db/uhub-users";
-            description = "Path to user database. Use the uhub-passwd utility to create the database and add/remove users.";
+            description = "Whether to enable TLS support.";
           };
-        };
 
-        logging = {
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = "Whether to enable the logging plugin.";
-          };
-          file = mkOption {
-            type = types.str;
-            default = "";
-            description = "Path of log file.";
-          };
-          syslog = mkOption {
-            type = types.bool;
-            default = false;
-            description = "If true then the system log is used instead of writing to file.";
-          };
-        };
-
-        welcome = {
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = "Whether to enable the welcome plugin.";
-          };
-          motd = mkOption {
-            default = "";
-            type = types.lines;
+          settings = mkOption {
+            inherit (settingsFormat) type;
             description = ''
-              Welcome message displayed to clients after connecting
-              and with the <literal>!motd</literal> command.
+              Configuration of uhub.
+              See https://www.uhub.org/doc/config.php for a list of options.
             '';
+            default = { };
+            example = {
+              server_bind_addr = "any";
+              server_port = 1511;
+              hub_name = "My Public Hub";
+              hub_description = "Yet another ADC hub";
+              max_users = 150;
+            };
           };
-          rules = mkOption {
-            default = "";
-            type = types.lines;
-            description = ''
-              Rules message, displayed to clients with the <literal>!rules</literal> command.
-            '';
-          };
-        };
 
-        history = {
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = "Whether to enable the history plugin.";
+          plugins = mkOption {
+            description = "Uhub plugin configuration.";
+            type = with types;
+              listOf (submodule {
+                options = {
+                  plugin = mkOption {
+                    type = path;
+                    example = literalExample
+                      "$${pkgs.uhub}/plugins/mod_auth_sqlite.so";
+                    description = "Path to plugin file.";
+                  };
+                  settings = mkOption {
+                    description = "Settings specific to this plugin.";
+                    type = with types; attrsOf str;
+                    example = { file = "/etc/uhub/users.db"; };
+                  };
+                };
+              });
+            default = [ ];
           };
-          max = mkOption {
-            type = types.int;
-            default = 200;
-            description = "The maximum number of messages to keep in history";
-          };
-          default = mkOption {
-            type = types.int;
-            default = 10;
-            description = "When !history is provided without arguments, then this default number of messages are returned.";
-          };
-          connect = mkOption {
-            type = types.int;
-            default = 5;
-            description = "The number of chat history messages to send when users connect (0 = do not send any history).";
-          };
-        };
 
-      };
+        };
+      });
     };
 
   };
 
-  config = mkIf cfg.enable {
-
-    users = {
-      users.uhub.uid = config.ids.uids.uhub;
-      groups.uhub.gid = config.ids.gids.uhub;
-    };
-
-    systemd.services.uhub = {
-      description = "high performance peer-to-peer hub for the ADC network";
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        Type = "notify";
-        ExecStart  = "${uhubPkg}/bin/uhub -c ${uhubConfigFile} -u uhub -g uhub -L";
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+  config = let
+    hubs = lib.attrsets.filterAttrs (_: cfg: cfg.enable) config.services.uhub;
+  in {
+
+    environment.etc = lib.attrsets.mapAttrs' (name: cfg:
+      let
+        settings' = cfg.settings // {
+          tls_enable = cfg.enableTLS;
+          file_plugins = pkgs.writeText "uhub-plugins.conf"
+            (lib.strings.concatStringsSep "\n" (map ({ plugin, settings }:
+              "plugin ${plugin} ${
+                toString
+                (lib.attrsets.mapAttrsToList (key: value: ''"${key}=${value}"'')
+                  settings)
+              }") cfg.plugins));
+        };
+      in {
+        name = "uhub/${name}.conf";
+        value.source = settingsFormat.generate "uhub-${name}.conf" settings';
+      }) hubs;
+
+    systemd.services = lib.attrsets.mapAttrs' (name: cfg: {
+      name = "uhub-${name}";
+      value = let pkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; };
+      in {
+        description = "high performance peer-to-peer hub for the ADC network";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        reloadIfChanged = true;
+        serviceConfig = {
+          Type = "notify";
+          ExecStart = "${pkg}/bin/uhub -c /etc/uhub/${name}.conf -L";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+          DynamicUser = true;
+        };
       };
-    };
+    }) hubs;
   };
 
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
index e0b2624b6cac..fb67bbfb8420 100644
--- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
@@ -6,6 +6,8 @@ let
   cfg = config.services.grafana;
   opt = options.services.grafana;
   declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins);
+  useMysql = cfg.database.type == "mysql";
+  usePostgresql = cfg.database.type == "postgres";
 
   envOptions = {
     PATHS_DATA = cfg.dataDir;
@@ -635,7 +637,7 @@ in {
     systemd.services.grafana = {
       description = "Grafana Service Daemon";
       wantedBy = ["multi-user.target"];
-      after = ["networking.target"];
+      after = ["networking.target"] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
       environment = {
         QT_QPA_PLATFORM = "offscreen";
       } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
diff --git a/nixpkgs/nixos/modules/services/monitoring/nagios.nix b/nixpkgs/nixos/modules/services/monitoring/nagios.nix
index 61214508a9c6..0afaefe04e18 100644
--- a/nixpkgs/nixos/modules/services/monitoring/nagios.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/nagios.nix
@@ -102,8 +102,8 @@ in
 
       plugins = mkOption {
         type = types.listOf types.package;
-        default = with pkgs; [ nagiosPluginsOfficial ssmtp mailutils ];
-        defaultText = "[pkgs.nagiosPluginsOfficial pkgs.ssmtp pkgs.mailutils]";
+        default = with pkgs; [ monitoring-plugins ssmtp mailutils ];
+        defaultText = "[pkgs.monitoring-plugins pkgs.ssmtp pkgs.mailutils]";
         description = "
           Packages to be added to the Nagios <envar>PATH</envar>.
           Typically used to add plugins, but can be anything.
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
index 3be247ffb24e..1161d18ab14b 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
@@ -10,7 +10,7 @@ let
   # a wrapper that verifies that the configuration is valid
   promtoolCheck = what: name: file:
     if cfg.checkConfig then
-      pkgs.runCommandNoCCLocal
+      pkgs.runCommandLocal
         "${name}-${replaceStrings [" "] [""] what}-checked"
         { buildInputs = [ cfg.package ]; } ''
       ln -s ${file} $out
@@ -19,7 +19,7 @@ let
 
   # Pretty-print JSON to a file
   writePrettyJSON = name: x:
-    pkgs.runCommandNoCCLocal name {} ''
+    pkgs.runCommandLocal name {} ''
       echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out
     '';
 
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
index d648de6a4148..9182c2f2ed87 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -33,6 +33,7 @@ let
     "domain"
     "dovecot"
     "fritzbox"
+    "influxdb"
     "json"
     "jitsi"
     "kea"
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
new file mode 100644
index 000000000000..ba45173e946a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.influxdb;
+in
+{
+  port = 9122;
+  extraOpts = {
+    sampleExpiry = mkOption {
+      type = types.str;
+      default = "5m";
+      example = "10m";
+      description = "How long a sample is valid for";
+    };
+    udpBindAddress = mkOption {
+      type = types.str;
+      default = ":9122";
+      example = "192.0.2.1:9122";
+      description = "Address on which to listen for udp packets";
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      RuntimeDirectory = "prometheus-influxdb-exporter";
+      ExecStart = ''
+        ${pkgs.prometheus-influxdb-exporter}/bin/influxdb_exporter \
+        --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+        --influxdb.sample-expiry ${cfg.sampleExpiry} ${concatStringsSep " " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/thanos.nix b/nixpkgs/nixos/modules/services/monitoring/thanos.nix
index 474ea4b25054..96addf392bd7 100644
--- a/nixpkgs/nixos/modules/services/monitoring/thanos.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/thanos.nix
@@ -63,7 +63,7 @@ let
     };
   };
 
-  toYAML = name: attrs: pkgs.runCommandNoCC name {
+  toYAML = name: attrs: pkgs.runCommand name {
     preferLocalBuild = true;
     json = builtins.toFile "${name}.json" (builtins.toJSON attrs);
     nativeBuildInputs = [ pkgs.remarshal ];
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix b/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix
index 2748571be1f7..57f5f6b006c8 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix
@@ -5,36 +5,41 @@ let
   opt = options.services.ipfs;
 
   ipfsFlags = toString ([
-    (optionalString  cfg.autoMount                   "--mount")
-    (optionalString  cfg.enableGC                    "--enable-gc")
-    (optionalString (cfg.serviceFdlimit != null)     "--manage-fdlimit=false")
-    (optionalString (cfg.defaultMode == "offline")   "--offline")
+    (optionalString cfg.autoMount "--mount")
+    (optionalString cfg.enableGC "--enable-gc")
+    (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false")
+    (optionalString (cfg.defaultMode == "offline") "--offline")
     (optionalString (cfg.defaultMode == "norouting") "--routing=none")
   ] ++ cfg.extraFlags);
 
   splitMulitaddr = addrRaw: lib.tail (lib.splitString "/" addrRaw);
 
-  multiaddrToListenStream = addrRaw: let
+  multiaddrToListenStream = addrRaw:
+    let
       addr = splitMulitaddr addrRaw;
       s = builtins.elemAt addr;
-    in if s 0 == "ip4" && s 2 == "tcp"
-      then "${s 1}:${s 3}"
+    in
+    if s 0 == "ip4" && s 2 == "tcp"
+    then "${s 1}:${s 3}"
     else if s 0 == "ip6" && s 2 == "tcp"
-      then "[${s 1}]:${s 3}"
+    then "[${s 1}]:${s 3}"
     else if s 0 == "unix"
-      then "/${lib.concatStringsSep "/" (lib.tail addr)}"
+    then "/${lib.concatStringsSep "/" (lib.tail addr)}"
     else null; # not valid for listen stream, skip
 
-  multiaddrToListenDatagram = addrRaw: let
+  multiaddrToListenDatagram = addrRaw:
+    let
       addr = splitMulitaddr addrRaw;
       s = builtins.elemAt addr;
-    in if s 0 == "ip4" && s 2 == "udp"
-      then "${s 1}:${s 3}"
+    in
+    if s 0 == "ip4" && s 2 == "udp"
+    then "${s 1}:${s 3}"
     else if s 0 == "ip6" && s 2 == "udp"
-      then "[${s 1}]:${s 3}"
+    then "[${s 1}]:${s 3}"
     else null; # not valid for listen datagram, skip
 
-in {
+in
+{
 
   ###### interface
 
@@ -65,9 +70,10 @@ in {
 
       dataDir = mkOption {
         type = types.str;
-        default = if versionAtLeast config.system.stateVersion "17.09"
-                  then "/var/lib/ipfs"
-                  else "/var/lib/ipfs/.ipfs";
+        default =
+          if versionAtLeast config.system.stateVersion "17.09"
+          then "/var/lib/ipfs"
+          else "/var/lib/ipfs/.ipfs";
         description = "The data dir for IPFS";
       };
 
@@ -83,6 +89,12 @@ in {
         description = "Whether IPFS should try to mount /ipfs and /ipns at startup.";
       };
 
+      autoMigrate = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Whether IPFS should try to run the fs-repo-migration at startup.";
+      };
+
       ipfsMountDir = mkOption {
         type = types.str;
         default = "/ipfs";
@@ -137,7 +149,7 @@ in {
           These are applied last, so may override configuration set by other options in this module.
           Keep in mind that this configuration is stateful; i.e., unsetting anything in here does not reset the value to the default!
         '';
-        default = {};
+        default = { };
         example = {
           Datastore.StorageMax = "100GB";
           Discovery.MDNS.Enabled = false;
@@ -153,7 +165,7 @@ in {
       extraFlags = mkOption {
         type = types.listOf types.str;
         description = "Extra flags passed to the IPFS daemon";
-        default = [];
+        default = [ ];
       };
 
       localDiscovery = mkOption {
@@ -168,7 +180,7 @@ in {
         type = types.nullOr types.int;
         default = null;
         description = "The fdlimit for the IPFS systemd unit or <literal>null</literal> to have the daemon attempt to manage it";
-        example = 64*1024;
+        example = 64 * 1024;
       };
 
       startWhenNeeded = mkOption {
@@ -186,6 +198,9 @@ in {
     environment.systemPackages = [ cfg.package ];
     environment.variables.IPFS_PATH = cfg.dataDir;
 
+    # https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size
+    boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000;
+
     programs.fuse = mkIf cfg.autoMount {
       userAllowOther = true;
     };
@@ -226,33 +241,36 @@ in {
             ${optionalString (! cfg.localDiscovery) "--profile=server"}
         else
           ${if cfg.localDiscovery
-            then "ipfs config profile apply local-discovery"
-            else "ipfs config profile apply server"
+            then "ipfs --offline config profile apply local-discovery"
+            else "ipfs --offline config profile apply server"
           }
         fi
       '' + optionalString cfg.autoMount ''
         ipfs --offline config Mounts.FuseAllowOther --json true
         ipfs --offline config Mounts.IPFS ${cfg.ipfsMountDir}
         ipfs --offline config Mounts.IPNS ${cfg.ipnsMountDir}
+      '' + optionalString cfg.autoMigrate ''
+        ${pkgs.ipfs-migrator}/bin/fs-repo-migrations -y
       '' + concatStringsSep "\n" (collect
-            isString
-            (mapAttrsRecursive
-              (path: value:
-              # Using heredoc below so that the value is never improperly quoted
-              ''
-                read value <<EOF
-                ${builtins.toJSON value}
-                EOF
-                ipfs --offline config --json "${concatStringsSep "." path}" "$value"
-              '')
-              ({ Addresses.API = cfg.apiAddress;
-                 Addresses.Gateway = cfg.gatewayAddress;
-                 Addresses.Swarm = cfg.swarmAddress;
-              } //
-              cfg.extraConfig))
-          );
+        isString
+        (mapAttrsRecursive
+          (path: value:
+            # Using heredoc below so that the value is never improperly quoted
+            ''
+              read value <<EOF
+              ${builtins.toJSON value}
+              EOF
+              ipfs --offline config --json "${concatStringsSep "." path}" "$value"
+            '')
+          ({
+            Addresses.API = cfg.apiAddress;
+            Addresses.Gateway = cfg.gatewayAddress;
+            Addresses.Swarm = cfg.swarmAddress;
+          } //
+          cfg.extraConfig))
+      );
       serviceConfig = {
-        ExecStart = ["" "${cfg.package}/bin/ipfs daemon ${ipfsFlags}"];
+        ExecStart = [ "" "${cfg.package}/bin/ipfs daemon ${ipfsFlags}" ];
         User = cfg.user;
         Group = cfg.group;
       } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; };
@@ -263,12 +281,16 @@ in {
     systemd.sockets.ipfs-gateway = {
       wantedBy = [ "sockets.target" ];
       socketConfig = {
-        ListenStream = let
+        ListenStream =
+          let
             fromCfg = multiaddrToListenStream cfg.gatewayAddress;
-          in [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
-        ListenDatagram = let
+          in
+          [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
+        ListenDatagram =
+          let
             fromCfg = multiaddrToListenDatagram cfg.gatewayAddress;
-          in [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
+          in
+          [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
       };
     };
 
@@ -276,9 +298,11 @@ in {
       wantedBy = [ "sockets.target" ];
       # We also include "%t/ipfs.sock" because there is no way to put the "%t"
       # in the multiaddr.
-      socketConfig.ListenStream = let
+      socketConfig.ListenStream =
+        let
           fromCfg = multiaddrToListenStream cfg.apiAddress;
-        in [ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg;
+        in
+        [ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg;
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix b/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix
new file mode 100644
index 000000000000..f1806c5af0a9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix
@@ -0,0 +1,100 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.litestream;
+  settingsFormat = pkgs.formats.yaml {};
+in
+{
+  options.services.litestream = {
+    enable = mkEnableOption "litestream";
+
+    package = mkOption {
+      description = "Package to use.";
+      default = pkgs.litestream;
+      defaultText = "pkgs.litestream";
+      type = types.package;
+    };
+
+    settings = mkOption {
+      description = ''
+        See the <link xlink:href="https://litestream.io/reference/config/">documentation</link>.
+      '';
+      type = settingsFormat.type;
+      example = {
+        dbs = [
+          {
+            path = "/var/lib/db1";
+            replicas = [
+              {
+                url = "s3://mybkt.litestream.io/db1";
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    environmentFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/secrets/litestream";
+      description = ''
+        Environment file as defined in <citerefentry>
+        <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
+        </citerefentry>.
+
+        Secrets may be passed to the service without adding them to the
+        world-readable Nix store, by specifying placeholder variables as
+        the option value in Nix and setting these variables accordingly in the
+        environment file.
+
+        By default, Litestream will perform environment variable expansion
+        within the config file before reading it. Any references to ''$VAR or
+        ''${VAR} formatted variables will be replaced with their environment
+        variable values. If no value is set then it will be replaced with an
+        empty string.
+
+        <programlisting>
+          # Content of the environment file
+          LITESTREAM_ACCESS_KEY_ID=AKIAxxxxxxxxxxxxxxxx
+          LITESTREAM_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxx
+        </programlisting>
+
+        Note that this file needs to be available on the host on which
+        this exporter is running.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    environment.etc = {
+      "litestream.yml" = {
+        source = settingsFormat.generate "litestream-config.yaml" cfg.settings;
+      };
+    };
+
+    systemd.services.litestream = {
+      description = "Litestream";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ];
+      serviceConfig = {
+        EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
+        ExecStart = "${cfg.package}/bin/litestream replicate";
+        Restart = "always";
+        User = "litestream";
+        Group = "litestream";
+      };
+    };
+
+    users.users.litestream = {
+      description = "Litestream user";
+      group = "litestream";
+      isSystemUser = true;
+    };
+    users.groups.litestream = {};
+  };
+  meta.doc = ./litestream.xml;
+}
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml b/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml
new file mode 100644
index 000000000000..598f9be8cf63
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml
@@ -0,0 +1,65 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-litestream">
+ <title>Litestream</title>
+ <para>
+  <link xlink:href="https://litestream.io/">Litestream</link> is a standalone streaming
+  replication tool for SQLite.
+ </para>
+
+ <section xml:id="module-services-litestream-configuration">
+  <title>Configuration</title>
+
+  <para>
+   Litestream service is managed by a dedicated user named <literal>litestream</literal>
+   which needs permission to the database file. Here's an example config which gives
+   required permissions to access <link linkend="opt-services.grafana.database.path">
+   grafana database</link>:
+<programlisting>
+{ pkgs, ... }:
+{
+  users.users.litestream.extraGroups = [ "grafana" ];
+
+  systemd.services.grafana.serviceConfig.ExecStartPost = "+" + pkgs.writeShellScript "grant-grafana-permissions" ''
+    timeout=10
+
+    while [ ! -f /var/lib/grafana/data/grafana.db ];
+    do
+      if [ "$timeout" == 0 ]; then
+        echo "ERROR: Timeout while waiting for /var/lib/grafana/data/grafana.db."
+        exit 1
+      fi
+
+      sleep 1
+
+      ((timeout--))
+    done
+
+    find /var/lib/grafana -type d -exec chmod -v 775 {} \;
+    find /var/lib/grafana -type f -exec chmod -v 660 {} \;
+  '';
+
+  services.litestream = {
+    enable = true;
+
+    environmentFile = "/run/secrets/litestream";
+
+    settings = {
+      dbs = [
+        {
+          path = "/var/lib/grafana/data/grafana.db";
+          replicas = [{
+            url = "s3://mybkt.litestream.io/grafana";
+          }];
+        }
+      ];
+    };
+  };
+}
+</programlisting>
+  </para>
+ </section>
+
+</chapter>
diff --git a/nixpkgs/nixos/modules/services/networking/autossh.nix b/nixpkgs/nixos/modules/services/networking/autossh.nix
index a8d9a027e9fa..245f2bfc2cf3 100644
--- a/nixpkgs/nixos/modules/services/networking/autossh.nix
+++ b/nixpkgs/nixos/modules/services/networking/autossh.nix
@@ -79,7 +79,7 @@ in
 
     systemd.services =
 
-      lib.fold ( s : acc : acc //
+      lib.foldr ( s : acc : acc //
         {
           "autossh-${s.name}" =
             let
diff --git a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
index 0b7d5575c11f..020a817f2596 100644
--- a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
@@ -240,8 +240,8 @@ in
 
     system.nssModules = optional cfg.nssmdns pkgs.nssmdns;
     system.nssDatabases.hosts = optionals cfg.nssmdns (mkMerge [
-      (mkOrder 900 [ "mdns_minimal [NOTFOUND=return]" ]) # must be before resolve
-      (mkOrder 1501 [ "mdns" ]) # 1501 to ensure it's after dns
+      (mkBefore [ "mdns_minimal [NOTFOUND=return]" ]) # before resolve
+      (mkAfter [ "mdns" ]) # after dns
     ]);
 
     environment.systemPackages = [ pkgs.avahi ];
diff --git a/nixpkgs/nixos/modules/services/networking/bird.nix b/nixpkgs/nixos/modules/services/networking/bird.nix
index 1923afdf83f2..c14adbda3c5a 100644
--- a/nixpkgs/nixos/modules/services/networking/bird.nix
+++ b/nixpkgs/nixos/modules/services/networking/bird.nix
@@ -10,8 +10,8 @@ let
       birdBin = if variant == "bird6" then "bird6" else "bird";
       birdc = if variant == "bird6" then "birdc6" else "birdc";
       descr =
-        { bird = "1.9.x with IPv4 suport";
-          bird6 = "1.9.x with IPv6 suport";
+        { bird = "1.6.x with IPv4 support";
+          bird6 = "1.6.x with IPv6 support";
           bird2 = "2.x";
         }.${variant};
     in {
diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix
index f1a504b3e3f4..ca95d00c2ff8 100644
--- a/nixpkgs/nixos/modules/services/networking/cjdns.nix
+++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix
@@ -39,7 +39,7 @@ let
   };
 
   # Additional /etc/hosts entries for peers with an associated hostname
-  cjdnsExtraHosts = pkgs.runCommandNoCC "cjdns-hosts" {} ''
+  cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" {} ''
     exec >$out
     ${concatStringsSep "\n" (mapAttrsToList (k: v:
         optionalString (v.hostname != "")
diff --git a/nixpkgs/nixos/modules/services/networking/connman.nix b/nixpkgs/nixos/modules/services/networking/connman.nix
index 11f66b05df12..608672c6446c 100644
--- a/nixpkgs/nixos/modules/services/networking/connman.nix
+++ b/nixpkgs/nixos/modules/services/networking/connman.nix
@@ -150,6 +150,7 @@ in {
       useDHCP = false;
       wireless = {
         enable = mkIf (!enableIwd) true;
+        dbusControlled = true;
         iwd = mkIf enableIwd {
           enable = true;
         };
diff --git a/nixpkgs/nixos/modules/services/networking/epmd.nix b/nixpkgs/nixos/modules/services/networking/epmd.nix
index f7cdc0fe79c0..3899d164f16a 100644
--- a/nixpkgs/nixos/modules/services/networking/epmd.nix
+++ b/nixpkgs/nixos/modules/services/networking/epmd.nix
@@ -4,9 +4,7 @@ with lib;
 
 let
   cfg = config.services.epmd;
-
 in
-
 {
   ###### interface
   options.services.epmd = {
@@ -27,16 +25,31 @@ in
         an Erlang runtime that is already installed for other purposes.
       '';
     };
+    listenStream = mkOption
+      {
+        type = types.str;
+        default = "[::]:4369";
+        description = ''
+          the listenStream used by the systemd socket.
+          see https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream= for more informations.
+          use this to change the port epmd will run on.
+          if not defined, epmd will use "[::]:4369"
+        '';
+      };
   };
 
   ###### implementation
   config = mkIf cfg.enable {
+    assertions = [{
+      assertion = cfg.listenStream == "[::]:4369" -> config.networking.enableIPv6;
+      message = "epmd listens by default on ipv6, enable ipv6 or change config.services.epmd.listenStream";
+    }];
     systemd.sockets.epmd = rec {
       description = "Erlang Port Mapper Daemon Activation Socket";
       wantedBy = [ "sockets.target" ];
       before = wantedBy;
       socketConfig = {
-        ListenStream = "4369";
+        ListenStream = cfg.listenStream;
         Accept = "false";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/networking/firewall.nix b/nixpkgs/nixos/modules/services/networking/firewall.nix
index cdc3a172ea70..f982621e2328 100644
--- a/nixpkgs/nixos/modules/services/networking/firewall.nix
+++ b/nixpkgs/nixos/modules/services/networking/firewall.nix
@@ -339,6 +339,8 @@ in
         description =
           ''
             Whether to log rejected or dropped incoming connections.
+            Note: The logs are found in the kernel logs, i.e. dmesg
+            or journalctl -k.
           '';
       };
 
@@ -350,6 +352,8 @@ in
             Whether to log all rejected or dropped incoming packets.
             This tends to give a lot of log messages, so it's mostly
             useful for debugging.
+            Note: The logs are found in the kernel logs, i.e. dmesg
+            or journalctl -k.
           '';
       };
 
diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix
index 93a21fd4c97e..fba0d817006e 100644
--- a/nixpkgs/nixos/modules/services/networking/i2pd.nix
+++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix
@@ -32,9 +32,9 @@ let
       description = "Bind address for ${name} endpoint.";
     };
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = port;
-      description = "Bind port for ${name} endoint.";
+      description = "Bind port for ${name} endpoint.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/networking/iwd.nix b/nixpkgs/nixos/modules/services/networking/iwd.nix
index 99e5e78badd2..8835f7f9372d 100644
--- a/nixpkgs/nixos/modules/services/networking/iwd.nix
+++ b/nixpkgs/nixos/modules/services/networking/iwd.nix
@@ -4,8 +4,31 @@ with lib;
 
 let
   cfg = config.networking.wireless.iwd;
+  ini = pkgs.formats.ini { };
+  configFile = ini.generate "main.conf" cfg.settings;
 in {
-  options.networking.wireless.iwd.enable = mkEnableOption "iwd";
+  options.networking.wireless.iwd = {
+    enable = mkEnableOption "iwd";
+
+    settings = mkOption {
+      type = ini.type;
+      default = { };
+
+      example = {
+        Settings.AutoConnect = true;
+
+        Network = {
+          EnableIPv6 = true;
+          RoutePriorityOffset = 300;
+        };
+      };
+
+      description = ''
+        Options passed to iwd.
+        See <link xlink:href="https://iwd.wiki.kernel.org/networkconfigurationsettings">here</link> for supported options.
+      '';
+    };
+  };
 
   config = mkIf cfg.enable {
     assertions = [{
@@ -15,6 +38,8 @@ in {
       '';
     }];
 
+    environment.etc."iwd/main.conf".source = configFile;
+
     # for iwctl
     environment.systemPackages =  [ pkgs.iwd ];
 
@@ -27,7 +52,10 @@ in {
       linkConfig.NamePolicy = "keep kernel";
     };
 
-    systemd.services.iwd.wantedBy = [ "multi-user.target" ];
+    systemd.services.iwd = {
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ configFile ];
+    };
   };
 
   meta.maintainers = with lib.maintainers; [ mic92 dtzWill ];
diff --git a/nixpkgs/nixos/modules/services/networking/kea.nix b/nixpkgs/nixos/modules/services/networking/kea.nix
index 72773b83a496..b11402204aec 100644
--- a/nixpkgs/nixos/modules/services/networking/kea.nix
+++ b/nixpkgs/nixos/modules/services/networking/kea.nix
@@ -238,6 +238,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        ctrlAgentConfig
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
         KillMode = "process";
@@ -269,6 +273,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        dhcp4Config
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
         # Kea does not request capabilities by itself
@@ -307,6 +315,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        dhcp6Config
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}";
         # Kea does not request capabilities by itself
@@ -343,6 +355,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        dhcpDdnsConfig
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}";
         AmbientCapabilites = [
diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
index 8ce71f26b3ee..9ec1ddc929e1 100644
--- a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
+++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
@@ -9,6 +9,7 @@ with lib;
     default = false;
     description = ''
       This option enables Mullvad VPN daemon.
+      This sets <option>networking.firewall.checkReversePath</option> to "loose", which might be undesirable for security.
     '';
   };
 
@@ -18,6 +19,9 @@ with lib;
     # mullvad-daemon writes to /etc/iproute2/rt_tables
     networking.iproute2.enable = true;
 
+    # See https://github.com/NixOS/nixpkgs/issues/113589
+    networking.firewall.checkReversePath = "loose";
+
     systemd.services.mullvad-daemon = {
       description = "Mullvad VPN daemon";
       wantedBy = [ "multi-user.target" ];
@@ -42,5 +46,5 @@ with lib;
     };
   };
 
-  meta.maintainers = [ maintainers.xfix ];
+  meta.maintainers = with maintainers; [ ymarkus ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/nats.nix b/nixpkgs/nixos/modules/services/networking/nats.nix
new file mode 100644
index 000000000000..eb0c65bc6561
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nats.nix
@@ -0,0 +1,159 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.nats;
+
+  format = pkgs.formats.json { };
+
+  configFile = format.generate "nats.conf" cfg.settings;
+
+in {
+
+  ### Interface
+
+  options = {
+    services.nats = {
+      enable = mkEnableOption "NATS messaging system";
+
+      user = mkOption {
+        type = types.str;
+        default = "nats";
+        description = "User account under which NATS runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nats";
+        description = "Group under which NATS runs.";
+      };
+
+      serverName = mkOption {
+        default = "nats";
+        example = "n1-c3";
+        type = types.str;
+        description = ''
+          Name of the NATS server, must be unique if clustered.
+        '';
+      };
+
+      jetstream = mkEnableOption "JetStream";
+
+      port = mkOption {
+        default = 4222;
+        example = 4222;
+        type = types.port;
+        description = ''
+          Port on which to listen.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = "/var/lib/nats";
+        type = types.path;
+        description = ''
+          The NATS data directory. Only used if JetStream is enabled, for
+          storing stream metadata and messages.
+
+          If left as the default value this directory will automatically be
+          created before the NATS server starts, otherwise the sysadmin is
+          responsible for ensuring the directory exists with appropriate
+          ownership and permissions.
+        '';
+      };
+
+      settings = mkOption {
+        default = { };
+        type = format.type;
+        example = literalExample ''
+          {
+            jetstream = {
+              max_mem = "1G";
+              max_file = "10G";
+            };
+          };
+        '';
+        description = ''
+          Declarative NATS configuration. See the
+          <link xlink:href="https://docs.nats.io/nats-server/configuration">
+          NATS documentation</link> for a list of options.
+        '';
+      };
+    };
+  };
+
+  ### Implementation
+
+  config = mkIf cfg.enable {
+    services.nats.settings = {
+      server_name = cfg.serverName;
+      port = cfg.port;
+      jetstream = optionalAttrs cfg.jetstream { store_dir = cfg.dataDir; };
+    };
+
+    systemd.services.nats = {
+      description = "NATS messaging system";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = mkMerge [
+        (mkIf (cfg.dataDir == "/var/lib/nats") {
+          StateDirectory = "nats";
+          StateDirectoryMode = "0750";
+        })
+        {
+          Type = "simple";
+          ExecStart = "${pkgs.nats-server}/bin/nats-server -c ${configFile}";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+          ExecStop = "${pkgs.coreutils}/bin/kill -SIGINT $MAINPID";
+          Restart = "on-failure";
+
+          User = cfg.user;
+          Group = cfg.group;
+
+          # Hardening
+          CapabilityBoundingSet = "";
+          LimitNOFILE = 800000; # JetStream requires 2 FDs open per stream.
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          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";
+          ReadOnlyPaths = [ ];
+          ReadWritePaths = [ cfg.dataDir ];
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+          UMask = "0077";
+        }
+      ];
+    };
+
+    users.users = mkIf (cfg.user == "nats") {
+      nats = {
+        description = "NATS daemon user";
+        isSystemUser = true;
+        group = cfg.group;
+        home = cfg.dataDir;
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "nats") { nats = { }; };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
index 064018057cdb..c8861171dd6c 100644
--- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -6,7 +6,6 @@ let
   cfg = config.networking.networkmanager;
 
   basePackages = with pkgs; [
-    crda
     modemmanager
     networkmanager
     networkmanager-fortisslvpn
@@ -49,6 +48,7 @@ let
       rc-manager =
         if config.networking.resolvconf.enable then "resolvconf"
         else "unmanaged";
+      firewall-backend = cfg.firewallBackend;
     })
     (mkSection "keyfile" {
       unmanaged-devices =
@@ -244,6 +244,15 @@ in {
         '';
       };
 
+      firewallBackend = mkOption {
+        type = types.enum [ "iptables" "nftables" "none" ];
+        default = "iptables";
+        description = ''
+          Which firewall backend should be used for configuring masquerading with shared mode.
+          If set to none, NetworkManager doesn't manage the configuration at all.
+        '';
+      };
+
       logLevel = mkOption {
         type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
         default = "WARN";
@@ -404,6 +413,8 @@ in {
       }
     ];
 
+    hardware.wirelessRegulatoryDatabase = true;
+
     environment.etc = with pkgs; {
       "NetworkManager/NetworkManager.conf".source = configFile;
 
diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix
index cb75142965ea..72f37c32253e 100644
--- a/nixpkgs/nixos/modules/services/networking/nftables.nix
+++ b/nixpkgs/nixos/modules/services/networking/nftables.nix
@@ -103,6 +103,7 @@ in
     }];
     boot.blacklistedKernelModules = [ "ip_tables" ];
     environment.systemPackages = [ pkgs.nftables ];
+    networking.networkmanager.firewallBackend = mkDefault "nftables";
     systemd.services.nftables = {
       description = "nftables firewall";
       before = [ "network-pre.target" ];
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
index 96c6444c23a1..ed61c178c685 100644
--- a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
+++ b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
@@ -81,17 +81,26 @@ in
         '';
       };
 
-      initstepslew = mkOption {
-        type = types.attrsOf (types.either types.bool types.int);
-        default = {
-          enabled = true;
-          threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s)
+      initstepslew = {
+        enabled = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Allow chronyd to make a rapid measurement of the system clock error
+            at boot time, and to correct the system clock by stepping before
+            normal operation begins.
+          '';
+        };
+
+        threshold = mkOption {
+          type = types.either types.float types.int;
+          default = 1000; # by default, same threshold as 'ntpd -g' (1000s)
+          description = ''
+            The threshold of system clock error (in seconds) above which the
+            clock will be stepped. If the correction required is less than the
+            threshold, a slew is used instead.
+          '';
         };
-        description = ''
-          Allow chronyd to make a rapid measurement of the system clock error at
-          boot time, and to correct the system clock by stepping before normal
-          operation begins.
-        '';
       };
 
       directory = mkOption {
@@ -148,7 +157,7 @@ in
         wantedBy = [ "multi-user.target" ];
         wants    = [ "time-sync.target" ];
         before   = [ "time-sync.target" ];
-        after    = [ "network.target" ];
+        after    = [ "network.target" "nss-lookup.target" ];
         conflicts = [ "ntpd.service" "systemd-timesyncd.service" ];
 
         path = [ chronyPkg ];
diff --git a/nixpkgs/nixos/modules/services/networking/nylon.nix b/nixpkgs/nixos/modules/services/networking/nylon.nix
index bfc358cb12fb..a20fa615af80 100644
--- a/nixpkgs/nixos/modules/services/networking/nylon.nix
+++ b/nixpkgs/nixos/modules/services/networking/nylon.nix
@@ -160,7 +160,7 @@ in
 
     users.groups.nylon.gid = config.ids.gids.nylon;
 
-    systemd.services = fold (a: b: a // b) {} nylonUnits;
+    systemd.services = foldr (a: b: a // b) {} nylonUnits;
 
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/quicktun.nix b/nixpkgs/nixos/modules/services/networking/quicktun.nix
index fb783c836464..438e67d5ebb6 100644
--- a/nixpkgs/nixos/modules/services/networking/quicktun.nix
+++ b/nixpkgs/nixos/modules/services/networking/quicktun.nix
@@ -87,7 +87,7 @@ with lib;
   };
 
   config = mkIf (cfg != []) {
-    systemd.services = fold (a: b: a // b) {} (
+    systemd.services = foldr (a: b: a // b) {} (
       mapAttrsToList (name: qtcfg: {
         "quicktun-${name}" = {
           wantedBy = [ "multi-user.target" ];
diff --git a/nixpkgs/nixos/modules/services/networking/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix
index a808a7f39d05..405808491ea4 100644
--- a/nixpkgs/nixos/modules/services/networking/shout.nix
+++ b/nixpkgs/nixos/modules/services/networking/shout.nix
@@ -41,7 +41,7 @@ in {
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 9000;
       description = "TCP port to listen on for http connections.";
     };
diff --git a/nixpkgs/nixos/modules/services/networking/soju.nix b/nixpkgs/nixos/modules/services/networking/soju.nix
new file mode 100644
index 000000000000..68a33e9dccba
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/soju.nix
@@ -0,0 +1,113 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.soju;
+  stateDir = "/var/lib/soju";
+  listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") cfg.listen;
+  tlsCfg = optionalString (cfg.tlsCertificate != null)
+    "tls ${cfg.tlsCertificate} ${cfg.tlsCertificateKey}";
+  logCfg = optionalString cfg.enableMessageLogging
+    "log fs ${stateDir}/logs";
+
+  configFile = pkgs.writeText "soju.conf" ''
+    ${listenCfg}
+    hostname ${cfg.hostName}
+    ${tlsCfg}
+    db sqlite3 ${stateDir}/soju.db
+    ${logCfg}
+    http-origin ${concatStringsSep " " cfg.httpOrigins}
+    accept-proxy-ip ${concatStringsSep " " cfg.acceptProxyIP}
+
+    ${cfg.extraConfig}
+  '';
+in
+{
+  ###### interface
+
+  options.services.soju = {
+    enable = mkEnableOption "soju";
+
+    listen = mkOption {
+      type = types.listOf types.str;
+      default = [ ":6697" ];
+      description = ''
+        Where soju should listen for incoming connections. See the
+        <literal>listen</literal> directive in
+        <citerefentry><refentrytitle>soju</refentrytitle>
+        <manvolnum>1</manvolnum></citerefentry>.
+      '';
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      default = config.networking.hostName;
+      description = "Server hostname.";
+    };
+
+    tlsCertificate = mkOption {
+      type = types.nullOr types.path;
+      example = "/var/host.cert";
+      description = "Path to server TLS certificate.";
+    };
+
+    tlsCertificateKey = mkOption {
+      type = types.nullOr types.path;
+      example = "/var/host.key";
+      description = "Path to server TLS certificate key.";
+    };
+
+    enableMessageLogging = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to enable message logging.";
+    };
+
+    httpOrigins = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        List of allowed HTTP origins for WebSocket listeners. The parameters are
+        interpreted as shell patterns, see
+        <citerefentry><refentrytitle>glob</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
+
+    acceptProxyIP = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Allow the specified IPs to act as a proxy. Proxys have the ability to
+        overwrite the remote and local connection addresses (via the X-Forwarded-\*
+        HTTP header fields). The special name "localhost" accepts the loopback
+        addresses 127.0.0.0/8 and ::1/128. By default, all IPs are rejected.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = "Lines added verbatim to the configuration file.";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.soju = {
+      description = "soju IRC bouncer";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        DynamicUser = true;
+        Restart = "always";
+        ExecStart = "${pkgs.soju}/bin/soju -config ${configFile}";
+        StateDirectory = "soju";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ malvo ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index d804c017f5d6..04879eb7d82d 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -531,11 +531,7 @@ in
             XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
         ''}
 
-        ${if cfg.forwardX11 then ''
-          X11Forwarding yes
-        '' else ''
-          X11Forwarding no
-        ''}
+        X11Forwarding ${if cfg.forwardX11 then "yes" else "no"}
 
         ${optionalString cfg.allowSFTP ''
           Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags}
@@ -568,11 +564,7 @@ in
 
         LogLevel ${cfg.logLevel}
 
-        ${if cfg.useDns then ''
-          UseDNS yes
-        '' else ''
-          UseDNS no
-        ''}
+        UseDNS ${if cfg.useDns then "yes" else "no"}
 
         StrictModes ${if cfg.strictModes then "yes" else "no"}
 
diff --git a/nixpkgs/nixos/modules/services/networking/syncplay.nix b/nixpkgs/nixos/modules/services/networking/syncplay.nix
index e3147c10502c..27a16fb2e29f 100644
--- a/nixpkgs/nixos/modules/services/networking/syncplay.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncplay.nix
@@ -21,7 +21,7 @@ in
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = 8999;
         description = ''
           TCP port to bind to.
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
index 28348c7893a0..ebe4d89a0e7f 100644
--- a/nixpkgs/nixos/modules/services/networking/syncthing.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -5,15 +5,16 @@ with lib;
 let
   cfg = config.services.syncthing;
   defaultUser = "syncthing";
+  defaultGroup = defaultUser;
 
   devices = mapAttrsToList (name: device: {
     deviceID = device.id;
-    inherit (device) name addresses introducer;
-  }) cfg.declarative.devices;
+    inherit (device) name addresses introducer autoAcceptFolders;
+  }) cfg.devices;
 
   folders = mapAttrsToList ( _: folder: {
     inherit (folder) path id label type;
-    devices = map (device: { deviceId = cfg.declarative.devices.${device}.id; }) folder.devices;
+    devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices;
     rescanIntervalS = folder.rescanInterval;
     fsWatcherEnabled = folder.watch;
     fsWatcherDelayS = folder.watchDelay;
@@ -23,215 +24,227 @@ let
   }) (filterAttrs (
     _: folder:
     folder.enable
-  ) cfg.declarative.folders);
-
-  # get the api key by parsing the config.xml
-  getApiKey = pkgs.writers.writeDash "getAPIKey" ''
-    ${pkgs.libxml2}/bin/xmllint \
-      --xpath 'string(configuration/gui/apikey)'\
-      ${cfg.configDir}/config.xml
-  '';
+  ) cfg.folders);
 
   updateConfig = pkgs.writers.writeDash "merge-syncthing-config" ''
     set -efu
-    # wait for syncthing port to open
-    until ${pkgs.curl}/bin/curl -Ss ${cfg.guiAddress} -o /dev/null; do
-      sleep 1
-    done
-
-    API_KEY=$(${getApiKey})
-    OLD_CFG=$(${pkgs.curl}/bin/curl -Ss \
-      -H "X-API-Key: $API_KEY" \
-      ${cfg.guiAddress}/rest/system/config)
-
-    # generate the new config by merging with the nixos config options
-    NEW_CFG=$(echo "$OLD_CFG" | ${pkgs.jq}/bin/jq -s '.[] as $in | $in * {
-      "devices": (${builtins.toJSON devices}${optionalString (! cfg.declarative.overrideDevices) " + $in.devices"}),
-      "folders": (${builtins.toJSON folders}${optionalString (! cfg.declarative.overrideFolders) " + $in.folders"})
-    }')
-
-    # POST the new config to syncthing
-    echo "$NEW_CFG" | ${pkgs.curl}/bin/curl -Ss \
-      -H "X-API-Key: $API_KEY" \
-      ${cfg.guiAddress}/rest/system/config -d @-
-
-    # restart syncthing after sending the new config
-    ${pkgs.curl}/bin/curl -Ss \
-      -H "X-API-Key: $API_KEY" \
-      -X POST \
-      ${cfg.guiAddress}/rest/system/restart
+
+    # get the api key by parsing the config.xml
+    while
+        ! api_key=$(${pkgs.libxml2}/bin/xmllint \
+            --xpath 'string(configuration/gui/apikey)' \
+            ${cfg.configDir}/config.xml)
+    do sleep 1; done
+
+    curl() {
+        ${pkgs.curl}/bin/curl -sSLk -H "X-API-Key: $api_key" \
+            --retry 1000 --retry-delay 1 --retry-all-errors \
+            "$@"
+    }
+
+    # query the old config
+    old_cfg=$(curl ${cfg.guiAddress}/rest/config)
+
+    # generate the new config by merging with the NixOS config options
+    new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * {
+        "devices": (${builtins.toJSON devices}${optionalString (! cfg.overrideDevices) " + .devices"}),
+        "folders": (${builtins.toJSON folders}${optionalString (! cfg.overrideFolders) " + .folders"})
+    } * ${builtins.toJSON cfg.extraOptions}')
+
+    # send the new config
+    curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config
+
+    # restart Syncthing if required
+    if curl ${cfg.guiAddress}/rest/config/restart-required |
+       ${pkgs.jq}/bin/jq -e .requiresRestart > /dev/null; then
+        curl -X POST ${cfg.guiAddress}/rest/system/restart
+    fi
   '';
 in {
   ###### interface
   options = {
     services.syncthing = {
 
-      enable = mkEnableOption ''
-        Syncthing - the self-hosted open-source alternative
-        to Dropbox and Bittorrent Sync. Initial interface will be
-        available on http://127.0.0.1:8384/.
-      '';
-
-      declarative = {
-        cert = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Path to users cert.pem file, will be copied into the syncthing's
-            <literal>configDir</literal>
-          '';
-        };
+      enable = mkEnableOption
+        "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync";
 
-        key = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Path to users key.pem file, will be copied into the syncthing's
-            <literal>configDir</literal>
-          '';
-        };
+      cert = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Path to the <literal>cert.pem</literal> file, which will be copied into Syncthing's
+          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        '';
+      };
 
-        overrideDevices = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            Whether to delete the devices which are not configured via the
-            <literal>declarative.devices</literal> option.
-            If set to false, devices added via the webinterface will
-            persist but will have to be deleted manually.
-          '';
+      key = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Path to the <literal>key.pem</literal> file, which will be copied into Syncthing's
+          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        '';
+      };
+
+      overrideDevices = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to delete the devices which are not configured via the
+          <link linkend="opt-services.syncthing.devices">devices</link> option.
+          If set to <literal>false</literal>, devices added via the web
+          interface will persist and will have to be deleted manually.
+        '';
+      };
+
+      devices = mkOption {
+        default = {};
+        description = ''
+          Peers/devices which Syncthing should communicate with.
+
+          Note that you can still add devices manually, but those changes
+          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          is enabled.
+        '';
+        example = {
+          bigbox = {
+            id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
+            addresses = [ "tcp://192.168.0.10:51820" ];
+          };
         };
+        type = types.attrsOf (types.submodule ({ name, ... }: {
+          options = {
+
+            name = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The name of the device.
+              '';
+            };
 
-        devices = mkOption {
-          default = {};
-          description = ''
-            Peers/devices which syncthing should communicate with.
-          '';
-          example = {
-            bigbox = {
-              id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
-              addresses = [ "tcp://192.168.0.10:51820" ];
+            addresses = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                The addresses used to connect to the device.
+                If this is left empty, dynamic configuration is attempted.
+              '';
+            };
+
+            id = mkOption {
+              type = types.str;
+              description = ''
+                The device ID. See <link xlink:href="https://docs.syncthing.net/dev/device-ids.html"/>.
+              '';
+            };
+
+            introducer = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether the device should act as an introducer and be allowed
+                to add folders on this computer.
+                See <link xlink:href="https://docs.syncthing.net/users/introducer.html"/>.
+              '';
             };
+
+            autoAcceptFolders = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Automatically create or share folders that this device advertises at the default path.
+                See <link xlink:href="https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format"/>.
+              '';
+            };
+
           };
-          type = types.attrsOf (types.submodule ({ name, ... }: {
-            options = {
-
-              name = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  Name of the device
-                '';
-              };
-
-              addresses = mkOption {
-                type = types.listOf types.str;
-                default = [];
-                description = ''
-                  The addresses used to connect to the device.
-                  If this is let empty, dynamic configuration is attempted
-                '';
-              };
-
-              id = mkOption {
-                type = types.str;
-                description = ''
-                  The id of the other peer, this is mandatory. It's documented at
-                  https://docs.syncthing.net/dev/device-ids.html
-                '';
-              };
-
-              introducer = mkOption {
-                type = types.bool;
-                default = false;
-                description = ''
-                  If the device should act as an introducer and be allowed
-                  to add folders on this computer.
-                '';
-              };
+        }));
+      };
 
+      overrideFolders = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to delete the folders which are not configured via the
+          <link linkend="opt-services.syncthing.folders">folders</link> option.
+          If set to <literal>false</literal>, folders added via the web
+          interface will persist and will have to be deleted manually.
+        '';
+      };
+
+      folders = mkOption {
+        default = {};
+        description = ''
+          Folders which should be shared by Syncthing.
+
+          Note that you can still add devices manually, but those changes
+          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          is enabled.
+        '';
+        example = literalExample ''
+          {
+            "/home/user/sync" = {
+              id = "syncme";
+              devices = [ "bigbox" ];
+            };
+          }
+        '';
+        type = types.attrsOf (types.submodule ({ name, ... }: {
+          options = {
+
+            enable = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether to share this folder.
+                This option is useful when you want to define all folders
+                in one place, but not every machine should share all folders.
+              '';
             };
-          }));
-        };
 
-        overrideFolders = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            Whether to delete the folders which are not configured via the
-            <literal>declarative.folders</literal> option.
-            If set to false, folders added via the webinterface will persist
-            but will have to be deleted manually.
-          '';
-        };
+            path = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The path to the folder which should be shared.
+              '';
+            };
 
-        folders = mkOption {
-          default = {};
-          description = ''
-            folders which should be shared by syncthing.
-          '';
-          example = literalExample ''
-            {
-              "/home/user/sync" = {
-                id = "syncme";
-                devices = [ "bigbox" ];
-              };
-            }
-          '';
-          type = types.attrsOf (types.submodule ({ name, ... }: {
-            options = {
-
-              enable = mkOption {
-                type = types.bool;
-                default = true;
-                description = ''
-                  share this folder.
-                  This option is useful when you want to define all folders
-                  in one place, but not every machine should share all folders.
-                '';
-              };
-
-              path = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  The path to the folder which should be shared.
-                '';
-              };
-
-              id = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  The id of the folder. Must be the same on all devices.
-                '';
-              };
-
-              label = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  The label of the folder.
-                '';
-              };
-
-              devices = mkOption {
-                type = types.listOf types.str;
-                default = [];
-                description = ''
-                  The devices this folder should be shared with. Must be defined
-                  in the <literal>declarative.devices</literal> attribute.
-                '';
-              };
-
-              versioning = mkOption {
-                default = null;
-                description = ''
-                  How to keep changed/deleted files with syncthing.
-                  There are 4 different types of versioning with different parameters.
-                  See https://docs.syncthing.net/users/versioning.html
-                '';
-                example = [
+            id = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The ID of the folder. Must be the same on all devices.
+              '';
+            };
+
+            label = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The label of the folder.
+              '';
+            };
+
+            devices = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                The devices this folder should be shared with. Each device must
+                be defined in the <link linkend="opt-services.syncthing.devices">devices</link> option.
+              '';
+            };
+
+            versioning = mkOption {
+              default = null;
+              description = ''
+                How to keep changed/deleted files with Syncthing.
+                There are 4 different types of versioning with different parameters.
+                See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+              '';
+              example = literalExample ''
+                [
                   {
                     versioning = {
                       type = "simple";
@@ -257,87 +270,99 @@ in {
                   {
                     versioning = {
                       type = "external";
-                      params.versionsPath = pkgs.writers.writeBash "backup" ''
+                      params.versionsPath = pkgs.writers.writeBash "backup" '''
                         folderpath="$1"
                         filepath="$2"
                         rm -rf "$folderpath/$filepath"
-                      '';
+                      ''';
                     };
                   }
-                ];
-                type = with types; nullOr (submodule {
-                  options = {
-                    type = mkOption {
-                      type = enum [ "external" "simple" "staggered" "trashcan" ];
-                      description = ''
-                        Type of versioning.
-                        See https://docs.syncthing.net/users/versioning.html
-                      '';
-                    };
-                    params = mkOption {
-                      type = attrsOf (either str path);
-                      description = ''
-                        Parameters for versioning. Structure depends on versioning.type.
-                        See https://docs.syncthing.net/users/versioning.html
-                      '';
-                    };
+                ]
+              '';
+              type = with types; nullOr (submodule {
+                options = {
+                  type = mkOption {
+                    type = enum [ "external" "simple" "staggered" "trashcan" ];
+                    description = ''
+                      The type of versioning.
+                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                    '';
+                  };
+                  params = mkOption {
+                    type = attrsOf (either str path);
+                    description = ''
+                      The parameters for versioning. Structure depends on
+                      <link linkend="opt-services.syncthing.folders._name_.versioning.type">versioning.type</link>.
+                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                    '';
                   };
-                });
-              };
-
-              rescanInterval = mkOption {
-                type = types.int;
-                default = 3600;
-                description = ''
-                  How often the folders should be rescaned for changes.
-                '';
-              };
-
-              type = mkOption {
-                type = types.enum [ "sendreceive" "sendonly" "receiveonly" ];
-                default = "sendreceive";
-                description = ''
-                  Whether to send only changes from this folder, only receive them
-                  or propagate both.
-                '';
-              };
-
-              watch = mkOption {
-                type = types.bool;
-                default = true;
-                description = ''
-                  Whether the folder should be watched for changes by inotify.
-                '';
-              };
-
-              watchDelay = mkOption {
-                type = types.int;
-                default = 10;
-                description = ''
-                  The delay after an inotify event is triggered.
-                '';
-              };
-
-              ignorePerms = mkOption {
-                type = types.bool;
-                default = true;
-                description = ''
-                  Whether to propagate permission changes.
-                '';
-              };
-
-              ignoreDelete = mkOption {
-                type = types.bool;
-                default = false;
-                description = ''
-                  Whether to delete files in destination. See <link
-                  xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html">
-                  upstream's docs</link>.
-                '';
-              };
+                };
+              });
+            };
+
+            rescanInterval = mkOption {
+              type = types.int;
+              default = 3600;
+              description = ''
+                How often the folder should be rescanned for changes.
+              '';
+            };
 
+            type = mkOption {
+              type = types.enum [ "sendreceive" "sendonly" "receiveonly" ];
+              default = "sendreceive";
+              description = ''
+                Whether to only send changes for this folder, only receive them
+                or both.
+              '';
             };
-          }));
+
+            watch = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether the folder should be watched for changes by inotify.
+              '';
+            };
+
+            watchDelay = mkOption {
+              type = types.int;
+              default = 10;
+              description = ''
+                The delay after an inotify event is triggered.
+              '';
+            };
+
+            ignorePerms = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether to ignore permission changes.
+              '';
+            };
+
+            ignoreDelete = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether to skip deleting files that are deleted by peers.
+                See <link xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"/>.
+              '';
+            };
+          };
+        }));
+      };
+
+      extraOptions = mkOption {
+        type = types.addCheck (pkgs.formats.json {}).type isAttrs;
+        default = {};
+        description = ''
+          Extra configuration options for Syncthing.
+          See <link xlink:href="https://docs.syncthing.net/users/config.html"/>.
+        '';
+        example = {
+          options.localAnnounceEnabled = false;
+          gui.theme = "black";
         };
       };
 
@@ -345,31 +370,35 @@ in {
         type = types.str;
         default = "127.0.0.1:8384";
         description = ''
-          Address to serve the GUI.
+          The address to serve the web interface at.
         '';
       };
 
       systemService = mkOption {
         type = types.bool;
         default = true;
-        description = "Auto launch Syncthing as a system service.";
+        description = ''
+          Whether to auto-launch Syncthing as a system service.
+        '';
       };
 
       user = mkOption {
         type = types.str;
         default = defaultUser;
+        example = "yourUser";
         description = ''
-          Syncthing will be run under this user (user will be created if it doesn't exist.
-          This can be your user name).
+          The user to run Syncthing as.
+          By default, a user named <literal>${defaultUser}</literal> will be created.
         '';
       };
 
       group = mkOption {
         type = types.str;
-        default = defaultUser;
+        default = defaultGroup;
+        example = "yourGroup";
         description = ''
-          Syncthing will be run under this group (group will not be created if it doesn't exist.
-          This can be your user name).
+          The group to run Syncthing under.
+          By default, a group named <literal>${defaultGroup}</literal> will be created.
         '';
       };
 
@@ -378,63 +407,76 @@ in {
         default = null;
         example = "socks5://address.com:1234";
         description = ''
-          Overwrites all_proxy environment variable for the syncthing process to
-          the given value. This is normaly used to let relay client connect
-          through SOCKS5 proxy server.
+          Overwrites the all_proxy environment variable for the Syncthing process to
+          the given value. This is normally used to let Syncthing connect
+          through a SOCKS5 proxy server.
+          See <link xlink:href="https://docs.syncthing.net/users/proxying.html"/>.
         '';
       };
 
       dataDir = mkOption {
         type = types.path;
         default = "/var/lib/syncthing";
+        example = "/home/yourUser";
         description = ''
-          Path where synced directories will exist.
+          The path where synchronised directories will exist.
         '';
       };
 
-      configDir = mkOption {
+      configDir = let
+        cond = versionAtLeast config.system.stateVersion "19.03";
+      in mkOption {
         type = types.path;
         description = ''
-          Path where the settings and keys will exist.
+          The path where the settings and keys will exist.
+        '';
+        default = cfg.dataDir + (optionalString cond "/.config/syncthing");
+        defaultText = literalExample "dataDir${optionalString cond " + \"/.config/syncthing\""}";
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "--reset-deltas" ];
+        description = ''
+          Extra flags passed to the syncthing command in the service definition.
         '';
-        default =
-          let
-            nixos = config.system.stateVersion;
-            cond  = versionAtLeast nixos "19.03";
-          in cfg.dataDir + (optionalString cond "/.config/syncthing");
       };
 
       openDefaultPorts = mkOption {
         type = types.bool;
         default = false;
-        example = literalExample "true";
+        example = true;
         description = ''
-          Open the default ports in the firewall:
-            - TCP 22000 for transfers
-            - UDP 21027 for discovery
-          If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled.
-          Alternatively, if are running only a single instance on this machine using the default ports, enable this.
+          Whether to open the default ports in the firewall: TCP 22000 for transfers
+          and UDP 21027 for discovery.
+
+          If multiple users are running Syncthing on this machine, you will need
+          to manually open a set of ports for each instance and leave this disabled.
+          Alternatively, if you are running only a single instance on this machine
+          using the default ports, enable this.
         '';
       };
 
       package = mkOption {
         type = types.package;
         default = pkgs.syncthing;
-        defaultText = "pkgs.syncthing";
-        example = literalExample "pkgs.syncthing";
+        defaultText = literalExample "pkgs.syncthing";
         description = ''
-          Syncthing package to use.
+          The Syncthing package to use.
         '';
       };
     };
   };
 
   imports = [
-    (mkRemovedOptionModule ["services" "syncthing" "useInotify"] ''
-      This option was removed because syncthing now has the inotify functionality included under the name "fswatcher".
-      It can be enabled on a per-folder basis through the webinterface.
+    (mkRemovedOptionModule [ "services" "syncthing" "useInotify" ] ''
+      This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher".
+      It can be enabled on a per-folder basis through the web interface.
     '')
-  ];
+  ] ++ map (o:
+    mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ]
+  ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"];
 
   ###### implementation
 
@@ -457,8 +499,8 @@ in {
         };
     };
 
-    users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) {
-      ${defaultUser}.gid =
+    users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) {
+      ${defaultGroup}.gid =
         config.ids.gids.syncthing;
     };
 
@@ -478,14 +520,14 @@ in {
           RestartForceExitStatus="3 4";
           User = cfg.user;
           Group = cfg.group;
-          ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null)
+          ExecStartPre = mkIf (cfg.cert != null || cfg.key != null)
             "+${pkgs.writers.writeBash "syncthing-copy-keys" ''
               install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir}
-              ${optionalString (cfg.declarative.cert != null) ''
-                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
+              ${optionalString (cfg.cert != null) ''
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.cert} ${cfg.configDir}/cert.pem
               ''}
-              ${optionalString (cfg.declarative.key != null) ''
-                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
+              ${optionalString (cfg.key != null) ''
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.key} ${cfg.configDir}/key.pem
               ''}
             ''}"
           ;
@@ -493,7 +535,7 @@ in {
             ${cfg.package}/bin/syncthing \
               -no-browser \
               -gui-address=${cfg.guiAddress} \
-              -home=${cfg.configDir}
+              -home=${cfg.configDir} ${escapeShellArgs cfg.extraFlags}
           '';
           MemoryDenyWriteExecute = true;
           NoNewPrivileges = true;
@@ -516,8 +558,10 @@ in {
         };
       };
       syncthing-init = mkIf (
-        cfg.declarative.devices != {} || cfg.declarative.folders != {}
+        cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {}
       ) {
+        description = "Syncthing configuration updater";
+        requisite = [ "syncthing.service" ];
         after = [ "syncthing.service" ];
         wantedBy = [ "multi-user.target" ];
 
diff --git a/nixpkgs/nixos/modules/services/networking/tinc.nix b/nixpkgs/nixos/modules/services/networking/tinc.nix
index b6afd83a9abd..22caf9f4ec56 100644
--- a/nixpkgs/nixos/modules/services/networking/tinc.nix
+++ b/nixpkgs/nixos/modules/services/networking/tinc.nix
@@ -351,7 +351,7 @@ in
 
   config = mkIf (cfg.networks != { }) {
 
-    environment.etc = fold (a: b: a // b) { }
+    environment.etc = foldr (a: b: a // b) { }
       (flip mapAttrsToList cfg.networks (network: data:
         flip mapAttrs' data.hosts (host: text: nameValuePair
           ("tinc/${network}/hosts/${host}")
@@ -427,9 +427,12 @@ in
       nameValuePair ("tinc.${network}") ({
         description = "Tinc daemon user for ${network}";
         isSystemUser = true;
+        group = "tinc.${network}";
       })
     );
-
+    users.groups = flip mapAttrs' cfg.networks (network: _:
+      nameValuePair "tinc.${network}" {}
+    );
   };
 
   meta.maintainers = with maintainers; [ minijackson ];
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 62bcf7a14972..2e320378cc9a 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -173,6 +173,41 @@ in
         User = "unifi";
         UMask = "0077";
         WorkingDirectory = "${stateDir}";
+
+        # Hardening
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+        # ProtectClock= adds DeviceAllow=char-rtc r
+        DeviceAllow = "";
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" ];
+
+        # Required for ProtectSystem=strict
+        BindPaths = [ stateDir ];
+
+        # Needs network access
+        PrivateNetwork = false;
+        # Cannot be true due to OpenJDK
+        MemoryDenyWriteExecute = false;
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/v2ray.nix b/nixpkgs/nixos/modules/services/networking/v2ray.nix
index 6a924a16449a..0b8b5b56e25b 100644
--- a/nixpkgs/nixos/modules/services/networking/v2ray.nix
+++ b/nixpkgs/nixos/modules/services/networking/v2ray.nix
@@ -25,7 +25,7 @@ with lib;
 
           Either <literal>configFile</literal> or <literal>config</literal> must be specified.
 
-          See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>.
+          See <link xlink:href="https://www.v2fly.org/en_US/config/overview.html"/>.
         '';
       };
 
@@ -47,7 +47,7 @@ with lib;
 
           Either `configFile` or `config` must be specified.
 
-          See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>.
+          See <link xlink:href="https://www.v2fly.org/en_US/config/overview.html"/>.
         '';
       };
     };
diff --git a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
index 35ff67937fc7..c6291366b0f1 100644
--- a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
+++ b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
@@ -19,7 +19,7 @@ let
     ${ethtool} -s ${interface} ${methodParameter {inherit method password;}}
   '';
 
-  concatStrings = fold (x: y: x + y) "";
+  concatStrings = foldr (x: y: x + y) "";
   lines = concatStrings (map (l: line l) interfaces);
 
 in
@@ -31,6 +31,20 @@ in
 
     services.wakeonlan.interfaces = mkOption {
       default = [ ];
+      type = types.listOf (types.submodule { options = {
+        interface = mkOption {
+          type = types.str;
+          description = "Interface to enable for Wake-On-Lan.";
+        };
+        method = mkOption {
+          type = types.enum [ "magicpacket" "password"];
+          description = "Wake-On-Lan method for this interface.";
+        };
+        password = mkOption {
+          type = types.strMatching "[a-fA-F0-9]{2}:([a-fA-F0-9]{2}:){4}[a-fA-F0-9]{2}";
+          description = "The password has the shape of six bytes in hexadecimal separated by a colon each.";
+        };
+      };});
       example = [
         {
           interface = "eth0";
diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
index c0a4ce40760a..155c6fdd0ab0 100644
--- a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
@@ -8,28 +8,108 @@ let
     else pkgs.wpa_supplicant;
 
   cfg = config.networking.wireless;
-  configFile = if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable then pkgs.writeText "wpa_supplicant.conf" ''
-    ${optionalString cfg.userControlled.enable ''
-      ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group}
-      update_config=1''}
-    ${cfg.extraConfig}
-    ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let
-      key = if psk != null
-        then ''"${psk}"''
-        else pskRaw;
-      baseAuth = if key != null
-        then "psk=${key}"
-        else "key_mgmt=NONE";
-    in ''
-      network={
-        ssid="${ssid}"
-        ${optionalString (priority != null) ''priority=${toString priority}''}
-        ${optionalString hidden "scan_ssid=1"}
-        ${if (auth != null) then auth else baseAuth}
-        ${extraConfig}
-      }
-    '') cfg.networks)}
-  '' else "/etc/wpa_supplicant.conf";
+
+  # Content of wpa_supplicant.conf
+  generatedConfig = concatStringsSep "\n" (
+    (mapAttrsToList mkNetwork cfg.networks)
+    ++ optional cfg.userControlled.enable (concatStringsSep "\n"
+      [ "ctrl_interface=/run/wpa_supplicant"
+        "ctrl_interface_group=${cfg.userControlled.group}"
+        "update_config=1"
+      ])
+    ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"''
+    ++ optional (cfg.extraConfig != "") cfg.extraConfig);
+
+  configFile =
+    if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable
+      then pkgs.writeText "wpa_supplicant.conf" generatedConfig
+      else "/etc/wpa_supplicant.conf";
+
+  # Creates a network block for wpa_supplicant.conf
+  mkNetwork = ssid: opts:
+  let
+    quote = x: ''"${x}"'';
+    indent = x: "  " + x;
+
+    pskString = if opts.psk != null
+      then quote opts.psk
+      else opts.pskRaw;
+
+    options = [
+      "ssid=${quote ssid}"
+      (if pskString != null || opts.auth != null
+        then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
+        else "key_mgmt=NONE")
+    ] ++ optional opts.hidden "scan_ssid=1"
+      ++ optional (pskString != null) "psk=${pskString}"
+      ++ optionals (opts.auth != null) (filter (x: x != "") (splitString "\n" opts.auth))
+      ++ optional (opts.priority != null) "priority=${toString opts.priority}"
+      ++ optional (opts.extraConfig != "") opts.extraConfig;
+  in ''
+    network={
+    ${concatMapStringsSep "\n" indent options}
+    }
+  '';
+
+  # Creates a systemd unit for wpa_supplicant bound to a given (or any) interface
+  mkUnit = iface:
+    let
+      deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device";
+      configStr = if cfg.allowAuxiliaryImperativeNetworks
+        then "-c /etc/wpa_supplicant.conf -I ${configFile}"
+        else "-c ${configFile}";
+    in {
+      description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}";
+
+      after = deviceUnit;
+      before = [ "network.target" ];
+      wants = [ "network.target" ];
+      requires = deviceUnit;
+      wantedBy = [ "multi-user.target" ];
+      stopIfChanged = false;
+
+      path = [ package ];
+
+      script =
+      ''
+        if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then
+          echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
+        fi
+
+        iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}"
+
+        ${if iface == null then ''
+          # detect interfaces automatically
+
+          # check if there are no wireless interfaces
+          if ! find -H /sys/class/net/* -name wireless | grep -q .; then
+            # if so, wait until one appears
+            echo "Waiting for wireless interfaces"
+            grep -q '^ACTION=add' < <(stdbuf -oL -- udevadm monitor -s net/wlan -pu)
+            # Note: the above line has been carefully written:
+            # 1. The process substitution avoids udevadm hanging (after grep has quit)
+            #    until it tries to write to the pipe again. Not even pipefail works here.
+            # 2. stdbuf is needed because udevadm output is buffered by default and grep
+            #    may hang until more udev events enter the pipe.
+          fi
+
+          # add any interface found to the daemon arguments
+          for name in $(find -H /sys/class/net/* -name wireless | cut -d/ -f 5); do
+            echo "Adding interface $name"
+            args+="''${args:+ -N} -i$name $iface_args"
+          done
+        '' else ''
+          # add known interface to the daemon arguments
+          args="-i${iface} $iface_args"
+        ''}
+
+        # finally start daemon
+        exec wpa_supplicant $args
+      '';
+    };
+
+  systemctl = "/run/current-system/systemd/bin/systemctl";
+
 in {
   options = {
     networking.wireless = {
@@ -42,11 +122,10 @@ in {
         description = ''
           The interfaces <command>wpa_supplicant</command> will use. If empty, it will
           automatically use all wireless interfaces.
-          <warning><para>
-            The automatic discovery of interfaces does not work reliably on boot:
-            it may fail and leave the system without network. When possible, specify
-            a known interface name.
-          </para></warning>
+
+          <note><para>
+            A separate wpa_supplicant instance will be started for each interface.
+          </para></note>
         '';
       };
 
@@ -66,6 +145,16 @@ in {
         '';
       };
 
+      scanOnLowSignal = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to periodically scan for (better) networks when the signal of
+          the current one is low. This will make roaming between access points
+          faster, but will consume more power.
+        '';
+      };
+
       networks = mkOption {
         type = types.attrsOf (types.submodule {
           options = {
@@ -94,11 +183,52 @@ in {
               '';
             };
 
+            authProtocols = mkOption {
+              default = [
+                # WPA2 and WPA3
+                "WPA-PSK" "WPA-EAP" "SAE"
+                # 802.11r variants of the above
+                "FT-PSK" "FT-EAP" "FT-SAE"
+              ];
+              # The list can be obtained by running this command
+              # awk '
+              #   /^# key_mgmt: /{ run=1 }
+              #   /^#$/{ run=0 }
+              #   /^# [A-Z0-9-]{2,}/{ if(run){printf("\"%s\"\n", $2)} }
+              # ' /run/current-system/sw/share/doc/wpa_supplicant/wpa_supplicant.conf.example
+              type = types.listOf (types.enum [
+                "WPA-PSK"
+                "WPA-EAP"
+                "IEEE8021X"
+                "NONE"
+                "WPA-NONE"
+                "FT-PSK"
+                "FT-EAP"
+                "FT-EAP-SHA384"
+                "WPA-PSK-SHA256"
+                "WPA-EAP-SHA256"
+                "SAE"
+                "FT-SAE"
+                "WPA-EAP-SUITE-B"
+                "WPA-EAP-SUITE-B-192"
+                "OSEN"
+                "FILS-SHA256"
+                "FILS-SHA384"
+                "FT-FILS-SHA256"
+                "FT-FILS-SHA384"
+                "OWE"
+                "DPP"
+              ]);
+              description = ''
+                The list of authentication protocols accepted by this network.
+                This corresponds to the <literal>key_mgmt</literal> option in wpa_supplicant.
+              '';
+            };
+
             auth = mkOption {
               type = types.nullOr types.str;
               default = null;
               example = ''
-                key_mgmt=WPA-EAP
                 eap=PEAP
                 identity="user@example.com"
                 password="secret"
@@ -205,6 +335,16 @@ in {
           description = "Members of this group can control wpa_supplicant.";
         };
       };
+
+      dbusControlled = mkOption {
+        type = types.bool;
+        default = lib.length cfg.interfaces < 2;
+        description = ''
+          Whether to enable the DBus control interface.
+          This is only needed when using NetworkManager or connman.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.str;
         default = "";
@@ -228,73 +368,47 @@ in {
     assertions = flip mapAttrsToList cfg.networks (name: cfg: {
       assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1;
       message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive'';
-    });
+    }) ++ [
+      {
+        assertion = length cfg.interfaces > 1 -> !cfg.dbusControlled;
+        message =
+          let daemon = if config.networking.networkmanager.enable then "NetworkManager" else
+                       if config.services.connman.enable then "connman" else null;
+              n = toString (length cfg.interfaces);
+          in ''
+            It's not possible to run multiple wpa_supplicant instances with DBus support.
+            Note: you're seeing this error because `networking.wireless.interfaces` has
+            ${n} entries, implying an equal number of wpa_supplicant instances.
+          '' + optionalString (daemon != null) ''
+            You don't need to change `networking.wireless.interfaces` when using ${daemon}:
+            in this case the interfaces will be configured automatically for you.
+          '';
+      }
+    ];
 
-    warnings =
-      optional (cfg.interfaces == [] && config.systemd.services.wpa_supplicant.wantedBy != [])
-      ''
-        No network interfaces for wpa_supplicant have been configured: the service
-        may randomly fail to start at boot. You should specify at least one using the option
-        networking.wireless.interfaces.
-      '';
+    hardware.wirelessRegulatoryDatabase = true;
 
     environment.systemPackages = [ package ];
-
-    services.dbus.packages = [ package ];
-    services.udev.packages = [ pkgs.crda ];
-
-    # FIXME: start a separate wpa_supplicant instance per interface.
-    systemd.services.wpa_supplicant = let
-      ifaces = cfg.interfaces;
-      deviceUnit = interface: [ "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device" ];
-    in {
-      description = "WPA Supplicant";
-
-      after = lib.concatMap deviceUnit ifaces;
-      before = [ "network.target" ];
-      wants = [ "network.target" ];
-      requires = lib.concatMap deviceUnit ifaces;
-      wantedBy = [ "multi-user.target" ];
-      stopIfChanged = false;
-
-      path = [ package ];
-
-      script = let
-        configStr = if cfg.allowAuxiliaryImperativeNetworks
-          then "-c /etc/wpa_supplicant.conf -I ${configFile}"
-          else "-c ${configFile}";
-      in ''
-        if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]
-        then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
-        fi
-        iface_args="-s -u -D${cfg.driver} ${configStr}"
-        ${if ifaces == [] then ''
-          for i in $(cd /sys/class/net && echo *); do
-            DEVTYPE=
-            UEVENT_PATH=/sys/class/net/$i/uevent
-            if [ -e "$UEVENT_PATH" ]; then
-              source "$UEVENT_PATH"
-              if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then
-                args+="''${args:+ -N} -i$i $iface_args"
-              fi
-            fi
-          done
-        '' else ''
-          args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}"
-        ''}
-        exec wpa_supplicant $args
-      '';
-    };
-
-    powerManagement.resumeCommands = ''
-      /run/current-system/systemd/bin/systemctl try-restart wpa_supplicant
-    '';
-
-    # Restart wpa_supplicant when a wlan device appears or disappears.
-    services.udev.extraRules = ''
-      ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="/run/current-system/systemd/bin/systemctl try-restart wpa_supplicant.service"
+    services.dbus.packages = optional cfg.dbusControlled package;
+
+    systemd.services =
+      if cfg.interfaces == []
+        then { wpa_supplicant = mkUnit null; }
+        else listToAttrs (map (i: nameValuePair "wpa_supplicant-${i}" (mkUnit i)) cfg.interfaces);
+
+    # Restart wpa_supplicant after resuming from sleep
+    powerManagement.resumeCommands = concatStringsSep "\n" (
+      optional (cfg.interfaces == []) "${systemctl} try-restart wpa_supplicant"
+      ++ map (i: "${systemctl} try-restart wpa_supplicant-${i}") cfg.interfaces
+    );
+
+    # Restart wpa_supplicant when a wlan device appears or disappears. This is
+    # only needed when an interface hasn't been specified by the user.
+    services.udev.extraRules = optionalString (cfg.interfaces == []) ''
+      ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", \
+      RUN+="${systemctl} try-restart wpa_supplicant.service"
     '';
   };
 
-  meta.maintainers = with lib.maintainers; [ globin ];
+  meta.maintainers = with lib.maintainers; [ globin rnhmjoj ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/zeronet.nix b/nixpkgs/nixos/modules/services/networking/zeronet.nix
index f354a9d42c79..a34b2d871541 100644
--- a/nixpkgs/nixos/modules/services/networking/zeronet.nix
+++ b/nixpkgs/nixos/modules/services/networking/zeronet.nix
@@ -32,7 +32,7 @@ in with lib; {
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 43110;
       example = 43110;
       description = "Optional zeronet web UI port.";
@@ -41,7 +41,7 @@ in with lib; {
     fileserverPort = mkOption {
       # Not optional: when absent zeronet tries to write one to the
       # read-only config file and crashes
-      type = types.int;
+      type = types.port;
       default = 12261;
       example = 12261;
       description = "Zeronet fileserver port.";
diff --git a/nixpkgs/nixos/modules/services/search/elasticsearch.nix b/nixpkgs/nixos/modules/services/search/elasticsearch.nix
index 91d8f544e16b..440f34b3dc5c 100644
--- a/nixpkgs/nixos/modules/services/search/elasticsearch.nix
+++ b/nixpkgs/nixos/modules/services/search/elasticsearch.nix
@@ -5,8 +5,6 @@ with lib;
 let
   cfg = config.services.elasticsearch;
 
-  es6 = builtins.compareVersions cfg.package.version "6" >= 0;
-
   esConfig = ''
     network.host: ${cfg.listenAddress}
     cluster.name: ${cfg.cluster_name}
@@ -36,7 +34,8 @@ let
     postBuild = "${pkgs.coreutils}/bin/mkdir -p $out/plugins";
   };
 
-in {
+in
+{
 
   ###### interface
 
@@ -116,20 +115,20 @@ in {
 
     extraCmdLineOptions = mkOption {
       description = "Extra command line options for the elasticsearch launcher.";
-      default = [];
+      default = [ ];
       type = types.listOf types.str;
     };
 
     extraJavaOptions = mkOption {
       description = "Extra command line options for Java.";
-      default = [];
+      default = [ ];
       type = types.listOf types.str;
       example = [ "-Djava.net.preferIPv4Stack=true" ];
     };
 
     plugins = mkOption {
       description = "Extra elasticsearch plugins";
-      default = [];
+      default = [ ];
       type = types.listOf types.package;
       example = lib.literalExample "[ pkgs.elasticsearchPlugins.discovery-ec2 ]";
     };
@@ -146,9 +145,7 @@ in {
       path = [ pkgs.inetutils ];
       environment = {
         ES_HOME = cfg.dataDir;
-        ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ]
-                                  ++ cfg.extraJavaOptions);
-      } // optionalAttrs es6 {
+        ES_JAVA_OPTS = toString cfg.extraJavaOptions;
         ES_PATH_CONF = configDir;
       };
       serviceConfig = {
@@ -187,7 +184,10 @@ in {
         rm -f "${configDir}/logging.yml"
         cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename}
         mkdir -p ${configDir}/scripts
-        ${optionalString es6 "cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options"}
+        cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options
+        # redirect jvm logs to the data directory
+        mkdir -m 0700 -p ${cfg.dataDir}/logs
+        ${pkgs.sd}/bin/sd 'logs/gc.log' '${cfg.dataDir}/logs/gc.log' ${configDir}/jvm.options \
 
         if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi
       '';
diff --git a/nixpkgs/nixos/modules/services/system/uptimed.nix b/nixpkgs/nixos/modules/services/system/uptimed.nix
index 1e256c51408e..67a03876e19f 100644
--- a/nixpkgs/nixos/modules/services/system/uptimed.nix
+++ b/nixpkgs/nixos/modules/services/system/uptimed.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.uptimed;
-  stateDir = "/var/spool/uptimed";
+  stateDir = "/var/lib/uptimed";
 in
 {
   options = {
@@ -21,12 +21,16 @@ in
   };
 
   config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.uptimed ];
+
     users.users.uptimed = {
       description = "Uptimed daemon user";
       home        = stateDir;
-      createHome  = true;
       uid         = config.ids.uids.uptimed;
+      group       = "uptimed";
     };
+    users.groups.uptimed = {};
 
     systemd.services.uptimed = {
       unitConfig.Documentation = "man:uptimed(8) man:uprecords(1)";
@@ -41,7 +45,7 @@ in
         PrivateTmp              = "yes";
         PrivateNetwork          = "yes";
         NoNewPrivileges         = "yes";
-        ReadWriteDirectories    = stateDir;
+        StateDirectory          = [ "uptimed" ];
         InaccessibleDirectories = "/home";
         ExecStart               = "${pkgs.uptimed}/sbin/uptimed -f -p ${stateDir}/pid";
       };
diff --git a/nixpkgs/nixos/modules/services/torrent/deluge.nix b/nixpkgs/nixos/modules/services/torrent/deluge.nix
index 7ca4fdcf64d4..151a1dd638d1 100644
--- a/nixpkgs/nixos/modules/services/torrent/deluge.nix
+++ b/nixpkgs/nixos/modules/services/torrent/deluge.nix
@@ -149,7 +149,7 @@ in {
 
         package = mkOption {
           type = types.package;
-          example = literalExample "pkgs.deluge-1_x";
+          example = literalExample "pkgs.deluge-2_x";
           description = ''
             Deluge package to use.
           '';
@@ -184,6 +184,13 @@ in {
       if versionAtLeast config.system.stateVersion "20.09" then
         pkgs.deluge-2_x
       else
+        # deluge-1_x is no longer packaged and this will resolve to an error
+        # thanks to the alias for this name.  This is left here so that anyone
+        # using NixOS older than 20.09 receives that error when they upgrade
+        # and is forced to make an intentional choice to switch to deluge-2_x.
+        # That might be slightly inconvenient but there is no path to
+        # downgrade from 2.x to 1.x so NixOS should not automatically perform
+        # this state migration.
         pkgs.deluge-1_x
     );
 
diff --git a/nixpkgs/nixos/modules/services/video/replay-sorcery.nix b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
new file mode 100644
index 000000000000..d78e782c7968
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.replay-sorcery;
+  configFile = generators.toKeyValue {} cfg.settings;
+in
+{
+  options = with types; {
+    services.replay-sorcery = {
+      enable = mkEnableOption "the ReplaySorcery service for instant-replays";
+
+      enableSysAdminCapability = mkEnableOption ''
+        the system admin capability to support hardware accelerated
+        video capture. This is equivalent to running ReplaySorcery as
+        root, so use with caution'';
+
+      autoStart = mkOption {
+        type = bool;
+        default = false;
+        description = "Automatically start ReplaySorcery when graphical-session.target starts.";
+      };
+
+      settings = mkOption {
+        type = attrsOf (oneOf [ str int ]);
+        default = {};
+        description = "System-wide configuration for ReplaySorcery (/etc/replay-sorcery.conf).";
+        example = literalExample ''
+          {
+            videoInput = "hwaccel"; # requires `services.replay-sorcery.enableSysAdminCapability = true`
+            videoFramerate = 60;
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ pkgs.replay-sorcery ];
+      etc."replay-sorcery.conf".text = configFile;
+    };
+
+    security.wrappers = mkIf cfg.enableSysAdminCapability {
+      replay-sorcery = {
+        source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
+        capabilities = "cap_sys_admin+ep";
+      };
+    };
+
+    systemd = {
+      packages = [ pkgs.replay-sorcery ];
+      user.services.replay-sorcery = {
+        wantedBy = mkIf cfg.autoStart [ "graphical-session.target" ];
+        partOf = mkIf cfg.autoStart [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = mkIf cfg.enableSysAdminCapability [
+            "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it.
+            "${config.security.wrapperDir}/replay-sorcery"
+          ];
+        };
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/wayland/cage.nix b/nixpkgs/nixos/modules/services/wayland/cage.nix
index 2e71abb69fc4..bd97a674eb86 100644
--- a/nixpkgs/nixos/modules/services/wayland/cage.nix
+++ b/nixpkgs/nixos/modules/services/wayland/cage.nix
@@ -82,7 +82,7 @@ in {
       auth    required pam_unix.so nullok
       account required pam_unix.so
       session required pam_unix.so
-      session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+      session required pam_env.so conffile=/etc/pam/environment readenv=0
       session required ${pkgs.systemd}/lib/security/pam_systemd.so
     '';
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
index 8d5302ba267b..050e4ee3d329 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
@@ -771,7 +771,6 @@ in
           "tmp"
           "assets/javascripts/plugins"
           "public"
-          "plugins"
           "sockets"
         ];
         RuntimeDirectoryMode = 0750;
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.xml b/nixpkgs/nixos/modules/services/web-apps/discourse.xml
index 1d6866e7b352..184c9c6363e5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.xml
@@ -284,12 +284,23 @@ services.discourse = {
       Ruby dependencies are listed in its
       <filename>plugin.rb</filename> file as function calls to
       <literal>gem</literal>. To construct the corresponding
-      <filename>Gemfile</filename>, run <command>bundle
+      <filename>Gemfile</filename> manually, run <command>bundle
       init</command>, then add the <literal>gem</literal> lines to it
       verbatim.
     </para>
 
     <para>
+      Much of the packaging can be done automatically by the
+      <filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename>
+      script - just add the plugin to the <literal>plugins</literal>
+      list in the <function>update_plugins</function> function and run
+      the script:
+      <programlisting language="bash">
+./update.py update-plugins
+</programlisting>.
+    </para>
+
+    <para>
       Some plugins provide <link
       linkend="module-services-discourse-site-settings">site
       settings</link>. Their defaults can be configured using <xref
diff --git a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
new file mode 100644
index 000000000000..c632b8ff7199
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
@@ -0,0 +1,64 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.fluidd;
+  moonraker = config.services.moonraker;
+in
+{
+  options.services.fluidd = {
+    enable = mkEnableOption "Fluidd, a Klipper web interface for managing your 3d printer";
+
+    package = mkOption {
+      type = types.package;
+      description = "Fluidd package to be used in the module";
+      default = pkgs.fluidd;
+      defaultText = "pkgs.fluidd";
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Hostname to serve fluidd on";
+    };
+
+    nginx = mkOption {
+      type = types.submodule
+        (import ../web-servers/nginx/vhost-options.nix { inherit config lib; });
+      default = { };
+      example = {
+        serverAliases = [ "fluidd.\${config.networking.domain}" ];
+      };
+      description = "Extra configuration for the nginx virtual host of fluidd.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.nginx = {
+      enable = true;
+      upstreams.fluidd-apiserver.servers."${moonraker.address}:${toString moonraker.port}" = { };
+      virtualHosts."${cfg.hostName}" = mkMerge [
+        cfg.nginx
+        {
+          root = mkForce "${cfg.package}/share/fluidd/htdocs";
+          locations = {
+            "/" = {
+              index = "index.html";
+              tryFiles = "$uri $uri/ /index.html";
+            };
+            "/index.html".extraConfig = ''
+              add_header Cache-Control "no-store, no-cache, must-revalidate";
+            '';
+            "/websocket" = {
+              proxyWebsockets = true;
+              proxyPass = "http://fluidd-apiserver/websocket";
+            };
+            "~ ^/(printer|api|access|machine|server)/" = {
+              proxyWebsockets = true;
+              proxyPass = "http://fluidd-apiserver$request_uri";
+            };
+          };
+        }
+      ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/isso.nix b/nixpkgs/nixos/modules/services/web-apps/isso.nix
new file mode 100644
index 000000000000..d05a99a3eedc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/isso.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkEnableOption mkIf mkOption types literalExample;
+
+  cfg = config.services.isso;
+
+  settingsFormat = pkgs.formats.ini { };
+  configFile = settingsFormat.generate "isso.conf" cfg.settings;
+in {
+
+  options = {
+    services.isso = {
+      enable = mkEnableOption ''
+        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.
+      '';
+
+      settings = mkOption {
+        description = ''
+          Configuration for <package>isso</package>.
+
+          See <link xlink:href="https://posativ.org/isso/docs/configuration/server/">Isso Server Configuration</link>
+          for supported values.
+        '';
+
+        type = types.submodule {
+          freeformType = settingsFormat.type;
+        };
+
+        example = literalExample ''
+          {
+            general = {
+              host = "http://localhost";
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.isso.settings.general.dbpath = lib.mkDefault "/var/lib/isso/comments.db";
+
+    systemd.services.isso = {
+      description = "isso, a commenting server similar to Disqus";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = "isso";
+        Group = "isso";
+
+        DynamicUser = true;
+
+        StateDirectory = "isso";
+
+        ExecStart = ''
+          ${pkgs.isso}/bin/isso -c ${configFile}
+        '';
+
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
index dc66c2966564..b1bea222c7f7 100644
--- a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
@@ -281,7 +281,7 @@ in
       createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.database.type == "postgresql";
       createLocalMySQL = databaseActuallyCreateLocally && cfg.database.type == "mysql";
 
-      mySqlCaKeystore = pkgs.runCommandNoCC "mysql-ca-keystore" {} ''
+      mySqlCaKeystore = pkgs.runCommand "mysql-ca-keystore" {} ''
         ${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt
       '';
 
@@ -553,7 +553,7 @@ in
 
       jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
 
-      keycloakConfig = pkgs.runCommandNoCC "keycloak-config" {
+      keycloakConfig = pkgs.runCommand "keycloak-config" {
         nativeBuildInputs = [ cfg.package ];
       } ''
         export JBOSS_BASE_DIR="$(pwd -P)";
diff --git a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
index 01710b1bd59c..1bbadafa2078 100644
--- a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
@@ -98,6 +98,29 @@ in
         EnvironmentFile = if cfg.adminCredentialsFile == null
         then defaultCredentials
         else cfg.adminCredentialsFile;
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        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" "~@resources" ];
+        UMask = "0077";
       };
 
       environment = cfg.config;
diff --git a/nixpkgs/nixos/modules/services/web-apps/moodle.nix b/nixpkgs/nixos/modules/services/web-apps/moodle.nix
index ad1e55d62d1d..c854e084e14d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/moodle.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/moodle.nix
@@ -56,7 +56,7 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  phpExt = pkgs.php.withExtensions
+  phpExt = pkgs.php74.withExtensions
         ({ enabled, all }: with all; [ iconv mbstring curl openssl tokenizer xmlrpc soap ctype zip gd simplexml dom  intl json sqlite3 pgsql pdo_sqlite pdo_pgsql pdo_odbc pdo_mysql pdo mysqli session zlib xmlreader fileinfo filter ]);
 in
 {
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index 111b31734696..ba5f6582cbec 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -502,8 +502,6 @@ in {
               ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"'';
               ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"'';
               "--database-pass" = dbpass;
-              ${if c.dbtableprefix != null
-                then "--database-table-prefix" else null} = ''"${toString c.dbtableprefix}"'';
               "--admin-user" = ''"${c.adminuser}"'';
               "--admin-pass" = adminpass;
               "--data-dir" = ''"${cfg.home}/data"'';
@@ -699,7 +697,6 @@ in {
         };
         extraConfig = ''
           index index.php index.html /index.php$request_uri;
-          expires 1m;
           add_header X-Content-Type-Options nosniff;
           add_header X-XSS-Protection "1; mode=block";
           add_header X-Robots-Tag none;
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
index 3af37b15dd56..ed84487d233a 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
@@ -84,47 +84,93 @@
 </para>
 
  </section>
- <section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
-  <title>Pitfalls</title>
-
-  <para>
-   Unfortunately Nextcloud appears to be very stateful when it comes to
-   managing its own configuration. The config file lives in the home directory
-   of the <literal>nextcloud</literal> user (by default
-   <literal>/var/lib/nextcloud/config/config.php</literal>) and is also used to
-   track several states of the application (e.g. whether installed or not).
-  </para>
 
-  <para>
-   All configuration parameters are also stored in
-   <literal>/var/lib/nextcloud/config/override.config.php</literal> which is generated by
-   the module and linked from the store to ensure that all values from <literal>config.php</literal>
-   can be modified by the module.
-   However <literal>config.php</literal> manages the application's state and shouldn't be touched
-   manually because of that.
-  </para>
-
-  <warning>
-   <para>Don't delete <literal>config.php</literal>! This file
-   tracks the application's state and a deletion can cause unwanted
-   side-effects!</para>
-  </warning>
-
-  <warning>
-   <para>Don't rerun <literal>nextcloud-occ
-   maintenance:install</literal>! This command tries to install the application
-   and can cause unwanted side-effects!</para>
-  </warning>
+ <section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
+  <title>Common problems</title>
+  <itemizedlist>
+   <listitem>
+    <formalpara>
+     <title>General notes</title>
+     <para>
+      Unfortunately Nextcloud appears to be very stateful when it comes to
+      managing its own configuration. The config file lives in the home directory
+      of the <literal>nextcloud</literal> user (by default
+      <literal>/var/lib/nextcloud/config/config.php</literal>) and is also used to
+      track several states of the application (e.g., whether installed or not).
+     </para>
+    </formalpara>
+    <para>
+     All configuration parameters are also stored in
+     <filename>/var/lib/nextcloud/config/override.config.php</filename> which is generated by
+     the module and linked from the store to ensure that all values from
+     <filename>config.php</filename> can be modified by the module.
+     However <filename>config.php</filename> manages the application's state and shouldn't be
+     touched manually because of that.
+    </para>
+    <warning>
+     <para>Don't delete <filename>config.php</filename>! This file
+     tracks the application's state and a deletion can cause unwanted
+     side-effects!</para>
+    </warning>
 
-  <para>
-   Nextcloud doesn't allow to move more than one major-version forward. If you're e.g. on
-   <literal>v16</literal>, you cannot upgrade to <literal>v18</literal>, you need to upgrade to
-   <literal>v17</literal> first. This is ensured automatically as long as the
-   <link linkend="opt-system.stateVersion">stateVersion</link> is declared properly. In that case
-   the oldest version available (one major behind the one from the previous NixOS
-   release) will be selected by default and the module will generate a warning that reminds
-   the user to upgrade to latest Nextcloud <emphasis>after</emphasis> that deploy.
-  </para>
+    <warning>
+     <para>Don't rerun <literal>nextcloud-occ
+     maintenance:install</literal>! This command tries to install the application
+     and can cause unwanted side-effects!</para>
+    </warning>
+   </listitem>
+   <listitem>
+    <formalpara>
+     <title>Multiple version upgrades</title>
+     <para>
+      Nextcloud doesn't allow to move more than one major-version forward. E.g., if you're on
+      <literal>v16</literal>, you cannot upgrade to <literal>v18</literal>, you need to upgrade to
+      <literal>v17</literal> first. This is ensured automatically as long as the
+      <link linkend="opt-system.stateVersion">stateVersion</link> is declared properly. In that case
+      the oldest version available (one major behind the one from the previous NixOS
+      release) will be selected by default and the module will generate a warning that reminds
+      the user to upgrade to latest Nextcloud <emphasis>after</emphasis> that deploy.
+     </para>
+    </formalpara>
+   </listitem>
+   <listitem>
+    <formalpara>
+     <title><literal>Error: Command "upgrade" is not defined.</literal></title>
+     <para>
+      This error usually occurs if the initial installation
+      (<command>nextcloud-occ maintenance:install</command>) has failed. After that, the application
+      is not installed, but the upgrade is attempted to be executed. Further context can
+      be found in <link xlink:href="https://github.com/NixOS/nixpkgs/issues/111175">NixOS/nixpkgs#111175</link>.
+     </para>
+    </formalpara>
+    <para>
+     First of all, it makes sense to find out what went wrong by looking at the logs
+     of the installation via <command>journalctl -u nextcloud-setup</command> and try to fix
+     the underlying issue.
+    </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       If this occurs on an <emphasis>existing</emphasis> setup, this is most likely because
+       the maintenance mode is active. It can be deactivated by running
+       <command>nextcloud-occ maintenance:mode --off</command>. It's advisable though to
+       check the logs first on why the maintenance mode was activated.
+      </para>
+     </listitem>
+     <listitem>
+      <warning><para>Only perform the following measures on
+      <emphasis>freshly installed instances!</emphasis></para></warning>
+      <para>
+       A re-run of the installer can be forced by <emphasis>deleting</emphasis>
+       <filename>/var/lib/nextcloud/config/config.php</filename>. This is the only time
+       advisable because the fresh install doesn't have any state that can be lost.
+       In case that doesn't help, an entire re-creation can be forced via
+       <command>rm -rf ~nextcloud/</command>.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </listitem>
+  </itemizedlist>
  </section>
 
  <section xml:id="module-services-nextcloud-httpd">
diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
new file mode 100644
index 000000000000..4f6850ace214
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
@@ -0,0 +1,148 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.node-red;
+  defaultUser = "node-red";
+  finalPackage = if cfg.withNpmAndGcc then node-red_withNpmAndGcc else cfg.package;
+  node-red_withNpmAndGcc = pkgs.runCommand "node-red" {
+    nativeBuildInputs = [ pkgs.makeWrapper ];
+  }
+  ''
+    mkdir -p $out/bin
+    makeWrapper ${pkgs.nodePackages.node-red}/bin/node-red $out/bin/node-red \
+      --set PATH '${lib.makeBinPath [ pkgs.nodePackages.npm pkgs.gcc ]}:$PATH' \
+  '';
+in
+{
+  options.services.node-red = {
+    enable = mkEnableOption "the Node-RED service";
+
+    package = mkOption {
+      default = pkgs.nodePackages.node-red;
+      defaultText = "pkgs.nodePackages.node-red";
+      type = types.package;
+      description = "Node-RED package to use.";
+    };
+
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open ports in the firewall for the server.
+      '';
+    };
+
+    withNpmAndGcc = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Give Node-RED access to NPM and GCC at runtime, so 'Nodes' can be
+        downloaded and managed imperatively via the 'Palette Manager'.
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.path;
+      default = "${cfg.package}/lib/node_modules/node-red/settings.js";
+      defaultText = "\${cfg.package}/lib/node_modules/node-red/settings.js";
+      description = ''
+        Path to the JavaScript configuration file.
+        See <link
+        xlink:href="https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js"/>
+        for a configuration example.
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 1880;
+      description = "Listening port.";
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = ''
+        User under which Node-RED runs.If left as the default value this user
+        will automatically be created on system activation, otherwise the
+        sysadmin is responsible for ensuring the user exists.
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = ''
+        Group under which Node-RED runs.If left as the default value this group
+        will automatically be created on system activation, otherwise the
+        sysadmin is responsible for ensuring the group exists.
+      '';
+    };
+
+    userDir = mkOption {
+      type = types.path;
+      default = "/var/lib/node-red";
+      description = ''
+        The directory to store all user data, such as flow and credential files and all library data. If left
+        as the default value this directory will automatically be created before the node-red service starts,
+        otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership
+        and permissions.
+      '';
+    };
+
+    safe = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to launch Node-RED in --safe mode.";
+    };
+
+    define = mkOption {
+      type = types.attrs;
+      default = {};
+      description = "List of settings.js overrides to pass via -D to Node-RED.";
+      example = literalExample ''
+        {
+          "logging.console.level" = "trace";
+        }
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users = optionalAttrs (cfg.user == defaultUser) {
+      ${defaultUser} = {
+        isSystemUser = true;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == defaultUser) {
+      ${defaultUser} = { };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+
+    systemd.services.node-red = {
+      description = "Node-RED Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ];
+      environment = {
+        HOME = cfg.userDir;
+      };
+      serviceConfig = mkMerge [
+        {
+          User = cfg.user;
+          Group = cfg.group;
+          ExecStart = "${finalPackage}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}";
+          PrivateTmp = true;
+          Restart = "always";
+          WorkingDirectory = cfg.userDir;
+        }
+        (mkIf (cfg.userDir == "/var/lib/node-red") { StateDirectory = "node-red"; })
+      ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
index b78487cc9281..bc18c824f394 100644
--- a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
@@ -6,10 +6,6 @@ let
 
   configVersion = 26;
 
-  cacheDir = "cache";
-  lockDir = "lock";
-  feedIconsDir = "feed-icons";
-
   dbPort = if cfg.database.port == null
     then (if cfg.database.type == "pgsql" then 5432 else 3306)
     else cfg.database.port;
@@ -19,86 +15,104 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  tt-rss-config = pkgs.writeText "config.php" ''
+  tt-rss-config = let
+    password =
+      if (cfg.database.password != null) then
+        "${(escape ["'" "\\"] cfg.database.password)}"
+      else if (cfg.database.passwordFile != null) then
+        "file_get_contents('${cfg.database.passwordFile}'"
+      else
+        ""
+      ;
+  in pkgs.writeText "config.php" ''
     <?php
+      putenv('TTRSS_PHP_EXECUTABLE=${pkgs.php}/bin/php');
 
-      define('PHP_EXECUTABLE', '${pkgs.php}/bin/php');
-
-      define('LOCK_DIRECTORY', '${lockDir}');
-      define('CACHE_DIR', '${cacheDir}');
-      define('ICONS_DIR', '${feedIconsDir}');
-      define('ICONS_URL', '${feedIconsDir}');
-      define('SELF_URL_PATH', '${cfg.selfUrlPath}');
+      putenv('TTRSS_LOCK_DIRECTORY=${cfg.root}/lock');
+      putenv('TTRSS_CACHE_DIR=${cfg.root}/cache');
+      putenv('TTRSS_ICONS_DIR=${cfg.root}/feed-icons');
+      putenv('TTRSS_ICONS_URL=feed-icons');
+      putenv('TTRSS_SELF_URL_PATH=${cfg.selfUrlPath}');
 
-      define('MYSQL_CHARSET', 'UTF8');
+      putenv('TTRSS_MYSQL_CHARSET=UTF8');
 
-      define('DB_TYPE', '${cfg.database.type}');
-      define('DB_HOST', '${optionalString (cfg.database.host != null) cfg.database.host}');
-      define('DB_USER', '${cfg.database.user}');
-      define('DB_NAME', '${cfg.database.name}');
-      define('DB_PASS', ${
-        if (cfg.database.password != null) then
-          "'${(escape ["'" "\\"] cfg.database.password)}'"
-        else if (cfg.database.passwordFile != null) then
-          "file_get_contents('${cfg.database.passwordFile}')"
-        else
-          "''"
-      });
-      define('DB_PORT', '${toString dbPort}');
+      putenv('TTRSS_DB_TYPE=${cfg.database.type}');
+      putenv('TTRSS_DB_HOST=${optionalString (cfg.database.host != null) cfg.database.host}');
+      putenv('TTRSS_DB_USER=${cfg.database.user}');
+      putenv('TTRSS_DB_NAME=${cfg.database.name}');
+      putenv('TTRSS_DB_PASS=${password}');
+      putenv('TTRSS_DB_PORT=${toString dbPort}');
 
-      define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate});
-      define('AUTH_AUTO_LOGIN', ${boolToString cfg.auth.autoLogin});
+      putenv('TTRSS_AUTH_AUTO_CREATE=${boolToString cfg.auth.autoCreate}');
+      putenv('TTRSS_AUTH_AUTO_LOGIN=${boolToString cfg.auth.autoLogin}');
 
-      define('FEED_CRYPT_KEY', '${escape ["'" "\\"] cfg.feedCryptKey}');
+      putenv('TTRSS_FEED_CRYPT_KEY=${escape ["'" "\\"] cfg.feedCryptKey}');
 
 
-      define('SINGLE_USER_MODE', ${boolToString cfg.singleUserMode});
+      putenv('TTRSS_SINGLE_USER_MODE=${boolToString cfg.singleUserMode}');
 
-      define('SIMPLE_UPDATE_MODE', ${boolToString cfg.simpleUpdateMode});
+      putenv('TTRSS_SIMPLE_UPDATE_MODE=${boolToString cfg.simpleUpdateMode}');
 
-      // Never check for updates - the running version of the code should be
-      // controlled entirely by the version of TT-RSS active in the current Nix
-      // profile. If TT-RSS updates itself to a version requiring a database
-      // schema upgrade, and then the SystemD tt-rss.service is restarted, the
-      // old code copied from the Nix store will overwrite the updated version,
-      // causing the code to detect the need for a schema "upgrade" (since the
-      // schema version in the database is different than in the code), but the
-      // update schema operation in TT-RSS will do nothing because the schema
-      // version in the database is newer than that in the code.
-      define('CHECK_FOR_UPDATES', false);
+      # Never check for updates - the running version of the code should
+      # be controlled entirely by the version of TT-RSS active in the
+      # current Nix profile. If TT-RSS updates itself to a version
+      # requiring a database schema upgrade, and then the SystemD
+      # tt-rss.service is restarted, the old code copied from the Nix
+      # store will overwrite the updated version, causing the code to
+      # detect the need for a schema "upgrade" (since the schema version
+      # in the database is different than in the code), but the update
+      # schema operation in TT-RSS will do nothing because the schema
+      # version in the database is newer than that in the code.
+      putenv('TTRSS_CHECK_FOR_UPDATES=false');
 
-      define('FORCE_ARTICLE_PURGE', ${toString cfg.forceArticlePurge});
-      define('SESSION_COOKIE_LIFETIME', ${toString cfg.sessionCookieLifetime});
-      define('ENABLE_GZIP_OUTPUT', ${boolToString cfg.enableGZipOutput});
+      putenv('TTRSS_FORCE_ARTICLE_PURGE=${toString cfg.forceArticlePurge}');
+      putenv('TTRSS_SESSION_COOKIE_LIFETIME=${toString cfg.sessionCookieLifetime}');
+      putenv('TTRSS_ENABLE_GZIP_OUTPUT=${boolToString cfg.enableGZipOutput}');
 
-      define('PLUGINS', '${builtins.concatStringsSep "," cfg.plugins}');
+      putenv('TTRSS_PLUGINS=${builtins.concatStringsSep "," cfg.plugins}');
 
-      define('LOG_DESTINATION', '${cfg.logDestination}');
-      define('CONFIG_VERSION', ${toString configVersion});
+      putenv('TTRSS_LOG_DESTINATION=${cfg.logDestination}');
+      putenv('TTRSS_CONFIG_VERSION=${toString configVersion}');
 
 
-      define('PUBSUBHUBBUB_ENABLED', ${boolToString cfg.pubSubHubbub.enable});
-      define('PUBSUBHUBBUB_HUB', '${cfg.pubSubHubbub.hub}');
+      putenv('TTRSS_PUBSUBHUBBUB_ENABLED=${boolToString cfg.pubSubHubbub.enable}');
+      putenv('TTRSS_PUBSUBHUBBUB_HUB=${cfg.pubSubHubbub.hub}');
 
-      define('SPHINX_SERVER', '${cfg.sphinx.server}');
-      define('SPHINX_INDEX', '${builtins.concatStringsSep "," cfg.sphinx.index}');
+      putenv('TTRSS_SPHINX_SERVER=${cfg.sphinx.server}');
+      putenv('TTRSS_SPHINX_INDEX=${builtins.concatStringsSep "," cfg.sphinx.index}');
 
-      define('ENABLE_REGISTRATION', ${boolToString cfg.registration.enable});
-      define('REG_NOTIFY_ADDRESS', '${cfg.registration.notifyAddress}');
-      define('REG_MAX_USERS', ${toString cfg.registration.maxUsers});
+      putenv('TTRSS_ENABLE_REGISTRATION=${boolToString cfg.registration.enable}');
+      putenv('TTRSS_REG_NOTIFY_ADDRESS=${cfg.registration.notifyAddress}');
+      putenv('TTRSS_REG_MAX_USERS=${toString cfg.registration.maxUsers}');
 
-      define('SMTP_SERVER', '${cfg.email.server}');
-      define('SMTP_LOGIN', '${cfg.email.login}');
-      define('SMTP_PASSWORD', '${escape ["'" "\\"] cfg.email.password}');
-      define('SMTP_SECURE', '${cfg.email.security}');
+      putenv('TTRSS_SMTP_SERVER=${cfg.email.server}');
+      putenv('TTRSS_SMTP_LOGIN=${cfg.email.login}');
+      putenv('TTRSS_SMTP_PASSWORD=${escape ["'" "\\"] cfg.email.password}');
+      putenv('TTRSS_SMTP_SECURE=${cfg.email.security}');
 
-      define('SMTP_FROM_NAME', '${escape ["'" "\\"] cfg.email.fromName}');
-      define('SMTP_FROM_ADDRESS', '${escape ["'" "\\"] cfg.email.fromAddress}');
-      define('DIGEST_SUBJECT', '${escape ["'" "\\"] cfg.email.digestSubject}');
+      putenv('TTRSS_SMTP_FROM_NAME=${escape ["'" "\\"] cfg.email.fromName}');
+      putenv('TTRSS_SMTP_FROM_ADDRESS=${escape ["'" "\\"] cfg.email.fromAddress}');
+      putenv('TTRSS_DIGEST_SUBJECT=${escape ["'" "\\"] cfg.email.digestSubject}');
 
       ${cfg.extraConfig}
   '';
 
+  # tt-rss and plugins and themes and config.php
+  servedRoot = pkgs.runCommand "tt-rss-served-root" {} ''
+    cp --no-preserve=mode -r ${pkgs.tt-rss} $out
+    cp ${tt-rss-config} $out/config.php
+    ${optionalString (cfg.pluginPackages != []) ''
+    for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
+    cp -r "$plugin"/* "$out/plugins.local/"
+    done
+    ''}
+    ${optionalString (cfg.themePackages != []) ''
+    for theme in ${concatStringsSep " " cfg.themePackages}; do
+    cp -r "$theme"/* "$out/themes.local/"
+    done
+    ''}
+  '';
+
  in {
 
   ###### interface
@@ -542,12 +556,16 @@ let
       enable = true;
       virtualHosts = {
         ${cfg.virtualHost} = {
-          root = "${cfg.root}";
+          root = "${cfg.root}/www";
 
           locations."/" = {
             index = "index.php";
           };
 
+          locations."^~ /feed-icons" = {
+            root = "${cfg.root}";
+          };
+
           locations."~ \\.php$" = {
             extraConfig = ''
               fastcgi_split_path_info ^(.+\.php)(/.+)$;
@@ -560,13 +578,22 @@ let
     };
 
     systemd.tmpfiles.rules = [
-      "d '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
-      "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}' 0555 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/lock' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache/upload' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache/images' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache/export' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/feed-icons' 0755 ${cfg.user} tt_rss - -"
+      "L+ '${cfg.root}/www' - - - - ${servedRoot}"
     ];
 
-    systemd.services.tt-rss =
-      {
+    systemd.services = {
+      phpfpm-tt-rss = mkIf (cfg.pool == "${poolName}") {
+        restartTriggers = [ servedRoot ];
+      };
 
+      tt-rss = {
         description = "Tiny Tiny RSS feeds update daemon";
 
         preStart = let
@@ -589,24 +616,7 @@ let
 
               else "";
 
-        in ''
-          rm -rf "${cfg.root}/*"
-          cp -r "${pkgs.tt-rss}/"* "${cfg.root}"
-          ${optionalString (cfg.pluginPackages != []) ''
-            for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
-              cp -r "$plugin"/* "${cfg.root}/plugins.local/"
-            done
-          ''}
-          ${optionalString (cfg.themePackages != []) ''
-            for theme in ${concatStringsSep " " cfg.themePackages}; do
-              cp -r "$theme"/* "${cfg.root}/themes.local/"
-            done
-          ''}
-          ln -sf "${tt-rss-config}" "${cfg.root}/config.php"
-          chmod -R 755 "${cfg.root}"
-        ''
-
-        + (optionalString (cfg.database.type == "pgsql") ''
+        in (optionalString (cfg.database.type == "pgsql") ''
           exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
           | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
 
@@ -631,7 +641,7 @@ let
         serviceConfig = {
           User = "${cfg.user}";
           Group = "tt_rss";
-          ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon --quiet";
+          ExecStart = "${pkgs.php}/bin/php ${cfg.root}/www/update.php --daemon --quiet";
           Restart = "on-failure";
           RestartSec = "60";
           SyslogIdentifier = "tt-rss";
@@ -640,6 +650,7 @@ let
         wantedBy = [ "multi-user.target" ];
         requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
         after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+      };
     };
 
     services.mysql = mkIf mysqlLocal {
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
index df7035c03cc2..ceb199870975 100644
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -36,11 +36,12 @@ let
   dependentCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
 
   mkListenInfo = hostOpts:
-    if hostOpts.listen != [] then hostOpts.listen
-    else (
-      optional (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) { ip = "*"; port = 443; ssl = true; } ++
-      optional (!hostOpts.onlySSL) { ip = "*"; port = 80; ssl = false; }
-    );
+    if hostOpts.listen != [] then
+      hostOpts.listen
+    else
+      optionals (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) (map (addr: { ip = addr; port = 443; ssl = true; }) hostOpts.listenAddresses) ++
+      optionals (!hostOpts.onlySSL) (map (addr: { ip = addr; port = 80; ssl = false; }) hostOpts.listenAddresses)
+    ;
 
   listenInfo = unique (concatMap mkListenInfo vhosts);
 
@@ -462,7 +463,7 @@ in
         default = "common";
         example = "combined";
         description = ''
-          Log format for log files. Possible values are: combined, common, referer, agent.
+          Log format for log files. Possible values are: combined, common, referer, agent, none.
           See <link xlink:href="https://httpd.apache.org/docs/2.4/logs.html"/> for more details.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
index 394f9a305546..3f732a5c9f33 100644
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
@@ -47,10 +47,27 @@ in
       ];
       description = ''
         Listen addresses and ports for this virtual host.
-        <note><para>
+        <note>
+        <para>
           This option overrides <literal>addSSL</literal>, <literal>forceSSL</literal> and <literal>onlySSL</literal>.
-        </para></note>
+        </para>
+        <para>
+          If you only want to set the addresses manually and not the ports, take a look at <literal>listenAddresses</literal>.
+        </para>
+        </note>
+      '';
+    };
+
+    listenAddresses = mkOption {
+      type = with types; nonEmptyListOf str;
+
+      description = ''
+        Listen addresses for this virtual host.
+        Compared to <literal>listen</literal> this only sets the addreses
+        and the ports are chosen automatically.
       '';
+      default = [ "*" ];
+      example = [ "127.0.0.1" ];
     };
 
     enableSSL = mkOption {
diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
index 955b9756406d..fd7102096343 100644
--- a/nixpkgs/nixos/modules/services/web-servers/caddy.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
@@ -4,42 +4,57 @@ with lib;
 
 let
   cfg = config.services.caddy;
-  configFile = pkgs.writeText "Caddyfile" cfg.config;
+  vhostToConfig = vhostName: vhostAttrs: ''
+    ${vhostName} ${builtins.concatStringsSep " " vhostAttrs.serverAliases} {
+      ${vhostAttrs.extraConfig}
+    }
+  '';
+  configFile = pkgs.writeText "Caddyfile" (builtins.concatStringsSep "\n"
+    ([ cfg.config ] ++ (mapAttrsToList vhostToConfig cfg.virtualHosts)));
+
+  formattedConfig = pkgs.runCommand "formattedCaddyFile" { } ''
+    ${cfg.package}/bin/caddy fmt ${configFile} > $out
+  '';
 
   tlsConfig = {
     apps.tls.automation.policies = [{
-      issuer = {
+      issuers = [{
         inherit (cfg) ca email;
         module = "acme";
-      };
+      }];
     }];
   };
 
   adaptedConfig = pkgs.runCommand "caddy-config-adapted.json" { } ''
     ${cfg.package}/bin/caddy adapt \
-      --config ${configFile} --adapter ${cfg.adapter} > $out
+      --config ${formattedConfig} --adapter ${cfg.adapter} > $out
   '';
   tlsJSON = pkgs.writeText "tls.json" (builtins.toJSON tlsConfig);
 
   # merge the TLS config options we expose with the ones originating in the Caddyfile
   configJSON =
-    let tlsConfigMerge = ''
-      {"apps":
-        {"tls":
-          {"automation":
-            {"policies":
-              (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies
-               then .[0].apps.tls.automation.policies
-               else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies)
-               end)
+    if cfg.ca != null then
+      let tlsConfigMerge = ''
+        {"apps":
+          {"tls":
+            {"automation":
+              {"policies":
+                (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies
+                 then .[0].apps.tls.automation.policies
+                 else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies)
+                 end)
+              }
             }
           }
-        }
-      }'';
-    in pkgs.runCommand "caddy-config.json" { } ''
-    ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out
-  '';
-in {
+        }'';
+      in
+      pkgs.runCommand "caddy-config.json" { } ''
+        ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out
+      ''
+    else
+      adaptedConfig;
+in
+{
   imports = [
     (mkRemovedOptionModule [ "services" "caddy" "agree" ] "this option is no longer necessary for Caddy 2")
   ];
@@ -63,6 +78,27 @@ in {
       '';
     };
 
+    virtualHosts = mkOption {
+      type = types.attrsOf (types.submodule (import ./vhost-options.nix {
+        inherit config lib;
+      }));
+      default = { };
+      example = literalExample ''
+        {
+          "hydra.example.com" = {
+            serverAliases = [ "www.hydra.example.com" ];
+            extraConfig = ''''''
+              encode gzip
+              log
+              root /srv/http
+            '''''';
+          };
+        };
+      '';
+      description = "Declarative vhost config";
+    };
+
+
     user = mkOption {
       default = "caddy";
       type = types.str;
@@ -85,11 +121,24 @@ in {
       '';
     };
 
+    resume = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Use saved config, if any (and prefer over configuration passed with <option>services.caddy.config</option>).
+      '';
+    };
+
     ca = mkOption {
       default = "https://acme-v02.api.letsencrypt.org/directory";
       example = "https://acme-staging-v02.api.letsencrypt.org/directory";
-      type = types.str;
-      description = "Certificate authority ACME server. The default (Let's Encrypt production server) should be fine for most people.";
+      type = types.nullOr types.str;
+      description = ''
+        Certificate authority ACME server. The default (Let's Encrypt
+        production server) should be fine for most people. Set it to null if
+        you don't want to include any authority (or if you want to write a more
+        fine-graned configuration manually)
+      '';
     };
 
     email = mkOption {
@@ -132,7 +181,7 @@ in {
       startLimitIntervalSec = 14400;
       startLimitBurst = 10;
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/caddy run --config ${configJSON}";
+        ExecStart = "${cfg.package}/bin/caddy run ${optionalString cfg.resume "--resume"} --config ${configJSON}";
         ExecReload = "${cfg.package}/bin/caddy reload --config ${configJSON}";
         Type = "simple";
         User = cfg.user;
diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix
new file mode 100644
index 000000000000..1f74295fc9a2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix
@@ -0,0 +1,28 @@
+# This file defines the options that can be used both for the Nginx
+# main server configuration, and for the virtual hosts.  (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{ lib, ... }:
+
+with lib;
+{
+  options = {
+    serverAliases = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "www.example.org" "example.org" ];
+      description = ''
+        Additional names of virtual hosts served by this virtual host configuration.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        These lines go into the vhost verbatim
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/minio.nix b/nixpkgs/nixos/modules/services/web-servers/minio.nix
index d075449012f7..6b10afad4991 100644
--- a/nixpkgs/nixos/modules/services/web-servers/minio.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/minio.nix
@@ -19,7 +19,13 @@ in
     listenAddress = mkOption {
       default = ":9000";
       type = types.str;
-      description = "Listen on a specific IP address and port.";
+      description = "IP address and port of the server.";
+    };
+
+    consoleAddress = mkOption {
+      default = ":9001";
+      type = types.str;
+      description = "IP address and port of the web UI (console).";
     };
 
     dataDir = mkOption {
@@ -99,7 +105,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --config-dir=${cfg.configDir} ${toString cfg.dataDir}";
+        ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --console-address ${cfg.consoleAddress} --config-dir=${cfg.configDir} ${toString cfg.dataDir}";
         Type = "simple";
         User = "minio";
         Group = "minio";
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
index ebb3c38d6c25..6682472fdb8e 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -22,7 +22,9 @@ let
     } // (optionalAttrs (vhostConfig.enableACME || vhostConfig.useACMEHost != null) {
       sslCertificate = "${certs.${certName}.directory}/fullchain.pem";
       sslCertificateKey = "${certs.${certName}.directory}/key.pem";
-      sslTrustedCertificate = "${certs.${certName}.directory}/chain.pem";
+      sslTrustedCertificate = if vhostConfig.sslTrustedCertificate != null
+                              then vhostConfig.sslTrustedCertificate
+                              else "${certs.${certName}.directory}/chain.pem";
     })
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
@@ -169,6 +171,14 @@ let
         map_hash_max_size ${toString cfg.mapHashMaxSize};
       ''}
 
+      ${optionalString (cfg.serverNamesHashBucketSize != null) ''
+        server_names_hash_bucket_size ${toString cfg.serverNamesHashBucketSize};
+      ''}
+
+      ${optionalString (cfg.serverNamesHashMaxSize != null) ''
+        server_names_hash_max_size ${toString cfg.serverNamesHashMaxSize};
+      ''}
+
       # $connection_upgrade is used for websocket proxying
       map $http_upgrade $connection_upgrade {
           default upgrade;
@@ -230,13 +240,13 @@ let
 
         defaultListen =
           if vhost.listen != [] then vhost.listen
-          else optionals (hasSSL || vhost.rejectSSL) (
-            singleton { addr = "0.0.0.0"; port = 443; ssl = true; }
-            ++ optional enableIPv6 { addr = "[::]"; port = 443; ssl = true; }
-          ) ++ optionals (!onlySSL) (
-            singleton { addr = "0.0.0.0"; port = 80; ssl = false; }
-            ++ optional enableIPv6 { addr = "[::]"; port = 80; ssl = false; }
-          );
+          else
+            let addrs = if vhost.listenAddresses != [] then vhost.listenAddresses else (
+              [ "0.0.0.0" ] ++ optional enableIPv6 "[::0]"
+            );
+            in
+          optionals (hasSSL || vhost.rejectSSL) (map (addr: { inherit addr; port = 443; ssl = true; }) addrs)
+          ++ optionals (!onlySSL) (map (addr: { inherit addr; port = 80; ssl = false; }) addrs);
 
         hostListen =
           if vhost.forceSSL
@@ -641,6 +651,23 @@ in
           '';
       };
 
+      serverNamesHashBucketSize = mkOption {
+        type = types.nullOr types.ints.positive;
+        default = null;
+        description = ''
+            Sets the bucket size for the server names hash tables. Default
+            value depends on the processor’s cache line size.
+          '';
+      };
+
+      serverNamesHashMaxSize = mkOption {
+        type = types.nullOr types.ints.positive;
+        default = null;
+        description = ''
+            Sets the maximum size of the server names hash tables.
+          '';
+      };
+
       resolver = mkOption {
         type = types.submodule {
           options = {
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
index bc18bcaa7b34..94645e927f86 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -43,7 +43,24 @@ with lib;
         IPv6 addresses must be enclosed in square brackets.
         Note: this option overrides <literal>addSSL</literal>
         and <literal>onlySSL</literal>.
+
+        If you only want to set the addresses manually and not
+        the ports, take a look at <literal>listenAddresses</literal>
+      '';
+    };
+
+    listenAddresses = mkOption {
+      type = with types; listOf str;
+
+      description = ''
+        Listen addresses for this virtual host.
+        Compared to <literal>listen</literal> this only sets the addreses
+        and the ports are choosen automatically.
+
+        Note: This option overrides <literal>enableIPv6</literal>
       '';
+      default = [];
+      example = [ "127.0.0.1" "::1" ];
     };
 
     enableACME = mkOption {
@@ -145,7 +162,7 @@ with lib;
     sslTrustedCertificate = mkOption {
       type = types.nullOr types.path;
       default = null;
-      example = "/var/root.cert";
+      example = "\${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
       description = "Path to root SSL certificate for stapling and client certificates.";
     };
 
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
index b0859321a525..4bc42525906c 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -372,6 +372,13 @@ in
       xdg.portal.enable = true;
       xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
 
+      # Harmonize Qt5 application style and also make them use the portal for file chooser dialog.
+      qt5 = {
+        enable = mkDefault true;
+        platformTheme = mkDefault "gnome";
+        style = mkDefault "adwaita";
+      };
+
       networking.networkmanager.enable = mkDefault true;
 
       services.xserver.updateDbusEnvironment = true;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index b6be524aea66..aac905fea437 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -271,13 +271,14 @@ in
           kmenuedit
           kscreen
           kscreenlocker
-          ksysguard
+          ksystemstats
           kwayland
           kwin
           kwrited
           libkscreen
           libksysguard
           milou
+          plasma-systemmonitor
           plasma-browser-integration
           plasma-integration
           polkit-kde-agent
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix b/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix
index dec5c06cb3ca..861976d1186f 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix
@@ -39,6 +39,6 @@ python3.pkgs.buildPythonApplication {
   '';
 
   meta = with lib; {
-    maintainers = with maintainers; [ ];
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 }
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
index e04fcdaf4145..584dfb63c4dc 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
@@ -18,7 +18,6 @@ let
 
   fontconfig = config.fonts.fontconfig;
   xresourcesXft = pkgs.writeText "Xresources-Xft" ''
-    ${optionalString (fontconfig.dpi != 0) ''Xft.dpi: ${toString fontconfig.dpi}''}
     Xft.antialias: ${if fontconfig.antialias then "1" else "0"}
     Xft.rgba: ${fontconfig.subpixel.rgba}
     Xft.lcdfilter: lcd${fontconfig.subpixel.lcdfilter}
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
index ef9ec438cc1c..5c4c6c67fd02 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
@@ -164,6 +164,11 @@ in
     systemd.packages = with pkgs.gnome; [ gdm gnome-session gnome-shell ];
     environment.systemPackages = [ pkgs.gnome.adwaita-icon-theme ];
 
+    # We dont use the upstream gdm service
+    # it has to be disabled since the gdm package has it
+    # https://github.com/NixOS/nixpkgs/issues/108672
+    systemd.services.gdm.enable = false;
+
     systemd.services.display-manager.wants = [
       # Because sd_login_monitor_new requires /run/systemd/machines
       "systemd-machined.service"
@@ -309,7 +314,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = gdm
-        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+        session  required       pam_env.so conffile=/etc/pam/environment readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
index 76f16646cf5e..f18e4a914e57 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
@@ -10,8 +10,8 @@ let
 
 in
 {
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   options = {
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix
index 3d497c9f25ee..41c1b635f5d6 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -69,8 +69,8 @@ let
 
 in
 {
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   # Note: the order in which lightdm greeter modules are imported
@@ -284,7 +284,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = lightdm
-        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+        session  required       pam_env.so conffile=/etc/pam/environment readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
index 116994db1c14..d79b3cda2fcc 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
@@ -229,7 +229,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = sddm
-        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+        session  required       pam_env.so conffile=/etc/pam/environment readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py b/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py
index 0cca80af44e8..75940efe32b4 100755
--- a/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py
@@ -72,11 +72,14 @@ def main():
                     f"Setting session name: {session}, as we found the existing wayland-session: {session_file}"
                 )
                 user.set_session(session)
+                user.set_session_type("wayland")
             elif is_session_xsession(session_file):
                 logging.debug(
                     f"Setting session name: {session}, as we found the existing xsession: {session_file}"
                 )
                 user.set_x_session(session)
+                user.set_session(session)
+                user.set_session_type("x11")
             else:
                 logging.error(f"Couldn't figure out session type for {session_file}")
                 sys.exit(1)
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix
new file mode 100644
index 000000000000..132531c0ddc0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xserver.displayManager.sx;
+
+in {
+  options = {
+    services.xserver.displayManager.sx = {
+      enable = mkEnableOption "sx pseudo-display manager" // {
+        description = ''
+          Whether to enable the "sx" pseudo-display manager, which allows users
+          to start manually via the "sx" command from a vt shell. The X server
+          runs under the user's id, not as root. The user must provide a
+          ~/.config/sx/sxrc file containing session startup commands, see
+          sx(1). This is not automatically generated from the desktopManager
+          and windowManager settings. sx doesn't have a way to directly set
+          X server flags, but it can be done by overriding its xorgserver
+          dependency.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.sx ];
+    services.xserver = {
+      exportConfiguration = true;
+      displayManager = {
+        job.execCmd = "";
+        lightdm.enable = mkForce false;
+      };
+      logFile = mkDefault null;
+    };
+    systemd.services.display-manager.enable = false;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix b/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix
index 089e9f769f0a..37a14e34f57e 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix
@@ -27,7 +27,7 @@ in
         default = [];
         type = types.listOf types.package;
         description = "List of lua packages available for being used in the Awesome configuration.";
-        example = literalExample "[ luaPackages.oocairo ]";
+        example = literalExample "[ pkgs.luaPackages.vicious ]";
       };
 
       package = mkOption {
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix
index 171660c53ac3..5015852db69f 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix
@@ -8,17 +8,27 @@ in
 
 {
   options = {
-    services.xserver.windowManager.clfswm.enable = mkEnableOption "clfswm";
+    services.xserver.windowManager.clfswm = {
+      enable = mkEnableOption "clfswm";
+      package = mkOption {
+        type        = types.package;
+        default     = pkgs.lispPackages.clfswm;
+        defaultText = "pkgs.lispPackages.clfswm";
+        description = ''
+          clfswm package to use.
+        '';
+      };
+    };
   };
 
   config = mkIf cfg.enable {
     services.xserver.windowManager.session = singleton {
       name = "clfswm";
       start = ''
-        ${pkgs.lispPackages.clfswm}/bin/clfswm &
+        ${cfg.package}/bin/clfswm &
         waitPID=$!
       '';
     };
-    environment.systemPackages = [ pkgs.lispPackages.clfswm ];
+    environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/default.nix b/nixpkgs/nixos/modules/services/x11/window-managers/default.nix
index 53285fbce877..d71738ea633f 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/default.nix
@@ -26,6 +26,7 @@ in
     ./leftwm.nix
     ./lwm.nix
     ./metacity.nix
+    ./mlvwm.nix
     ./mwm.nix
     ./openbox.nix
     ./pekwm.nix
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix
new file mode 100644
index 000000000000..08dd04020296
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xserver.windowManager.mlvwm;
+
+in
+{
+
+  options.services.xserver.windowManager.mlvwm = {
+    enable = mkEnableOption "Macintosh-like Virtual Window Manager";
+
+    configFile = mkOption {
+      default = null;
+      type = with types; nullOr path;
+      description = ''
+        Path to the mlvwm configuration file.
+        If left at the default value, $HOME/.mlvwmrc will be used.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = [{
+      name = "mlvwm";
+      start = ''
+        ${pkgs.mlvwm}/bin/mlvwm ${optionalString (cfg.configFile != null)
+          "-f /etc/mlvwm/mlvwmrc"
+        } &
+        waitPID=$!
+      '';
+    }];
+
+    environment.etc."mlvwm/mlvwmrc" = mkIf (cfg.configFile != null) {
+      source = cfg.configFile;
+    };
+
+    environment.systemPackages = [ pkgs.mlvwm ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix
index cadc316bbc4f..835b41d4ada9 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix
@@ -15,7 +15,7 @@ in
     services.xserver.windowManager.session = [{
       name = "qtile";
       start = ''
-        ${pkgs.qtile}/bin/qtile &
+        ${pkgs.qtile}/bin/qtile start &
         waitPID=$!
       '';
     }];
diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix
index 37e004ae80a7..ad9bd88f98aa 100644
--- a/nixpkgs/nixos/modules/services/x11/xserver.nix
+++ b/nixpkgs/nixos/modules/services/x11/xserver.nix
@@ -297,7 +297,11 @@ in
       dpi = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = "DPI resolution to use for X server.";
+        description = ''
+          Force global DPI resolution to use for X server. It's recommended to
+          use this only when DPI is detected incorrectly; also consider using
+          <literal>Monitor</literal> section in configuration file instead.
+        '';
       };
 
       updateDbusEnvironment = mkOption {
@@ -657,6 +661,7 @@ in
         pkgs.xterm
         pkgs.xdg-utils
         xorg.xf86inputevdev.out # get evdev.4 man page
+        pkgs.nixos-icons # needed for gnome and pantheon about dialog, nixos-manual and maybe more
       ]
       ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
 
@@ -680,7 +685,7 @@ in
     systemd.services.display-manager =
       { description = "X11 Server";
 
-        after = [ "acpid.service" "systemd-logind.service" ];
+        after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
 
         restartIfChanged = false;