about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/services')
-rw-r--r--nixpkgs/nixos/modules/services/backup/automysqlbackup.nix1
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgbackup.nix1
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix43
-rw-r--r--nixpkgs/nixos/modules/services/cluster/spark/default.nix162
-rw-r--r--nixpkgs/nixos/modules/services/databases/influxdb.nix1
-rw-r--r--nixpkgs/nixos/modules/services/databases/memcached.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/mongodb.nix4
-rw-r--r--nixpkgs/nixos/modules/services/databases/neo4j.nix4
-rw-r--r--nixpkgs/nixos/modules/services/databases/redis.nix1
-rw-r--r--nixpkgs/nixos/modules/services/desktops/cpupower-gui.nix56
-rw-r--r--nixpkgs/nixos/modules/services/desktops/gnome/gnome-keyring.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/gsignond.nix2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json41
-rw-r--r--nixpkgs/nixos/modules/services/display-managers/greetd.nix7
-rw-r--r--nixpkgs/nixos/modules/services/games/minecraft-server.nix4
-rw-r--r--nixpkgs/nixos/modules/services/hardware/tcsd.nix6
-rw-r--r--nixpkgs/nixos/modules/services/logging/graylog.nix4
-rw-r--r--nixpkgs/nixos/modules/services/logging/logcheck.nix6
-rw-r--r--nixpkgs/nixos/modules/services/mail/exim.nix7
-rw-r--r--nixpkgs/nixos/modules/services/mail/mail.nix3
-rw-r--r--nixpkgs/nixos/modules/services/mail/opensmtpd.nix5
-rw-r--r--nixpkgs/nixos/modules/services/mail/postfix.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/airsonic.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/apache-kafka.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/cgminer.nix8
-rw-r--r--nixpkgs/nixos/modules/services/misc/docker-registry.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/etcd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/gammu-smsd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitlab.nix49
-rw-r--r--nixpkgs/nixos/modules/services/misc/gpsd.nix1
-rw-r--r--nixpkgs/nixos/modules/services/misc/home-assistant.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/mame.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/nix-ssh-serve.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/owncast.nix98
-rw-r--r--nixpkgs/nixos/modules/services/misc/ripple-data-api.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/rippled.nix8
-rw-r--r--nixpkgs/nixos/modules/services/misc/safeeyes.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/weechat.nix7
-rw-r--r--nixpkgs/nixos/modules/services/misc/zookeeper.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix6
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/graphite.nix1
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/heapster.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/incron.nix7
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/netdata.nix74
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/parsedmarc.md113
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/parsedmarc.nix537
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/parsedmarc.xml125
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/tuptime.nix1
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix7
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/bind.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/consul.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/coturn.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/dnsmasq.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/flannel.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/git-daemon.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/iodine.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/morty.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/ncdns.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/ngircd.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/nntp-proxy.nix14
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/pleroma.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/pleroma.xml298
-rw-r--r--nixpkgs/nixos/modules/services/networking/radicale.nix7
-rw-r--r--nixpkgs/nixos/modules/services/networking/radvd.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/rdnssd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/shout.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/smokeping.nix13
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinydns.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix21
-rw-r--r--nixpkgs/nixos/modules/services/networking/toxvpn.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/tvheadend.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/vsftpd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/x2goserver.nix2
-rw-r--r--nixpkgs/nixos/modules/services/scheduling/atd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/scheduling/cron.nix7
-rw-r--r--nixpkgs/nixos/modules/services/scheduling/fcron.nix3
-rw-r--r--nixpkgs/nixos/modules/services/search/elasticsearch.nix15
-rw-r--r--nixpkgs/nixos/modules/services/search/kibana.nix4
-rw-r--r--nixpkgs/nixos/modules/services/search/meilisearch.md39
-rw-r--r--nixpkgs/nixos/modules/services/search/meilisearch.nix132
-rw-r--r--nixpkgs/nixos/modules/services/search/meilisearch.xml85
-rw-r--r--nixpkgs/nixos/modules/services/security/hockeypuck.nix2
-rw-r--r--nixpkgs/nixos/modules/services/security/opensnitch.nix24
-rw-r--r--nixpkgs/nixos/modules/services/security/physlock.nix10
-rw-r--r--nixpkgs/nixos/modules/services/security/tor.nix2
-rw-r--r--nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix2
-rw-r--r--nixpkgs/nixos/modules/services/system/localtime.nix2
-rw-r--r--nixpkgs/nixos/modules/services/torrent/magnetico.nix2
-rw-r--r--nixpkgs/nixos/modules/services/torrent/peerflix.nix6
-rw-r--r--nixpkgs/nixos/modules/services/ttys/getty.nix8
-rw-r--r--nixpkgs/nixos/modules/services/video/replay-sorcery.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.nix32
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix595
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mastodon.nix35
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/node-red.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/wordpress.nix29
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/trafficserver/default.nix (renamed from nixpkgs/nixos/modules/services/web-servers/trafficserver.nix)16
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/trafficserver/ip_allow.json36
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/trafficserver/logging.json37
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/zope2.nix6
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix5
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix21
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix28
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.xml10
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix24
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/extra-layouts.nix52
-rw-r--r--nixpkgs/nixos/modules/services/x11/hardware/libinput.nix11
-rw-r--r--nixpkgs/nixos/modules/services/x11/touchegg.nix38
-rw-r--r--nixpkgs/nixos/modules/services/x11/xserver.nix3
119 files changed, 2576 insertions, 659 deletions
diff --git a/nixpkgs/nixos/modules/services/backup/automysqlbackup.nix b/nixpkgs/nixos/modules/services/backup/automysqlbackup.nix
index e3a8d1f79934..4fcaf9eb9301 100644
--- a/nixpkgs/nixos/modules/services/backup/automysqlbackup.nix
+++ b/nixpkgs/nixos/modules/services/backup/automysqlbackup.nix
@@ -73,6 +73,7 @@ in
     services.automysqlbackup.config = mapAttrs (name: mkDefault) {
       mysql_dump_username = user;
       mysql_dump_host = "localhost";
+      mysql_dump_socket = "/run/mysqld/mysqld.sock";
       backup_dir = "/var/backup/mysql";
       db_exclude = [ "information_schema" "performance_schema" ];
       mailcontent = "stdout";
diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
index ccbc7726392d..c4174286fc0b 100644
--- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
@@ -169,6 +169,7 @@ let
         (map (mkAuthorizedKey cfg false) cfg.authorizedKeys
         ++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly);
       useDefaultShell = true;
+      group = cfg.group;
       isSystemUser = true;
     };
     groups.${cfg.group} = { };
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix
index 33d217ba60ed..08b214181806 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix
@@ -5,28 +5,33 @@ with lib;
 let
   cfg = config.services.kubernetes;
 
-  defaultContainerdConfigFile = pkgs.writeText "containerd.toml" ''
-    version = 2
-    root = "/var/lib/containerd"
-    state = "/run/containerd"
-    oom_score = 0
-
-    [grpc]
-      address = "/run/containerd/containerd.sock"
+  defaultContainerdSettings = {
+    version = 2;
+    root = "/var/lib/containerd";
+    state = "/run/containerd";
+    oom_score = 0;
+
+    grpc = {
+      address = "/run/containerd/containerd.sock";
+    };
 
-    [plugins."io.containerd.grpc.v1.cri"]
-      sandbox_image = "pause:latest"
+    plugins."io.containerd.grpc.v1.cri" = {
+      sandbox_image = "pause:latest";
 
-    [plugins."io.containerd.grpc.v1.cri".cni]
-      bin_dir = "/opt/cni/bin"
-      max_conf_num = 0
+      cni = {
+        bin_dir = "/opt/cni/bin";
+        max_conf_num = 0;
+      };
 
-    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
-      runtime_type = "io.containerd.runc.v2"
+      containerd.runtimes.runc = {
+        runtime_type = "io.containerd.runc.v2";
+      };
 
-    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes."io.containerd.runc.v2".options]
-      SystemdCgroup = true
-  '';
+      containerd.runtimes."io.containerd.runc.v2".options = {
+        SystemdCgroup = true;
+      };
+    };
+  };
 
   mkKubeConfig = name: conf: pkgs.writeText "${name}-kubeconfig" (builtins.toJSON {
     apiVersion = "v1";
@@ -248,7 +253,7 @@ in {
     (mkIf cfg.kubelet.enable {
       virtualisation.containerd = {
         enable = mkDefault true;
-        configFile = mkDefault defaultContainerdConfigFile;
+        settings = mapAttrsRecursive (name: mkDefault) defaultContainerdSettings;
       };
     })
 
diff --git a/nixpkgs/nixos/modules/services/cluster/spark/default.nix b/nixpkgs/nixos/modules/services/cluster/spark/default.nix
new file mode 100644
index 000000000000..bbfe0489f115
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/cluster/spark/default.nix
@@ -0,0 +1,162 @@
+{config, pkgs, lib, ...}:
+let
+  cfg = config.services.spark;
+in
+with lib;
+{
+  options = {
+    services.spark = {
+      master = {
+        enable = mkEnableOption "Spark master service";
+        bind = mkOption {
+          type = types.str;
+          description = "Address the spark master binds to.";
+          default = "127.0.0.1";
+          example = "0.0.0.0";
+        };
+        restartIfChanged  = mkOption {
+          type = types.bool;
+          description = ''
+            Automatically restart master service on config change.
+            This can be set to false to defer restarts on clusters running critical applications.
+            Please consider the security implications of inadvertently running an older version,
+            and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
+          '';
+          default = true;
+        };
+        extraEnvironment = mkOption {
+          type = types.attrsOf types.str;
+          description = "Extra environment variables to pass to spark master. See spark-standalone documentation.";
+          default = {};
+          example = {
+            SPARK_MASTER_WEBUI_PORT = 8181;
+            SPARK_MASTER_OPTS = "-Dspark.deploy.defaultCores=5";
+          };
+        };
+      };
+      worker = {
+        enable = mkEnableOption "Spark worker service";
+        workDir = mkOption {
+          type = types.path;
+          description = "Spark worker work dir.";
+          default = "/var/lib/spark";
+        };
+        master = mkOption {
+          type = types.str;
+          description = "Address of the spark master.";
+          default = "127.0.0.1:7077";
+        };
+        restartIfChanged  = mkOption {
+          type = types.bool;
+          description = ''
+            Automatically restart worker service on config change.
+            This can be set to false to defer restarts on clusters running critical applications.
+            Please consider the security implications of inadvertently running an older version,
+            and the possibility of unexpected behavior caused by inconsistent versions across a cluster when disabling this option.
+          '';
+          default = true;
+        };
+        extraEnvironment = mkOption {
+          type = types.attrsOf types.str;
+          description = "Extra environment variables to pass to spark worker.";
+          default = {};
+          example = {
+            SPARK_WORKER_CORES = 5;
+            SPARK_WORKER_MEMORY = "2g";
+          };
+        };
+      };
+      confDir = mkOption {
+        type = types.path;
+        description = "Spark configuration directory. Spark will use the configuration files (spark-defaults.conf, spark-env.sh, log4j.properties, etc) from this directory.";
+        default = "${cfg.package}/lib/${cfg.package.untarDir}/conf";
+        defaultText = literalExample "\${cfg.package}/lib/\${cfg.package.untarDir}/conf";
+      };
+      logDir = mkOption {
+        type = types.path;
+        description = "Spark log directory.";
+        default = "/var/log/spark";
+      };
+      package = mkOption {
+        type = types.package;
+        description = "Spark package.";
+        default = pkgs.spark;
+        defaultText = "pkgs.spark";
+        example = literalExample ''pkgs.spark.overrideAttrs (super: rec {
+          pname = "spark";
+          version = "2.4.4";
+
+          src = pkgs.fetchzip {
+            url    = "mirror://apache/spark/"''${pname}-''${version}/''${pname}-''${version}-bin-without-hadoop.tgz";
+            sha256 = "1a9w5k0207fysgpxx6db3a00fs5hdc2ncx99x4ccy2s0v5ndc66g";
+          };
+        })'';
+      };
+    };
+  };
+  config = lib.mkIf (cfg.worker.enable || cfg.master.enable) {
+    environment.systemPackages = [ cfg.package ];
+    systemd = {
+      services = {
+        spark-master = lib.mkIf cfg.master.enable {
+          path = with pkgs; [ procps openssh nettools ];
+          description = "spark master service.";
+          after = [ "network.target" ];
+          wantedBy = [ "multi-user.target" ];
+          restartIfChanged = cfg.master.restartIfChanged;
+          environment = cfg.master.extraEnvironment // {
+            SPARK_MASTER_HOST = cfg.master.bind;
+            SPARK_CONF_DIR = cfg.confDir;
+            SPARK_LOG_DIR = cfg.logDir;
+          };
+          serviceConfig = {
+            Type = "forking";
+            User = "spark";
+            Group = "spark";
+            WorkingDirectory = "${cfg.package}/lib/${cfg.package.untarDir}";
+            ExecStart = "${cfg.package}/lib/${cfg.package.untarDir}/sbin/start-master.sh";
+            ExecStop  = "${cfg.package}/lib/${cfg.package.untarDir}/sbin/stop-master.sh";
+            TimeoutSec = 300;
+            StartLimitBurst=10;
+            Restart = "always";
+          };
+        };
+        spark-worker = lib.mkIf cfg.worker.enable {
+          path = with pkgs; [ procps openssh nettools rsync ];
+          description = "spark master service.";
+          after = [ "network.target" ];
+          wantedBy = [ "multi-user.target" ];
+          restartIfChanged = cfg.worker.restartIfChanged;
+          environment = cfg.worker.extraEnvironment // {
+            SPARK_MASTER = cfg.worker.master;
+            SPARK_CONF_DIR = cfg.confDir;
+            SPARK_LOG_DIR = cfg.logDir;
+            SPARK_WORKER_DIR = cfg.worker.workDir;
+          };
+          serviceConfig = {
+            Type = "forking";
+            User = "spark";
+            WorkingDirectory = "${cfg.package}/lib/${cfg.package.untarDir}";
+            ExecStart = "${cfg.package}/lib/${cfg.package.untarDir}/sbin/start-worker.sh spark://${cfg.worker.master}";
+            ExecStop  = "${cfg.package}/lib/${cfg.package.untarDir}/sbin/stop-worker.sh";
+            TimeoutSec = 300;
+            StartLimitBurst=10;
+            Restart = "always";
+          };
+        };
+      };
+      tmpfiles.rules = [
+        "d '${cfg.worker.workDir}' - spark spark - -"
+        "d '${cfg.logDir}' - spark spark - -"
+      ];
+    };
+    users = {
+      users.spark = {
+        description = "spark user.";
+        group = "spark";
+        isSystemUser = true;
+      };
+      groups.spark = { };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/databases/influxdb.nix b/nixpkgs/nixos/modules/services/databases/influxdb.nix
index dd5d69b1147a..3b8c00929ba9 100644
--- a/nixpkgs/nixos/modules/services/databases/influxdb.nix
+++ b/nixpkgs/nixos/modules/services/databases/influxdb.nix
@@ -185,6 +185,7 @@ in
     users.users = optionalAttrs (cfg.user == "influxdb") {
       influxdb = {
         uid = config.ids.uids.influxdb;
+        group = "influxdb";
         description = "Influxdb daemon user";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/databases/memcached.nix b/nixpkgs/nixos/modules/services/databases/memcached.nix
index ca7b20eb049a..1c06937e2f30 100644
--- a/nixpkgs/nixos/modules/services/databases/memcached.nix
+++ b/nixpkgs/nixos/modules/services/databases/memcached.nix
@@ -67,7 +67,9 @@ in
     users.users = optionalAttrs (cfg.user == "memcached") {
       memcached.description = "Memcached server user";
       memcached.isSystemUser = true;
+      memcached.group = "memcached";
     };
+    users.groups = optionalAttrs (cfg.user == "memcached") { memcached = {}; };
 
     environment.systemPackages = [ memcached ];
 
diff --git a/nixpkgs/nixos/modules/services/databases/mongodb.nix b/nixpkgs/nixos/modules/services/databases/mongodb.nix
index db1e5fedf50d..5121e0415db1 100644
--- a/nixpkgs/nixos/modules/services/databases/mongodb.nix
+++ b/nixpkgs/nixos/modules/services/databases/mongodb.nix
@@ -123,9 +123,11 @@ in
 
     users.users.mongodb = mkIf (cfg.user == "mongodb")
       { name = "mongodb";
-        uid = config.ids.uids.mongodb;
+        isSystemUser = true;
+        group = "mongodb";
         description = "MongoDB server user";
       };
+    users.groups.mongodb = mkIf (cfg.user == "mongodb") {};
 
     environment.systemPackages = [ mongodb ];
 
diff --git a/nixpkgs/nixos/modules/services/databases/neo4j.nix b/nixpkgs/nixos/modules/services/databases/neo4j.nix
index 53760bb24c4a..2a30923538db 100644
--- a/nixpkgs/nixos/modules/services/databases/neo4j.nix
+++ b/nixpkgs/nixos/modules/services/databases/neo4j.nix
@@ -651,10 +651,12 @@ in {
       environment.systemPackages = [ cfg.package ];
 
       users.users.neo4j = {
-        uid = config.ids.uids.neo4j;
+        isSystemUser = true;
+        group = "neo4j";
         description = "Neo4j daemon user";
         home = cfg.directories.home;
       };
+      users.groups.neo4j = {};
     };
 
   meta = {
diff --git a/nixpkgs/nixos/modules/services/databases/redis.nix b/nixpkgs/nixos/modules/services/databases/redis.nix
index 8873f6d00e0b..1b9358c81a12 100644
--- a/nixpkgs/nixos/modules/services/databases/redis.nix
+++ b/nixpkgs/nixos/modules/services/databases/redis.nix
@@ -246,6 +246,7 @@ in {
 
     users.users.redis = {
       description = "Redis database user";
+      group = "redis";
       isSystemUser = true;
     };
     users.groups.redis = {};
diff --git a/nixpkgs/nixos/modules/services/desktops/cpupower-gui.nix b/nixpkgs/nixos/modules/services/desktops/cpupower-gui.nix
new file mode 100644
index 000000000000..f66afc0a3dc1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/desktops/cpupower-gui.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cpupower-gui;
+in {
+  options = {
+    services.cpupower-gui = {
+      enable = mkOption {
+        type = lib.types.bool;
+        default = false;
+        example = true;
+        description = ''
+          Enables dbus/systemd service needed by cpupower-gui.
+          These services are responsible for retrieving and modifying cpu power
+          saving settings.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.cpupower-gui ];
+    services.dbus.packages = [ pkgs.cpupower-gui ];
+    systemd.user = {
+      services.cpupower-gui-user = {
+        description = "Apply cpupower-gui config at user login";
+        wantedBy = [ "graphical-session.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkgs.cpupower-gui}/bin/cpupower-gui config";
+        };
+      };
+    };
+    systemd.services = {
+      cpupower-gui = {
+        description = "Apply cpupower-gui config at boot";
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${pkgs.cpupower-gui}/bin/cpupower-gui config";
+        };
+      };
+      cpupower-gui-helper = {
+        description = "cpupower-gui system helper";
+        aliases = [ "dbus-org.rnd2.cpupower_gui.helper.service" ];
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "org.rnd2.cpupower_gui.helper";
+          ExecStart = "${pkgs.cpupower-gui}/lib/cpupower-gui/cpupower-gui-helper";
+        };
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/desktops/gnome/gnome-keyring.nix b/nixpkgs/nixos/modules/services/desktops/gnome/gnome-keyring.nix
index cda44bab8bfa..d821da164beb 100644
--- a/nixpkgs/nixos/modules/services/desktops/gnome/gnome-keyring.nix
+++ b/nixpkgs/nixos/modules/services/desktops/gnome/gnome-keyring.nix
@@ -52,8 +52,10 @@ with lib;
     security.pam.services.login.enableGnomeKeyring = true;
 
     security.wrappers.gnome-keyring-daemon = {
-      source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
+      owner = "root";
+      group = "root";
       capabilities = "cap_ipc_lock=ep";
+      source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/desktops/gsignond.nix b/nixpkgs/nixos/modules/services/desktops/gsignond.nix
index 5ab9add9f32d..465acd73fa64 100644
--- a/nixpkgs/nixos/modules/services/desktops/gsignond.nix
+++ b/nixpkgs/nixos/modules/services/desktops/gsignond.nix
@@ -9,7 +9,7 @@ let
 in
 {
 
-  meta.maintainers = pkgs.pantheon.maintainers;
+  meta.maintainers = teams.pantheon.members;
 
   ###### interface
 
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 46697ece4483..e5e7517e38d4 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
@@ -34,6 +34,13 @@
       ]
     },
     {
+      "name": "D50s",
+      "address": "~^00:13:ef:",
+      "no-features": [
+        "hw-volume"
+      ]
+    },
+    {
       "name": "JBL Endurance RUN BT",
       "no-features": [
         "msbc-alt1",
@@ -45,6 +52,18 @@
       "name": "JBL LIVE650BTNC"
     },
     {
+      "name": "Motorola DC800",
+      "no-features": [
+        "sbc-xq"
+      ]
+    },
+    {
+      "name": "Motorola S305",
+      "no-features": [
+        "sbc-xq"
+      ]
+    },
+    {
       "name": "Soundcore Life P2-L",
       "no-features": [
         "msbc-alt1",
@@ -191,47 +210,35 @@
     },
     {
       "sysname": "Linux",
-      "release": "~^5\\.(8|9|10)\\.",
+      "release": "~^5\\.(8|9)\\.",
       "no-features": [
         "msbc-alt1"
       ]
     },
     {
       "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\\.",
+      "release": "~^5\\.10\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|51|52|53|54|55|56|57|58|59|60|61)($|[^0-9])",
       "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\\.",
+      "release": "~^5\\.12\\.(18|19)($|[^0-9])",
       "no-features": [
         "msbc-alt1"
       ]
     },
     {
       "sysname": "Linux",
-      "release": "~^5\\.13\\.(1|2)($|[^0-9])"
-    },
-    {
-      "sysname": "Linux",
-      "release": "~^5\\.13\\.",
+      "release": "~^5\\.13\\.(3|4|5|6|7|8|9|10|11|12|13)($|[^0-9])",
       "no-features": [
         "msbc-alt1"
       ]
     },
     {
       "sysname": "Linux",
-      "release": "~^5\\.14\\.",
+      "release": "~^5\\.14($|[^0-9])",
       "no-features": [
         "msbc-alt1"
       ]
diff --git a/nixpkgs/nixos/modules/services/display-managers/greetd.nix b/nixpkgs/nixos/modules/services/display-managers/greetd.nix
index c3072bf09964..d4f5dc267d09 100644
--- a/nixpkgs/nixos/modules/services/display-managers/greetd.nix
+++ b/nixpkgs/nixos/modules/services/display-managers/greetd.nix
@@ -99,7 +99,12 @@ in
 
     systemd.defaultUnit = "graphical.target";
 
-    users.users.greeter.isSystemUser = true;
+    users.users.greeter = {
+      isSystemUser = true;
+      group = "greeter";
+    };
+
+    users.groups.greeter = {};
   };
 
   meta.maintainers = with maintainers; [ queezle ];
diff --git a/nixpkgs/nixos/modules/services/games/minecraft-server.nix b/nixpkgs/nixos/modules/services/games/minecraft-server.nix
index eb9288fca586..458e57fef846 100644
--- a/nixpkgs/nixos/modules/services/games/minecraft-server.nix
+++ b/nixpkgs/nixos/modules/services/games/minecraft-server.nix
@@ -167,8 +167,10 @@ in {
       description     = "Minecraft server service user";
       home            = cfg.dataDir;
       createHome      = true;
-      uid             = config.ids.uids.minecraft;
+      isSystemUser    = true;
+      group           = "minecraft";
     };
+    users.groups.minecraft = {};
 
     systemd.services.minecraft-server = {
       description   = "Minecraft Server Service";
diff --git a/nixpkgs/nixos/modules/services/hardware/tcsd.nix b/nixpkgs/nixos/modules/services/hardware/tcsd.nix
index 0d36bce357ba..c549a6775013 100644
--- a/nixpkgs/nixos/modules/services/hardware/tcsd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/tcsd.nix
@@ -149,12 +149,10 @@ in
     users.users = optionalAttrs (cfg.user == "tss") {
       tss = {
         group = "tss";
-        uid = config.ids.uids.tss;
+        isSystemUser = true;
       };
     };
 
-    users.groups = optionalAttrs (cfg.group == "tss") {
-      tss.gid = config.ids.gids.tss;
-    };
+    users.groups = optionalAttrs (cfg.group == "tss") { tss = {}; };
   };
 }
diff --git a/nixpkgs/nixos/modules/services/logging/graylog.nix b/nixpkgs/nixos/modules/services/logging/graylog.nix
index af70d27fcf99..5e20a10f2490 100644
--- a/nixpkgs/nixos/modules/services/logging/graylog.nix
+++ b/nixpkgs/nixos/modules/services/logging/graylog.nix
@@ -128,10 +128,12 @@ in
 
     users.users = mkIf (cfg.user == "graylog") {
       graylog = {
-        uid = config.ids.uids.graylog;
+        isSystemUser = true;
+        group = "graylog";
         description = "Graylog server daemon user";
       };
     };
+    users.groups = mkIf (cfg.user == "graylog") {};
 
     systemd.tmpfiles.rules = [
       "d '${cfg.messageJournalDir}' - ${cfg.user} - - -"
diff --git a/nixpkgs/nixos/modules/services/logging/logcheck.nix b/nixpkgs/nixos/modules/services/logging/logcheck.nix
index 4296b2270c29..348ed8adf903 100644
--- a/nixpkgs/nixos/modules/services/logging/logcheck.nix
+++ b/nixpkgs/nixos/modules/services/logging/logcheck.nix
@@ -215,12 +215,16 @@ in
 
     users.users = optionalAttrs (cfg.user == "logcheck") {
       logcheck = {
-        uid = config.ids.uids.logcheck;
+        group = "logcheck";
+        isSystemUser = true;
         shell = "/bin/sh";
         description = "Logcheck user account";
         extraGroups = cfg.extraGroups;
       };
     };
+    users.groups = optionalAttrs (cfg.user == "logcheck") {
+      logcheck = {};
+    };
 
     system.activationScripts.logcheck = ''
       mkdir -m 700 -p /var/{lib,lock}/logcheck
diff --git a/nixpkgs/nixos/modules/services/mail/exim.nix b/nixpkgs/nixos/modules/services/mail/exim.nix
index 8927d84b478c..25b533578c94 100644
--- a/nixpkgs/nixos/modules/services/mail/exim.nix
+++ b/nixpkgs/nixos/modules/services/mail/exim.nix
@@ -104,7 +104,12 @@ in
       gid = config.ids.gids.exim;
     };
 
-    security.wrappers.exim.source = "${cfg.package}/bin/exim";
+    security.wrappers.exim =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${cfg.package}/bin/exim";
+      };
 
     systemd.services.exim = {
       description = "Exim Mail Daemon";
diff --git a/nixpkgs/nixos/modules/services/mail/mail.nix b/nixpkgs/nixos/modules/services/mail/mail.nix
index fed313e4738e..fcc7ff6db91b 100644
--- a/nixpkgs/nixos/modules/services/mail/mail.nix
+++ b/nixpkgs/nixos/modules/services/mail/mail.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, options, lib, ... }:
 
 with lib;
 
@@ -11,6 +11,7 @@ with lib;
     services.mail = {
 
       sendmailSetuidWrapper = mkOption {
+        type = types.nullOr options.security.wrappers.type.nestedTypes.elemType;
         default = null;
         internal = true;
         description = ''
diff --git a/nixpkgs/nixos/modules/services/mail/opensmtpd.nix b/nixpkgs/nixos/modules/services/mail/opensmtpd.nix
index c838d3b949db..ef7d53e7d927 100644
--- a/nixpkgs/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixpkgs/nixos/modules/services/mail/opensmtpd.nix
@@ -103,12 +103,15 @@ in {
     };
 
     security.wrappers.smtpctl = {
+      owner = "nobody";
       group = "smtpq";
+      setuid = false;
       setgid = true;
       source = "${cfg.package}/bin/smtpctl";
     };
 
-    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail security.wrappers.smtpctl;
+    services.mail.sendmailSetuidWrapper = mkIf cfg.setSendmail
+      (security.wrappers.smtpctl // { program = "sendmail"; });
 
     systemd.tmpfiles.rules = [
       "d /var/spool/smtpd 711 root - - -"
diff --git a/nixpkgs/nixos/modules/services/mail/postfix.nix b/nixpkgs/nixos/modules/services/mail/postfix.nix
index 9b0a5bba2feb..2b8edb9c51f8 100644
--- a/nixpkgs/nixos/modules/services/mail/postfix.nix
+++ b/nixpkgs/nixos/modules/services/mail/postfix.nix
@@ -673,6 +673,7 @@ in
       services.mail.sendmailSetuidWrapper = mkIf config.services.postfix.setSendmail {
         program = "sendmail";
         source = "${pkgs.postfix}/bin/sendmail";
+        owner = "nobody";
         group = setgidGroup;
         setuid = false;
         setgid = true;
@@ -681,6 +682,7 @@ in
       security.wrappers.mailq = {
         program = "mailq";
         source = "${pkgs.postfix}/bin/mailq";
+        owner = "nobody";
         group = setgidGroup;
         setuid = false;
         setgid = true;
@@ -689,6 +691,7 @@ in
       security.wrappers.postqueue = {
         program = "postqueue";
         source = "${pkgs.postfix}/bin/postqueue";
+        owner = "nobody";
         group = setgidGroup;
         setuid = false;
         setgid = true;
@@ -697,6 +700,7 @@ in
       security.wrappers.postdrop = {
         program = "postdrop";
         source = "${pkgs.postfix}/bin/postdrop";
+        owner = "nobody";
         group = setgidGroup;
         setuid = false;
         setgid = true;
diff --git a/nixpkgs/nixos/modules/services/misc/airsonic.nix b/nixpkgs/nixos/modules/services/misc/airsonic.nix
index 490f6c5a5c06..c1ce515750b0 100644
--- a/nixpkgs/nixos/modules/services/misc/airsonic.nix
+++ b/nixpkgs/nixos/modules/services/misc/airsonic.nix
@@ -165,10 +165,12 @@ in {
 
     users.users.airsonic = {
       description = "Airsonic service user";
+      group = "airsonic";
       name = cfg.user;
       home = cfg.home;
       createHome = true;
       isSystemUser = true;
     };
+    users.groups.airsonic = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/misc/apache-kafka.nix b/nixpkgs/nixos/modules/services/misc/apache-kafka.nix
index 69dfadfe54e0..8bc307311a42 100644
--- a/nixpkgs/nixos/modules/services/misc/apache-kafka.nix
+++ b/nixpkgs/nixos/modules/services/misc/apache-kafka.nix
@@ -120,10 +120,12 @@ in {
     environment.systemPackages = [cfg.package];
 
     users.users.apache-kafka = {
-      uid = config.ids.uids.apache-kafka;
+      isSystemUser = true;
+      group = "apache-kafka";
       description = "Apache Kafka daemon user";
       home = head cfg.logDirs;
     };
+    users.groups.apache-kafka = {};
 
     systemd.tmpfiles.rules = map (logDir: "d '${logDir}' 0700 apache-kafka - - -") cfg.logDirs;
 
diff --git a/nixpkgs/nixos/modules/services/misc/cgminer.nix b/nixpkgs/nixos/modules/services/misc/cgminer.nix
index 662570f9451f..5afc1546efa9 100644
--- a/nixpkgs/nixos/modules/services/misc/cgminer.nix
+++ b/nixpkgs/nixos/modules/services/misc/cgminer.nix
@@ -86,7 +86,7 @@ in
 
       config = mkOption {
         default = {};
-        type = (types.either types.bool types.int);
+        type = types.attrsOf (types.either types.bool types.int);
         description = "Additional config";
         example = {
           auto-fan = true;
@@ -110,10 +110,14 @@ in
 
     users.users = optionalAttrs (cfg.user == "cgminer") {
       cgminer = {
-        uid = config.ids.uids.cgminer;
+        isSystemUser = true;
+        group = "cgminer";
         description = "Cgminer user";
       };
     };
+    users.groups = optionalAttrs (cfg.user == "cgminer") {
+      cgminer = {};
+    };
 
     environment.systemPackages = [ cfg.package ];
 
diff --git a/nixpkgs/nixos/modules/services/misc/docker-registry.nix b/nixpkgs/nixos/modules/services/misc/docker-registry.nix
index e212f581c28a..cb68a29c530b 100644
--- a/nixpkgs/nixos/modules/services/misc/docker-registry.nix
+++ b/nixpkgs/nixos/modules/services/misc/docker-registry.nix
@@ -151,7 +151,9 @@ in {
         home = cfg.storagePath;
       }
       else {}) // {
+        group = "docker-registry";
         isSystemUser = true;
       };
+    users.groups.docker-registry = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/misc/etcd.nix b/nixpkgs/nixos/modules/services/misc/etcd.nix
index eb266f043ebc..2b667fab6b04 100644
--- a/nixpkgs/nixos/modules/services/misc/etcd.nix
+++ b/nixpkgs/nixos/modules/services/misc/etcd.nix
@@ -187,9 +187,11 @@ in {
     environment.systemPackages = [ pkgs.etcd ];
 
     users.users.etcd = {
-      uid = config.ids.uids.etcd;
+      isSystemUser = true;
+      group = "etcd";
       description = "Etcd daemon user";
       home = cfg.dataDir;
     };
+    users.groups.etcd = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/misc/gammu-smsd.nix b/nixpkgs/nixos/modules/services/misc/gammu-smsd.nix
index 552725f1384d..d4bb58d81dde 100644
--- a/nixpkgs/nixos/modules/services/misc/gammu-smsd.nix
+++ b/nixpkgs/nixos/modules/services/misc/gammu-smsd.nix
@@ -202,8 +202,8 @@ in {
   config = mkIf cfg.enable {
     users.users.${cfg.user} = {
       description = "gammu-smsd user";
-      uid = config.ids.uids.gammu-smsd;
-      extraGroups = [ "${cfg.device.group}" ];
+      isSystemUser = true;
+      group = cfg.device.group;
     };
 
     environment.systemPackages = with cfg.backend; [ gammuPackage ]
diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix
index 805deeee0c04..c1a1491b3cee 100644
--- a/nixpkgs/nixos/modules/services/misc/gitlab.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix
@@ -821,6 +821,40 @@ in {
         '';
       };
 
+      logrotate = {
+        enable = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Enable rotation of log files.
+          '';
+        };
+
+        frequency = mkOption {
+          type = types.str;
+          default = "daily";
+          description = "How often to rotate the logs.";
+        };
+
+        keep = mkOption {
+          type = types.int;
+          default = 30;
+          description = "How many rotations to keep.";
+        };
+
+        extraConfig = mkOption {
+          type = types.lines;
+          default = ''
+            copytruncate
+            compress
+          '';
+          description = ''
+            Extra logrotate config options for this path. Refer to
+            <link xlink:href="https://linux.die.net/man/8/logrotate"/> for details.
+          '';
+        };
+      };
+
       extraConfig = mkOption {
         type = types.attrs;
         default = {};
@@ -932,6 +966,21 @@ in {
       ensureUsers = singleton { name = cfg.databaseUsername; };
     };
 
+    # Enable rotation of log files
+    services.logrotate = {
+      enable = cfg.logrotate.enable;
+      paths = {
+        gitlab = {
+          path = "${cfg.statePath}/log/*.log";
+          user = cfg.user;
+          group = cfg.group;
+          frequency = cfg.logrotate.frequency;
+          keep = cfg.logrotate.keep;
+          extraConfig = cfg.logrotate.extraConfig;
+        };
+      };
+    };
+
     # The postgresql module doesn't currently support concepts like
     # objects owners and extensions; for now we tack on what's needed
     # here.
diff --git a/nixpkgs/nixos/modules/services/misc/gpsd.nix b/nixpkgs/nixos/modules/services/misc/gpsd.nix
index fafea10daba7..6494578f7647 100644
--- a/nixpkgs/nixos/modules/services/misc/gpsd.nix
+++ b/nixpkgs/nixos/modules/services/misc/gpsd.nix
@@ -88,6 +88,7 @@ in
 
     users.users.gpsd =
       { inherit uid;
+        group = "gpsd";
         description = "gpsd daemon user";
         home = "/var/empty";
       };
diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
index 73ec3b9a17a2..800bea4ff571 100644
--- a/nixpkgs/nixos/modules/services/misc/home-assistant.nix
+++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
@@ -310,11 +310,13 @@ in {
           "serial_pm"
           "sms"
           "upb"
+          "usb"
           "velbus"
           "w800rf32"
           "xbee"
           "zha"
           "zwave"
+          "zwave_js"
         ];
       in {
         ExecStart = "${package}/bin/hass --runner --config '${cfg.configDir}'";
diff --git a/nixpkgs/nixos/modules/services/misc/mame.nix b/nixpkgs/nixos/modules/services/misc/mame.nix
index 4b9a04be7c29..dd6c5ef9aa00 100644
--- a/nixpkgs/nixos/modules/services/misc/mame.nix
+++ b/nixpkgs/nixos/modules/services/misc/mame.nix
@@ -45,8 +45,10 @@ in
     environment.systemPackages = [ pkgs.mame ];
 
     security.wrappers."${mame}" = {
-      source = "${pkgs.mame}/bin/${mame}";
+      owner = "root";
+      group = "root";
       capabilities = "cap_net_admin,cap_net_raw+eip";
+      source = "${pkgs.mame}/bin/${mame}";
     };
 
     systemd.services.mame = {
diff --git a/nixpkgs/nixos/modules/services/misc/nix-ssh-serve.nix b/nixpkgs/nixos/modules/services/misc/nix-ssh-serve.nix
index 1764c6d79649..d5c64fdb2647 100644
--- a/nixpkgs/nixos/modules/services/misc/nix-ssh-serve.nix
+++ b/nixpkgs/nixos/modules/services/misc/nix-ssh-serve.nix
@@ -44,9 +44,11 @@ in {
 
     users.users.nix-ssh = {
       description = "Nix SSH store user";
-      uid = config.ids.uids.nix-ssh;
+      isSystemUser = true;
+      group = "nix-ssh";
       useDefaultShell = true;
     };
+    users.groups.nix-ssh = {};
 
     services.openssh.enable = true;
 
diff --git a/nixpkgs/nixos/modules/services/misc/owncast.nix b/nixpkgs/nixos/modules/services/misc/owncast.nix
new file mode 100644
index 000000000000..0852335238fd
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/owncast.nix
@@ -0,0 +1,98 @@
+{ lib, pkgs, config, ... }:
+with lib;
+let cfg = config.services.owncast;
+in {
+
+  options.services.owncast = {
+
+    enable = mkEnableOption "owncast";
+
+    dataDir = mkOption {
+      type = types.str;
+      default = "/var/lib/owncast";
+      description = ''
+        The directory where owncast stores its data files. If left as the default value this directory will automatically be created before the owncast server starts, otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership and permissions.
+      '';
+    };
+
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open the appropriate ports in the firewall for owncast.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "owncast";
+      description = "User account under which owncast runs.";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "owncast";
+      description = "Group under which owncast runs.";
+    };
+
+    listen = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      example = "0.0.0.0";
+      description = "The IP address to bind the owncast web server to.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8080;
+      description = ''
+        TCP port where owncast web-gui listens.
+      '';
+    };
+
+    rtmp-port = mkOption {
+      type = types.port;
+      default = 1935;
+      description = ''
+        TCP port where owncast rtmp service listens.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd.services.owncast = {
+      description = "A self-hosted live video and web chat server";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = mkMerge [
+        {
+          User = cfg.user;
+          Group = cfg.group;
+          WorkingDirectory = cfg.dataDir;
+          ExecStart = "${pkgs.owncast}/bin/owncast -webserverport ${toString cfg.port} -rtmpport ${toString cfg.rtmp-port} -webserverip ${cfg.listen}";
+          Restart = "on-failure";
+        }
+        (mkIf (cfg.dataDir == "/var/lib/owncast") {
+          StateDirectory = "owncast";
+        })
+      ];
+    };
+
+    users.users = mkIf (cfg.user == "owncast") {
+      owncast = {
+        isSystemUser = true;
+        group = cfg.group;
+        description = "owncast system user";
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "owncast") { owncast = { }; };
+
+    networking.firewall =
+      mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.rtmp-port ] ++ optional (cfg.listen != "127.0.0.1") cfg.port; };
+
+  };
+  meta = { maintainers = with lib.maintainers; [ MayNiklas ]; };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/ripple-data-api.nix b/nixpkgs/nixos/modules/services/misc/ripple-data-api.nix
index 9fab462f7e3b..93eba98b7d30 100644
--- a/nixpkgs/nixos/modules/services/misc/ripple-data-api.nix
+++ b/nixpkgs/nixos/modules/services/misc/ripple-data-api.nix
@@ -187,7 +187,9 @@ in {
 
     users.users.ripple-data-api =
       { description = "Ripple data api user";
-        uid = config.ids.uids.ripple-data-api;
+        isSystemUser = true;
+        group = "ripple-data-api";
       };
+    users.groups.ripple-data-api = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/misc/rippled.nix b/nixpkgs/nixos/modules/services/misc/rippled.nix
index 2fce3b9dc94c..8cdfe0875d89 100644
--- a/nixpkgs/nixos/modules/services/misc/rippled.nix
+++ b/nixpkgs/nixos/modules/services/misc/rippled.nix
@@ -407,12 +407,14 @@ in
 
   config = mkIf cfg.enable {
 
-    users.users.rippled =
-      { description = "Ripple server user";
-        uid = config.ids.uids.rippled;
+    users.users.rippled = {
+        description = "Ripple server user";
+        isSystemUser = true;
+        group = "rippled";
         home = cfg.databasePath;
         createHome = true;
       };
+    users.groups.rippled = {};
 
     systemd.services.rippled = {
       after = [ "network.target" ];
diff --git a/nixpkgs/nixos/modules/services/misc/safeeyes.nix b/nixpkgs/nixos/modules/services/misc/safeeyes.nix
index 1e748195e41a..638218d8bb00 100644
--- a/nixpkgs/nixos/modules/services/misc/safeeyes.nix
+++ b/nixpkgs/nixos/modules/services/misc/safeeyes.nix
@@ -26,12 +26,16 @@ in
 
   config = mkIf cfg.enable {
 
+    environment.systemPackages = [ pkgs.safeeyes ];
+
     systemd.user.services.safeeyes = {
       description = "Safeeyes";
 
       wantedBy = [ "graphical-session.target" ];
       partOf   = [ "graphical-session.target" ];
 
+      path = [ pkgs.alsa-utils ];
+
       startLimitIntervalSec = 350;
       startLimitBurst = 10;
       serviceConfig = {
diff --git a/nixpkgs/nixos/modules/services/misc/weechat.nix b/nixpkgs/nixos/modules/services/misc/weechat.nix
index b71250f62e0f..9ac2b0ea490c 100644
--- a/nixpkgs/nixos/modules/services/misc/weechat.nix
+++ b/nixpkgs/nixos/modules/services/misc/weechat.nix
@@ -52,7 +52,12 @@ in
       wants = [ "network.target" ];
     };
 
-    security.wrappers.screen.source = "${pkgs.screen}/bin/screen";
+    security.wrappers.screen =
+      { setuid = true;
+        owner = "root";
+        group = "root";
+        source = "${pkgs.screen}/bin/screen";
+      };
   };
 
   meta.doc = ./weechat.xml;
diff --git a/nixpkgs/nixos/modules/services/misc/zookeeper.nix b/nixpkgs/nixos/modules/services/misc/zookeeper.nix
index 1d12e81a9eca..0e5880983e44 100644
--- a/nixpkgs/nixos/modules/services/misc/zookeeper.nix
+++ b/nixpkgs/nixos/modules/services/misc/zookeeper.nix
@@ -148,9 +148,11 @@ in {
     };
 
     users.users.zookeeper = {
-      uid = config.ids.uids.zookeeper;
+      isSystemUser = true;
+      group = "zookeeper";
       description = "Zookeeper daemon user";
       home = cfg.dataDir;
     };
+    users.groups.zookeeper = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix b/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix
index b25a53435d06..ea9eca180902 100644
--- a/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/datadog-agent.nix
@@ -51,7 +51,7 @@ in {
   options.services.datadog-agent = {
     enable = mkOption {
       description = ''
-        Whether to enable the datadog-agent v6 monitoring service
+        Whether to enable the datadog-agent v7 monitoring service
       '';
       default = false;
       type = types.bool;
@@ -61,7 +61,7 @@ in {
       default = pkgs.datadog-agent;
       defaultText = "pkgs.datadog-agent";
       description = ''
-        Which DataDog v6 agent package to use. Note that the provided
+        Which DataDog v7 agent package to use. Note that the provided
         package is expected to have an overridable `pythonPackages`-attribute
         which configures the Python environment with the Datadog
         checks.
@@ -274,7 +274,7 @@ in {
         path = [ ];
         script = ''
           export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
-          ${pkgs.datadog-process-agent}/bin/agent --config /etc/datadog-agent/datadog.yaml
+          ${pkgs.datadog-process-agent}/bin/process-agent --config /etc/datadog-agent/datadog.yaml
         '';
       });
 
diff --git a/nixpkgs/nixos/modules/services/monitoring/graphite.nix b/nixpkgs/nixos/modules/services/monitoring/graphite.nix
index 9213748d3c9a..502afce5233b 100644
--- a/nixpkgs/nixos/modules/services/monitoring/graphite.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/graphite.nix
@@ -561,6 +561,7 @@ in {
      ) {
       users.users.graphite = {
         uid = config.ids.uids.graphite;
+        group = "graphite";
         description = "Graphite daemon user";
         home = dataDir;
       };
diff --git a/nixpkgs/nixos/modules/services/monitoring/heapster.nix b/nixpkgs/nixos/modules/services/monitoring/heapster.nix
index 0a9dfa12eaa5..1bf7203d6823 100644
--- a/nixpkgs/nixos/modules/services/monitoring/heapster.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/heapster.nix
@@ -50,8 +50,10 @@ in {
     };
 
     users.users.heapster = {
-      uid = config.ids.uids.heapster;
+      isSystemUser = true;
+      group = "heapster";
       description = "Heapster user";
     };
+    users.groups.heapster = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/incron.nix b/nixpkgs/nixos/modules/services/monitoring/incron.nix
index dc97af58562e..255e1d9e30ba 100644
--- a/nixpkgs/nixos/modules/services/monitoring/incron.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/incron.nix
@@ -71,7 +71,12 @@ in
 
     environment.systemPackages = [ pkgs.incron ];
 
-    security.wrappers.incrontab.source = "${pkgs.incron}/bin/incrontab";
+    security.wrappers.incrontab =
+    { setuid = true;
+      owner = "root";
+      group = "root";
+      source = "${pkgs.incron}/bin/incrontab";
+    };
 
     # incron won't read symlinks
     environment.etc."incron.d/system" = {
diff --git a/nixpkgs/nixos/modules/services/monitoring/netdata.nix b/nixpkgs/nixos/modules/services/monitoring/netdata.nix
index 561ce3eec625..3ea84ca815f4 100644
--- a/nixpkgs/nixos/modules/services/monitoring/netdata.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/netdata.nix
@@ -9,9 +9,9 @@ let
     mkdir -p $out/libexec/netdata/plugins.d
     ln -s /run/wrappers/bin/apps.plugin $out/libexec/netdata/plugins.d/apps.plugin
     ln -s /run/wrappers/bin/cgroup-network $out/libexec/netdata/plugins.d/cgroup-network
-    ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
     ln -s /run/wrappers/bin/perf.plugin $out/libexec/netdata/plugins.d/perf.plugin
     ln -s /run/wrappers/bin/slabinfo.plugin $out/libexec/netdata/plugins.d/slabinfo.plugin
+    ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
   '';
 
   plugins = [
@@ -211,44 +211,47 @@ in {
 
     systemd.enableCgroupAccounting = true;
 
-    security.wrappers."apps.plugin" = {
-      source = "${cfg.package}/libexec/netdata/plugins.d/apps.plugin.org";
-      capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
-      owner = cfg.user;
-      group = cfg.group;
-      permissions = "u+rx,g+x,o-rwx";
-    };
+    security.wrappers = {
+      "apps.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/apps.plugin.org";
+        capabilities = "cap_dac_read_search,cap_sys_ptrace+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
 
-    security.wrappers."cgroup-network" = {
-      source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
-      capabilities = "cap_setuid+ep";
-      owner = cfg.user;
-      group = cfg.group;
-      permissions = "u+rx,g+x,o-rwx";
-    };
+      "cgroup-network" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
+        capabilities = "cap_setuid+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
 
-    security.wrappers."freeipmi.plugin" = {
-      source = "${cfg.package}/libexec/netdata/plugins.d/freeipmi.plugin.org";
-      capabilities = "cap_dac_override,cap_fowner+ep";
-      owner = cfg.user;
-      group = cfg.group;
-      permissions = "u+rx,g+x,o-rwx";
-    };
+      "perf.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/perf.plugin.org";
+        capabilities = "cap_sys_admin+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
 
-    security.wrappers."perf.plugin" = {
-      source = "${cfg.package}/libexec/netdata/plugins.d/perf.plugin.org";
-      capabilities = "cap_sys_admin+ep";
-      owner = cfg.user;
-      group = cfg.group;
-      permissions = "u+rx,g+x,o-rwx";
-    };
+      "slabinfo.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org";
+        capabilities = "cap_dac_override+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
 
-    security.wrappers."slabinfo.plugin" = {
-      source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org";
-      capabilities = "cap_dac_override+ep";
-      owner = cfg.user;
-      group = cfg.group;
-      permissions = "u+rx,g+x,o-rwx";
+    } // optionalAttrs (cfg.package.withIpmi) {
+      "freeipmi.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/freeipmi.plugin.org";
+        capabilities = "cap_dac_override,cap_fowner+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
     };
 
     security.pam.loginLimits = [
@@ -258,6 +261,7 @@ in {
 
     users.users = optionalAttrs (cfg.user == defaultUser) {
       ${defaultUser} = {
+        group = defaultUser;
         isSystemUser = true;
       };
     };
diff --git a/nixpkgs/nixos/modules/services/monitoring/parsedmarc.md b/nixpkgs/nixos/modules/services/monitoring/parsedmarc.md
new file mode 100644
index 000000000000..d93134a4cc76
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/parsedmarc.md
@@ -0,0 +1,113 @@
+# parsedmarc {#module-services-parsedmarc}
+[parsedmarc](https://domainaware.github.io/parsedmarc/) is a service
+which parses incoming [DMARC](https://dmarc.org/) reports and stores
+or sends them to a downstream service for further analysis. In
+combination with Elasticsearch, Grafana and the included Grafana
+dashboard, it provides a handy overview of DMARC reports over time.
+
+## Basic usage {#module-services-parsedmarc-basic-usage}
+A very minimal setup which reads incoming reports from an external
+email address and saves them to a local Elasticsearch instance looks
+like this:
+
+```nix
+services.parsedmarc = {
+  enable = true;
+  settings.imap = {
+    host = "imap.example.com";
+    user = "alice@example.com";
+    password = "/path/to/imap_password_file";
+    watch = true;
+  };
+  provision.geoIp = false; # Not recommended!
+};
+```
+
+Note that GeoIP provisioning is disabled in the example for
+simplicity, but should be turned on for fully functional reports.
+
+## Local mail
+Instead of watching an external inbox, a local inbox can be
+automatically provisioned. The recipient's name is by default set to
+`dmarc`, but can be configured in
+[services.parsedmarc.provision.localMail.recipientName](options.html#opt-services.parsedmarc.provision.localMail.recipientName). You
+need to add an MX record pointing to the host. More concretely: for
+the example to work, an MX record needs to be set up for
+`monitoring.example.com` and the complete email address that should be
+configured in the domain's dmarc policy is
+`dmarc@monitoring.example.com`.
+
+```nix
+services.parsedmarc = {
+  enable = true;
+  provision = {
+    localMail = {
+      enable = true;
+      hostname = monitoring.example.com;
+    };
+    geoIp = false; # Not recommended!
+  };
+};
+```
+
+## Grafana and GeoIP
+The reports can be visualized and summarized with parsedmarc's
+official Grafana dashboard. For all views to work, and for the data to
+be complete, GeoIP databases are also required. The following example
+shows a basic deployment where the provisioned Elasticsearch instance
+is automatically added as a Grafana datasource, and the dashboard is
+added to Grafana as well.
+
+```nix
+services.parsedmarc = {
+  enable = true;
+  provision = {
+    localMail = {
+      enable = true;
+      hostname = url;
+    };
+    grafana = {
+      datasource = true;
+      dashboard = true;
+    };
+  };
+};
+
+# Not required, but recommended for full functionality
+services.geoipupdate = {
+  settings = {
+    AccountID = 000000;
+    LicenseKey = "/path/to/license_key_file";
+  };
+};
+
+services.grafana = {
+  enable = true;
+  addr = "0.0.0.0";
+  domain = url;
+  rootUrl = "https://" + url;
+  protocol = "socket";
+  security = {
+    adminUser = "admin";
+    adminPasswordFile = "/path/to/admin_password_file";
+    secretKeyFile = "/path/to/secret_key_file";
+  };
+};
+
+services.nginx = {
+  enable = true;
+  recommendedTlsSettings = true;
+  recommendedOptimisation = true;
+  recommendedGzipSettings = true;
+  recommendedProxySettings = true;
+  upstreams.grafana.servers."unix:/${config.services.grafana.socket}" = {};
+  virtualHosts.${url} = {
+    root = config.services.grafana.staticRootPath;
+    enableACME = true;
+    forceSSL = true;
+    locations."/".tryFiles = "$uri @grafana";
+    locations."@grafana".proxyPass = "http://grafana";
+  };
+};
+users.users.nginx.extraGroups = [ "grafana" ];
+```
diff --git a/nixpkgs/nixos/modules/services/monitoring/parsedmarc.nix b/nixpkgs/nixos/modules/services/monitoring/parsedmarc.nix
new file mode 100644
index 000000000000..e6a72dea0260
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/parsedmarc.nix
@@ -0,0 +1,537 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.parsedmarc;
+  ini = pkgs.formats.ini {};
+in
+{
+  options.services.parsedmarc = {
+
+    enable = lib.mkEnableOption ''
+      parsedmarc, a DMARC report monitoring service
+    '';
+
+    provision = {
+      localMail = {
+        enable = lib.mkOption {
+          type = lib.types.bool;
+          default = false;
+          description = ''
+            Whether Postfix and Dovecot should be set up to receive
+            mail locally. parsedmarc will be configured to watch the
+            local inbox as the automatically created user specified in
+            <xref linkend="opt-services.parsedmarc.provision.localMail.recipientName" />
+          '';
+        };
+
+        recipientName = lib.mkOption {
+          type = lib.types.str;
+          default = "dmarc";
+          description = ''
+            The DMARC mail recipient name, i.e. the name part of the
+            email address which receives DMARC reports.
+
+            A local user with this name will be set up and assigned a
+            randomized password on service start.
+          '';
+        };
+
+        hostname = lib.mkOption {
+          type = lib.types.str;
+          default = config.networking.fqdn;
+          defaultText = "config.networking.fqdn";
+          example = "monitoring.example.com";
+          description = ''
+            The hostname to use when configuring Postfix.
+
+            Should correspond to the host's fully qualified domain
+            name and the domain part of the email address which
+            receives DMARC reports. You also have to set up an MX record
+            pointing to this domain name.
+          '';
+        };
+      };
+
+      geoIp = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = ''
+          Whether to enable and configure the <link
+          linkend="opt-services.geoipupdate.enable">geoipupdate</link>
+          service to automatically fetch GeoIP databases. Not crucial,
+          but recommended for full functionality.
+
+          To finish the setup, you need to manually set the <xref
+          linkend="opt-services.geoipupdate.settings.AccountID" /> and
+          <xref linkend="opt-services.geoipupdate.settings.LicenseKey" />
+          options.
+        '';
+      };
+
+      elasticsearch = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = ''
+          Whether to set up and use a local instance of Elasticsearch.
+        '';
+      };
+
+      grafana = {
+        datasource = lib.mkOption {
+          type = lib.types.bool;
+          default = cfg.provision.elasticsearch && config.services.grafana.enable;
+          apply = x: x && cfg.provision.elasticsearch;
+          description = ''
+            Whether the automatically provisioned Elasticsearch
+            instance should be added as a grafana datasource. Has no
+            effect unless
+            <xref linkend="opt-services.parsedmarc.provision.elasticsearch" />
+            is also enabled.
+          '';
+        };
+
+        dashboard = lib.mkOption {
+          type = lib.types.bool;
+          default = config.services.grafana.enable;
+          description = ''
+            Whether the official parsedmarc grafana dashboard should
+            be provisioned to the local grafana instance.
+          '';
+        };
+      };
+    };
+
+    settings = lib.mkOption {
+      description = ''
+        Configuration parameters to set in
+        <filename>parsedmarc.ini</filename>. For a full list of
+        available parameters, see
+        <link xlink:href="https://domainaware.github.io/parsedmarc/#configuration-file" />.
+      '';
+
+      type = lib.types.submodule {
+        freeformType = ini.type;
+
+        options = {
+          general = {
+            save_aggregate = lib.mkOption {
+              type = lib.types.bool;
+              default = true;
+              description = ''
+                Save aggregate report data to Elasticsearch and/or Splunk.
+              '';
+            };
+
+            save_forensic = lib.mkOption {
+              type = lib.types.bool;
+              default = true;
+              description = ''
+                Save forensic report data to Elasticsearch and/or Splunk.
+              '';
+            };
+          };
+
+          imap = {
+            host = lib.mkOption {
+              type = lib.types.str;
+              default = "localhost";
+              description = ''
+                The IMAP server hostname or IP address.
+              '';
+            };
+
+            port = lib.mkOption {
+              type = lib.types.port;
+              default = 993;
+              description = ''
+                The IMAP server port.
+              '';
+            };
+
+            ssl = lib.mkOption {
+              type = lib.types.bool;
+              default = true;
+              description = ''
+                Use an encrypted SSL/TLS connection.
+              '';
+            };
+
+            user = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = ''
+                The IMAP server username.
+              '';
+            };
+
+            password = lib.mkOption {
+              type = with lib.types; nullOr path;
+              default = null;
+              description = ''
+                The path to a file containing the IMAP server password.
+              '';
+            };
+
+            watch = lib.mkOption {
+              type = lib.types.bool;
+              default = true;
+              description = ''
+                Use the IMAP IDLE command to process messages as they arrive.
+              '';
+            };
+
+            delete = lib.mkOption {
+              type = lib.types.bool;
+              default = false;
+              description = ''
+                Delete messages after processing them, instead of archiving them.
+              '';
+            };
+          };
+
+          smtp = {
+            host = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = ''
+                The SMTP server hostname or IP address.
+              '';
+            };
+
+            port = lib.mkOption {
+              type = with lib.types; nullOr port;
+              default = null;
+              description = ''
+                The SMTP server port.
+              '';
+            };
+
+            ssl = lib.mkOption {
+              type = with lib.types; nullOr bool;
+              default = null;
+              description = ''
+                Use an encrypted SSL/TLS connection.
+              '';
+            };
+
+            user = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = ''
+                The SMTP server username.
+              '';
+            };
+
+            password = lib.mkOption {
+              type = with lib.types; nullOr path;
+              default = null;
+              description = ''
+                The path to a file containing the SMTP server password.
+              '';
+            };
+
+            from = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = ''
+                The <literal>From</literal> address to use for the
+                outgoing mail.
+              '';
+            };
+
+            to = lib.mkOption {
+              type = with lib.types; nullOr (listOf str);
+              default = null;
+              description = ''
+                The addresses to send outgoing mail to.
+              '';
+            };
+          };
+
+          elasticsearch = {
+            hosts = lib.mkOption {
+              default = [];
+              type = with lib.types; listOf str;
+              apply = x: if x == [] then null else lib.concatStringsSep "," x;
+              description = ''
+                A list of Elasticsearch hosts to push parsed reports
+                to.
+              '';
+            };
+
+            user = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = ''
+                Username to use when connecting to Elasticsearch, if
+                required.
+              '';
+            };
+
+            password = lib.mkOption {
+              type = with lib.types; nullOr path;
+              default = null;
+              description = ''
+                The path to a file containing the password to use when
+                connecting to Elasticsearch, if required.
+              '';
+            };
+
+            ssl = lib.mkOption {
+              type = lib.types.bool;
+              default = false;
+              description = ''
+                Whether to use an encrypted SSL/TLS connection.
+              '';
+            };
+
+            cert_path = lib.mkOption {
+              type = lib.types.path;
+              default = "/etc/ssl/certs/ca-certificates.crt";
+              description = ''
+                The path to a TLS certificate bundle used to verify
+                the server's certificate.
+              '';
+            };
+          };
+
+          kafka = {
+            hosts = lib.mkOption {
+              default = [];
+              type = with lib.types; listOf str;
+              apply = x: if x == [] then null else lib.concatStringsSep "," x;
+              description = ''
+                A list of Apache Kafka hosts to publish parsed reports
+                to.
+              '';
+            };
+
+            user = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = ''
+                Username to use when connecting to Kafka, if
+                required.
+              '';
+            };
+
+            password = lib.mkOption {
+              type = with lib.types; nullOr path;
+              default = null;
+              description = ''
+                The path to a file containing the password to use when
+                connecting to Kafka, if required.
+              '';
+            };
+
+            ssl = lib.mkOption {
+              type = with lib.types; nullOr bool;
+              default = null;
+              description = ''
+                Whether to use an encrypted SSL/TLS connection.
+              '';
+            };
+
+            aggregate_topic = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              example = "aggregate";
+              description = ''
+                The Kafka topic to publish aggregate reports on.
+              '';
+            };
+
+            forensic_topic = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              example = "forensic";
+              description = ''
+                The Kafka topic to publish forensic reports on.
+              '';
+            };
+          };
+
+        };
+
+      };
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    services.elasticsearch.enable = lib.mkDefault cfg.provision.elasticsearch;
+
+    services.geoipupdate = lib.mkIf cfg.provision.geoIp {
+      enable = true;
+      settings = {
+        EditionIDs = [
+          "GeoLite2-ASN"
+          "GeoLite2-City"
+          "GeoLite2-Country"
+        ];
+        DatabaseDirectory = "/var/lib/GeoIP";
+      };
+    };
+
+    services.dovecot2 = lib.mkIf cfg.provision.localMail.enable {
+      enable = true;
+      protocols = [ "imap" ];
+    };
+
+    services.postfix = lib.mkIf cfg.provision.localMail.enable {
+      enable = true;
+      origin = cfg.provision.localMail.hostname;
+      config = {
+        myhostname = cfg.provision.localMail.hostname;
+        mydestination = cfg.provision.localMail.hostname;
+      };
+    };
+
+    services.grafana = {
+      declarativePlugins = with pkgs.grafanaPlugins;
+        lib.mkIf cfg.provision.grafana.dashboard [
+          grafana-worldmap-panel
+          grafana-piechart-panel
+        ];
+
+      provision = {
+        enable = cfg.provision.grafana.datasource || cfg.provision.grafana.dashboard;
+        datasources =
+          let
+            pkgVer = lib.getVersion config.services.elasticsearch.package;
+            esVersion =
+              if lib.versionOlder pkgVer "7" then
+                "60"
+              else if lib.versionOlder pkgVer "8" then
+                "70"
+              else
+                throw "When provisioning parsedmarc grafana datasources: unknown Elasticsearch version.";
+          in
+            lib.mkIf cfg.provision.grafana.datasource [
+              {
+                name = "dmarc-ag";
+                type = "elasticsearch";
+                access = "proxy";
+                url = "localhost:9200";
+                jsonData = {
+                  timeField = "date_range";
+                  inherit esVersion;
+                };
+              }
+              {
+                name = "dmarc-fo";
+                type = "elasticsearch";
+                access = "proxy";
+                url = "localhost:9200";
+                jsonData = {
+                  timeField = "date_range";
+                  inherit esVersion;
+                };
+              }
+            ];
+        dashboards = lib.mkIf cfg.provision.grafana.dashboard [{
+          name = "parsedmarc";
+          options.path = "${pkgs.python3Packages.parsedmarc.dashboard}";
+        }];
+      };
+    };
+
+    services.parsedmarc.settings = lib.mkMerge [
+      (lib.mkIf cfg.provision.elasticsearch {
+        elasticsearch = {
+          hosts = [ "localhost:9200" ];
+          ssl = false;
+        };
+      })
+      (lib.mkIf cfg.provision.localMail.enable {
+        imap = {
+          host = "localhost";
+          port = 143;
+          ssl = false;
+          user = cfg.provision.localMail.recipientName;
+          password = "${pkgs.writeText "imap-password" "@imap-password@"}";
+          watch = true;
+        };
+      })
+    ];
+
+    systemd.services.parsedmarc =
+      let
+        # Remove any empty attributes from the config, i.e. empty
+        # lists, empty attrsets and null. This makes it possible to
+        # list interesting options in `settings` without them always
+        # ending up in the resulting config.
+        filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! builtins.elem v [ null [] {} ])) cfg.settings;
+        parsedmarcConfig = ini.generate "parsedmarc.ini" filteredConfig;
+        mkSecretReplacement = file:
+          lib.optionalString (file != null) ''
+            replace-secret '${file}' '${file}' /run/parsedmarc/parsedmarc.ini
+          '';
+      in
+        {
+          wantedBy = [ "multi-user.target" ];
+          after = [ "postfix.service" "dovecot2.service" "elasticsearch.service" ];
+          path = with pkgs; [ replace-secret openssl shadow ];
+          serviceConfig = {
+            ExecStartPre = let
+              startPreFullPrivileges = ''
+                set -o errexit -o pipefail -o nounset -o errtrace
+                shopt -s inherit_errexit
+
+                umask u=rwx,g=,o=
+                cp ${parsedmarcConfig} /run/parsedmarc/parsedmarc.ini
+                chown parsedmarc:parsedmarc /run/parsedmarc/parsedmarc.ini
+                ${mkSecretReplacement cfg.settings.smtp.password}
+                ${mkSecretReplacement cfg.settings.imap.password}
+                ${mkSecretReplacement cfg.settings.elasticsearch.password}
+                ${mkSecretReplacement cfg.settings.kafka.password}
+              '' + lib.optionalString cfg.provision.localMail.enable ''
+                openssl rand -hex 64 >/run/parsedmarc/dmarc_user_passwd
+                replace-secret '@imap-password@' '/run/parsedmarc/dmarc_user_passwd' /run/parsedmarc/parsedmarc.ini
+                echo "Setting new randomized password for user '${cfg.provision.localMail.recipientName}'."
+                cat <(echo -n "${cfg.provision.localMail.recipientName}:") /run/parsedmarc/dmarc_user_passwd | chpasswd
+              '';
+            in
+              "+${pkgs.writeShellScript "parsedmarc-start-pre-full-privileges" startPreFullPrivileges}";
+            Type = "simple";
+            User = "parsedmarc";
+            Group = "parsedmarc";
+            DynamicUser = true;
+            RuntimeDirectory = "parsedmarc";
+            RuntimeDirectoryMode = 0700;
+            CapabilityBoundingSet = "";
+            PrivateDevices = true;
+            PrivateMounts = true;
+            PrivateUsers = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHome = true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            ProtectProc = "invisible";
+            ProcSubset = "pid";
+            SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+            RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+            RestrictRealtime = true;
+            RestrictNamespaces = true;
+            MemoryDenyWriteExecute = true;
+            LockPersonality = true;
+            SystemCallArchitectures = "native";
+            ExecStart = "${pkgs.python3Packages.parsedmarc}/bin/parsedmarc -c /run/parsedmarc/parsedmarc.ini";
+          };
+        };
+
+    users.users.${cfg.provision.localMail.recipientName} = lib.mkIf cfg.provision.localMail.enable {
+      isNormalUser = true;
+      description = "DMARC mail recipient";
+    };
+  };
+
+  # Don't edit the docbook xml directly, edit the md and generate it:
+  # `pandoc parsedmarc.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > parsedmarc.xml`
+  meta.doc = ./parsedmarc.xml;
+  meta.maintainers = [ lib.maintainers.talyz ];
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/parsedmarc.xml b/nixpkgs/nixos/modules/services/monitoring/parsedmarc.xml
new file mode 100644
index 000000000000..7167b52d0357
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/parsedmarc.xml
@@ -0,0 +1,125 @@
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-parsedmarc">
+  <title>parsedmarc</title>
+  <para>
+    <link xlink:href="https://domainaware.github.io/parsedmarc/">parsedmarc</link>
+    is a service which parses incoming
+    <link xlink:href="https://dmarc.org/">DMARC</link> reports and
+    stores or sends them to a downstream service for further analysis.
+    In combination with Elasticsearch, Grafana and the included Grafana
+    dashboard, it provides a handy overview of DMARC reports over time.
+  </para>
+  <section xml:id="module-services-parsedmarc-basic-usage">
+    <title>Basic usage</title>
+    <para>
+      A very minimal setup which reads incoming reports from an external
+      email address and saves them to a local Elasticsearch instance
+      looks like this:
+    </para>
+    <programlisting language="bash">
+services.parsedmarc = {
+  enable = true;
+  settings.imap = {
+    host = &quot;imap.example.com&quot;;
+    user = &quot;alice@example.com&quot;;
+    password = &quot;/path/to/imap_password_file&quot;;
+    watch = true;
+  };
+  provision.geoIp = false; # Not recommended!
+};
+</programlisting>
+    <para>
+      Note that GeoIP provisioning is disabled in the example for
+      simplicity, but should be turned on for fully functional reports.
+    </para>
+  </section>
+  <section xml:id="local-mail">
+    <title>Local mail</title>
+    <para>
+      Instead of watching an external inbox, a local inbox can be
+      automatically provisioned. The recipient’s name is by default set
+      to <literal>dmarc</literal>, but can be configured in
+      <link xlink:href="options.html#opt-services.parsedmarc.provision.localMail.recipientName">services.parsedmarc.provision.localMail.recipientName</link>.
+      You need to add an MX record pointing to the host. More
+      concretely: for the example to work, an MX record needs to be set
+      up for <literal>monitoring.example.com</literal> and the complete
+      email address that should be configured in the domain’s dmarc
+      policy is <literal>dmarc@monitoring.example.com</literal>.
+    </para>
+    <programlisting language="bash">
+services.parsedmarc = {
+  enable = true;
+  provision = {
+    localMail = {
+      enable = true;
+      hostname = monitoring.example.com;
+    };
+    geoIp = false; # Not recommended!
+  };
+};
+</programlisting>
+  </section>
+  <section xml:id="grafana-and-geoip">
+    <title>Grafana and GeoIP</title>
+    <para>
+      The reports can be visualized and summarized with parsedmarc’s
+      official Grafana dashboard. For all views to work, and for the
+      data to be complete, GeoIP databases are also required. The
+      following example shows a basic deployment where the provisioned
+      Elasticsearch instance is automatically added as a Grafana
+      datasource, and the dashboard is added to Grafana as well.
+    </para>
+    <programlisting language="bash">
+services.parsedmarc = {
+  enable = true;
+  provision = {
+    localMail = {
+      enable = true;
+      hostname = url;
+    };
+    grafana = {
+      datasource = true;
+      dashboard = true;
+    };
+  };
+};
+
+# Not required, but recommended for full functionality
+services.geoipupdate = {
+  settings = {
+    AccountID = 000000;
+    LicenseKey = &quot;/path/to/license_key_file&quot;;
+  };
+};
+
+services.grafana = {
+  enable = true;
+  addr = &quot;0.0.0.0&quot;;
+  domain = url;
+  rootUrl = &quot;https://&quot; + url;
+  protocol = &quot;socket&quot;;
+  security = {
+    adminUser = &quot;admin&quot;;
+    adminPasswordFile = &quot;/path/to/admin_password_file&quot;;
+    secretKeyFile = &quot;/path/to/secret_key_file&quot;;
+  };
+};
+
+services.nginx = {
+  enable = true;
+  recommendedTlsSettings = true;
+  recommendedOptimisation = true;
+  recommendedGzipSettings = true;
+  recommendedProxySettings = true;
+  upstreams.grafana.servers.&quot;unix:/${config.services.grafana.socket}&quot; = {};
+  virtualHosts.${url} = {
+    root = config.services.grafana.staticRootPath;
+    enableACME = true;
+    forceSSL = true;
+    locations.&quot;/&quot;.tryFiles = &quot;$uri @grafana&quot;;
+    locations.&quot;@grafana&quot;.proxyPass = &quot;http://grafana&quot;;
+  };
+};
+users.users.nginx.extraGroups = [ &quot;grafana&quot; ];
+</programlisting>
+  </section>
+</chapter>
diff --git a/nixpkgs/nixos/modules/services/monitoring/tuptime.nix b/nixpkgs/nixos/modules/services/monitoring/tuptime.nix
index 17c5c1f56eaf..de80282559ae 100644
--- a/nixpkgs/nixos/modules/services/monitoring/tuptime.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/tuptime.nix
@@ -36,6 +36,7 @@ in {
       groups._tuptime.members = [ "_tuptime" ];
       users._tuptime = {
         isSystemUser = true;
+        group = "_tuptime";
         description = "tuptime database owner";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix
index 2c8b8b92cb38..8c7a2970e9b3 100644
--- a/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -262,7 +262,12 @@ in
     };
 
     security.wrappers = {
-      fping.source = "${pkgs.fping}/bin/fping";
+      fping =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.fping}/bin/fping";
+        };
     };
 
     systemd.services.zabbix-proxy = {
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix b/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix
index 8eb754fe6110..8c55ccf5ffb0 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/orangefs/server.nix
@@ -193,7 +193,10 @@ in {
     environment.systemPackages = [ pkgs.orangefs ];
 
     # orangefs daemon will run as user
-    users.users.orangefs.isSystemUser = true;
+    users.users.orangefs = {
+      isSystemUser = true;
+      group = "orangfs";
+    };
     users.groups.orangefs = {};
 
     # To format the file system the config file is needed.
diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix
index 480d5a184f25..0c23fb7e40f0 100644
--- a/nixpkgs/nixos/modules/services/networking/bind.nix
+++ b/nixpkgs/nixos/modules/services/networking/bind.nix
@@ -229,9 +229,11 @@ in
 
     users.users.${bindUser} =
       {
-        uid = config.ids.uids.bind;
+        group = bindUser;
         description = "BIND daemon user";
+        isSystemUser = true;
       };
+    users.groups.${bindUser} = {};
 
     systemd.services.bind = {
       description = "BIND Domain Name Server";
diff --git a/nixpkgs/nixos/modules/services/networking/consul.nix b/nixpkgs/nixos/modules/services/networking/consul.nix
index ae7998913ee0..476ca738dd1b 100644
--- a/nixpkgs/nixos/modules/services/networking/consul.nix
+++ b/nixpkgs/nixos/modules/services/networking/consul.nix
@@ -159,10 +159,12 @@ in
 
       users.users.consul = {
         description = "Consul agent daemon user";
-        uid = config.ids.uids.consul;
+        isSystemUser = true;
+        group = "consul";
         # The shell is needed for health checks
         shell = "/run/current-system/sw/bin/bash";
       };
+      users.groups.consul = {};
 
       environment = {
         etc."consul.json".text = builtins.toJSON configOptions;
diff --git a/nixpkgs/nixos/modules/services/networking/coturn.nix b/nixpkgs/nixos/modules/services/networking/coturn.nix
index 5f7d2893ae27..12098ec6d338 100644
--- a/nixpkgs/nixos/modules/services/networking/coturn.nix
+++ b/nixpkgs/nixos/modules/services/networking/coturn.nix
@@ -311,6 +311,7 @@ in {
     {
       users.users.turnserver =
         { uid = config.ids.uids.turnserver;
+          group = "turnserver";
           description = "coturn TURN server user";
         };
       users.groups.turnserver =
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpd.nix b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
index 8966deac76cb..54e4f9002859 100644
--- a/nixpkgs/nixos/modules/services/networking/dhcpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/dhcpd.nix
@@ -212,9 +212,11 @@ in
 
     users = {
       users.dhcpd = {
-        uid = config.ids.uids.dhcpd;
+        isSystemUser = true;
+        group = "dhcpd";
         description = "DHCP daemon user";
       };
+      groups.dhcpd = {};
     };
 
     systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6;
diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix
index 89360f4bf373..400d6e67044e 100644
--- a/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix
+++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix
@@ -217,6 +217,7 @@ in {
       home = "${dataDir}";
       createHome = true;
       isSystemUser = true;
+      group = "dnscrypt-wrapper";
     };
     users.groups.dnscrypt-wrapper = { };
 
diff --git a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
index 377d7bc57058..59a3ca2f28e3 100644
--- a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
+++ b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix
@@ -87,9 +87,11 @@ in
     services.dbus.packages = [ dnsmasq ];
 
     users.users.dnsmasq = {
-      uid = config.ids.uids.dnsmasq;
+      isSystemUser = true;
+      group = "dnsmasq";
       description = "Dnsmasq daemon user";
     };
+    users.groups.dnsmasq = {};
 
     networking.resolvconf = mkIf cfg.resolveLocalQueries {
       useLocalResolver = mkDefault true;
diff --git a/nixpkgs/nixos/modules/services/networking/flannel.nix b/nixpkgs/nixos/modules/services/networking/flannel.nix
index 32a7eb3ed69e..2d67a2a2ad22 100644
--- a/nixpkgs/nixos/modules/services/networking/flannel.nix
+++ b/nixpkgs/nixos/modules/services/networking/flannel.nix
@@ -164,7 +164,7 @@ in {
       path = [ pkgs.iptables ];
       preStart = optionalString (cfg.storageBackend == "etcd") ''
         echo "setting network configuration"
-        until ${pkgs.etcdctl}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}'
+        until ${pkgs.etcd}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}'
         do
           echo "setting network configuration, retry"
           sleep 1
diff --git a/nixpkgs/nixos/modules/services/networking/git-daemon.nix b/nixpkgs/nixos/modules/services/networking/git-daemon.nix
index 98f80dd4bc40..6be72505c216 100644
--- a/nixpkgs/nixos/modules/services/networking/git-daemon.nix
+++ b/nixpkgs/nixos/modules/services/networking/git-daemon.nix
@@ -107,6 +107,7 @@ in
     users.users = optionalAttrs (cfg.user == "git") {
       git = {
         uid = config.ids.uids.git;
+        group = "git";
         description = "Git daemon user";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/networking/iodine.nix b/nixpkgs/nixos/modules/services/networking/iodine.nix
index 46051d7044b5..f67e2d9a5e71 100644
--- a/nixpkgs/nixos/modules/services/networking/iodine.nix
+++ b/nixpkgs/nixos/modules/services/networking/iodine.nix
@@ -190,6 +190,7 @@ in
 
     users.users.${iodinedUser} = {
       uid = config.ids.uids.iodined;
+      group = "iodined";
       description = "Iodine daemon user";
     };
     users.groups.iodined.gid = config.ids.gids.iodined;
diff --git a/nixpkgs/nixos/modules/services/networking/morty.nix b/nixpkgs/nixos/modules/services/networking/morty.nix
index e110a5c86101..c627feb527b6 100644
--- a/nixpkgs/nixos/modules/services/networking/morty.nix
+++ b/nixpkgs/nixos/modules/services/networking/morty.nix
@@ -77,7 +77,9 @@ in
         createHome = true;
         home = "/var/lib/morty";
         isSystemUser = true;
+        group = "morty";
       };
+    users.groups.morty = {};
 
     systemd.services.morty =
       {
diff --git a/nixpkgs/nixos/modules/services/networking/ncdns.nix b/nixpkgs/nixos/modules/services/networking/ncdns.nix
index d30fe0f6f6d1..c5ea5d950573 100644
--- a/nixpkgs/nixos/modules/services/networking/ncdns.nix
+++ b/nixpkgs/nixos/modules/services/networking/ncdns.nix
@@ -245,8 +245,10 @@ in
 
     users.users.ncdns = {
       isSystemUser = true;
+      group = "ncdns";
       description = "ncdns daemon user";
     };
+    users.groups.ncdns = {};
 
     systemd.services.ncdns = {
       description = "ncdns daemon";
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
index c8861171dd6c..ba13f575c39e 100644
--- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -464,6 +464,7 @@ in {
     users.users = {
       nm-openvpn = {
         uid = config.ids.uids.nm-openvpn;
+        group = "nm-openvpn";
         extraGroups = [ "networkmanager" ];
       };
       nm-iodine = {
diff --git a/nixpkgs/nixos/modules/services/networking/ngircd.nix b/nixpkgs/nixos/modules/services/networking/ngircd.nix
index 4b2fa7795922..1b631de3b025 100644
--- a/nixpkgs/nixos/modules/services/networking/ngircd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ngircd.nix
@@ -52,8 +52,11 @@ in {
     };
 
     users.users.ngircd = {
-      uid = config.ids.uids.ngircd;
+      isSystemUser = true;
+      group = "ngircd";
       description = "ngircd user.";
     };
+    users.groups.ngircd = {};
+
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix
index cc061bf6e3b9..0083990cff5a 100644
--- a/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix
+++ b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix
@@ -6,8 +6,6 @@ let
 
   inherit (pkgs) nntp-proxy;
 
-  proxyUser = "nntp-proxy";
-
   cfg = config.services.nntp-proxy;
 
   configBool = b: if b then "TRUE" else "FALSE";
@@ -210,16 +208,18 @@ in
 
   config = mkIf cfg.enable {
 
-    users.users.${proxyUser} =
-      { uid = config.ids.uids.nntp-proxy;
-        description = "NNTP-Proxy daemon user";
-      };
+    users.users.nntp-proxy = {
+      isSystemUser = true;
+      group = "nntp-proxy";
+      description = "NNTP-Proxy daemon user";
+    };
+    users.groups.nntp-proxy = {};
 
     systemd.services.nntp-proxy = {
       description = "NNTP proxy";
       after = [ "network.target" "nss-lookup.target" ];
       wantedBy = [ "multi-user.target" ];
-      serviceConfig = { User="${proxyUser}"; };
+      serviceConfig = { User="nntp-proxy"; };
       serviceConfig.ExecStart = "${nntp-proxy}/bin/nntp-proxy ${confFile}";
       preStart = ''
         if [ ! \( -f ${cfg.sslCert} -a -f ${cfg.sslKey} \) ]; then
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix b/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix
index 861b0db01a48..1dffbd78bbe4 100644
--- a/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix
@@ -10,8 +10,6 @@ let
 
   stateDir = "/var/lib/ntp";
 
-  ntpUser = "ntp";
-
   configFile = pkgs.writeText "ntp.conf" ''
     driftfile ${stateDir}/ntp.drift
 
@@ -27,7 +25,7 @@ let
     ${cfg.extraConfig}
   '';
 
-  ntpFlags = "-c ${configFile} -u ${ntpUser}:nogroup ${toString cfg.extraFlags}";
+  ntpFlags = "-c ${configFile} -u ntp:ntp ${toString cfg.extraFlags}";
 
 in
 
@@ -119,11 +117,13 @@ in
 
     systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "ntpd.service"; };
 
-    users.users.${ntpUser} =
-      { uid = config.ids.uids.ntp;
+    users.users.ntp =
+      { isSystemUser = true;
+        group = "ntp";
         description = "NTP daemon user";
         home = stateDir;
       };
+    users.groups.ntp = {};
 
     systemd.services.ntpd =
       { description = "NTP Daemon";
@@ -135,7 +135,7 @@ in
         preStart =
           ''
             mkdir -m 0755 -p ${stateDir}
-            chown ${ntpUser} ${stateDir}
+            chown ntp ${stateDir}
           '';
 
         serviceConfig = {
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix b/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix
index 67a04d48d308..9f3892e3b538 100644
--- a/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix
@@ -61,10 +61,12 @@ in
     environment.etc."ntpd.conf".text = configFile;
 
     users.users.ntp = {
-      uid = config.ids.uids.ntp;
+      isSystemUser = true;
+      group = "ntp";
       description = "OpenNTP daemon user";
       home = "/var/empty";
     };
+    users.groups.ntp = {};
 
     systemd.services.openntpd = {
       description = "OpenNTP Server";
diff --git a/nixpkgs/nixos/modules/services/networking/pleroma.nix b/nixpkgs/nixos/modules/services/networking/pleroma.nix
index bd75083a4a78..93ab29b71e5c 100644
--- a/nixpkgs/nixos/modules/services/networking/pleroma.nix
+++ b/nixpkgs/nixos/modules/services/networking/pleroma.nix
@@ -74,7 +74,7 @@ in {
       users."${cfg.user}" = {
         description = "Pleroma user";
         home = cfg.stateDir;
-        extraGroups = [ cfg.group ];
+        group = cfg.group;
         isSystemUser = true;
       };
       groups."${cfg.group}" = {};
diff --git a/nixpkgs/nixos/modules/services/networking/pleroma.xml b/nixpkgs/nixos/modules/services/networking/pleroma.xml
index 9ab0be3d947c..ad0a481af28b 100644
--- a/nixpkgs/nixos/modules/services/networking/pleroma.xml
+++ b/nixpkgs/nixos/modules/services/networking/pleroma.xml
@@ -4,129 +4,185 @@
          version="5.0"
          xml:id="module-services-pleroma">
  <title>Pleroma</title>
- <para><link xlink:href="https://pleroma.social/">Pleroma</link> is a lightweight activity pub server.</para>
- <section xml:id="module-services-pleroma-getting-started">
-   <title>Quick Start</title>
-   <para>To get quickly started, you can use this sample NixOS configuration and adapt it to your use case.</para>
-   <para><programlisting>
-    {
-      security.acme = {
-        email = "root@tld";
-        acceptTerms = true;
-        certs = {
-          "social.tld.com" = {
-            webroot = "/var/www/social.tld.com";
-            email = "root@tld";
-            group = "nginx";
-          };
-        };
-      };
-      services = {
-        pleroma = {
-          enable = true;
-          secretConfigFile = "/var/lib/pleroma/secrets.exs";
-          configs = [
-          ''
-            import Config
-
-            config :pleroma, Pleroma.Web.Endpoint,
-            url: [host: "social.tld.com", scheme: "https", port: 443],
-            http: [ip: {127, 0, 0, 1}, port: 4000]
-
-            config :pleroma, :instance,
-            name: "NixOS test pleroma server",
-            email: "pleroma@social.tld.com",
-            notify_email: "pleroma@social.tld.com",
-            limit: 5000,
-            registrations_open: true
-
-            config :pleroma, :media_proxy,
-            enabled: false,
-            redirect_on_failure: true
-            #base_url: "https://cache.pleroma.social"
-
-            config :pleroma, Pleroma.Repo,
-            adapter: Ecto.Adapters.Postgres,
-            username: "pleroma",
-            password: "${test-db-passwd}",
-            database: "pleroma",
-            hostname: "localhost",
-            pool_size: 10,
-            prepare: :named,
-            parameters: [
-                plan_cache_mode: "force_custom_plan"
-            ]
-
-            config :pleroma, :database, rum_enabled: false
-            config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
-            config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
-            config :pleroma, configurable_from_database: false
-          ''
-          ];
-        };
-        postgresql = {
-          enable = true;
-          package = pkgs.postgresql_12;
-        };
-        nginx = {
-          enable = true;
-          addSSL = true;
-          sslCertificate = "/var/lib/acme/social.tld.com/fullchain.pem";
-          sslCertificateKey = "/var/lib/acme/social.tld.com/key.pem";
-          root = "/var/www/social.tld.com";
-          # ACME endpoint
-          locations."/.well-known/acme-challenge" = {
-              root = "/var/www/social.tld.com/";
-          };
-          virtualHosts."social.tld.com" = {
-            addSSL = true;
-            locations."/" = {
-              proxyPass = "http://127.0.0.1:4000";
-              extraConfig = ''
-                add_header 'Access-Control-Allow-Origin' '*' always;
-                add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
-                add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
-                add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
-                if ($request_method = OPTIONS) {
-                    return 204;
-                }
-                add_header X-XSS-Protection "1; mode=block";
-                add_header X-Permitted-Cross-Domain-Policies none;
-                add_header X-Frame-Options DENY;
-                add_header X-Content-Type-Options nosniff;
-                add_header Referrer-Policy same-origin;
-                add_header X-Download-Options noopen;
-                proxy_http_version 1.1;
-                proxy_set_header Upgrade $http_upgrade;
-                proxy_set_header Connection "upgrade";
-                proxy_set_header Host $host;
-                client_max_body_size 16m;
-              '';
-            };
-          };
-        };
-      };
-    };
-   </programlisting></para>
-   <para>Note that you'll need to seed your database and upload your pleroma secrets to the path pointed by <literal>config.pleroma.secretConfigFile</literal>. You can find more informations about how to do that in the <link linkend="module-services-pleroma-generate-config">next</link> section.</para>
- </section>
+ <para>
+  <link xlink:href="https://pleroma.social/">Pleroma</link> is a lightweight activity pub server.</para>
  <section xml:id="module-services-pleroma-generate-config">
-   <title>Generating the Pleroma Config and Seed the Database</title>
-
-   <para>Before using this service, you'll need to generate your
-server configuration and its associated database seed. The
-<literal>pleroma_ctl</literal> CLI utility can help you with that. You
-can start with <literal>pleroma_ctl instance gen --output config.exs
---output-psql setup.psql</literal>, this will prompt you some
-questions and will generate both your config file and database initial
-migration. </para>
-<para>For more details about this configuration format, please have a look at the <link xlink:href="https://docs-develop.pleroma.social/backend/configuration/cheatsheet/">upstream documentation</link>.</para>
-<para>To seed your database, you can use the <literal>setup.psql</literal> file you just generated by running
+  <title>Generating the Pleroma config</title>
+  <para>The <literal>pleroma_ctl</literal> CLI utility will prompt you some questions and it will generate an initial config file. This is an example of usage
+<programlisting>
+<prompt>$ </prompt>mkdir tmp-pleroma
+<prompt>$ </prompt>cd tmp-pleroma
+<prompt>$ </prompt>nix-shell -p pleroma-otp
+<prompt>$ </prompt>pleroma_ctl instance gen --output config.exs --output-psql setup.psql
+</programlisting>
+  </para>
+  <para>The <literal>config.exs</literal> file can be further customized following the instructions on the <link xlink:href="https://docs-develop.pleroma.social/backend/configuration/cheatsheet/">upstream documentation</link>. Many refinements can be applied also after the service is running.</para>
+ </section>
+ <section xml:id="module-services-pleroma-initialize-db">
+  <title>Initializing the database</title>
+  <para>First, the Postgresql service must be enabled in the NixOS configuration
+<programlisting>
+services.postgresql = {
+  enable = true;
+  package = pkgs.postgresql_13;
+};
+</programlisting>
+and activated with the usual
+<programlisting>
+<prompt>$ </prompt>nixos-rebuild switch
+</programlisting>
+  </para>
+  <para>Then you can create and seed the database, using the <literal>setup.psql</literal> file that you generated in the previous section, by running
+<programlisting>
+<prompt>$ </prompt>sudo -u postgres psql -f setup.psql
+</programlisting>
+  </para>
+ </section>
+ <section xml:id="module-services-pleroma-enable">
+  <title>Enabling the Pleroma service locally</title>
+  <para>In this section we will enable the Pleroma service only locally, so its configurations can be improved incrementally.</para>
+  <para>This is an example of configuration, where <link linkend="opt-services.pleroma.configs">services.pleroma.configs</link> option contains the content of the file <literal>config.exs</literal>, generated <link linkend="module-services-pleroma-generate-config">in the first section</link>, but with the secrets (database password, endpoint secret key, salts, etc.) removed. Removing secrets is important, because otherwise they will be stored publicly in the Nix store.
+<programlisting>
+services.pleroma = {
+  enable = true;
+  secretConfigFile = "/var/lib/pleroma/secrets.exs";
+  configs = [
+    ''
+    import Config
+
+    config :pleroma, Pleroma.Web.Endpoint,
+      url: [host: "pleroma.example.net", scheme: "https", port: 443],
+      http: [ip: {127, 0, 0, 1}, port: 4000]
+
+    config :pleroma, :instance,
+      name: "Test",
+      email: "admin@example.net",
+      notify_email: "admin@example.net",
+      limit: 5000,
+      registrations_open: true
+
+    config :pleroma, :media_proxy,
+      enabled: false,
+      redirect_on_failure: true
+
+    config :pleroma, Pleroma.Repo,
+      adapter: Ecto.Adapters.Postgres,
+      username: "pleroma",
+      database: "pleroma",
+      hostname: "localhost"
+
+    # Configure web push notifications
+    config :web_push_encryption, :vapid_details,
+      subject: "mailto:admin@example.net"
+
+    # ... TO CONTINUE ...
+    ''
+  ];
+};
+</programlisting>
+  </para>
+  <para>Secrets must be moved into a file pointed by <link linkend="opt-services.pleroma.secretConfigFile">services.pleroma.secretConfigFile</link>, in our case <literal>/var/lib/pleroma/secrets.exs</literal>. This file can be created copying the previously generated <literal>config.exs</literal> file and then removing all the settings, except the secrets. This is an example
+<programlisting>
+# Pleroma instance passwords
+
+import Config
+
+config :pleroma, Pleroma.Web.Endpoint,
+   secret_key_base: "&lt;the secret generated by pleroma_ctl&gt;",
+   signing_salt: "&lt;the secret generated by pleroma_ctl&gt;"
+
+config :pleroma, Pleroma.Repo,
+  password: "&lt;the secret generated by pleroma_ctl&gt;"
+
+# Configure web push notifications
+config :web_push_encryption, :vapid_details,
+  public_key: "&lt;the secret generated by pleroma_ctl&gt;",
+  private_key: "&lt;the secret generated by pleroma_ctl&gt;"
+
+# ... TO CONTINUE ...
+</programlisting>
+  Note that the lines of the same configuration group are comma separated (i.e. all the lines end with a comma, except the last one), so when the lines with passwords are added or removed, commas must be adjusted accordingly.</para>
+
+  <para>The service can be enabled with the usual
+<programlisting>
+<prompt>$ </prompt>nixos-rebuild switch
+</programlisting>
+  </para>
+  <para>The service is accessible only from the local <literal>127.0.0.1:4000</literal> port. It can be tested using a port forwarding like this
+<programlisting>
+<prompt>$ </prompt>ssh -L 4000:localhost:4000 myuser@example.net
+</programlisting>
+and then accessing <link xlink:href="http://localhost:4000">http://localhost:4000</link> from a web browser.</para>
+ </section>
+ <section xml:id="module-services-pleroma-admin-user">
+  <title>Creating the admin user</title>
+  <para>After Pleroma service is running, all <link xlink:href="https://docs-develop.pleroma.social/">Pleroma administration utilities</link> can be used. In particular an admin user can be created with
+<programlisting>
+<prompt>$ </prompt>pleroma_ctl user new &lt;nickname&gt; &lt;email&gt;  --admin --moderator --password &lt;password&gt;
+</programlisting>
+  </para>
+ </section>
+ <section xml:id="module-services-pleroma-nginx">
+  <title>Configuring Nginx</title>
+  <para>In this configuration, Pleroma is listening only on the local port 4000. Nginx can be configured as a Reverse Proxy, for forwarding requests from public ports to the Pleroma service. This is an example of configuration, using
+<link xlink:href="https://letsencrypt.org/">Let's Encrypt</link> for the TLS certificates
 <programlisting>
-    sudo -u postgres psql -f setup.psql
-</programlisting></para>
-   <para>In regard of the pleroma service configuration you also just generated, you'll need to split it in two parts. The "public" part, which do not contain any secrets and thus can be safely stored in the Nix store and its "private" counterpart containing some secrets (database password, endpoint secret key, salts, etc.).</para>
+security.acme = {
+  email = "root@example.net";
+  acceptTerms = true;
+};
 
-   <para>The public part will live in your NixOS machine configuration in the <link linkend="opt-services.pleroma.configs">services.pleroma.configs</link> option. However, it's up to you to upload the secret pleroma configuration to the path pointed by <link linkend="opt-services.pleroma.secretConfigFile">services.pleroma.secretConfigFile</link>. You can do that manually or rely on a third party tool such as <link xlink:href="https://github.com/DBCDK/morph">Morph</link> or <link xlink:href="https://github.com/NixOS/nixops">NixOps</link>.</para>
+services.nginx = {
+  enable = true;
+  addSSL = true;
+
+  recommendedTlsSettings = true;
+  recommendedOptimisation = true;
+  recommendedGzipSettings = true;
+
+  recommendedProxySettings = false;
+  # NOTE: if enabled, the NixOS proxy optimizations will override the Pleroma
+  # specific settings, and they will enter in conflict.
+
+  virtualHosts = {
+    "pleroma.example.net" = {
+      http2 = true;
+      enableACME = true;
+      forceSSL = true;
+
+      locations."/" = {
+        proxyPass = "http://127.0.0.1:4000";
+
+        extraConfig = ''
+          etag on;
+          gzip on;
+
+          add_header 'Access-Control-Allow-Origin' '*' always;
+          add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
+          add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
+          add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
+          if ($request_method = OPTIONS) {
+            return 204;
+          }
+          add_header X-XSS-Protection "1; mode=block";
+          add_header X-Permitted-Cross-Domain-Policies none;
+          add_header X-Frame-Options DENY;
+          add_header X-Content-Type-Options nosniff;
+          add_header Referrer-Policy same-origin;
+          add_header X-Download-Options noopen;
+          proxy_http_version 1.1;
+          proxy_set_header Upgrade $http_upgrade;
+          proxy_set_header Connection "upgrade";
+          proxy_set_header Host $host;
+
+          client_max_body_size 16m;
+          # NOTE: increase if users need to upload very big files
+        '';
+      };
+    };
+  };
+};
+</programlisting>
+  </para>
  </section>
 </chapter>
diff --git a/nixpkgs/nixos/modules/services/networking/radicale.nix b/nixpkgs/nixos/modules/services/networking/radicale.nix
index 8c632c319d3c..368259b5b0bf 100644
--- a/nixpkgs/nixos/modules/services/networking/radicale.nix
+++ b/nixpkgs/nixos/modules/services/networking/radicale.nix
@@ -140,9 +140,12 @@ in {
 
     environment.systemPackages = [ pkg ];
 
-    users.users.radicale.uid = config.ids.uids.radicale;
+    users.users.radicale = {
+      isSystemUser = true;
+      group = "radicale";
+    };
 
-    users.groups.radicale.gid = config.ids.gids.radicale;
+    users.groups.radicale = {};
 
     systemd.services.radicale = {
       description = "A Simple Calendar and Contact Server";
diff --git a/nixpkgs/nixos/modules/services/networking/radvd.nix b/nixpkgs/nixos/modules/services/networking/radvd.nix
index 53fac4b7b72d..6e8db55bbf0d 100644
--- a/nixpkgs/nixos/modules/services/networking/radvd.nix
+++ b/nixpkgs/nixos/modules/services/networking/radvd.nix
@@ -55,9 +55,12 @@ in
   config = mkIf cfg.enable {
 
     users.users.radvd =
-      { uid = config.ids.uids.radvd;
+      {
+        isSystemUser = true;
+        group = "radvd";
         description = "Router Advertisement Daemon User";
       };
+    users.groups.radvd = {};
 
     systemd.services.radvd =
       { description = "IPv6 Router Advertisement Daemon";
diff --git a/nixpkgs/nixos/modules/services/networking/rdnssd.nix b/nixpkgs/nixos/modules/services/networking/rdnssd.nix
index 469504c43172..fd04bb8108f0 100644
--- a/nixpkgs/nixos/modules/services/networking/rdnssd.nix
+++ b/nixpkgs/nixos/modules/services/networking/rdnssd.nix
@@ -72,8 +72,10 @@ in
 
     users.users.rdnssd = {
       description = "RDNSSD Daemon User";
-      uid = config.ids.uids.rdnssd;
+      isSystemUser = true;
+      group = "rdnssd";
     };
+    users.groups.rdnssd = {};
 
   };
 
diff --git a/nixpkgs/nixos/modules/services/networking/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix
index 405808491ea4..cca03a8f88a1 100644
--- a/nixpkgs/nixos/modules/services/networking/shout.nix
+++ b/nixpkgs/nixos/modules/services/networking/shout.nix
@@ -83,11 +83,13 @@ in {
 
   config = mkIf cfg.enable {
     users.users.shout = {
-      uid = config.ids.uids.shout;
+      isSystemUser = true;
+      group = "shout";
       description = "Shout daemon user";
       home = shoutHome;
       createHome = true;
     };
+    users.groups.shout = {};
 
     systemd.services.shout = {
       description = "Shout web IRC client";
diff --git a/nixpkgs/nixos/modules/services/networking/smokeping.nix b/nixpkgs/nixos/modules/services/networking/smokeping.nix
index 4470c18fd533..0f123fd18776 100644
--- a/nixpkgs/nixos/modules/services/networking/smokeping.nix
+++ b/nixpkgs/nixos/modules/services/networking/smokeping.nix
@@ -259,7 +259,7 @@ in
       user = mkOption {
         type = types.str;
         default = "smokeping";
-        description = "User that runs smokeping and (optionally) thttpd";
+        description = "User that runs smokeping and (optionally) thttpd. A group of the same name will be created as well.";
       };
       webService = mkOption {
         type = types.bool;
@@ -278,18 +278,23 @@ in
       }
     ];
     security.wrappers = {
-      fping.source = "${pkgs.fping}/bin/fping";
-      fping6.source = "${pkgs.fping}/bin/fping6";
+      fping =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.fping}/bin/fping";
+        };
     };
     environment.systemPackages = [ pkgs.fping ];
     users.users.${cfg.user} = {
       isNormalUser = false;
       isSystemUser = true;
-      uid = config.ids.uids.smokeping;
+      group = cfg.user;
       description = "smokeping daemon user";
       home = smokepingHome;
       createHome = true;
     };
+    users.groups.${cfg.user} = {};
     systemd.services.smokeping = {
       wantedBy = [ "multi-user.target"];
       serviceConfig = {
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index 04879eb7d82d..311fd5abccad 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -410,11 +410,13 @@ in
     users.users = {
       sshd = {
         isSystemUser = true;
+        group = "sshd";
         description = "SSH privilege separation user";
       };
     } // (optionalAttrs (cfg.authorizedKeysCommand != null) {
       ${cfg.authorizedKeysCommandUser} = {};
     });
+    users.groups.sshd = {};
 
     services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli";
     services.openssh.sftpServerExecutable = mkDefault "${cfgc.package}/libexec/sftp-server";
diff --git a/nixpkgs/nixos/modules/services/networking/tinydns.nix b/nixpkgs/nixos/modules/services/networking/tinydns.nix
index 79507b2ebcdd..2c44ad49296d 100644
--- a/nixpkgs/nixos/modules/services/networking/tinydns.nix
+++ b/nixpkgs/nixos/modules/services/networking/tinydns.nix
@@ -32,7 +32,11 @@ with lib;
   config = mkIf config.services.tinydns.enable {
     environment.systemPackages = [ pkgs.djbdns ];
 
-    users.users.tinydns.isSystemUser = true;
+    users.users.tinydns = {
+      isSystemUser = true;
+      group = "tinydns";
+    };
+    users.groups.tinydns = {};
 
     systemd.services.tinydns = {
       description = "djbdns tinydns server";
diff --git a/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix
index f88e34827d00..7c13724e084a 100644
--- a/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix
+++ b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix
@@ -3,15 +3,15 @@
 with lib;
 
 let
-  home = "/var/lib/tox-bootstrapd";
-  PIDFile = "${home}/pid";
+  WorkingDirectory = "/var/lib/tox-bootstrapd";
+  PIDFile = "${WorkingDirectory}/pid";
 
   pkg = pkgs.libtoxcore;
   cfg = config.services.toxBootstrapd;
   cfgFile = builtins.toFile "tox-bootstrapd.conf"
     ''
       port = ${toString cfg.port}
-      keys_file_path = "${home}/keys"
+      keys_file_path = "${WorkingDirectory}/keys"
       pid_file_path = "${PIDFile}"
       ${cfg.extraConfig}
     '';
@@ -36,7 +36,7 @@ in
 
           keysFile = mkOption {
             type = types.str;
-            default = "${home}/keys";
+            default = "${WorkingDirectory}/keys";
             description = "Node key file.";
           };
 
@@ -56,13 +56,6 @@ in
 
   config = mkIf config.services.toxBootstrapd.enable {
 
-    users.users.tox-bootstrapd =
-      { uid = config.ids.uids.tox-bootstrapd;
-        description = "Tox bootstrap daemon user";
-        inherit home;
-        createHome = true;
-      };
-
     systemd.services.tox-bootstrapd = {
       description = "Tox DHT bootstrap daemon";
       after = [ "network.target" ];
@@ -70,8 +63,10 @@ in
       serviceConfig =
         { ExecStart = "${pkg}/bin/tox-bootstrapd --config=${cfgFile}";
           Type = "forking";
-          inherit PIDFile;
-          User = "tox-bootstrapd";
+          inherit PIDFile WorkingDirectory;
+          AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
+          DynamicUser = true;
+          StateDirectory = "tox-bootstrapd";
         };
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/toxvpn.nix b/nixpkgs/nixos/modules/services/networking/toxvpn.nix
index 9e97faeebc1e..1765ef3ea2d9 100644
--- a/nixpkgs/nixos/modules/services/networking/toxvpn.nix
+++ b/nixpkgs/nixos/modules/services/networking/toxvpn.nix
@@ -59,10 +59,12 @@ with lib;
 
     users.users = {
       toxvpn = {
-        uid        = config.ids.uids.toxvpn;
+        isSystemUser = true;
+        group = "toxvpn";
         home       = "/var/lib/toxvpn";
         createHome = true;
       };
     };
+    users.groups.toxvpn = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/tvheadend.nix b/nixpkgs/nixos/modules/services/networking/tvheadend.nix
index ccf879996631..19a10a03bd9b 100644
--- a/nixpkgs/nixos/modules/services/networking/tvheadend.nix
+++ b/nixpkgs/nixos/modules/services/networking/tvheadend.nix
@@ -29,8 +29,10 @@ in
       description = "Tvheadend Service user";
       home        = "/var/lib/tvheadend";
       createHome  = true;
-      uid         = config.ids.uids.tvheadend;
+      isSystemUser = true;
+      group = "tvheadend";
     };
+    users.groups.tvheadend = {};
 
     systemd.services.tvheadend = {
       description = "Tvheadend TV streaming server";
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 2e320378cc9a..73170ebfc903 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -115,10 +115,12 @@ in
   config = mkIf cfg.enable {
 
     users.users.unifi = {
-      uid = config.ids.uids.unifi;
+      isSystemUser = true;
+      group = "unifi";
       description = "UniFi controller daemon user";
       home = "${stateDir}";
     };
+    users.groups.unifi = {};
 
     networking.firewall = mkIf cfg.openPorts {
       # https://help.ubnt.com/hc/en-us/articles/218506997
diff --git a/nixpkgs/nixos/modules/services/networking/vsftpd.nix b/nixpkgs/nixos/modules/services/networking/vsftpd.nix
index c57994533c17..5489f74bf032 100644
--- a/nixpkgs/nixos/modules/services/networking/vsftpd.nix
+++ b/nixpkgs/nixos/modules/services/networking/vsftpd.nix
@@ -282,7 +282,8 @@ in
 
     users.users = {
       "vsftpd" = {
-        uid = config.ids.uids.vsftpd;
+        group = "vsftpd";
+        isSystemUser = true;
         description = "VSFTPD user";
         home = if cfg.localRoot != null
                then cfg.localRoot # <= Necessary for virtual users.
@@ -297,6 +298,7 @@ in
         };
     };
 
+    users.groups.vsftpd = {};
     users.groups.ftp.gid = config.ids.gids.ftp;
 
     # If you really have to access root via FTP use mkOverride or userlistDeny
diff --git a/nixpkgs/nixos/modules/services/networking/x2goserver.nix b/nixpkgs/nixos/modules/services/networking/x2goserver.nix
index 48020fc1ceca..554e51f9d4ff 100644
--- a/nixpkgs/nixos/modules/services/networking/x2goserver.nix
+++ b/nixpkgs/nixos/modules/services/networking/x2goserver.nix
@@ -88,12 +88,14 @@ in {
       source = "${pkgs.x2goserver}/lib/x2go/libx2go-server-db-sqlite3-wrapper.pl";
       owner = "x2go";
       group = "x2go";
+      setuid = false;
       setgid = true;
     };
     security.wrappers.x2goprintWrapper = {
       source = "${pkgs.x2goserver}/bin/x2goprint";
       owner = "x2go";
       group = "x2go";
+      setuid = false;
       setgid = true;
     };
 
diff --git a/nixpkgs/nixos/modules/services/scheduling/atd.nix b/nixpkgs/nixos/modules/services/scheduling/atd.nix
index 37f6651ec4cf..9bb0191ee469 100644
--- a/nixpkgs/nixos/modules/services/scheduling/atd.nix
+++ b/nixpkgs/nixos/modules/services/scheduling/atd.nix
@@ -58,7 +58,9 @@ in
     security.pam.services.atd = {};
 
     users.users.atd =
-      { uid = config.ids.uids.atd;
+      {
+        uid = config.ids.uids.atd;
+        group = "atd";
         description = "atd user";
         home = "/var/empty";
       };
diff --git a/nixpkgs/nixos/modules/services/scheduling/cron.nix b/nixpkgs/nixos/modules/services/scheduling/cron.nix
index 3bc31832946b..c28956b3bfeb 100644
--- a/nixpkgs/nixos/modules/services/scheduling/cron.nix
+++ b/nixpkgs/nixos/modules/services/scheduling/cron.nix
@@ -93,7 +93,12 @@ in
 
     { services.cron.enable = mkDefault (allFiles != []); }
     (mkIf (config.services.cron.enable) {
-      security.wrappers.crontab.source = "${cronNixosPkg}/bin/crontab";
+      security.wrappers.crontab =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${cronNixosPkg}/bin/crontab";
+        };
       environment.systemPackages = [ cronNixosPkg ];
       environment.etc.crontab =
         { source = pkgs.runCommand "crontabs" { inherit allFiles; preferLocalBuild = true; }
diff --git a/nixpkgs/nixos/modules/services/scheduling/fcron.nix b/nixpkgs/nixos/modules/services/scheduling/fcron.nix
index 42bed21bf25b..acaa995f7395 100644
--- a/nixpkgs/nixos/modules/services/scheduling/fcron.nix
+++ b/nixpkgs/nixos/modules/services/scheduling/fcron.nix
@@ -136,10 +136,13 @@ in
         owner = "fcron";
         group = "fcron";
         setgid = true;
+        setuid = false;
       };
       fcronsighup = {
         source = "${pkgs.fcron}/bin/fcronsighup";
+        owner = "root";
         group = "fcron";
+        setuid = true;
       };
     };
     systemd.services.fcron = {
diff --git a/nixpkgs/nixos/modules/services/search/elasticsearch.nix b/nixpkgs/nixos/modules/services/search/elasticsearch.nix
index 1d7a28d5d245..40ebe29c9a60 100644
--- a/nixpkgs/nixos/modules/services/search/elasticsearch.nix
+++ b/nixpkgs/nixos/modules/services/search/elasticsearch.nix
@@ -5,13 +5,13 @@ with lib;
 let
   cfg = config.services.elasticsearch;
 
+  es7 = builtins.compareVersions cfg.package.version "7" >= 0;
+
   esConfig = ''
     network.host: ${cfg.listenAddress}
     cluster.name: ${cfg.cluster_name}
-    ${lib.optionalString cfg.single_node ''
-      discovery.type: single-node
-      gateway.auto_import_dangling_indices: true
-    ''}
+    ${lib.optionalString cfg.single_node "discovery.type: single-node"}
+    ${lib.optionalString (cfg.single_node && es7) "gateway.auto_import_dangling_indices: true"}
 
     http.port: ${toString cfg.port}
     transport.port: ${toString cfg.tcp_port}
@@ -201,6 +201,13 @@ in
 
         if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi
       '';
+      postStart = ''
+        # Make sure elasticsearch is up and running before dependents
+        # are started
+        while ! ${pkgs.curl}/bin/curl -sS -f http://localhost:${toString cfg.port} 2>/dev/null; do
+          sleep 1
+        done
+      '';
     };
 
     environment.systemPackages = [ cfg.package ];
diff --git a/nixpkgs/nixos/modules/services/search/kibana.nix b/nixpkgs/nixos/modules/services/search/kibana.nix
index 2beb265ee5d1..b3093abfa5c5 100644
--- a/nixpkgs/nixos/modules/services/search/kibana.nix
+++ b/nixpkgs/nixos/modules/services/search/kibana.nix
@@ -199,10 +199,12 @@ in {
     environment.systemPackages = [ cfg.package ];
 
     users.users.kibana = {
-      uid = config.ids.uids.kibana;
+      isSystemUser = true;
       description = "Kibana service user";
       home = cfg.dataDir;
       createHome = true;
+      group = "kibana";
     };
+    users.groups.kibana = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/search/meilisearch.md b/nixpkgs/nixos/modules/services/search/meilisearch.md
new file mode 100644
index 000000000000..98e7c542cb9a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/search/meilisearch.md
@@ -0,0 +1,39 @@
+# Meilisearch {#module-services-meilisearch}
+
+Meilisearch is a lightweight, fast and powerful search engine. Think elastic search with a much smaller footprint.
+
+## Quickstart
+
+the minimum to start meilisearch is
+
+```nix
+services.meilisearch.enable = true;
+```
+
+this will start the http server included with meilisearch on port 7700.
+
+test with `curl -X GET 'http://localhost:7700/health'`
+
+## Usage
+
+you first need to add documents to an index before you can search for documents.
+
+### Add a documents to the `movies` index
+
+`curl -X POST 'http://127.0.0.1:7700/indexes/movies/documents' --data '[{"id": "123", "title": "Superman"}, {"id": 234, "title": "Batman"}]'`
+
+### Search documents in the `movies` index
+
+`curl 'http://127.0.0.1:7700/indexes/movies/search' --data '{ "q": "botman" }'` (note the typo is intentional and there to demonstrate the typo tolerant capabilities)
+
+## Defaults
+
+- The default nixos package doesn't come with the [dashboard](https://docs.meilisearch.com/learn/getting_started/quick_start.html#search), since the dashboard features makes some assets downloads at compile time.
+
+- Anonimized Analytics sent to meilisearch are disabled by default.
+
+- Default deployment is development mode. It doesn't require a secret master key. All routes are not protected and accessible.
+
+## Missing
+
+- the snapshot feature is not yet configurable from the module, it's just a matter of adding the relevant environment variables.
diff --git a/nixpkgs/nixos/modules/services/search/meilisearch.nix b/nixpkgs/nixos/modules/services/search/meilisearch.nix
new file mode 100644
index 000000000000..f6210f6f16e1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/search/meilisearch.nix
@@ -0,0 +1,132 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.meilisearch;
+
+in
+{
+
+  meta.maintainers = with maintainers; [ Br1ght0ne happysalada ];
+  # Don't edit the docbook xml directly, edit the md and generate it:
+  # `pandoc meilisearch.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > meilisearch.xml`
+  meta.doc = ./meilisearch.xml;
+
+  ###### interface
+
+  options.services.meilisearch = {
+    enable = mkEnableOption "MeiliSearch - a RESTful search API";
+
+    package = mkOption {
+      description = "The package to use for meilisearch. Use this if you require specific features to be enabled. The default package has no features.";
+      default = pkgs.meilisearch;
+      defaultText = "pkgs.meilisearch";
+      type = types.package;
+    };
+
+    listenAddress = mkOption {
+      description = "MeiliSearch listen address.";
+      default = "127.0.0.1";
+      type = types.str;
+    };
+
+    listenPort = mkOption {
+      description = "MeiliSearch port to listen on.";
+      default = 7700;
+      type = types.port;
+    };
+
+    environment = mkOption {
+      description = "Defines the running environment of MeiliSearch.";
+      default = "development";
+      type = types.enum [ "development" "production" ];
+    };
+
+    # TODO change this to LoadCredentials once possible
+    masterKeyEnvironmentFile = mkOption {
+      description = ''
+        Path to file which contains the master key.
+        By doing so, all routes will be protected and will require a key to be accessed.
+        If no master key is provided, all routes can be accessed without requiring any key.
+        The format is the following:
+        MEILI_MASTER_KEY=my_secret_key
+      '';
+      default = null;
+      type = with types; nullOr path;
+    };
+
+    noAnalytics = mkOption {
+      description = ''
+        Deactivates analytics.
+        Analytics allow MeiliSearch to know how many users are using MeiliSearch,
+        which versions and which platforms are used.
+        This process is entirely anonymous.
+      '';
+      default = true;
+      type = types.bool;
+    };
+
+    logLevel = mkOption {
+      description = ''
+        Defines how much detail should be present in MeiliSearch's logs.
+        MeiliSearch currently supports four log levels, listed in order of increasing verbosity:
+        - 'ERROR': only log unexpected events indicating MeiliSearch is not functioning as expected
+        - 'WARN:' log all unexpected events, regardless of their severity
+        - 'INFO:' log all events. This is the default value
+        - 'DEBUG': log all events and including detailed information on MeiliSearch's internal processes.
+          Useful when diagnosing issues and debugging
+      '';
+      default = "INFO";
+      type = types.str;
+    };
+
+    maxIndexSize = mkOption {
+      description = ''
+        Sets the maximum size of the index.
+        Value must be given in bytes or explicitly stating a base unit.
+        For example, the default value can be written as 107374182400, '107.7Gb', or '107374 Mb'.
+        Default is 100 GiB
+      '';
+      default = "107374182400";
+      type = types.str;
+    };
+
+    payloadSizeLimit = mkOption {
+      description = ''
+        Sets the maximum size of accepted JSON payloads.
+        Value must be given in bytes or explicitly stating a base unit.
+        For example, the default value can be written as 107374182400, '107.7Gb', or '107374 Mb'.
+        Default is ~ 100 MB
+      '';
+      default = "104857600";
+      type = types.str;
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.meilisearch = {
+      description = "MeiliSearch daemon";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        MEILI_DB_PATH = "/var/lib/meilisearch";
+        MEILI_HTTP_ADDR = "${cfg.listenAddress}:${toString cfg.listenPort}";
+        MEILI_NO_ANALYTICS = toString cfg.noAnalytics;
+        MEILI_ENV = cfg.environment;
+        MEILI_DUMPS_DIR = "/var/lib/meilisearch/dumps";
+        MEILI_LOG_LEVEL = cfg.logLevel;
+        MEILI_MAX_INDEX_SIZE = cfg.maxIndexSize;
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/meilisearch";
+        DynamicUser = true;
+        StateDirectory = "meilisearch";
+        EnvironmentFile = mkIf (cfg.masterKeyEnvironmentFile != null) cfg.masterKeyEnvironmentFile;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/search/meilisearch.xml b/nixpkgs/nixos/modules/services/search/meilisearch.xml
new file mode 100644
index 000000000000..c1a73f358c28
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/search/meilisearch.xml
@@ -0,0 +1,85 @@
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="module-services-meilisearch">
+  <title>Meilisearch</title>
+  <para>
+    Meilisearch is a lightweight, fast and powerful search engine. Think
+    elastic search with a much smaller footprint.
+  </para>
+  <section xml:id="quickstart">
+    <title>Quickstart</title>
+    <para>
+      the minimum to start meilisearch is
+    </para>
+    <programlisting language="bash">
+services.meilisearch.enable = true;
+</programlisting>
+    <para>
+      this will start the http server included with meilisearch on port
+      7700.
+    </para>
+    <para>
+      test with
+      <literal>curl -X GET 'http://localhost:7700/health'</literal>
+    </para>
+  </section>
+  <section xml:id="usage">
+    <title>Usage</title>
+    <para>
+      you first need to add documents to an index before you can search
+      for documents.
+    </para>
+    <section xml:id="add-a-documents-to-the-movies-index">
+      <title>Add a documents to the <literal>movies</literal>
+      index</title>
+      <para>
+        <literal>curl -X POST 'http://127.0.0.1:7700/indexes/movies/documents' --data '[{&quot;id&quot;: &quot;123&quot;, &quot;title&quot;: &quot;Superman&quot;}, {&quot;id&quot;: 234, &quot;title&quot;: &quot;Batman&quot;}]'</literal>
+      </para>
+    </section>
+    <section xml:id="search-documents-in-the-movies-index">
+      <title>Search documents in the <literal>movies</literal>
+      index</title>
+      <para>
+        <literal>curl 'http://127.0.0.1:7700/indexes/movies/search' --data '{ &quot;q&quot;: &quot;botman&quot; }'</literal>
+        (note the typo is intentional and there to demonstrate the typo
+        tolerant capabilities)
+      </para>
+    </section>
+  </section>
+  <section xml:id="defaults">
+    <title>Defaults</title>
+    <itemizedlist>
+      <listitem>
+        <para>
+          The default nixos package doesn’t come with the
+          <link xlink:href="https://docs.meilisearch.com/learn/getting_started/quick_start.html#search">dashboard</link>,
+          since the dashboard features makes some assets downloads at
+          compile time.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Anonimized Analytics sent to meilisearch are disabled by
+          default.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Default deployment is development mode. It doesn’t require a
+          secret master key. All routes are not protected and
+          accessible.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </section>
+  <section xml:id="missing">
+    <title>Missing</title>
+    <itemizedlist spacing="compact">
+      <listitem>
+        <para>
+          the snapshot feature is not yet configurable from the module,
+          it’s just a matter of adding the relevant environment
+          variables.
+        </para>
+      </listitem>
+    </itemizedlist>
+  </section>
+</chapter>
diff --git a/nixpkgs/nixos/modules/services/security/hockeypuck.nix b/nixpkgs/nixos/modules/services/security/hockeypuck.nix
index 686634c8add8..2e98624bb2ee 100644
--- a/nixpkgs/nixos/modules/services/security/hockeypuck.nix
+++ b/nixpkgs/nixos/modules/services/security/hockeypuck.nix
@@ -82,8 +82,10 @@ in {
 
     users.users.hockeypuck = {
       isSystemUser = true;
+      group = "hockeypuck";
       description = "Hockeypuck user";
     };
+    users.groups.hockeypuck = {};
 
     systemd.services.hockeypuck = {
       description = "Hockeypuck OpenPGP Key Server";
diff --git a/nixpkgs/nixos/modules/services/security/opensnitch.nix b/nixpkgs/nixos/modules/services/security/opensnitch.nix
new file mode 100644
index 000000000000..919346cf2bb1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/security/opensnitch.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  name = "opensnitch";
+  cfg = config.services.opensnitch;
+in {
+  options = {
+    services.opensnitch = {
+      enable = mkEnableOption "Opensnitch application firewall";
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    systemd = {
+      packages = [ pkgs.opensnitch ];
+      services.opensnitchd.wantedBy = [ "multi-user.target" ];
+    };
+
+  };
+}
+
diff --git a/nixpkgs/nixos/modules/services/security/physlock.nix b/nixpkgs/nixos/modules/services/security/physlock.nix
index da5c22a90a09..760e80f147f7 100644
--- a/nixpkgs/nixos/modules/services/security/physlock.nix
+++ b/nixpkgs/nixos/modules/services/security/physlock.nix
@@ -38,9 +38,6 @@ in
           setuid wrapper to allow any user to start physlock as root, which
           is a minor security risk. Call the physlock binary to use this instead
           of using the systemd service.
-
-          Note that you might need to relog to have the correct binary in your
-          PATH upon changing this option.
         '';
       };
 
@@ -129,7 +126,12 @@ in
 
     (mkIf cfg.allowAnyUser {
 
-      security.wrappers.physlock = { source = "${pkgs.physlock}/bin/physlock"; user = "root"; };
+      security.wrappers.physlock =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.physlock}/bin/physlock";
+        };
 
     })
   ]);
diff --git a/nixpkgs/nixos/modules/services/security/tor.nix b/nixpkgs/nixos/modules/services/security/tor.nix
index 9e8f18e93c85..1e1f443905d4 100644
--- a/nixpkgs/nixos/modules/services/security/tor.nix
+++ b/nixpkgs/nixos/modules/services/security/tor.nix
@@ -1022,7 +1022,7 @@ in
         ProtectKernelTunables = true;
         ProtectSystem = "strict";
         RemoveIPC = true;
-        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
         RestrictNamespaces = true;
         RestrictRealtime = true;
         RestrictSUIDSGID = true;
diff --git a/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix b/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix
index f0e56c7951a4..837c59caa562 100644
--- a/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix
+++ b/nixpkgs/nixos/modules/services/system/kerberos/heimdal.nix
@@ -27,7 +27,7 @@ in
 {
   # No documentation about correct triggers, so guessing at them.
 
-  config = mkIf (cfg.enable && kerberos == pkgs.heimdalFull) {
+  config = mkIf (cfg.enable && kerberos == pkgs.heimdal) {
     systemd.services.kadmind = {
       description = "Kerberos Administration Daemon";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixpkgs/nixos/modules/services/system/localtime.nix b/nixpkgs/nixos/modules/services/system/localtime.nix
index bb99e5e36ff8..8f23454af9df 100644
--- a/nixpkgs/nixos/modules/services/system/localtime.nix
+++ b/nixpkgs/nixos/modules/services/system/localtime.nix
@@ -37,7 +37,9 @@ in {
     users.users.localtimed = {
       description = "localtime daemon";
       isSystemUser = true;
+      group = "localtimed";
     };
+    users.groups.localtimed = {};
 
     systemd.services.localtime = {
       wantedBy = [ "multi-user.target" ];
diff --git a/nixpkgs/nixos/modules/services/torrent/magnetico.nix b/nixpkgs/nixos/modules/services/torrent/magnetico.nix
index 7465c10e002c..ada6f9b1e3a9 100644
--- a/nixpkgs/nixos/modules/services/torrent/magnetico.nix
+++ b/nixpkgs/nixos/modules/services/torrent/magnetico.nix
@@ -172,8 +172,10 @@ in {
 
     users.users.magnetico = {
       description = "Magnetico daemons user";
+      group = "magnetico";
       isSystemUser = true;
     };
+    users.groups.magnetico = {};
 
     systemd.services.magneticod = {
       description = "Magnetico DHT crawler";
diff --git a/nixpkgs/nixos/modules/services/torrent/peerflix.nix b/nixpkgs/nixos/modules/services/torrent/peerflix.nix
index a74f65984328..3e5f80960dc7 100644
--- a/nixpkgs/nixos/modules/services/torrent/peerflix.nix
+++ b/nixpkgs/nixos/modules/services/torrent/peerflix.nix
@@ -60,6 +60,10 @@ in {
       };
     };
 
-    users.users.peerflix.uid = config.ids.uids.peerflix;
+    users.users.peerflix = {
+      isSystemUser = true;
+      group = "peerflix";
+    };
+    users.groups.peerflix = {};
   };
 }
diff --git a/nixpkgs/nixos/modules/services/ttys/getty.nix b/nixpkgs/nixos/modules/services/ttys/getty.nix
index 7cf2ff87da26..eb966c37ce7f 100644
--- a/nixpkgs/nixos/modules/services/ttys/getty.nix
+++ b/nixpkgs/nixos/modules/services/ttys/getty.nix
@@ -131,6 +131,14 @@ in
         restartIfChanged = false;
       };
 
+    systemd.services."autovt@" =
+      { serviceConfig.ExecStart = [
+          "" # override upstream default with an empty ExecStart
+          (gettyCmd "--noclear %I $TERM")
+        ];
+        restartIfChanged = false;
+      };
+
     systemd.services."container-getty@" =
       { serviceConfig.ExecStart = [
           "" # override upstream default with an empty ExecStart
diff --git a/nixpkgs/nixos/modules/services/video/replay-sorcery.nix b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
index d78e782c7968..7ce5be8a5a1c 100644
--- a/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
+++ b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
@@ -44,8 +44,10 @@ in
 
     security.wrappers = mkIf cfg.enableSysAdminCapability {
       replay-sorcery = {
-        source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
+        owner = "root";
+        group = "root";
         capabilities = "cap_sys_admin+ep";
+        source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
index 050e4ee3d329..b28e3cf0deb2 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
@@ -172,6 +172,15 @@ in
       };
 
       admin = {
+        skipCreate = lib.mkOption {
+          type = lib.types.bool;
+          default = false;
+          description = ''
+            Do not create the admin account, instead rely on other
+            existing admin accounts.
+          '';
+        };
+
         email = lib.mkOption {
           type = lib.types.str;
           example = "admin@example.com";
@@ -721,12 +730,24 @@ in
             lib.optionalString (file != null) ''
               replace-secret '${file}' '${file}' /run/discourse/config/discourse.conf
             '';
+
+          mkAdmin = ''
+            export ADMIN_EMAIL="${cfg.admin.email}"
+            export ADMIN_NAME="${cfg.admin.fullName}"
+            export ADMIN_USERNAME="${cfg.admin.username}"
+            ADMIN_PASSWORD="$(<${cfg.admin.passwordFile})"
+            export ADMIN_PASSWORD
+            discourse-rake admin:create_noninteractively
+          '';
+
         in ''
           set -o errexit -o pipefail -o nounset -o errtrace
           shopt -s inherit_errexit
 
           umask u=rwx,g=rx,o=
 
+          rm -rf /var/lib/discourse/tmp/*
+
           cp -r ${cfg.package}/share/discourse/config.dist/* /run/discourse/config/
           cp -r ${cfg.package}/share/discourse/public.dist/* /run/discourse/public/
           ln -sf /var/lib/discourse/uploads /run/discourse/public/uploads
@@ -748,14 +769,9 @@ in
           )
 
           discourse-rake db:migrate >>/var/log/discourse/db_migration.log
-          chmod -R u+w /run/discourse/tmp/
+          chmod -R u+w /var/lib/discourse/tmp/
 
-          export ADMIN_EMAIL="${cfg.admin.email}"
-          export ADMIN_NAME="${cfg.admin.fullName}"
-          export ADMIN_USERNAME="${cfg.admin.username}"
-          ADMIN_PASSWORD="$(<${cfg.admin.passwordFile})"
-          export ADMIN_PASSWORD
-          discourse-rake admin:create_noninteractively
+          ${lib.optionalString (!cfg.admin.skipCreate) mkAdmin}
 
           discourse-rake themes:update
           discourse-rake uploads:regenerate_missing_optimized
@@ -768,7 +784,6 @@ in
         RuntimeDirectory = map (p: "discourse/" + p) [
           "config"
           "home"
-          "tmp"
           "assets/javascripts/plugins"
           "public"
           "sockets"
@@ -777,6 +792,7 @@ in
         StateDirectory = map (p: "discourse/" + p) [
           "uploads"
           "backups"
+          "tmp"
         ];
         StateDirectoryMode = 0750;
         LogsDirectory = "discourse";
diff --git a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
index 685cb4967030..964fef23addf 100644
--- a/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/dokuwiki.nix
@@ -1,16 +1,21 @@
-{ config, lib, pkgs, ... }:
+{ config, pkgs, lib, ... }:
 
 let
+  inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types maintainers recursiveUpdate;
+  inherit (lib) any attrValues concatMapStrings concatMapStringsSep flatten literalExample;
+  inherit (lib) filterAttrs mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString;
 
-  inherit (lib) mkEnableOption mkForce mkIf mkMerge mkOption optionalAttrs recursiveUpdate types maintainers;
-  inherit (lib) concatMapStringsSep flatten mapAttrs mapAttrs' mapAttrsToList nameValuePair concatMapStringSep;
-
-  eachSite = config.services.dokuwiki;
-
+  cfg = migrateOldAttrs config.services.dokuwiki;
+  eachSite = cfg.sites;
   user = "dokuwiki";
-  group = config.services.nginx.group;
+  webserver = config.services.${cfg.webserver};
+  stateDir = hostName: "/var/lib/dokuwiki/${hostName}/data";
+
+  # Migrate config.services.dokuwiki.<hostName> to config.services.dokuwiki.sites.<hostName>
+  oldSites = filterAttrs (o: _: o != "sites" && o != "webserver");
+  migrateOldAttrs = cfg: cfg // { sites = cfg.sites // oldSites cfg; };
 
-  dokuwikiAclAuthConfig = cfg: pkgs.writeText "acl.auth.php" ''
+  dokuwikiAclAuthConfig = hostName: cfg: pkgs.writeText "acl.auth-${hostName}.php" ''
     # acl.auth.php
     # <?php exit()?>
     #
@@ -19,7 +24,7 @@ let
     ${toString cfg.acl}
   '';
 
-  dokuwikiLocalConfig = cfg: pkgs.writeText "local.php" ''
+  dokuwikiLocalConfig = hostName: cfg: pkgs.writeText "local-${hostName}.php" ''
     <?php
     $conf['savedir'] = '${cfg.stateDir}';
     $conf['superuser'] = '${toString cfg.superUser}';
@@ -28,11 +33,12 @@ let
     ${toString cfg.extraConfig}
   '';
 
-  dokuwikiPluginsLocalConfig = cfg: pkgs.writeText "plugins.local.php" ''
+  dokuwikiPluginsLocalConfig = hostName: cfg: pkgs.writeText "plugins.local-${hostName}.php" ''
     <?php
     ${cfg.pluginsConfig}
   '';
 
+
   pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
     pname = "dokuwiki-${hostName}";
     version = src.version;
@@ -43,13 +49,13 @@ let
       cp -r * $out/
 
       # symlink the dokuwiki config
-      ln -s ${dokuwikiLocalConfig cfg} $out/share/dokuwiki/local.php
+      ln -s ${dokuwikiLocalConfig hostName cfg} $out/share/dokuwiki/local.php
 
       # symlink plugins config
-      ln -s ${dokuwikiPluginsLocalConfig cfg} $out/share/dokuwiki/plugins.local.php
+      ln -s ${dokuwikiPluginsLocalConfig hostName cfg} $out/share/dokuwiki/plugins.local.php
 
       # symlink acl
-      ln -s ${dokuwikiAclAuthConfig cfg} $out/share/dokuwiki/acl.auth.php
+      ln -s ${dokuwikiAclAuthConfig hostName cfg} $out/share/dokuwiki/acl.auth.php
 
       # symlink additional plugin(s) and templates(s)
       ${concatMapStringsSep "\n" (template: "ln -s ${template} $out/share/dokuwiki/lib/tpl/${template.name}") cfg.templates}
@@ -57,332 +63,385 @@ let
     '';
   };
 
-  siteOpts = { config, lib, name, ...}: {
-    options = {
-      enable = mkEnableOption "DokuWiki web application.";
-
-      package = mkOption {
-        type = types.package;
-        default = pkgs.dokuwiki;
-        description = "Which dokuwiki package to use.";
-      };
+  siteOpts = { config, lib, name, ... }:
+    {
+      options = {
+        package = mkOption {
+          type = types.package;
+          default = pkgs.dokuwiki;
+          description = "Which DokuWiki package to use.";
+        };
 
-      hostName = mkOption {
-        type = types.str;
-        default = "localhost";
-        description = "FQDN for the instance.";
-      };
+        stateDir = mkOption {
+          type = types.path;
+          default = "/var/lib/dokuwiki/${name}/data";
+          description = "Location of the DokuWiki state directory.";
+        };
 
-      stateDir = mkOption {
-        type = types.path;
-        default = "/var/lib/dokuwiki/${name}/data";
-        description = "Location of the dokuwiki state directory.";
-      };
+        acl = mkOption {
+          type = types.nullOr types.lines;
+          default = null;
+          example = "*               @ALL               8";
+          description = ''
+            Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/>
+            Mutually exclusive with services.dokuwiki.aclFile
+            Set this to a value other than null to take precedence over aclFile option.
+
+            Warning: Consider using aclFile instead if you do not
+            want to store the ACL in the world-readable Nix store.
+          '';
+        };
 
-      acl = mkOption {
-        type = types.nullOr types.lines;
-        default = null;
-        example = "*               @ALL               8";
-        description = ''
-          Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/>
-          Mutually exclusive with services.dokuwiki.aclFile
-          Set this to a value other than null to take precedence over aclFile option.
-
-          Warning: Consider using aclFile instead if you do not
-          want to store the ACL in the world-readable Nix store.
-        '';
-      };
+        aclFile = mkOption {
+          type = with types; nullOr str;
+          default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null;
+          description = ''
+            Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl
+            Mutually exclusive with services.dokuwiki.acl which is preferred.
+            Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions.
+            Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/>
+          '';
+          example = "/var/lib/dokuwiki/${name}/acl.auth.php";
+        };
 
-      aclFile = mkOption {
-        type = with types; nullOr str;
-        default = if (config.aclUse && config.acl == null) then "/var/lib/dokuwiki/${name}/acl.auth.php" else null;
-        description = ''
-          Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl
-          Mutually exclusive with services.dokuwiki.acl which is preferred.
-          Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions.
-          Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/>
-        '';
-        example = "/var/lib/dokuwiki/${name}/acl.auth.php";
-      };
+        aclUse = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Necessary for users to log in into the system.
+            Also limits anonymous users. When disabled,
+            everyone is able to create and edit content.
+          '';
+        };
 
-      aclUse = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Necessary for users to log in into the system.
-          Also limits anonymous users. When disabled,
-          everyone is able to create and edit content.
-        '';
-      };
+        pluginsConfig = mkOption {
+          type = types.lines;
+          default = ''
+            $plugins['authad'] = 0;
+            $plugins['authldap'] = 0;
+            $plugins['authmysql'] = 0;
+            $plugins['authpgsql'] = 0;
+          '';
+          description = ''
+            List of the dokuwiki (un)loaded plugins.
+          '';
+        };
 
-      pluginsConfig = mkOption {
-        type = types.lines;
-        default = ''
-          $plugins['authad'] = 0;
-          $plugins['authldap'] = 0;
-          $plugins['authmysql'] = 0;
-          $plugins['authpgsql'] = 0;
-        '';
-        description = ''
-          List of the dokuwiki (un)loaded plugins.
-        '';
-      };
+        superUser = mkOption {
+          type = types.nullOr types.str;
+          default = "@admin";
+          description = ''
+            You can set either a username, a list of usernames (“admin1,admin2”),
+            or the name of a group by prepending an @ char to the groupname
+            Consult documentation <link xlink:href="https://www.dokuwiki.org/config:superuser"/> for further instructions.
+          '';
+        };
 
-      superUser = mkOption {
-        type = types.nullOr types.str;
-        default = "@admin";
-        description = ''
-          You can set either a username, a list of usernames (“admin1,admin2”),
-          or the name of a group by prepending an @ char to the groupname
-          Consult documentation <link xlink:href="https://www.dokuwiki.org/config:superuser"/> for further instructions.
-        '';
-      };
+        usersFile = mkOption {
+          type = with types; nullOr str;
+          default = if config.aclUse then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
+          description = ''
+            Location of the dokuwiki users file. List of users. Format:
+            login:passwordhash:Real Name:email:groups,comma,separated
+            Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1`
+            Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/users.auth.php.dist"/>
+            '';
+          example = "/var/lib/dokuwiki/${name}/users.auth.php";
+        };
 
-      usersFile = mkOption {
-        type = with types; nullOr str;
-        default = if config.aclUse then "/var/lib/dokuwiki/${name}/users.auth.php" else null;
-        description = ''
-          Location of the dokuwiki users file. List of users. Format:
-          login:passwordhash:Real Name:email:groups,comma,separated
-          Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1`
-          Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/users.auth.php.dist"/>
+        disableActions = mkOption {
+          type = types.nullOr types.str;
+          default = "";
+          example = "search,register";
+          description = ''
+            Disable individual action modes. Refer to
+            <link xlink:href="https://www.dokuwiki.org/config:action_modes"/>
+            for details on supported values.
           '';
-        example = "/var/lib/dokuwiki/${name}/users.auth.php";
-      };
+        };
 
-      disableActions = mkOption {
-        type = types.nullOr types.str;
-        default = "";
-        example = "search,register";
-        description = ''
-          Disable individual action modes. Refer to
-          <link xlink:href="https://www.dokuwiki.org/config:action_modes"/>
-          for details on supported values.
-        '';
-      };
+        plugins = mkOption {
+          type = types.listOf types.path;
+          default = [];
+          description = ''
+                List of path(s) to respective plugin(s) which are copied from the 'plugin' directory.
+                <note><para>These plugins need to be packaged before use, see example.</para></note>
+          '';
+          example = ''
+                # Let's package the icalevents plugin
+                plugin-icalevents = pkgs.stdenv.mkDerivation {
+                  name = "icalevents";
+                  # Download the plugin from the dokuwiki site
+                  src = pkgs.fetchurl {
+                    url = "https://github.com/real-or-random/dokuwiki-plugin-icalevents/releases/download/2017-06-16/dokuwiki-plugin-icalevents-2017-06-16.zip";
+                    sha256 = "e40ed7dd6bbe7fe3363bbbecb4de481d5e42385b5a0f62f6a6ce6bf3a1f9dfa8";
+                  };
+                  sourceRoot = ".";
+                  # We need unzip to build this package
+                  buildInputs = [ pkgs.unzip ];
+                  # Installing simply means copying all files to the output directory
+                  installPhase = "mkdir -p $out; cp -R * $out/";
+                };
 
-      extraConfig = mkOption {
-        type = types.nullOr types.lines;
-        default = null;
-        example = ''
-          $conf['title'] = 'My Wiki';
-          $conf['userewrite'] = 1;
-        '';
-        description = ''
-          DokuWiki configuration. Refer to
-          <link xlink:href="https://www.dokuwiki.org/config"/>
-          for details on supported values.
-        '';
-      };
+                # And then pass this theme to the plugin list like this:
+                plugins = [ plugin-icalevents ];
+          '';
+        };
 
-      plugins = mkOption {
-        type = types.listOf types.path;
-        default = [];
-        description = ''
-              List of path(s) to respective plugin(s) which are copied from the 'plugin' directory.
-              <note><para>These plugins need to be packaged before use, see example.</para></note>
-        '';
-        example = ''
-              # Let's package the icalevents plugin
-              plugin-icalevents = pkgs.stdenv.mkDerivation {
-                name = "icalevents";
-                # Download the plugin from the dokuwiki site
-                src = pkgs.fetchurl {
-                  url = "https://github.com/real-or-random/dokuwiki-plugin-icalevents/releases/download/2017-06-16/dokuwiki-plugin-icalevents-2017-06-16.zip";
-                  sha256 = "e40ed7dd6bbe7fe3363bbbecb4de481d5e42385b5a0f62f6a6ce6bf3a1f9dfa8";
+        templates = mkOption {
+          type = types.listOf types.path;
+          default = [];
+          description = ''
+                List of path(s) to respective template(s) which are copied from the 'tpl' directory.
+                <note><para>These templates need to be packaged before use, see example.</para></note>
+          '';
+          example = ''
+                # Let's package the bootstrap3 theme
+                template-bootstrap3 = pkgs.stdenv.mkDerivation {
+                  name = "bootstrap3";
+                  # Download the theme from the dokuwiki site
+                  src = pkgs.fetchurl {
+                    url = "https://github.com/giterlizzi/dokuwiki-template-bootstrap3/archive/v2019-05-22.zip";
+                    sha256 = "4de5ff31d54dd61bbccaf092c9e74c1af3a4c53e07aa59f60457a8f00cfb23a6";
+                  };
+                  # We need unzip to build this package
+                  buildInputs = [ pkgs.unzip ];
+                  # Installing simply means copying all files to the output directory
+                  installPhase = "mkdir -p $out; cp -R * $out/";
                 };
-                sourceRoot = ".";
-                # We need unzip to build this package
-                nativeBuildInputs = [ pkgs.unzip ];
-                # Installing simply means copying all files to the output directory
-                installPhase = "mkdir -p $out; cp -R * $out/";
-              };
-
-              # And then pass this theme to the plugin list like this:
-              plugins = [ plugin-icalevents ];
-        '';
-      };
 
-      templates = mkOption {
-        type = types.listOf types.path;
-        default = [];
-        description = ''
-              List of path(s) to respective template(s) which are copied from the 'tpl' directory.
-              <note><para>These templates need to be packaged before use, see example.</para></note>
-        '';
-        example = ''
-              # Let's package the bootstrap3 theme
-              template-bootstrap3 = pkgs.stdenv.mkDerivation {
-                name = "bootstrap3";
-                # Download the theme from the dokuwiki site
-                src = pkgs.fetchurl {
-                  url = "https://github.com/giterlizzi/dokuwiki-template-bootstrap3/archive/v2019-05-22.zip";
-                  sha256 = "4de5ff31d54dd61bbccaf092c9e74c1af3a4c53e07aa59f60457a8f00cfb23a6";
-                };
-                # We need unzip to build this package
-                nativeBuildInputs = [ pkgs.unzip ];
-                # Installing simply means copying all files to the output directory
-                installPhase = "mkdir -p $out; cp -R * $out/";
-              };
-
-              # And then pass this theme to the template list like this:
-              templates = [ template-bootstrap3 ];
-        '';
-      };
+                # And then pass this theme to the template list like this:
+                templates = [ template-bootstrap3 ];
+          '';
+        };
 
-      poolConfig = mkOption {
-        type = with types; attrsOf (oneOf [ str int bool ]);
-        default = {
-          "pm" = "dynamic";
-          "pm.max_children" = 32;
-          "pm.start_servers" = 2;
-          "pm.min_spare_servers" = 2;
-          "pm.max_spare_servers" = 4;
-          "pm.max_requests" = 500;
+        poolConfig = mkOption {
+          type = with types; attrsOf (oneOf [ str int bool ]);
+          default = {
+            "pm" = "dynamic";
+            "pm.max_children" = 32;
+            "pm.start_servers" = 2;
+            "pm.min_spare_servers" = 2;
+            "pm.max_spare_servers" = 4;
+            "pm.max_requests" = 500;
+          };
+          description = ''
+            Options for the DokuWiki PHP pool. See the documentation on <literal>php-fpm.conf</literal>
+            for details on configuration directives.
+          '';
         };
-        description = ''
-          Options for the dokuwiki PHP pool. See the documentation on <literal>php-fpm.conf</literal>
-          for details on configuration directives.
-        '';
-      };
 
-      nginx = mkOption {
-        type = types.submodule (
-          recursiveUpdate
-            (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) {}
-        );
-        default = {};
-        example = {
-          serverAliases = [
-            "wiki.\${config.networking.domain}"
-          ];
-          # To enable encryption and let let's encrypt take care of certificate
-          forceSSL = true;
-          enableACME = true;
+        extraConfig = mkOption {
+          type = types.nullOr types.lines;
+          default = null;
+          example = ''
+            $conf['title'] = 'My Wiki';
+            $conf['userewrite'] = 1;
+          '';
+          description = ''
+            DokuWiki configuration. Refer to
+            <link xlink:href="https://www.dokuwiki.org/config"/>
+            for details on supported values.
+          '';
         };
-        description = ''
-          With this option, you can customize the nginx virtualHost settings.
-        '';
+
       };
+
     };
-  };
 in
 {
   # interface
   options = {
     services.dokuwiki = mkOption {
-      type = types.attrsOf (types.submodule siteOpts);
+      type = types.submodule {
+        # Used to support old interface
+        freeformType = types.attrsOf (types.submodule siteOpts);
+
+        # New interface
+        options.sites = mkOption {
+          type = types.attrsOf (types.submodule siteOpts);
+          default = {};
+          description = "Specification of one or more DokuWiki sites to serve";
+        };
+
+        options.webserver = mkOption {
+          type = types.enum [ "nginx" "caddy" ];
+          default = "nginx";
+          description = ''
+            Whether to use nginx or caddy for 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.
+
+            Further apache2 configuration can be done by adapting <literal>services.httpd.virtualHosts.&lt;name&gt;</literal>.
+            See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
+          '';
+        };
+      };
       default = {};
-      description = "Sepcification of one or more dokuwiki sites to serve.";
+      description = "DokuWiki configuration";
     };
+
   };
 
   # implementation
-
-  config = mkIf (eachSite != {}) {
-
-    warnings = mapAttrsToList (hostName: cfg: mkIf (cfg.superUser == null) "Not setting services.dokuwiki.${hostName} superUser will impair your ability to administer DokuWiki") eachSite;
+  config = mkIf (eachSite != {}) (mkMerge [{
 
     assertions = flatten (mapAttrsToList (hostName: cfg:
     [{
       assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null);
-      message = "Either services.dokuwiki.${hostName}.acl or services.dokuwiki.${hostName}.aclFile is mandatory if aclUse true";
+      message = "Either services.dokuwiki.sites.${hostName}.acl or services.dokuwiki.sites.${hostName}.aclFile is mandatory if aclUse true";
     }
     {
       assertion = cfg.usersFile != null -> cfg.aclUse != false;
-      message = "services.dokuwiki.${hostName}.aclUse must must be true if usersFile is not null";
+      message = "services.dokuwiki.sites.${hostName}.aclUse must must be true if usersFile is not null";
     }
     ]) eachSite);
 
+    warnings = mapAttrsToList (hostName: _: ''services.dokuwiki."${hostName}" is deprecated use services.dokuwiki.sites."${hostName}"'') (oldSites cfg);
+
     services.phpfpm.pools = mapAttrs' (hostName: cfg: (
       nameValuePair "dokuwiki-${hostName}" {
         inherit user;
-        inherit group;
+        group = webserver.group;
+
         phpEnv = {
-          DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig cfg}";
-          DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig cfg}";
+          DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig hostName cfg}";
+          DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig hostName cfg}";
         } // optionalAttrs (cfg.usersFile != null) {
           DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}";
         } //optionalAttrs (cfg.aclUse) {
-          DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig cfg}" else "${toString cfg.aclFile}";
+          DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig hostName cfg}" else "${toString cfg.aclFile}";
         };
 
         settings = {
-          "listen.mode" = "0660";
-          "listen.owner" = user;
-          "listen.group" = group;
+          "listen.owner" = webserver.user;
+          "listen.group" = webserver.group;
         } // cfg.poolConfig;
-      })) eachSite;
-
-    services.nginx = {
-      enable = true;
-      virtualHosts = mapAttrs (hostName: cfg:  mkMerge [ cfg.nginx {
-        root = mkForce "${pkg hostName cfg}/share/dokuwiki";
-        extraConfig = lib.optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;";
-
-        locations."~ /(conf/|bin/|inc/|install.php)" = {
-          extraConfig = "deny all;";
-        };
+      }
+    )) eachSite;
 
-        locations."~ ^/data/" = {
-          root = "${cfg.stateDir}";
-          extraConfig = "internal;";
-        };
+  }
 
-        locations."~ ^/lib.*\\.(js|css|gif|png|ico|jpg|jpeg)$" = {
-          extraConfig = "expires 365d;";
-        };
+  {
+    systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
+      "d ${stateDir hostName}/attic 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/cache 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/index 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/locks 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/media 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/media_attic 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/media_meta 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/meta 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/pages 0750 ${user} ${webserver.group} - -"
+      "d ${stateDir hostName}/tmp 0750 ${user} ${webserver.group} - -"
+    ] ++ lib.optional (cfg.aclFile != null) "C ${cfg.aclFile} 0640 ${user} ${webserver.group} - ${pkg hostName cfg}/share/dokuwiki/conf/acl.auth.php.dist"
+    ++ lib.optional (cfg.usersFile != null) "C ${cfg.usersFile} 0640 ${user} ${webserver.group} - ${pkg hostName cfg}/share/dokuwiki/conf/users.auth.php.dist"
+    ) eachSite);
 
-        locations."/" = {
-          priority = 1;
-          index = "doku.php";
-          extraConfig = "try_files $uri $uri/ @dokuwiki;";
-        };
+    users.users.${user} = {
+      group = webserver.group;
+      isSystemUser = true;
+    };
+  }
 
-        locations."@dokuwiki" = {
-          extraConfig = ''
+  (mkIf (cfg.webserver == "nginx") {
+    services.nginx = {
+      enable = true;
+      virtualHosts = mapAttrs (hostName: cfg: {
+        serverName = mkDefault hostName;
+        root = "${pkg hostName cfg}/share/dokuwiki";
+
+        locations = {
+          "~ /(conf/|bin/|inc/|install.php)" = {
+            extraConfig = "deny all;";
+          };
+
+          "~ ^/data/" = {
+            root = "${stateDir hostName}";
+            extraConfig = "internal;";
+          };
+
+          "~ ^/lib.*\.(js|css|gif|png|ico|jpg|jpeg)$" = {
+            extraConfig = "expires 365d;";
+          };
+
+          "/" = {
+            priority = 1;
+            index = "doku.php";
+            extraConfig = ''try_files $uri $uri/ @dokuwiki;'';
+          };
+
+          "@dokuwiki" = {
+            extraConfig = ''
               # rewrites "doku.php/" out of the URLs if you set the userwrite setting to .htaccess in dokuwiki config page
               rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
               rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
               rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
               rewrite ^/(.*) /doku.php?id=$1&$args last;
-          '';
-        };
+            '';
+          };
 
-        locations."~ \\.php$" = {
-          extraConfig = ''
+          "~ \\.php$" = {
+            extraConfig = ''
               try_files $uri $uri/ /doku.php;
               include ${pkgs.nginx}/conf/fastcgi_params;
               fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
               fastcgi_param REDIRECT_STATUS 200;
               fastcgi_pass unix:${config.services.phpfpm.pools."dokuwiki-${hostName}".socket};
-              ${lib.optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;"}
-          '';
+              '';
+          };
+
         };
-      }]) eachSite;
+      }) eachSite;
     };
+  })
 
-    systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
-      "d ${cfg.stateDir}/attic 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/cache 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/index 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/locks 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/media 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/media_attic 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/media_meta 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/meta 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/pages 0750 ${user} ${group} - -"
-      "d ${cfg.stateDir}/tmp 0750 ${user} ${group} - -"
-    ] ++ lib.optional (cfg.aclFile != null) "C ${cfg.aclFile} 0640 ${user} ${group} - ${pkg hostName cfg}/share/dokuwiki/conf/acl.auth.php.dist"
-    ++ lib.optional (cfg.usersFile != null) "C ${cfg.usersFile} 0640 ${user} ${group} - ${pkg hostName cfg}/share/dokuwiki/conf/users.auth.php.dist"
-    ) eachSite);
+  (mkIf (cfg.webserver == "caddy") {
+    services.caddy = {
+      enable = true;
+      virtualHosts = mapAttrs' (hostName: cfg: (
+        nameValuePair "http://${hostName}" {
+          extraConfig = ''
+            root * ${pkg hostName cfg}/share/dokuwiki
+            file_server
 
-    users.users.${user} = {
-      group = group;
-      isSystemUser = true;
+            encode zstd gzip
+            php_fastcgi unix/${config.services.phpfpm.pools."dokuwiki-${hostName}".socket}
+
+            @restrict_files {
+              path /data/* /conf/* /bin/* /inc/* /vendor/* /install.php
+            }
+
+            respond @restrict_files 404
+
+            @allow_media {
+              path_regexp path ^/_media/(.*)$
+            }
+            rewrite @allow_media /lib/exe/fetch.php?media=/{http.regexp.path.1}
+
+            @allow_detail   {
+              path /_detail*
+            }
+            rewrite @allow_detail /lib/exe/detail.php?media={path}
+
+            @allow_export   {
+              path /_export*
+              path_regexp export /([^/]+)/(.*)
+            }
+            rewrite @allow_export /doku.php?do=export_{http.regexp.export.1}&id={http.regexp.export.2}
+
+            try_files {path} {path}/ /doku.php?id={path}&{query}
+          '';
+        }
+      )) eachSite;
     };
-  };
+  })
 
-  meta.maintainers = with maintainers; [ _1000101 ];
+  ]);
 
+  meta.maintainers = with maintainers; [
+    _1000101
+    onny
+  ];
 }
diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
index 5e24bd06ffdb..5bda7d5a5dd2 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
@@ -9,6 +9,13 @@ let
     RAILS_ENV = "production";
     NODE_ENV = "production";
 
+    # mastodon-web concurrency.
+    WEB_CONCURRENCY = toString cfg.webProcesses;
+    MAX_THREADS = toString cfg.webThreads;
+
+    # mastodon-streaming concurrency.
+    STREAMING_CLUSTER_NUM = toString cfg.streamingProcesses;
+
     DB_USER = cfg.database.user;
 
     REDIS_HOST = cfg.redis.host;
@@ -146,18 +153,41 @@ in {
         type = lib.types.port;
         default = 55000;
       };
+      streamingProcesses = lib.mkOption {
+        description = ''
+          Processes used by the mastodon-streaming service.
+          Defaults to the number of CPU cores minus one.
+        '';
+        type = lib.types.nullOr lib.types.int;
+        default = null;
+      };
 
       webPort = lib.mkOption {
         description = "TCP port used by the mastodon-web service.";
         type = lib.types.port;
         default = 55001;
       };
+      webProcesses = lib.mkOption {
+        description = "Processes used by the mastodon-web service.";
+        type = lib.types.int;
+        default = 2;
+      };
+      webThreads = lib.mkOption {
+        description = "Threads per process used by the mastodon-web service.";
+        type = lib.types.int;
+        default = 5;
+      };
 
       sidekiqPort = lib.mkOption {
-        description = "TCP port used by the mastodon-sidekiq service";
+        description = "TCP port used by the mastodon-sidekiq service.";
         type = lib.types.port;
         default = 55002;
       };
+      sidekiqThreads = lib.mkOption {
+        description = "Worker threads used by the mastodon-sidekiq service.";
+        type = lib.types.int;
+        default = 25;
+      };
 
       vapidPublicKeyFile = lib.mkOption {
         description = ''
@@ -524,9 +554,10 @@ in {
       wantedBy = [ "multi-user.target" ];
       environment = env // {
         PORT = toString(cfg.sidekiqPort);
+        DB_POOL = toString cfg.sidekiqThreads;
       };
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/sidekiq -c 25 -r ${cfg.package}";
+        ExecStart = "${cfg.package}/bin/sidekiq -c ${toString cfg.sidekiqThreads} -r ${cfg.package}";
         Restart = "always";
         RestartSec = 20;
         EnvironmentFile = "/var/lib/mastodon/.secrets_env";
diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
index 4f6850ace214..400790576d67 100644
--- a/nixpkgs/nixos/modules/services/web-apps/node-red.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
@@ -114,6 +114,7 @@ in
     users.users = optionalAttrs (cfg.user == defaultUser) {
       ${defaultUser} = {
         isSystemUser = true;
+        group = defaultUser;
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
index 6f1ef815bc46..eb91256045cc 100644
--- a/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/wordpress.nix
@@ -278,7 +278,7 @@ in
         };
 
         options.webserver = mkOption {
-          type = types.enum [ "httpd" "nginx" ];
+          type = types.enum [ "httpd" "nginx" "caddy" ];
           default = "httpd";
           description = ''
             Whether to use apache2 or nginx for virtual host management.
@@ -458,5 +458,32 @@ in
     };
   })
 
+  (mkIf (cfg.webserver == "caddy") {
+    services.caddy = {
+      enable = true;
+      virtualHosts = mapAttrs' (hostName: cfg: (
+        nameValuePair "http://${hostName}" {
+          extraConfig = ''
+            root    * /${pkg hostName cfg}/share/wordpress
+            file_server
+
+            php_fastcgi unix/${config.services.phpfpm.pools."wordpress-${hostName}".socket}
+
+            @uploads {
+              path_regexp path /uploads\/(.*)\.php
+            }
+            rewrite @uploads /
+
+            @wp-admin {
+              path  not ^\/wp-admin/*
+            }
+            rewrite @wp-admin {path}/index.php?{query}
+          '';
+        }
+      )) eachSite;
+    };
+  })
+
+
   ]);
 }
diff --git a/nixpkgs/nixos/modules/services/web-servers/trafficserver.nix b/nixpkgs/nixos/modules/services/web-servers/trafficserver/default.nix
index db0e2ac0bd05..341e8b13976a 100644
--- a/nixpkgs/nixos/modules/services/web-servers/trafficserver.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/trafficserver/default.nix
@@ -8,21 +8,9 @@ let
   group = config.users.groups.trafficserver.name;
 
   getManualUrl = name: "https://docs.trafficserver.apache.org/en/latest/admin-guide/files/${name}.en.html";
-  getConfPath = name: "${pkgs.trafficserver}/etc/trafficserver/${name}";
 
   yaml = pkgs.formats.yaml { };
 
-  fromYAML = f:
-    let
-      jsonFile = pkgs.runCommand "in.json"
-        {
-          nativeBuildInputs = [ pkgs.remarshal ];
-        } ''
-        yaml2json < "${f}" > "$out"
-      '';
-    in
-    builtins.fromJSON (builtins.readFile jsonFile);
-
   mkYamlConf = name: cfg:
     if cfg != null then {
       "trafficserver/${name}.yaml".source = yaml.generate "${name}.yaml" cfg;
@@ -73,7 +61,7 @@ in
 
     ipAllow = mkOption {
       type = types.nullOr yaml.type;
-      default = fromYAML (getConfPath "ip_allow.yaml");
+      default = builtins.fromJSON (builtins.readFile ./ip_allow.json);
       defaultText = "upstream defaults";
       example = literalExample {
         ip_allow = [{
@@ -94,7 +82,7 @@ in
 
     logging = mkOption {
       type = types.nullOr yaml.type;
-      default = fromYAML (getConfPath "logging.yaml");
+      default = builtins.fromJSON (builtins.readFile ./logging.json);
       defaultText = "upstream defaults";
       example = literalExample { };
       description = ''
diff --git a/nixpkgs/nixos/modules/services/web-servers/trafficserver/ip_allow.json b/nixpkgs/nixos/modules/services/web-servers/trafficserver/ip_allow.json
new file mode 100644
index 000000000000..fc2db8037286
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/trafficserver/ip_allow.json
@@ -0,0 +1,36 @@
+{
+  "ip_allow": [
+    {
+      "apply": "in",
+      "ip_addrs": "127.0.0.1",
+      "action": "allow",
+      "methods": "ALL"
+    },
+    {
+      "apply": "in",
+      "ip_addrs": "::1",
+      "action": "allow",
+      "methods": "ALL"
+    },
+    {
+      "apply": "in",
+      "ip_addrs": "0/0",
+      "action": "deny",
+      "methods": [
+        "PURGE",
+        "PUSH",
+        "DELETE"
+      ]
+    },
+    {
+      "apply": "in",
+      "ip_addrs": "::/0",
+      "action": "deny",
+      "methods": [
+        "PURGE",
+        "PUSH",
+        "DELETE"
+      ]
+    }
+  ]
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/trafficserver/logging.json b/nixpkgs/nixos/modules/services/web-servers/trafficserver/logging.json
new file mode 100644
index 000000000000..81e7ba0186c6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/trafficserver/logging.json
@@ -0,0 +1,37 @@
+{
+  "logging": {
+    "formats": [
+      {
+        "name": "welf",
+        "format": "id=firewall time=\"%<cqtd> %<cqtt>\" fw=%<phn> pri=6 proto=%<cqus> duration=%<ttmsf> sent=%<psql> rcvd=%<cqhl> src=%<chi> dst=%<shi> dstname=%<shn> user=%<caun> op=%<cqhm> arg=\"%<cqup>\" result=%<pssc> ref=\"%<{Referer}cqh>\" agent=\"%<{user-agent}cqh>\" cache=%<crc>"
+      },
+      {
+        "name": "squid_seconds_only_timestamp",
+        "format": "%<cqts> %<ttms> %<chi> %<crc>/%<pssc> %<psql> %<cqhm> %<cquc> %<caun> %<phr>/%<shn> %<psct>"
+      },
+      {
+        "name": "squid",
+        "format": "%<cqtq> %<ttms> %<chi> %<crc>/%<pssc> %<psql> %<cqhm> %<cquc> %<caun> %<phr>/%<shn> %<psct>"
+      },
+      {
+        "name": "common",
+        "format": "%<chi> - %<caun> [%<cqtn>] \"%<cqtx>\" %<pssc> %<pscl>"
+      },
+      {
+        "name": "extended",
+        "format": "%<chi> - %<caun> [%<cqtn>] \"%<cqtx>\" %<pssc> %<pscl> %<sssc> %<sscl> %<cqcl> %<pqcl> %<cqhl> %<pshl> %<pqhl> %<sshl> %<tts>"
+      },
+      {
+        "name": "extended2",
+        "format": "%<chi> - %<caun> [%<cqtn>] \"%<cqtx>\" %<pssc> %<pscl> %<sssc> %<sscl> %<cqcl> %<pqcl> %<cqhl> %<pshl> %<pqhl> %<sshl> %<tts> %<phr> %<cfsc> %<pfsc> %<crc>"
+      }
+    ],
+    "logs": [
+      {
+        "filename": "squid",
+        "format": "squid",
+        "mode": "binary"
+      }
+    ]
+  }
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/zope2.nix b/nixpkgs/nixos/modules/services/web-servers/zope2.nix
index 3abd506827c0..ab12e87502eb 100644
--- a/nixpkgs/nixos/modules/services/web-servers/zope2.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/zope2.nix
@@ -103,7 +103,11 @@ in
 
   config = mkIf (cfg.instances != {}) {
 
-    users.users.zope2.uid = config.ids.uids.zope2;
+    users.users.zope2 = {
+      isSystemUser = true;
+      group = "zope2";
+    };
+    users.groups.zope2 = {};
 
     systemd.services =
       let
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix
index 3f1575a0ca63..24ca82fca796 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/cde.nix
@@ -49,9 +49,10 @@ in {
     users.groups.mail = {};
     security.wrappers = {
       dtmail = {
-        source = "${pkgs.cdesktopenv}/bin/dtmail";
-        group = "mail";
         setgid = true;
+        owner = "nobody";
+        group = "mail";
+        source = "${pkgs.cdesktopenv}/bin/dtmail";
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 3a7ab64510b5..e3d876e82fdd 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -65,9 +65,24 @@ in
 
     # Wrappers for programs installed by enlightenment that should be setuid
     security.wrappers = {
-      enlightenment_ckpasswd.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_ckpasswd";
-      enlightenment_sys.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_sys";
-      enlightenment_system.source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_system";
+      enlightenment_ckpasswd =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_ckpasswd";
+        };
+      enlightenment_sys =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_sys";
+        };
+      enlightenment_system =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.enlightenment.enlightenment}/lib/enlightenment/utils/enlightenment_system";
+        };
     };
 
     environment.etc."X11/xkb".source = xcfg.xkbDir;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
index 4bc42525906c..9bb671adbecf 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -476,6 +476,8 @@ in
     (mkIf serviceCfg.experimental-features.realtime-scheduling {
       security.wrappers.".gnome-shell-wrapped" = {
         source = "${pkgs.gnome.gnome-shell}/bin/.gnome-shell-wrapped";
+        owner = "root";
+        group = "root";
         capabilities = "cap_sys_nice=ep";
       };
 
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
index e492073b80ff..887d6c91e83b 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -18,7 +18,7 @@ in
 
   meta = {
     doc = ./pantheon.xml;
-    maintainers = pkgs.pantheon.maintainers;
+    maintainers = teams.pantheon.members;
   };
 
   options = {
@@ -134,6 +134,9 @@ in
       services.accounts-daemon.enable = true;
       services.bamf.enable = true;
       services.colord.enable = mkDefault true;
+      services.fwupd.enable = mkDefault true;
+      services.touchegg.enable = mkDefault true;
+      services.touchegg.package = pkgs.pantheon.touchegg;
       services.tumbler.enable = mkDefault true;
       services.system-config-printer.enable = (mkIf config.services.printing.enable (mkDefault true));
       services.dbus.packages = with pkgs.pantheon; [
@@ -162,12 +165,11 @@ in
         isAllowed = true;
         isSystem = true;
       };
-      # Use gnome-settings-daemon fork
       services.udev.packages = [
-        pkgs.pantheon.elementary-settings-daemon
+        pkgs.gnome.gnome-settings-daemon338
       ];
       systemd.packages = [
-        pkgs.pantheon.elementary-settings-daemon
+        pkgs.gnome.gnome-settings-daemon338
       ];
       programs.dconf.enable = true;
       networking.networkmanager.enable = mkDefault true;
@@ -180,7 +182,6 @@ in
         gnome.adwaita-icon-theme
         gtk3.out
         hicolor-icon-theme
-        lightlocker
         onboard
         qgnomeplatform
         shared-mime-info
@@ -208,15 +209,13 @@ in
 
         # Services
         elementary-capnet-assist
-        elementary-dpms-helper
         elementary-notifications
         elementary-settings-daemon
         pantheon-agent-geoclue2
         pantheon-agent-polkit
       ]) ++ (gnome.removePackagesByName [
-        gnome.geary
-        gnome.epiphany
         gnome.gnome-font-viewer
+        gnome.gnome-settings-daemon338
       ] config.environment.pantheon.excludePackages);
 
       programs.evince.enable = mkDefault true;
@@ -224,9 +223,12 @@ in
 
       # Settings from elementary-default-settings
       environment.sessionVariables.GTK_CSD = "1";
-      environment.sessionVariables.GTK3_MODULES = [ "pantheon-filechooser-module" ];
       environment.etc."gtk-3.0/settings.ini".source = "${pkgs.pantheon.elementary-default-settings}/etc/gtk-3.0/settings.ini";
 
+      xdg.portal.extraPortals = [
+        pkgs.pantheon.elementary-files
+      ];
+
       # Override GSettings schemas
       environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${nixos-gsettings-desktop-schemas}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
 
@@ -254,6 +256,8 @@ in
 
       # Default Fonts
       fonts.fonts = with pkgs; [
+        inter
+        open-dyslexic
         open-sans
         roboto-mono
       ];
@@ -271,14 +275,16 @@ in
         elementary-camera
         elementary-code
         elementary-files
+        elementary-mail
         elementary-music
         elementary-photos
-        elementary-screenshot-tool
+        elementary-screenshot
         elementary-terminal
         elementary-videos
+        epiphany
       ] config.environment.pantheon.excludePackages);
 
-      # needed by screenshot-tool
+      # needed by screenshot
       fonts.fonts = [
         pkgs.pantheon.elementary-redacted-script
       ];
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.xml b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.xml
index 7905ceebd9aa..fe0a1c496223 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.xml
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/pantheon.xml
@@ -22,7 +22,7 @@
 <programlisting>
 <xref linkend="opt-services.pantheon.apps.enable"/> = false;
 </programlisting>
-   You can also use <xref linkend="opt-environment.pantheon.excludePackages"/> to remove any other app (like <package>geary</package>).
+   You can also use <xref linkend="opt-environment.pantheon.excludePackages"/> to remove any other app (like <package>elementary-mail</package>).
   </para>
  </section>
  <section xml:id="sec-pantheon-wingpanel-switchboard">
@@ -105,8 +105,14 @@ switchboard-with-plugs.override {
     </term>
     <listitem>
      <para>
-      AppCenter has been available since 20.03, but it is of little use. This is because there is no functioning PackageKit backend for Nix 2.0. In the near future you will be able to install Flatpak applications from AppCenter on NixOS. See this <link xlink:href="https://github.com/NixOS/nixpkgs/issues/70214">issue</link>.
+      AppCenter has been available since 20.03, but it is of little use. This is because there is no functioning PackageKit backend for Nix 2.0. Starting from 21.11, the Flatpak backend should work so you can install some Flatpak applications using it. See this <link xlink:href="https://github.com/NixOS/nixpkgs/issues/70214">issue</link>.
      </para>
+     <para>
+      To use AppCenter on NixOS, add <literal>pantheon.appcenter</literal> to <xref linkend="opt-environment.systemPackages" />, <link linkend="module-services-flatpak">enable Flatpak support</link> and optionally add the <literal>appcenter</literal> Flatpak remote:
+     </para>
+<screen>
+<prompt>$ </prompt>flatpak remote-add --if-not-exists appcenter https://flatpak.elementary.io/repo.flatpakrepo
+</screen>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index aac905fea437..d8dc2675f068 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -197,12 +197,24 @@ in
       };
 
       security.wrappers = {
-        kcheckpass.source = "${lib.getBin libsForQt5.kscreenlocker}/libexec/kcheckpass";
-        start_kdeinit.source = "${lib.getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit";
-        kwin_wayland = {
-          source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
-          capabilities = "cap_sys_nice+ep";
-        };
+        kcheckpass =
+          { setuid = true;
+            owner = "root";
+            group = "root";
+            source = "${lib.getBin libsForQt5.kscreenlocker}/libexec/kcheckpass";
+          };
+        start_kdeinit =
+          { setuid = true;
+            owner = "root";
+            group = "root";
+            source = "${lib.getBin libsForQt5.kinit}/libexec/kf5/start_kdeinit";
+          };
+        kwin_wayland =
+          { owner = "root";
+            group = "root";
+            capabilities = "cap_sys_nice+ep";
+            source = "${lib.getBin plasma5.kwin}/bin/kwin_wayland";
+          };
       };
 
       # DDC support
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
index 5c4c6c67fd02..3df576038a9f 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
@@ -174,9 +174,6 @@ in
       "systemd-machined.service"
       # setSessionScript wants AccountsService
       "accounts-daemon.service"
-      # Failed to open gpu '/dev/dri/card0': GDBus.Error:org.freedesktop.DBus.Error.AccessDenied: Operation not permitted
-      # https://github.com/NixOS/nixpkgs/pull/25311#issuecomment-609417621
-      "systemd-udev-settle.service"
     ];
 
     systemd.services.display-manager.after = [
@@ -186,7 +183,6 @@ in
       "getty@tty${gdm.initialVT}.service"
       "plymouth-quit.service"
       "plymouth-start.service"
-      "systemd-udev-settle.service"
     ];
     systemd.services.display-manager.conflicts = [
       "getty@tty${gdm.initialVT}.service"
diff --git a/nixpkgs/nixos/modules/services/x11/extra-layouts.nix b/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
index 0e2edc6a5309..b1c4e04975f9 100644
--- a/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
+++ b/nixpkgs/nixos/modules/services/x11/extra-layouts.nix
@@ -79,6 +79,10 @@ let
     };
   };
 
+  xkb_patched = pkgs.xorg.xkeyboardconfig_custom {
+    layouts = config.services.xserver.extraLayouts;
+  };
+
 in
 
 {
@@ -114,58 +118,14 @@ in
 
   config = mkIf (layouts != { }) {
 
-    # We don't override xkeyboard_config directly to
-    # reduce the amount of packages to be recompiled.
-    # Only the following packages are necessary to set
-    # a custom layout anyway:
-    nixpkgs.overlays = lib.singleton (self: super: {
-
-      xkb_patched = self.xorg.xkeyboardconfig_custom {
-        layouts = config.services.xserver.extraLayouts;
-      };
-
-      xorg = super.xorg // {
-        xorgserver = super.xorg.xorgserver.overrideAttrs (old: {
-          configureFlags = old.configureFlags ++ [
-            "--with-xkb-bin-directory=${self.xorg.xkbcomp}/bin"
-            "--with-xkb-path=${self.xkb_patched}/share/X11/xkb"
-          ];
-        });
-
-        setxkbmap = super.xorg.setxkbmap.overrideAttrs (old: {
-          postInstall =
-            ''
-              mkdir -p $out/share
-              ln -sfn ${self.xkb_patched}/etc/X11 $out/share/X11
-            '';
-        });
-
-        xkbcomp = super.xorg.xkbcomp.overrideAttrs (old: {
-          configureFlags = [ "--with-xkb-config-root=${self.xkb_patched}/share/X11/xkb" ];
-        });
-
-      };
-
-      ckbcomp = super.ckbcomp.override {
-        xkeyboard_config = self.xkb_patched;
-      };
-
-      xkbvalidate = super.xkbvalidate.override {
-        libxkbcommon = self.libxkbcommon.override {
-          xkeyboard_config = self.xkb_patched;
-        };
-      };
-
-    });
-
     environment.sessionVariables = {
       # runtime override supported by multiple libraries e. g. libxkbcommon
       # https://xkbcommon.org/doc/current/group__include-path.html
-      XKB_CONFIG_ROOT = "${pkgs.xkb_patched}/etc/X11/xkb";
+      XKB_CONFIG_ROOT = "${xkb_patched}/etc/X11/xkb";
     };
 
     services.xserver = {
-      xkbDir = "${pkgs.xkb_patched}/etc/X11/xkb";
+      xkbDir = "${xkb_patched}/etc/X11/xkb";
       exportConfiguration = config.services.xserver.displayManager.startx.enable
         || config.services.xserver.displayManager.sx.enable;
     };
diff --git a/nixpkgs/nixos/modules/services/x11/hardware/libinput.nix b/nixpkgs/nixos/modules/services/x11/hardware/libinput.nix
index 439708bc47ed..e2fb7d0918e3 100644
--- a/nixpkgs/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixpkgs/nixos/modules/services/x11/hardware/libinput.nix
@@ -163,6 +163,15 @@ let cfg = config.services.xserver.libinput;
           '';
       };
 
+      transformationMatrix = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          A  string  of  9 space-separated floating point numbers.  Sets the transformation matrix to
+          the 3x3 matrix where the first row is (abc), the second row is (def) and the third row is (ghi).
+        '';
+      };
+
       disableWhileTyping = mkOption {
         type = types.bool;
         default = false;
@@ -196,6 +205,7 @@ let cfg = config.services.xserver.libinput;
       ${optionalString (cfg.${deviceType}.accelSpeed != null) ''Option "AccelSpeed" "${cfg.${deviceType}.accelSpeed}"''}
       ${optionalString (cfg.${deviceType}.buttonMapping != null) ''Option "ButtonMapping" "${cfg.${deviceType}.buttonMapping}"''}
       ${optionalString (cfg.${deviceType}.calibrationMatrix != null) ''Option "CalibrationMatrix" "${cfg.${deviceType}.calibrationMatrix}"''}
+      ${optionalString (cfg.${deviceType}.transformationMatrix != null) ''Option "TransformationMatrix" "${cfg.${deviceType}.transformationMatrix}"''}
       ${optionalString (cfg.${deviceType}.clickMethod != null) ''Option "ClickMethod" "${cfg.${deviceType}.clickMethod}"''}
       Option "LeftHanded" "${xorgBool cfg.${deviceType}.leftHanded}"
       Option "MiddleEmulation" "${xorgBool cfg.${deviceType}.middleEmulation}"
@@ -227,6 +237,7 @@ in {
       "sendEventsMode"
       "tapping"
       "tappingDragLock"
+      "transformationMatrix"
       "disableWhileTyping"
       "additionalOptions"
     ]);
diff --git a/nixpkgs/nixos/modules/services/x11/touchegg.nix b/nixpkgs/nixos/modules/services/x11/touchegg.nix
new file mode 100644
index 000000000000..fab7fac3f017
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/touchegg.nix
@@ -0,0 +1,38 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.touchegg;
+
+in {
+  meta = {
+    maintainers = teams.pantheon.members;
+  };
+
+  ###### interface
+  options.services.touchegg = {
+    enable = mkEnableOption "touchegg, a multi-touch gesture recognizer";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.touchegg;
+      defaultText = "pkgs.touchegg";
+      description = "touchegg derivation to use.";
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    systemd.services.touchegg = {
+      description = "Touchegg Daemon";
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${cfg.package}/bin/touchegg --daemon";
+        Restart = "on-failure";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix
index ad9bd88f98aa..ee190ac3cc44 100644
--- a/nixpkgs/nixos/modules/services/x11/xserver.nix
+++ b/nixpkgs/nixos/modules/services/x11/xserver.nix
@@ -738,6 +738,9 @@ in
       nativeBuildInputs = with pkgs.buildPackages; [ xkbvalidate ];
       preferLocalBuild = true;
     } ''
+      ${optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
+        "export XKB_CONFIG_ROOT=${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
+      }
       xkbvalidate "$xkbModel" "$layout" "$xkbVariant" "$xkbOptions"
       touch "$out"
     '');