about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2021-09-08 17:57:14 +0000
committerAlyssa Ross <hi@alyssa.is>2021-09-13 11:31:47 +0000
commitee7984efa14902a2ddd820c937457667a4f40c6a (patch)
treec9c1d046733cefe5e21fdd8a52104175d47b2443 /nixpkgs/nixos/modules
parentffc9d4ba381da62fd08b361bacd1e71e2a3d934d (diff)
parentb3c692172e5b5241b028a98e1977f9fb12eeaf42 (diff)
downloadnixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.gz
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.bz2
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.lz
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.xz
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.tar.zst
nixlib-ee7984efa14902a2ddd820c937457667a4f40c6a.zip
Merge commit 'b3c692172e5b5241b028a98e1977f9fb12eeaf42'
Diffstat (limited to 'nixpkgs/nixos/modules')
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fontconfig.nix18
-rw-r--r--nixpkgs/nixos/modules/config/fonts/fonts.nix70
-rw-r--r--nixpkgs/nixos/modules/config/ldap.nix2
-rw-r--r--nixpkgs/nixos/modules/config/networking.nix2
-rw-r--r--nixpkgs/nixos/modules/config/nsswitch.nix4
-rw-r--r--nixpkgs/nixos/modules/config/swap.nix11
-rw-r--r--nixpkgs/nixos/modules/config/system-environment.nix70
-rw-r--r--nixpkgs/nixos/modules/config/users-groups.nix15
-rw-r--r--nixpkgs/nixos/modules/hardware/all-firmware.nix15
-rw-r--r--nixpkgs/nixos/modules/hardware/onlykey.udev4
-rw-r--r--nixpkgs/nixos/modules/hardware/onlykey/default.nix (renamed from nixpkgs/nixos/modules/hardware/onlykey.nix)0
-rw-r--r--nixpkgs/nixos/modules/hardware/onlykey/onlykey.udev18
-rw-r--r--nixpkgs/nixos/modules/hardware/openrazer.nix23
-rw-r--r--nixpkgs/nixos/modules/hardware/sensor/iio.nix2
-rw-r--r--nixpkgs/nixos/modules/hardware/video/hidpi.nix1
-rw-r--r--nixpkgs/nixos/modules/hardware/video/nvidia.nix26
-rw-r--r--nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-base.nix5
-rw-r--r--nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix94
-rw-r--r--nixpkgs/nixos/modules/installer/netboot/netboot.nix8
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixpkgs/nixos/modules/misc/crashdump.nix2
-rw-r--r--nixpkgs/nixos/modules/misc/documentation.nix3
-rw-r--r--nixpkgs/nixos/modules/misc/ids.nix9
-rw-r--r--nixpkgs/nixos/modules/misc/nixpkgs.nix2
-rw-r--r--nixpkgs/nixos/modules/misc/version.nix5
-rw-r--r--nixpkgs/nixos/modules/module-list.nix29
-rw-r--r--nixpkgs/nixos/modules/profiles/headless.nix2
-rw-r--r--nixpkgs/nixos/modules/profiles/installation-device.nix6
-rw-r--r--nixpkgs/nixos/modules/programs/calls.nix25
-rw-r--r--nixpkgs/nixos/modules/programs/captive-browser.nix14
-rw-r--r--nixpkgs/nixos/modules/programs/neovim.nix13
-rw-r--r--nixpkgs/nixos/modules/programs/ssh.nix2
-rw-r--r--nixpkgs/nixos/modules/programs/udevil.nix3
-rw-r--r--nixpkgs/nixos/modules/programs/zsh/zsh.nix5
-rw-r--r--nixpkgs/nixos/modules/security/acme.nix54
-rw-r--r--nixpkgs/nixos/modules/security/apparmor/includes.nix2
-rw-r--r--nixpkgs/nixos/modules/security/pam.nix14
-rw-r--r--nixpkgs/nixos/modules/security/polkit.nix2
-rw-r--r--nixpkgs/nixos/modules/services/admin/meshcentral.nix53
-rw-r--r--nixpkgs/nixos/modules/services/amqp/rabbitmq.nix40
-rw-r--r--nixpkgs/nixos/modules/services/audio/hqplayerd.nix142
-rw-r--r--nixpkgs/nixos/modules/services/audio/navidrome.nix71
-rw-r--r--nixpkgs/nixos/modules/services/audio/networkaudiod.nix19
-rw-r--r--nixpkgs/nixos/modules/services/audio/roon-bridge.nix14
-rw-r--r--nixpkgs/nixos/modules/services/audio/roon-server.nix14
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgbackup.nix2
-rw-r--r--nixpkgs/nixos/modules/services/backup/postgresql-backup.nix45
-rw-r--r--nixpkgs/nixos/modules/services/backup/sanoid.nix221
-rw-r--r--nixpkgs/nixos/modules/services/backup/syncoid.nix524
-rw-r--r--nixpkgs/nixos/modules/services/backup/znapzend.nix2
-rw-r--r--nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix4
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix54
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix11
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix4
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix2
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix3
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix12
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix8
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/influxdb2.nix53
-rw-r--r--nixpkgs/nixos/modules/services/databases/postgresql.nix3
-rw-r--r--nixpkgs/nixos/modules/services/databases/redis.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/victoriametrics.nix8
-rw-r--r--nixpkgs/nixos/modules/services/desktops/bamf.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/geoclue2.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json46
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json12
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json1
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json1
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix2
-rw-r--r--nixpkgs/nixos/modules/services/desktops/tumbler.nix4
-rw-r--r--nixpkgs/nixos/modules/services/desktops/zeitgeist.nix4
-rw-r--r--nixpkgs/nixos/modules/services/development/distccd.nix155
-rw-r--r--nixpkgs/nixos/modules/services/development/hoogle.nix2
-rw-r--r--nixpkgs/nixos/modules/services/hardware/sane.nix27
-rw-r--r--nixpkgs/nixos/modules/services/mail/dovecot.nix5
-rw-r--r--nixpkgs/nixos/modules/services/mail/nullmailer.nix2
-rw-r--r--nixpkgs/nixos/modules/services/mail/postfix.nix4
-rw-r--r--nixpkgs/nixos/modules/services/mail/postfixadmin.nix199
-rw-r--r--nixpkgs/nixos/modules/services/mail/rspamd.nix5
-rw-r--r--nixpkgs/nixos/modules/services/misc/airsonic.nix23
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitea.nix14
-rw-r--r--nixpkgs/nixos/modules/services/misc/gitlab.nix6
-rw-r--r--nixpkgs/nixos/modules/services/misc/home-assistant.nix3
-rw-r--r--nixpkgs/nixos/modules/services/misc/klipper.nix11
-rw-r--r--nixpkgs/nixos/modules/services/misc/libreddit.nix66
-rw-r--r--nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/matrix-synapse.nix5
-rw-r--r--nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/moonraker.nix135
-rw-r--r--nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix120
-rw-r--r--nixpkgs/nixos/modules/services/misc/nitter.nix351
-rw-r--r--nixpkgs/nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/misc/nzbget.nix54
-rw-r--r--nixpkgs/nixos/modules/services/misc/octoprint.nix3
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless-ng.nix308
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless.nix183
-rw-r--r--nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix4
-rw-r--r--nixpkgs/nixos/modules/services/misc/uhub.nix238
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/grafana.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/nagios.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix4
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix1
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix34
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/thanos.nix2
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix114
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix100
-rw-r--r--nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml65
-rw-r--r--nixpkgs/nixos/modules/services/networking/autossh.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/avahi-daemon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/bird.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/cjdns.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/connman.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/epmd.nix19
-rw-r--r--nixpkgs/nixos/modules/services/networking/firewall.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/i2pd.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/iwd.nix32
-rw-r--r--nixpkgs/nixos/modules/services/networking/kea.nix16
-rw-r--r--nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix6
-rw-r--r--nixpkgs/nixos/modules/services/networking/nats.nix159
-rw-r--r--nixpkgs/nixos/modules/services/networking/networkmanager.nix13
-rw-r--r--nixpkgs/nixos/modules/services/networking/nftables.nix1
-rw-r--r--nixpkgs/nixos/modules/services/networking/ntp/chrony.nix31
-rw-r--r--nixpkgs/nixos/modules/services/networking/nylon.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/quicktun.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/shout.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/soju.nix113
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix12
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncplay.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/syncthing.nix656
-rw-r--r--nixpkgs/nixos/modules/services/networking/tinc.nix7
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix35
-rw-r--r--nixpkgs/nixos/modules/services/networking/v2ray.nix4
-rw-r--r--nixpkgs/nixos/modules/services/networking/wakeonlan.nix16
-rw-r--r--nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix296
-rw-r--r--nixpkgs/nixos/modules/services/networking/zeronet.nix4
-rw-r--r--nixpkgs/nixos/modules/services/search/elasticsearch.nix20
-rw-r--r--nixpkgs/nixos/modules/services/system/uptimed.nix10
-rw-r--r--nixpkgs/nixos/modules/services/torrent/deluge.nix9
-rw-r--r--nixpkgs/nixos/modules/services/video/replay-sorcery.nix70
-rw-r--r--nixpkgs/nixos/modules/services/wayland/cage.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/discourse.xml13
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/fluidd.nix64
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/isso.nix69
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/keycloak.nix4
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/miniflux.nix23
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/moodle.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.nix3
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/nextcloud.xml124
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/node-red.nix148
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/tt-rss.nix181
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix13
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix21
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/caddy/default.nix (renamed from nixpkgs/nixos/modules/services/web-servers/caddy.nix)93
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix28
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/minio.nix10
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix43
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix19
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix7
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix3
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix7
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix6
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix2
-rwxr-xr-xnixpkgs/nixos/modules/services/x11/display-managers/set-session.py3
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sx.nix37
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix16
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix41
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/xserver.nix9
-rw-r--r--nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl10
-rw-r--r--nixpkgs/nixos/modules/system/activation/top-level.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/binfmt.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/kexec.nix6
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl31
-rw-r--r--nixpkgs/nixos/modules/system/boot/resolved.nix3
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1-init.sh2
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd.nix14
-rw-r--r--nixpkgs/nixos/modules/system/etc/etc.nix73
-rw-r--r--nixpkgs/nixos/modules/system/etc/make-etc.sh45
-rw-r--r--nixpkgs/nixos/modules/tasks/encrypted-devices.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems.nix29
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/exfat.nix8
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/zfs.nix100
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix3
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces.nix12
-rw-r--r--nixpkgs/nixos/modules/virtualisation/amazon-image.nix36
-rw-r--r--nixpkgs/nixos/modules/virtualisation/amazon-options.nix54
-rw-r--r--nixpkgs/nixos/modules/virtualisation/containerd.nix1
-rw-r--r--nixpkgs/nixos/modules/virtualisation/google-compute-image.nix11
-rw-r--r--nixpkgs/nixos/modules/virtualisation/podman.nix2
-rw-r--r--nixpkgs/nixos/modules/virtualisation/virtualbox-image.nix8
203 files changed, 5509 insertions, 1765 deletions
diff --git a/nixpkgs/nixos/modules/config/fonts/fontconfig.nix b/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
index 72827c5abaae..1e68fef7ce74 100644
--- a/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixpkgs/nixos/modules/config/fonts/fontconfig.nix
@@ -78,14 +78,6 @@ let
         </edit>
       </match>
 
-      ${optionalString (cfg.dpi != 0) ''
-      <match target="pattern">
-        <edit name="dpi" mode="assign">
-          <double>${toString cfg.dpi}</double>
-        </edit>
-      </match>
-      ''}
-
     </fontconfig>
   '';
 
@@ -237,6 +229,7 @@ in
     (mkRemovedOptionModule [ "fonts" "fontconfig" "hinting" "style" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
+    (mkRemovedOptionModule [ "fonts" "fontconfig" "dpi" ] "Use display server-specific options")
   ] ++ lib.forEach [ "enable" "substitutions" "preset" ]
      (opt: lib.mkRemovedOptionModule [ "fonts" "fontconfig" "ultimate" "${opt}" ] ''
        The fonts.fontconfig.ultimate module and configuration is obsolete.
@@ -282,15 +275,6 @@ in
           '';
         };
 
-        dpi = mkOption {
-          type = types.int;
-          default = 0;
-          description = ''
-            Force DPI setting. Setting to <literal>0</literal> disables DPI
-            forcing; the DPI detected for the display will be used.
-          '';
-        };
-
         localConf = mkOption {
           type = types.lines;
           default = "";
diff --git a/nixpkgs/nixos/modules/config/fonts/fonts.nix b/nixpkgs/nixos/modules/config/fonts/fonts.nix
index 3911196c1013..f87e61e3ef9f 100644
--- a/nixpkgs/nixos/modules/config/fonts/fonts.nix
+++ b/nixpkgs/nixos/modules/config/fonts/fonts.nix
@@ -2,6 +2,52 @@
 
 with lib;
 
+let
+  # A scalable variant of the X11 "core" cursor
+  #
+  # If not running a fancy desktop environment, the cursor is likely set to
+  # the default `cursor.pcf` bitmap font. This is 17px wide, so it's very
+  # small and almost invisible on 4K displays.
+  fontcursormisc_hidpi = pkgs.xorg.fontxfree86type1.overrideAttrs (old:
+    let
+      # The scaling constant is 230/96: the scalable `left_ptr` glyph at
+      # about 23 points is rendered as 17px, on a 96dpi display.
+      # Note: the XLFD font size is in decipoints.
+      size = 2.39583 * config.services.xserver.dpi;
+      sizeString = builtins.head (builtins.split "\\." (toString size));
+    in
+    {
+      postInstall = ''
+        alias='cursor -xfree86-cursor-medium-r-normal--0-${sizeString}-0-0-p-0-adobe-fontspecific'
+        echo "$alias" > $out/lib/X11/fonts/Type1/fonts.alias
+      '';
+    });
+
+  hasHidpi =
+    config.hardware.video.hidpi.enable &&
+    config.services.xserver.dpi != null;
+
+  defaultFonts =
+    [ pkgs.dejavu_fonts
+      pkgs.freefont_ttf
+      pkgs.gyre-fonts # TrueType substitutes for standard PostScript fonts
+      pkgs.liberation_ttf
+      pkgs.unifont
+      pkgs.noto-fonts-emoji
+    ];
+
+  defaultXFonts =
+    [ (if hasHidpi then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
+      pkgs.xorg.fontmiscmisc
+    ] ++ optionals (config.nixpkgs.config.allowUnfree or false)
+    [ # these are unfree, and will make usage with xserver fail
+      pkgs.xorg.fontbhlucidatypewriter100dpi
+      pkgs.xorg.fontbhlucidatypewriter75dpi
+      pkgs.xorg.fontbh100dpi
+    ];
+
+in
+
 {
   imports = [
     (mkRemovedOptionModule [ "fonts" "enableCoreFonts" ] "Use fonts.fonts = [ pkgs.corefonts ]; instead.")
@@ -32,25 +78,9 @@ with lib;
 
   };
 
-  config = {
-
-    fonts.fonts = mkIf config.fonts.enableDefaultFonts
-      ([
-        pkgs.dejavu_fonts
-        pkgs.freefont_ttf
-        pkgs.gyre-fonts # TrueType substitutes for standard PostScript fonts
-        pkgs.liberation_ttf
-        pkgs.xorg.fontmiscmisc
-        pkgs.xorg.fontcursormisc
-        pkgs.unifont
-        pkgs.noto-fonts-emoji
-      ] ++ lib.optionals (config.nixpkgs.config.allowUnfree or false) [
-        # these are unfree, and will make usage with xserver fail
-        pkgs.xorg.fontbhlucidatypewriter100dpi
-        pkgs.xorg.fontbhlucidatypewriter75dpi
-        pkgs.xorg.fontbh100dpi
-      ]);
-
-  };
+  config = mkMerge [
+    { fonts.fonts = mkIf config.fonts.enableDefaultFonts defaultFonts; }
+    { fonts.fonts = mkIf config.services.xserver.enable defaultXFonts; }
+  ];
 
 }
diff --git a/nixpkgs/nixos/modules/config/ldap.nix b/nixpkgs/nixos/modules/config/ldap.nix
index 35813c168fd8..85cad8b93d8c 100644
--- a/nixpkgs/nixos/modules/config/ldap.nix
+++ b/nixpkgs/nixos/modules/config/ldap.nix
@@ -42,7 +42,7 @@ let
   # nslcd normally reads configuration from /etc/nslcd.conf.
   # this file might contain secrets. We append those at runtime,
   # so redirect its location to something more temporary.
-  nslcdWrapped = runCommandNoCC "nslcd-wrapped" { nativeBuildInputs = [ makeWrapper ]; } ''
+  nslcdWrapped = runCommand "nslcd-wrapped" { nativeBuildInputs = [ makeWrapper ]; } ''
     mkdir -p $out/bin
     makeWrapper ${nss_pam_ldapd}/sbin/nslcd $out/bin/nslcd \
       --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
diff --git a/nixpkgs/nixos/modules/config/networking.nix b/nixpkgs/nixos/modules/config/networking.nix
index dba8977e482c..8c4eec510e5d 100644
--- a/nixpkgs/nixos/modules/config/networking.nix
+++ b/nixpkgs/nixos/modules/config/networking.nix
@@ -190,7 +190,7 @@ in
         protocols.source  = pkgs.iana-etc + "/etc/protocols";
 
         # /etc/hosts: Hostname-to-IP mappings.
-        hosts.source = pkgs.runCommandNoCC "hosts" {} ''
+        hosts.source = pkgs.runCommand "hosts" {} ''
           cat ${escapeShellArgs cfg.hostFiles} > $out
         '';
 
diff --git a/nixpkgs/nixos/modules/config/nsswitch.nix b/nixpkgs/nixos/modules/config/nsswitch.nix
index d19d35a48906..91a36cef10e6 100644
--- a/nixpkgs/nixos/modules/config/nsswitch.nix
+++ b/nixpkgs/nixos/modules/config/nsswitch.nix
@@ -124,8 +124,8 @@ with lib;
       group = mkBefore [ "files" ];
       shadow = mkBefore [ "files" ];
       hosts = mkMerge [
-        (mkBefore [ "files" ])
-        (mkAfter [ "dns" ])
+        (mkOrder 998 [ "files" ])
+        (mkOrder 1499 [ "dns" ])
       ];
       services = mkBefore [ "files" ];
     };
diff --git a/nixpkgs/nixos/modules/config/swap.nix b/nixpkgs/nixos/modules/config/swap.nix
index a37b46b8c468..ff2ae1da31bd 100644
--- a/nixpkgs/nixos/modules/config/swap.nix
+++ b/nixpkgs/nixos/modules/config/swap.nix
@@ -127,6 +127,15 @@ let
         '';
       };
 
+      options = mkOption {
+        default = [ "defaults" ];
+        example = [ "nofail" ];
+        type = types.listOf types.nonEmptyStr;
+        description = ''
+          Options used to mount the swap.
+        '';
+      };
+
       deviceName = mkOption {
         type = types.str;
         internal = true;
@@ -215,7 +224,7 @@ in
                   fi
                 ''}
                 ${optionalString sw.randomEncryption.enable ''
-                  cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} ${sw.device} ${sw.deviceName}
+                  cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} ${optionalString (sw.discardPolicy != null) "--allow-discards"} ${sw.device} ${sw.deviceName}
                   mkswap ${sw.realDevice}
                 ''}
               '';
diff --git a/nixpkgs/nixos/modules/config/system-environment.nix b/nixpkgs/nixos/modules/config/system-environment.nix
index 4888740ba3d5..d2a66b8d932d 100644
--- a/nixpkgs/nixos/modules/config/system-environment.nix
+++ b/nixpkgs/nixos/modules/config/system-environment.nix
@@ -65,42 +65,40 @@ in
   };
 
   config = {
-
-    system.build.pamEnvironment =
-      let
-        suffixedVariables =
-          flip mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
-            flip concatMap cfg.profiles (profile:
-              map (suffix: "${profile}${suffix}") suffixes
-            )
-          );
-
-        # We're trying to use the same syntax for PAM variables and env variables.
-        # That means we need to map the env variables that people might use to their
-        # equivalent PAM variable.
-        replaceEnvVars = replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
-
-        pamVariable = n: v:
-          ''${n}   DEFAULT="${concatStringsSep ":" (map replaceEnvVars (toList v))}"'';
-
-        pamVariables =
-          concatStringsSep "\n"
-          (mapAttrsToList pamVariable
-          (zipAttrsWith (n: concatLists)
-            [
-              # Make sure security wrappers are prioritized without polluting
-              # shell environments with an extra entry. Sessions which depend on
-              # pam for its environment will otherwise have eg. broken sudo. In
-              # particular Gnome Shell sometimes fails to source a proper
-              # environment from a shell.
-              { PATH = [ config.security.wrapperDir ]; }
-
-              (mapAttrs (n: toList) cfg.sessionVariables)
-              suffixedVariables
-            ]));
-      in
-        pkgs.writeText "pam-environment" "${pamVariables}\n";
-
+    environment.etc."pam/environment".text = let
+      suffixedVariables =
+        flip mapAttrs cfg.profileRelativeSessionVariables (envVar: suffixes:
+          flip concatMap cfg.profiles (profile:
+            map (suffix: "${profile}${suffix}") suffixes
+          )
+        );
+
+      # We're trying to use the same syntax for PAM variables and env variables.
+      # That means we need to map the env variables that people might use to their
+      # equivalent PAM variable.
+      replaceEnvVars = replaceStrings ["$HOME" "$USER"] ["@{HOME}" "@{PAM_USER}"];
+
+      pamVariable = n: v:
+        ''${n}   DEFAULT="${concatStringsSep ":" (map replaceEnvVars (toList v))}"'';
+
+      pamVariables =
+        concatStringsSep "\n"
+        (mapAttrsToList pamVariable
+        (zipAttrsWith (n: concatLists)
+          [
+            # Make sure security wrappers are prioritized without polluting
+            # shell environments with an extra entry. Sessions which depend on
+            # pam for its environment will otherwise have eg. broken sudo. In
+            # particular Gnome Shell sometimes fails to source a proper
+            # environment from a shell.
+            { PATH = [ config.security.wrapperDir ]; }
+
+            (mapAttrs (n: toList) cfg.sessionVariables)
+            suffixedVariables
+          ]));
+    in ''
+      ${pamVariables}
+    '';
   };
 
 }
diff --git a/nixpkgs/nixos/modules/config/users-groups.nix b/nixpkgs/nixos/modules/config/users-groups.nix
index 567a8b6f3b97..f86be3be2c65 100644
--- a/nixpkgs/nixos/modules/config/users-groups.nix
+++ b/nixpkgs/nixos/modules/config/users-groups.nix
@@ -324,7 +324,7 @@ let
 
   };
 
-  groupOpts = { name, ... }: {
+  groupOpts = { name, config, ... }: {
 
     options = {
 
@@ -358,6 +358,10 @@ let
 
     config = {
       name = mkDefault name;
+
+      members = mapAttrsToList (n: u: u.name) (
+        filterAttrs (n: u: elem config.name u.extraGroups) cfg.users
+      );
     };
 
   };
@@ -396,7 +400,7 @@ let
     };
   };
 
-  idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
+  idsAreUnique = set: idAttr: !(foldr (name: args@{ dup, acc }:
     let
       id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
       exists = builtins.hasAttr id acc;
@@ -419,12 +423,7 @@ let
           initialPassword initialHashedPassword;
         shell = utils.toShellPath u.shell;
       }) cfg.users;
-    groups = mapAttrsToList (n: g:
-      { inherit (g) name gid;
-        members = g.members ++ (mapAttrsToList (n: u: u.name) (
-          filterAttrs (n: u: elem g.name u.extraGroups) cfg.users
-        ));
-      }) cfg.groups;
+    groups = attrValues cfg.groups;
   });
 
   systemShells =
diff --git a/nixpkgs/nixos/modules/hardware/all-firmware.nix b/nixpkgs/nixos/modules/hardware/all-firmware.nix
index 3e88a4c20adc..bdf90816740c 100644
--- a/nixpkgs/nixos/modules/hardware/all-firmware.nix
+++ b/nixpkgs/nixos/modules/hardware/all-firmware.nix
@@ -35,6 +35,14 @@ in {
       '';
     };
 
+    hardware.wirelessRegulatoryDatabase = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Load the wireless regulatory database at boot.
+      '';
+    };
+
   };
 
 
@@ -50,14 +58,16 @@ in {
         rtl8723bs-firmware
         rtl8761b-firmware
         rtw88-firmware
+        rtw89-firmware
         zd1211fw
         alsa-firmware
         sof-firmware
-        openelec-dvb-firmware
+        libreelec-dvb-firmware
       ] ++ optional (pkgs.stdenv.hostPlatform.isAarch32 || pkgs.stdenv.hostPlatform.isAarch64) raspberrypiWirelessFirmware
         ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "4.13") [
         rtl8723bs-firmware
       ];
+      hardware.wirelessRegulatoryDatabase = true;
     })
     (mkIf cfg.enableAllFirmware {
       assertions = [{
@@ -75,5 +85,8 @@ in {
         b43FirmwareCutter
       ] ++ optional (pkgs.stdenv.hostPlatform.isi686 || pkgs.stdenv.hostPlatform.isx86_64) facetimehd-firmware;
     })
+    (mkIf cfg.wirelessRegulatoryDatabase {
+      hardware.firmware = [ pkgs.wireless-regdb ];
+    })
   ];
 }
diff --git a/nixpkgs/nixos/modules/hardware/onlykey.udev b/nixpkgs/nixos/modules/hardware/onlykey.udev
deleted file mode 100644
index 6583530e5684..000000000000
--- a/nixpkgs/nixos/modules/hardware/onlykey.udev
+++ /dev/null
@@ -1,4 +0,0 @@
-ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
-SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", GROUP+="plugdev"
-KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", GROUP+="plugdev"
diff --git a/nixpkgs/nixos/modules/hardware/onlykey.nix b/nixpkgs/nixos/modules/hardware/onlykey/default.nix
index 07358c8a8782..07358c8a8782 100644
--- a/nixpkgs/nixos/modules/hardware/onlykey.nix
+++ b/nixpkgs/nixos/modules/hardware/onlykey/default.nix
diff --git a/nixpkgs/nixos/modules/hardware/onlykey/onlykey.udev b/nixpkgs/nixos/modules/hardware/onlykey/onlykey.udev
new file mode 100644
index 000000000000..61e3ee4e8828
--- /dev/null
+++ b/nixpkgs/nixos/modules/hardware/onlykey/onlykey.udev
@@ -0,0 +1,18 @@
+# UDEV Rules for OnlyKey, https://docs.crp.to/linux.html
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", MODE:="0666"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", MODE:="0666"
+
+
+# The udev rules were updated upstream without an explanation as you can
+# see in [this comment][commit]. Assuming that hey have changed the
+# idVendor/idProduct, I've kept the old values.
+# TODO: Contact them upstream.
+#
+# [commit]: https://github.com/trustcrypto/trustcrypto.github.io/commit/0bcf928adaea559e75efa02ebd1040f0a15f611d
+#
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", GROUP+="plugdev"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", GROUP+="plugdev"
diff --git a/nixpkgs/nixos/modules/hardware/openrazer.nix b/nixpkgs/nixos/modules/hardware/openrazer.nix
index b5c3d6744142..bd9fc485e17e 100644
--- a/nixpkgs/nixos/modules/hardware/openrazer.nix
+++ b/nixpkgs/nixos/modules/hardware/openrazer.nix
@@ -49,7 +49,9 @@ in
 {
   options = {
     hardware.openrazer = {
-      enable = mkEnableOption "OpenRazer drivers and userspace daemon";
+      enable = mkEnableOption ''
+        OpenRazer drivers and userspace daemon.
+      '';
 
       verboseLogging = mkOption {
         type = types.bool;
@@ -92,6 +94,15 @@ in
           generate a heatmap.
         '';
       };
+
+      users = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Usernames to be added to the "openrazer" group, so that they
+          can start and interact with the OpenRazer userspace daemon.
+        '';
+      };
     };
   };
 
@@ -106,10 +117,12 @@ in
     services.udev.packages = [ kernelPackages.openrazer ];
     services.dbus.packages = [ dbusServiceFile ];
 
-    # A user must be a member of the plugdev group in order to start
-    # the openrazer-daemon. Therefore we make sure that the plugdev
-    # group exists.
-    users.groups.plugdev = {};
+    # A user must be a member of the openrazer group in order to start
+    # the openrazer-daemon. Therefore we make sure that the group
+    # exists.
+    users.groups.openrazer = {
+      members = cfg.users;
+    };
 
     systemd.user.services.openrazer-daemon = {
       description = "Daemon to manage razer devices in userspace";
diff --git a/nixpkgs/nixos/modules/hardware/sensor/iio.nix b/nixpkgs/nixos/modules/hardware/sensor/iio.nix
index 4c359c3b1725..8b3ba87a7d9c 100644
--- a/nixpkgs/nixos/modules/hardware/sensor/iio.nix
+++ b/nixpkgs/nixos/modules/hardware/sensor/iio.nix
@@ -9,7 +9,7 @@ with lib;
     hardware.sensor.iio = {
       enable = mkOption {
         description = ''
-          Enable this option to support IIO sensors.
+          Enable this option to support IIO sensors with iio-sensor-proxy.
 
           IIO sensors are used for orientation and ambient light
           sensors on some mobile devices.
diff --git a/nixpkgs/nixos/modules/hardware/video/hidpi.nix b/nixpkgs/nixos/modules/hardware/video/hidpi.nix
index ac72b652504e..c480cc481dfc 100644
--- a/nixpkgs/nixos/modules/hardware/video/hidpi.nix
+++ b/nixpkgs/nixos/modules/hardware/video/hidpi.nix
@@ -12,5 +12,6 @@ with lib;
     boot.loader.systemd-boot.consoleMode = mkDefault "1";
 
     # TODO Find reasonable defaults X11 & wayland
+    services.xserver.dpi = lib.mkDefault 192;
   };
 }
diff --git a/nixpkgs/nixos/modules/hardware/video/nvidia.nix b/nixpkgs/nixos/modules/hardware/video/nvidia.nix
index 2be9da8f42a1..cf87ca5377dd 100644
--- a/nixpkgs/nixos/modules/hardware/video/nvidia.nix
+++ b/nixpkgs/nixos/modules/hardware/video/nvidia.nix
@@ -23,6 +23,7 @@ let
   offloadCfg = pCfg.offload;
   primeEnabled = syncCfg.enable || offloadCfg.enable;
   nvidiaPersistencedEnabled =  cfg.nvidiaPersistenced;
+  nvidiaSettings = cfg.nvidiaSettings;
 in
 
 {
@@ -143,6 +144,15 @@ in
       '';
     };
 
+    hardware.nvidia.nvidiaSettings = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to add nvidia-settings, NVIDIA's GUI configuration tool, to
+        systemPackages.
+      '';
+    };
+
     hardware.nvidia.nvidiaPersistenced = mkOption {
       default = false;
       type = types.bool;
@@ -179,28 +189,41 @@ in
           You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor.
         '';
       }
+
       {
         assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
         message = ''
           When NVIDIA PRIME is enabled, the GPU bus IDs must configured.
         '';
       }
+
       {
         assertion = offloadCfg.enable -> versionAtLeast nvidia_x11.version "435.21";
         message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
       }
+
       {
         assertion = !(syncCfg.enable && offloadCfg.enable);
         message = "Only one NVIDIA PRIME solution may be used at a time.";
       }
+
       {
         assertion = !(syncCfg.enable && cfg.powerManagement.finegrained);
         message = "Sync precludes powering down the NVIDIA GPU.";
       }
+
       {
         assertion = cfg.powerManagement.enable -> offloadCfg.enable;
         message = "Fine-grained power management requires offload to be enabled.";
       }
+
+      {
+        assertion = cfg.powerManagement.enable -> (
+          builtins.pathExists (cfg.package.out + "/bin/nvidia-sleep.sh") &&
+          builtins.pathExists (cfg.package.out + "/lib/systemd/system-sleep/nvidia")
+        );
+        message = "Required files for driver based power management don't exist.";
+      }
     ];
 
     # If Optimus/PRIME is enabled, we:
@@ -266,7 +289,8 @@ in
     hardware.opengl.extraPackages = optional offloadCfg.enable nvidia_x11.out;
     hardware.opengl.extraPackages32 = optional offloadCfg.enable nvidia_x11.lib32;
 
-    environment.systemPackages = [ nvidia_x11.bin nvidia_x11.settings ]
+    environment.systemPackages = [ nvidia_x11.bin ]
+      ++ optionals nvidiaSettings [ nvidia_x11.settings ]
       ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ];
 
     systemd.packages = optional cfg.powerManagement.enable nvidia_x11.out;
diff --git a/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index aecb65b8c576..618057618d0c 100644
--- a/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -30,6 +30,11 @@ with lib;
   # Add Memtest86+ to the CD.
   boot.loader.grub.memtest86.enable = true;
 
+  # An installation media cannot tolerate a host config defined file
+  # system layout on a fresh machine, before it has been formatted.
+  swapDevices = mkImageMediaOverride [ ];
+  fileSystems = mkImageMediaOverride config.lib.isoFileSystems;
+
   boot.postBootCommands = ''
     for o in $(</proc/cmdline); do
       case "$o" in
diff --git a/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix b/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix
index d94af0b5bf74..78cbf14bbaf6 100644
--- a/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixpkgs/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -615,53 +615,19 @@ in
 
   };
 
-  config = {
-    assertions = [
+  # store them in lib so we can mkImageMediaOverride the
+  # entire file system layout in installation media (only)
+  config.lib.isoFileSystems = {
+    "/" = mkImageMediaOverride
       {
-        assertion = !(stringLength config.isoImage.volumeID > 32);
-        # https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor
-        # Volume Identifier can only be 32 bytes
-        message = let
-          length = stringLength config.isoImage.volumeID;
-          howmany = toString length;
-          toomany = toString (length - 32);
-        in
-        "isoImage.volumeID ${config.isoImage.volumeID} is ${howmany} characters. That is ${toomany} characters longer than the limit of 32.";
-      }
-    ];
-
-    boot.loader.grub.version = 2;
-
-    # Don't build the GRUB menu builder script, since we don't need it
-    # here and it causes a cyclic dependency.
-    boot.loader.grub.enable = false;
-
-    environment.systemPackages =  [ grubPkgs.grub2 grubPkgs.grub2_efi ]
-      ++ optional canx86BiosBoot pkgs.syslinux
-    ;
-
-    # In stage 1 of the boot, mount the CD as the root FS by label so
-    # that we don't need to know its device.  We pass the label of the
-    # root filesystem on the kernel command line, rather than in
-    # `fileSystems' below.  This allows CD-to-USB converters such as
-    # UNetbootin to rewrite the kernel command line to pass the label or
-    # UUID of the USB stick.  It would be nicer to write
-    # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't
-    # recognise that.
-    boot.kernelParams =
-      [ "root=LABEL=${config.isoImage.volumeID}"
-        "boot.shell_on_fail"
-      ];
-
-    fileSystems."/" =
-      { fsType = "tmpfs";
+        fsType = "tmpfs";
         options = [ "mode=0755" ];
       };
 
     # Note that /dev/root is a symlink to the actual root device
     # specified on the kernel command line, created in the stage 1
     # init script.
-    fileSystems."/iso" =
+    "/iso" = mkImageMediaOverride
       { device = "/dev/root";
         neededForBoot = true;
         noCheck = true;
@@ -669,20 +635,20 @@ in
 
     # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
     # image) to make this a live CD.
-    fileSystems."/nix/.ro-store" =
+    "/nix/.ro-store" = mkImageMediaOverride
       { fsType = "squashfs";
         device = "/iso/nix-store.squashfs";
         options = [ "loop" ];
         neededForBoot = true;
       };
 
-    fileSystems."/nix/.rw-store" =
+    "/nix/.rw-store" = mkImageMediaOverride
       { fsType = "tmpfs";
         options = [ "mode=0755" ];
         neededForBoot = true;
       };
 
-    fileSystems."/nix/store" =
+    "/nix/store" = mkImageMediaOverride
       { fsType = "overlay";
         device = "overlay";
         options = [
@@ -690,13 +656,53 @@ in
           "upperdir=/nix/.rw-store/store"
           "workdir=/nix/.rw-store/work"
         ];
-
         depends = [
           "/nix/.ro-store"
           "/nix/.rw-store/store"
           "/nix/.rw-store/work"
         ];
       };
+  };
+
+  config = {
+    assertions = [
+      {
+        assertion = !(stringLength config.isoImage.volumeID > 32);
+        # https://wiki.osdev.org/ISO_9660#The_Primary_Volume_Descriptor
+        # Volume Identifier can only be 32 bytes
+        message = let
+          length = stringLength config.isoImage.volumeID;
+          howmany = toString length;
+          toomany = toString (length - 32);
+        in
+        "isoImage.volumeID ${config.isoImage.volumeID} is ${howmany} characters. That is ${toomany} characters longer than the limit of 32.";
+      }
+    ];
+
+    boot.loader.grub.version = 2;
+
+    # Don't build the GRUB menu builder script, since we don't need it
+    # here and it causes a cyclic dependency.
+    boot.loader.grub.enable = false;
+
+    environment.systemPackages =  [ grubPkgs.grub2 grubPkgs.grub2_efi ]
+      ++ optional canx86BiosBoot pkgs.syslinux
+    ;
+
+    # In stage 1 of the boot, mount the CD as the root FS by label so
+    # that we don't need to know its device.  We pass the label of the
+    # root filesystem on the kernel command line, rather than in
+    # `fileSystems' below.  This allows CD-to-USB converters such as
+    # UNetbootin to rewrite the kernel command line to pass the label or
+    # UUID of the USB stick.  It would be nicer to write
+    # `root=/dev/disk/by-label/...' here, but UNetbootin doesn't
+    # recognise that.
+    boot.kernelParams =
+      [ "root=LABEL=${config.isoImage.volumeID}"
+        "boot.shell_on_fail"
+      ];
+
+    fileSystems = config.lib.isoFileSystems;
 
     boot.initrd.availableKernelModules = [ "squashfs" "iso9660" "uas" "overlay" ];
 
diff --git a/nixpkgs/nixos/modules/installer/netboot/netboot.nix b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
index 238ab6d0617b..28b6c39b29df 100644
--- a/nixpkgs/nixos/modules/installer/netboot/netboot.nix
+++ b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
@@ -29,27 +29,27 @@ with lib;
           then []
           else [ pkgs.grub2 pkgs.syslinux ]);
 
-    fileSystems."/" =
+    fileSystems."/" = mkImageMediaOverride
       { fsType = "tmpfs";
         options = [ "mode=0755" ];
       };
 
     # In stage 1, mount a tmpfs on top of /nix/store (the squashfs
     # image) to make this a live CD.
-    fileSystems."/nix/.ro-store" =
+    fileSystems."/nix/.ro-store" = mkImageMediaOverride
       { fsType = "squashfs";
         device = "../nix-store.squashfs";
         options = [ "loop" ];
         neededForBoot = true;
       };
 
-    fileSystems."/nix/.rw-store" =
+    fileSystems."/nix/.rw-store" = mkImageMediaOverride
       { fsType = "tmpfs";
         options = [ "mode=0755" ];
         neededForBoot = true;
       };
 
-    fileSystems."/nix/store" =
+    fileSystems."/nix/store" = mkImageMediaOverride
       { fsType = "overlay";
         device = "overlay";
         options = [
diff --git a/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix
index e3576074a5b7..15c76287e34e 100644
--- a/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixpkgs/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/qsgz2hhn6mzlzp53a7pwf9z2pq3l5z6h-nix-2.3.14";
-  i686-linux = "/nix/store/1yw40bj04lykisw2jilq06lir3k9ga4a-nix-2.3.14";
-  aarch64-linux = "/nix/store/32yzwmynmjxfrkb6y6l55liaqdrgkj4a-nix-2.3.14";
-  x86_64-darwin = "/nix/store/06j0vi2d13w4l0p3jsigq7lk4x6gkycj-nix-2.3.14";
-  aarch64-darwin = "/nix/store/77wi7vpbrghw5rgws25w30bwb8yggnk9-nix-2.3.14";
+  x86_64-linux = "/nix/store/jhbxh1jwjc3hjhzs9y2hifdn0rmnfwaj-nix-2.3.15";
+  i686-linux = "/nix/store/9pspwnkdrgzma1l4xlv7arhwa56y16di-nix-2.3.15";
+  aarch64-linux = "/nix/store/72aqi5g7f4fhgvgafbcqwcpqjgnczj48-nix-2.3.15";
+  x86_64-darwin = "/nix/store/6p6qwp73dgfkqhynmxrzbx1lcfgfpqal-nix-2.3.15";
+  aarch64-darwin = "/nix/store/dmq2vksdhssgfl822shd0ky3x5x0klh4-nix-2.3.15";
 }
diff --git a/nixpkgs/nixos/modules/misc/crashdump.nix b/nixpkgs/nixos/modules/misc/crashdump.nix
index 796078d7ef8c..b0f75d9caaa3 100644
--- a/nixpkgs/nixos/modules/misc/crashdump.nix
+++ b/nixpkgs/nixos/modules/misc/crashdump.nix
@@ -50,7 +50,7 @@ in
     boot = {
       postBootCommands = ''
         echo "loading crashdump kernel...";
-        ${pkgs.kexectools}/sbin/kexec -p /run/current-system/kernel \
+        ${pkgs.kexec-tools}/sbin/kexec -p /run/current-system/kernel \
         --initrd=/run/current-system/initrd \
         --reset-vga --console-vga \
         --command-line="init=$(readlink -f /run/current-system/init) irqpoll maxcpus=1 reset_devices ${kernelParams}"
diff --git a/nixpkgs/nixos/modules/misc/documentation.nix b/nixpkgs/nixos/modules/misc/documentation.nix
index 13f8007cd626..f00d58369a72 100644
--- a/nixpkgs/nixos/modules/misc/documentation.nix
+++ b/nixpkgs/nixos/modules/misc/documentation.nix
@@ -269,8 +269,7 @@ in
 
       environment.systemPackages = []
         ++ optional cfg.man.enable manual.manpages
-        ++ optionals cfg.doc.enable ([ manual.manualHTML nixos-help ]
-           ++ optionals config.services.xserver.enable [ pkgs.nixos-icons ]);
+        ++ optionals cfg.doc.enable [ manual.manualHTML nixos-help ];
 
       services.getty.helpLine = mkIf cfg.doc.enable (
           "\nRun 'nixos-help' for the NixOS manual."
diff --git a/nixpkgs/nixos/modules/misc/ids.nix b/nixpkgs/nixos/modules/misc/ids.nix
index fe6e563d7ee5..93ea73f303cb 100644
--- a/nixpkgs/nixos/modules/misc/ids.nix
+++ b/nixpkgs/nixos/modules/misc/ids.nix
@@ -178,7 +178,7 @@ in
       radvd = 139;
       zookeeper = 140;
       dnsmasq = 141;
-      uhub = 142;
+      #uhub = 142; # unused
       yandexdisk = 143;
       mxisd = 144; # was once collectd
       consul = 145;
@@ -187,6 +187,7 @@ in
       #seeks = 148; # removed 2020-06-21
       prosody = 149;
       i2pd = 150;
+      systemd-coredump = 151;
       systemd-network = 152;
       systemd-resolve = 153;
       systemd-timesync = 154;
@@ -347,6 +348,9 @@ in
       #mailman = 316;  # removed 2019-08-30
       zigbee2mqtt = 317;
       # shadow = 318; # unused
+      hqplayer = 319;
+      moonraker = 320;
+      distcc = 321;
 
       # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
 
@@ -649,6 +653,9 @@ in
       #mailman = 316;  # removed 2019-08-30
       zigbee2mqtt = 317;
       shadow = 318;
+      hqplayer = 319;
+      moonraker = 320;
+      distcc = 321;
 
       # When adding a gid, make sure it doesn't match an existing
       # uid. Users and groups with the same name should have equal
diff --git a/nixpkgs/nixos/modules/misc/nixpkgs.nix b/nixpkgs/nixos/modules/misc/nixpkgs.nix
index 8160bfef4a3c..a2ac5c58528a 100644
--- a/nixpkgs/nixos/modules/misc/nixpkgs.nix
+++ b/nixpkgs/nixos/modules/misc/nixpkgs.nix
@@ -39,7 +39,7 @@ let
             if c x then true
             else lib.traceSeqN 1 x false;
       in traceXIfNot isConfig;
-    merge = args: fold (def: mergeConfig def.value) {};
+    merge = args: foldr (def: mergeConfig def.value) {};
   };
 
   overlayType = mkOptionType {
diff --git a/nixpkgs/nixos/modules/misc/version.nix b/nixpkgs/nixos/modules/misc/version.nix
index a6fffb76f6e8..8f246a9278b7 100644
--- a/nixpkgs/nixos/modules/misc/version.nix
+++ b/nixpkgs/nixos/modules/misc/version.nix
@@ -103,9 +103,10 @@ in
       ''
         NAME=NixOS
         ID=nixos
-        VERSION="${cfg.version} (${cfg.codeName})"
+        VERSION="${cfg.release} (${cfg.codeName})"
         VERSION_CODENAME=${toLower cfg.codeName}
-        VERSION_ID="${cfg.version}"
+        VERSION_ID="${cfg.release}"
+        BUILD_ID="${cfg.version}"
         PRETTY_NAME="NixOS ${cfg.release} (${cfg.codeName})"
         LOGO="nix-snowflake"
         HOME_URL="https://nixos.org/"
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index 0ae556cea138..b8a7c35626e7 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -72,7 +72,7 @@
   ./hardware/tuxedo-keyboard.nix
   ./hardware/ubertooth.nix
   ./hardware/usb-wwan.nix
-  ./hardware/onlykey.nix
+  ./hardware/onlykey/default.nix
   ./hardware/opentabletdriver.nix
   ./hardware/sata.nix
   ./hardware/wooting.nix
@@ -121,6 +121,7 @@
   ./programs/bash-my-aws.nix
   ./programs/bcc.nix
   ./programs/browserpass.nix
+  ./programs/calls.nix
   ./programs/captive-browser.nix
   ./programs/ccache.nix
   ./programs/cdemu.nix
@@ -236,6 +237,7 @@
   ./security/doas.nix
   ./security/systemd-confinement.nix
   ./security/tpm2.nix
+  ./services/admin/meshcentral.nix
   ./services/admin/oxidized.nix
   ./services/admin/salt/master.nix
   ./services/admin/salt/minion.nix
@@ -243,13 +245,17 @@
   ./services/amqp/rabbitmq.nix
   ./services/audio/alsa.nix
   ./services/audio/botamusique.nix
-  ./services/audio/jack.nix
+  ./services/audio/hqplayerd.nix
   ./services/audio/icecast.nix
+  ./services/audio/jack.nix
   ./services/audio/jmusicbot.nix
   ./services/audio/liquidsoap.nix
   ./services/audio/mpd.nix
   ./services/audio/mpdscribble.nix
   ./services/audio/mopidy.nix
+  ./services/audio/networkaudiod.nix
+  ./services/audio/roon-bridge.nix
+  ./services/audio/navidrome.nix
   ./services/audio/roon-server.nix
   ./services/audio/slimserver.nix
   ./services/audio/snapserver.nix
@@ -317,6 +323,7 @@
   ./services/databases/foundationdb.nix
   ./services/databases/hbase.nix
   ./services/databases/influxdb.nix
+  ./services/databases/influxdb2.nix
   ./services/databases/memcached.nix
   ./services/databases/monetdb.nix
   ./services/databases/mongodb.nix
@@ -367,6 +374,7 @@
   ./services/desktops/zeitgeist.nix
   ./services/development/bloop.nix
   ./services/development/blackfire.nix
+  ./services/development/distccd.nix
   ./services/development/hoogle.nix
   ./services/development/jupyter/default.nix
   ./services/development/jupyterhub/default.nix
@@ -455,6 +463,7 @@
   ./services/mail/opensmtpd.nix
   ./services/mail/pfix-srsd.nix
   ./services/mail/postfix.nix
+  ./services/mail/postfixadmin.nix
   ./services/mail/postsrsd.nix
   ./services/mail/postgrey.nix
   ./services/mail/public-inbox.nix
@@ -521,6 +530,7 @@
   ./services/misc/logkeys.nix
   ./services/misc/leaps.nix
   ./services/misc/lidarr.nix
+  ./services/misc/libreddit.nix
   ./services/misc/lifecycled.nix
   ./services/misc/mame.nix
   ./services/misc/matrix-appservice-discord.nix
@@ -530,8 +540,11 @@
   ./services/misc/mbpfan.nix
   ./services/misc/mediatomb.nix
   ./services/misc/metabase.nix
+  ./services/misc/moonraker.nix
   ./services/misc/mwlib.nix
+  ./services/misc/mx-puppet-discord.nix
   ./services/misc/n8n.nix
+  ./services/misc/nitter.nix
   ./services/misc/nix-daemon.nix
   ./services/misc/nix-gc.nix
   ./services/misc/nix-optimise.nix
@@ -543,7 +556,7 @@
   ./services/misc/ombi.nix
   ./services/misc/osrm.nix
   ./services/misc/packagekit.nix
-  ./services/misc/paperless.nix
+  ./services/misc/paperless-ng.nix
   ./services/misc/parsoid.nix
   ./services/misc/plex.nix
   ./services/misc/plikd.nix
@@ -635,6 +648,7 @@
   ./services/network-filesystems/glusterfs.nix
   ./services/network-filesystems/kbfs.nix
   ./services/network-filesystems/ipfs.nix
+  ./services/network-filesystems/litestream/default.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
   ./services/network-filesystems/openafs/client.nix
@@ -758,6 +772,7 @@
   ./services/networking/namecoind.nix
   ./services/networking/nar-serve.nix
   ./services/networking/nat.nix
+  ./services/networking/nats.nix
   ./services/networking/ndppd.nix
   ./services/networking/nebula.nix
   ./services/networking/networkmanager.nix
@@ -821,6 +836,7 @@
   ./services/networking/smokeping.nix
   ./services/networking/softether.nix
   ./services/networking/solanum.nix
+  ./services/networking/soju.nix
   ./services/networking/spacecookie.nix
   ./services/networking/spiped.nix
   ./services/networking/squid.nix
@@ -931,6 +947,7 @@
   ./services/wayland/cage.nix
   ./services/video/epgstation/default.nix
   ./services/video/mirakurun.nix
+  ./services/video/replay-sorcery.nix
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
@@ -942,6 +959,7 @@
   ./services/web-apps/documize.nix
   ./services/web-apps/dokuwiki.nix
   ./services/web-apps/engelsystem.nix
+  ./services/web-apps/fluidd.nix
   ./services/web-apps/galene.nix
   ./services/web-apps/gerrit.nix
   ./services/web-apps/gotify-server.nix
@@ -951,6 +969,7 @@
   ./services/web-apps/icingaweb2/icingaweb2.nix
   ./services/web-apps/icingaweb2/module-monitoring.nix
   ./services/web-apps/ihatemoney
+  ./services/web-apps/isso.nix
   ./services/web-apps/jirafeau.nix
   ./services/web-apps/jitsi-meet.nix
   ./services/web-apps/keycloak.nix
@@ -962,6 +981,7 @@
   ./services/web-apps/moodle.nix
   ./services/web-apps/nextcloud.nix
   ./services/web-apps/nexus.nix
+  ./services/web-apps/node-red.nix
   ./services/web-apps/plantuml-server.nix
   ./services/web-apps/plausible.nix
   ./services/web-apps/pgpkeyserver-lite.nix
@@ -983,7 +1003,7 @@
   ./services/web-apps/youtrack.nix
   ./services/web-apps/zabbix.nix
   ./services/web-servers/apache-httpd/default.nix
-  ./services/web-servers/caddy.nix
+  ./services/web-servers/caddy/default.nix
   ./services/web-servers/darkhttpd.nix
   ./services/web-servers/fcgiwrap.nix
   ./services/web-servers/hitch/default.nix
@@ -1022,6 +1042,7 @@
   ./services/x11/display-managers/sddm.nix
   ./services/x11/display-managers/slim.nix
   ./services/x11/display-managers/startx.nix
+  ./services/x11/display-managers/sx.nix
   ./services/x11/display-managers/xpra.nix
   ./services/x11/fractalart.nix
   ./services/x11/hardware/libinput.nix
diff --git a/nixpkgs/nixos/modules/profiles/headless.nix b/nixpkgs/nixos/modules/profiles/headless.nix
index 46a9b6a7d8d5..c17cb287b72b 100644
--- a/nixpkgs/nixos/modules/profiles/headless.nix
+++ b/nixpkgs/nixos/modules/profiles/headless.nix
@@ -9,7 +9,7 @@ with lib;
   boot.vesa = false;
 
   # Don't start a tty on the serial consoles.
-  systemd.services."serial-getty@ttyS0".enable = false;
+  systemd.services."serial-getty@ttyS0".enable = lib.mkDefault false;
   systemd.services."serial-getty@hvc0".enable = false;
   systemd.services."getty@tty1".enable = false;
   systemd.services."autovt@".enable = false;
diff --git a/nixpkgs/nixos/modules/profiles/installation-device.nix b/nixpkgs/nixos/modules/profiles/installation-device.nix
index 8e3aa20daa65..3c503fba2a39 100644
--- a/nixpkgs/nixos/modules/profiles/installation-device.nix
+++ b/nixpkgs/nixos/modules/profiles/installation-device.nix
@@ -54,7 +54,12 @@ with lib;
       An ssh daemon is running. You then must set a password
       for either "root" or "nixos" with `passwd` or add an ssh key
       to /home/nixos/.ssh/authorized_keys be able to login.
+
+      If you need a wireless connection, type
+      `sudo systemctl start wpa_supplicant` and configure a
+      network using `wpa_cli`. See the NixOS manual for details.
     '' + optionalString config.services.xserver.enable ''
+
       Type `sudo systemctl start display-manager' to
       start the graphical user interface.
     '';
@@ -71,6 +76,7 @@ with lib;
 
     # Enable wpa_supplicant, but don't start it by default.
     networking.wireless.enable = mkDefault true;
+    networking.wireless.userControlled.enable = true;
     systemd.services.wpa_supplicant.wantedBy = mkOverride 50 [];
 
     # Tell the Nix evaluator to garbage collect more aggressively.
diff --git a/nixpkgs/nixos/modules/programs/calls.nix b/nixpkgs/nixos/modules/programs/calls.nix
new file mode 100644
index 000000000000..59961625e5d9
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/calls.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.calls;
+in {
+  options = {
+    programs.calls = {
+      enable = mkEnableOption ''
+        Whether to enable GNOME calls: a phone dialer and call handler.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.calls
+    ];
+
+    services.dbus.packages = [
+      pkgs.callaudiod
+    ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/programs/captive-browser.nix b/nixpkgs/nixos/modules/programs/captive-browser.nix
index 1f223e2475ce..d7684d08c6c7 100644
--- a/nixpkgs/nixos/modules/programs/captive-browser.nix
+++ b/nixpkgs/nixos/modules/programs/captive-browser.nix
@@ -27,6 +27,7 @@ in
       browser = mkOption {
         type = types.str;
         default = concatStringsSep " " [
+          ''env XDG_CONFIG_HOME="$PREV_CONFIG_HOME"''
           ''${pkgs.chromium}/bin/chromium''
           ''--user-data-dir=''${XDG_DATA_HOME:-$HOME/.local/share}/chromium-captive''
           ''--proxy-server="socks5://$PROXY"''
@@ -84,18 +85,18 @@ in
 
     programs.captive-browser.dhcp-dns =
       let
-        iface = prefix:
-          optionalString cfg.bindInterface (concatStringsSep " " (map escapeShellArg [ prefix cfg.interface ]));
+        iface = prefixes:
+          optionalString cfg.bindInterface (escapeShellArgs (prefixes ++ [ cfg.interface ]));
       in
       mkOptionDefault (
         if config.networking.networkmanager.enable then
-          "${pkgs.networkmanager}/bin/nmcli dev show ${iface ""} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS"
+          "${pkgs.networkmanager}/bin/nmcli dev show ${iface []} | ${pkgs.gnugrep}/bin/fgrep IP4.DNS"
         else if config.networking.dhcpcd.enable then
-          "${pkgs.dhcpcd}/bin/dhcpcd ${iface "-U"} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers"
+          "${pkgs.dhcpcd}/bin/dhcpcd ${iface ["-U"]} | ${pkgs.gnugrep}/bin/fgrep domain_name_servers"
         else if config.networking.useNetworkd then
-          "${cfg.package}/bin/systemd-networkd-dns ${iface ""}"
+          "${cfg.package}/bin/systemd-networkd-dns ${iface []}"
         else
-          "${config.security.wrapperDir}/udhcpc --quit --now -f ${iface "-i"} -O dns --script ${
+          "${config.security.wrapperDir}/udhcpc --quit --now -f ${iface ["-i"]} -O dns --script ${
           pkgs.writeShellScript "udhcp-script" ''
             if [ "$1" = bound ]; then
               echo "$dns"
@@ -111,6 +112,7 @@ in
     security.wrappers.captive-browser = {
       capabilities = "cap_net_raw+p";
       source = pkgs.writeShellScript "captive-browser" ''
+        export PREV_CONFIG_HOME="$XDG_CONFIG_HOME"
         export XDG_CONFIG_HOME=${pkgs.writeTextDir "captive-browser.toml" ''
                                   browser = """${cfg.browser}"""
                                   dhcp-dns = """${cfg.dhcp-dns}"""
diff --git a/nixpkgs/nixos/modules/programs/neovim.nix b/nixpkgs/nixos/modules/programs/neovim.nix
index 0a1a2ac2b752..781c31d2b0ce 100644
--- a/nixpkgs/nixos/modules/programs/neovim.nix
+++ b/nixpkgs/nixos/modules/programs/neovim.nix
@@ -7,18 +7,7 @@ let
 
   runtime' = filter (f: f.enable) (attrValues cfg.runtime);
 
-  # taken from the etc module
-  runtime = pkgs.stdenvNoCC.mkDerivation {
-    name = "runtime";
-
-    builder = ../system/etc/make-etc.sh;
-
-    preferLocalBuild = true;
-    allowSubstitutes = false;
-
-    sources = map (x: x.source) runtime';
-    targets = map (x: x.target) runtime';
-  };
+  runtime = pkgs.linkFarm "neovim-runtime" (map (x: { name = x.target; path = x.source; }) runtime');
 
 in {
   options.programs.neovim = {
diff --git a/nixpkgs/nixos/modules/programs/ssh.nix b/nixpkgs/nixos/modules/programs/ssh.nix
index d4a7769bbd6d..795f1a9f7b44 100644
--- a/nixpkgs/nixos/modules/programs/ssh.nix
+++ b/nixpkgs/nixos/modules/programs/ssh.nix
@@ -14,7 +14,7 @@ let
     ''
       #! ${pkgs.runtimeShell} -e
       export DISPLAY="$(systemctl --user show-environment | ${pkgs.gnused}/bin/sed 's/^DISPLAY=\(.*\)/\1/; t; d')"
-      exec ${askPassword}
+      exec ${askPassword} "$@"
     '';
 
   knownHosts = map (h: getAttr h cfg.knownHosts) (attrNames cfg.knownHosts);
diff --git a/nixpkgs/nixos/modules/programs/udevil.nix b/nixpkgs/nixos/modules/programs/udevil.nix
index 25975d88ec86..ba5670f9dfe9 100644
--- a/nixpkgs/nixos/modules/programs/udevil.nix
+++ b/nixpkgs/nixos/modules/programs/udevil.nix
@@ -10,8 +10,5 @@ in {
 
   config = mkIf cfg.enable {
     security.wrappers.udevil.source = "${lib.getBin pkgs.udevil}/bin/udevil";
-
-    systemd.packages = [ pkgs.udevil ];
-    systemd.services."devmon@".wantedBy = [ "multi-user.target" ];
   };
 }
diff --git a/nixpkgs/nixos/modules/programs/zsh/zsh.nix b/nixpkgs/nixos/modules/programs/zsh/zsh.nix
index 6c824a692b75..dc6c958ca88b 100644
--- a/nixpkgs/nixos/modules/programs/zsh/zsh.nix
+++ b/nixpkgs/nixos/modules/programs/zsh/zsh.nix
@@ -278,7 +278,10 @@ in
         fi
       '';
 
-    environment.etc.zinputrc.source = ./zinputrc;
+    # Bug in nix flakes:
+    # If we use `.source` here the path is garbage collected also we point to it with a symlink
+    # see https://github.com/NixOS/nixpkgs/issues/132732
+    environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
 
     environment.systemPackages =
       let
diff --git a/nixpkgs/nixos/modules/security/acme.nix b/nixpkgs/nixos/modules/security/acme.nix
index 22bf34198a30..bcbd17d8e10e 100644
--- a/nixpkgs/nixos/modules/security/acme.nix
+++ b/nixpkgs/nixos/modules/security/acme.nix
@@ -21,15 +21,51 @@ let
   # The Group can vary depending on what the user has specified in
   # security.acme.certs.<cert>.group on some of the services.
   commonServiceConfig = {
-      Type = "oneshot";
-      User = "acme";
-      Group = mkDefault "acme";
-      UMask = 0022;
-      StateDirectoryMode = 750;
-      ProtectSystem = "full";
-      PrivateTmp = true;
-
-      WorkingDirectory = "/tmp";
+    Type = "oneshot";
+    User = "acme";
+    Group = mkDefault "acme";
+    UMask = 0022;
+    StateDirectoryMode = 750;
+    ProtectSystem = "strict";
+    ReadWritePaths = [
+      "/var/lib/acme"
+    ];
+    PrivateTmp = true;
+
+    WorkingDirectory = "/tmp";
+
+    CapabilityBoundingSet = [ "" ];
+    DevicePolicy = "closed";
+    LockPersonality = true;
+    MemoryDenyWriteExecute = true;
+    NoNewPrivileges = true;
+    PrivateDevices = true;
+    ProtectClock = true;
+    ProtectHome = true;
+    ProtectHostname = true;
+    ProtectControlGroups = true;
+    ProtectKernelLogs = true;
+    ProtectKernelModules = true;
+    ProtectKernelTunables = true;
+    ProtectProc = "invisible";
+    ProcSubset = "pid";
+    RemoveIPC = true;
+    RestrictAddressFamilies = [
+      "AF_INET"
+      "AF_INET6"
+    ];
+    RestrictNamespaces = true;
+    RestrictRealtime = true;
+    RestrictSUIDSGID = true;
+    SystemCallArchitectures = "native";
+    SystemCallFilter = [
+      # 1. allow a reasonable set of syscalls
+      "@system-service"
+      # 2. and deny unreasonable ones
+      "~@privileged @resources"
+      # 3. then allow the required subset within denied groups
+      "@chown"
+    ];
   };
 
   # In order to avoid race conditions creating the CA for selfsigned certs,
diff --git a/nixpkgs/nixos/modules/security/apparmor/includes.nix b/nixpkgs/nixos/modules/security/apparmor/includes.nix
index e3dd410b3bb5..f290e95a296d 100644
--- a/nixpkgs/nixos/modules/security/apparmor/includes.nix
+++ b/nixpkgs/nixos/modules/security/apparmor/includes.nix
@@ -95,7 +95,7 @@ config.security.apparmor.includes = {
      include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles"
   '';
   "abstractions/cups-client" = ''
-    include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cpus-client"
+    include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cups-client"
     ${etcRule "cups/cups-client.conf"}
   '';
   "abstractions/dbus-session-strict" = ''
diff --git a/nixpkgs/nixos/modules/security/pam.nix b/nixpkgs/nixos/modules/security/pam.nix
index 5699025601f5..163d75d7caf2 100644
--- a/nixpkgs/nixos/modules/security/pam.nix
+++ b/nixpkgs/nixos/modules/security/pam.nix
@@ -406,7 +406,7 @@ let
           ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
               "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
           ${let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth
-              "auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}"}
+              "auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}"}
           ${optionalString cfg.fprintAuth
               "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"}
         '' +
@@ -475,7 +475,7 @@ let
 
           # Session management.
           ${optionalString cfg.setEnvironment ''
-            session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+            session required pam_env.so conffile=/etc/pam/environment readenv=0
           ''}
           session required pam_unix.so
           ${optionalString cfg.setLoginUid
@@ -826,6 +826,16 @@ in
           xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
         '';
       };
+      challengeResponsePath = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          If not null, set the path used by yubico pam module where the challenge expected response is stored.
+
+          More information can be found <link
+          xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
+        '';
+      };
     };
 
     security.pam.enableEcryptfs = mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
diff --git a/nixpkgs/nixos/modules/security/polkit.nix b/nixpkgs/nixos/modules/security/polkit.nix
index a6724bd75832..f556cca23cdc 100644
--- a/nixpkgs/nixos/modules/security/polkit.nix
+++ b/nixpkgs/nixos/modules/security/polkit.nix
@@ -96,8 +96,10 @@ in
     users.users.polkituser = {
       description = "PolKit daemon";
       uid = config.ids.uids.polkituser;
+      group = "polkituser";
     };
 
+    users.groups.polkituser = {};
   };
 
 }
diff --git a/nixpkgs/nixos/modules/services/admin/meshcentral.nix b/nixpkgs/nixos/modules/services/admin/meshcentral.nix
new file mode 100644
index 000000000000..ae7b6edda7d5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/admin/meshcentral.nix
@@ -0,0 +1,53 @@
+{ config, pkgs, lib, ... }:
+let
+  cfg = config.services.meshcentral;
+  configFormat = pkgs.formats.json {};
+  configFile = configFormat.generate "meshcentral-config.json" cfg.settings;
+in with lib; {
+  options.services.meshcentral = with types; {
+    enable = mkEnableOption "MeshCentral computer management server";
+    package = mkOption {
+      description = "MeshCentral package to use. Replacing this may be necessary to add dependencies for extra functionality.";
+      type = types.package;
+      default = pkgs.meshcentral;
+      defaultText = "pkgs.meshcentral";
+    };
+    settings = mkOption {
+      description = ''
+        Settings for MeshCentral. Refer to upstream documentation for details:
+
+        <itemizedlist>
+          <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/meshcentral-config-schema.json">JSON Schema definition</link></para></listitem>
+          <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config.json">simple sample configuration</link></para></listitem>
+          <listitem><para><link xlink:href="https://github.com/Ylianst/MeshCentral/blob/master/sample-config-advanced.json">complex sample configuration</link></para></listitem>
+          <listitem><para><link xlink:href="https://www.meshcommander.com/meshcentral2">Old homepage) with documentation link</link></para></listitem>
+        </itemizedlist>
+      '';
+      type = types.submodule {
+        freeformType = configFormat.type;
+      };
+      example = {
+        settings = {
+          WANonly = true;
+          Cert = "meshcentral.example.com";
+          TlsOffload = "10.0.0.2,fd42::2";
+          Port = 4430;
+        };
+        domains."".certUrl = "https://meshcentral.example.com/";
+      };
+    };
+  };
+  config = mkIf cfg.enable {
+    services.meshcentral.settings.settings.autoBackup.backupPath = lib.mkDefault "/var/lib/meshcentral/backups";
+    systemd.services.meshcentral = {
+      wantedBy = ["multi-user.target"];
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/meshcentral --datapath /var/lib/meshcentral --configfile ${configFile}";
+        DynamicUser = true;
+        StateDirectory = "meshcentral";
+        CacheDirectory = "meshcentral";
+      };
+    };
+  };
+  meta.maintainers = [ maintainers.lheckemann ];
+}
diff --git a/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix b/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix
index fc8a1bc3c23c..8fdfda9a66d8 100644
--- a/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix
+++ b/nixpkgs/nixos/modules/services/amqp/rabbitmq.nix
@@ -7,12 +7,13 @@ let
 
   inherit (builtins) concatStringsSep;
 
-  config_file_content = lib.generators.toKeyValue {} cfg.configItems;
+  config_file_content = lib.generators.toKeyValue { } cfg.configItems;
   config_file = pkgs.writeText "rabbitmq.conf" config_file_content;
 
   advanced_config_file = pkgs.writeText "advanced.config" cfg.config;
 
-in {
+in
+{
   ###### interface
   options = {
     services.rabbitmq = {
@@ -79,7 +80,7 @@ in {
       };
 
       configItems = mkOption {
-        default = {};
+        default = { };
         type = types.attrsOf types.str;
         example = literalExample ''
           {
@@ -123,16 +124,38 @@ in {
       };
 
       plugins = mkOption {
-        default = [];
+        default = [ ];
         type = types.listOf types.str;
         description = "The names of plugins to enable";
       };
 
       pluginDirs = mkOption {
-        default = [];
+        default = [ ];
         type = types.listOf types.path;
         description = "The list of directories containing external plugins";
       };
+
+      managementPlugin = mkOption {
+        description = "The options to run the management plugin";
+        type = types.submodule {
+          options = {
+            enable = mkOption {
+              default = false;
+              type = types.bool;
+              description = ''
+                Whether to enable the management plugin
+              '';
+            };
+            port = mkOption {
+              default = 15672;
+              type = types.port;
+              description = ''
+                On which port to run the management plugin
+              '';
+            };
+          };
+        };
+      };
     };
   };
 
@@ -157,8 +180,13 @@ in {
 
     services.rabbitmq.configItems = {
       "listeners.tcp.1" = mkDefault "${cfg.listenAddress}:${toString cfg.port}";
+    } // optionalAttrs cfg.managementPlugin.enable {
+      "management.tcp.port" = toString cfg.managementPlugin.port;
+      "management.tcp.ip" = cfg.listenAddress;
     };
 
+    services.rabbitmq.plugins = optional cfg.managementPlugin.enable "rabbitmq_management";
+
     systemd.services.rabbitmq = {
       description = "RabbitMQ Server";
 
@@ -180,7 +208,7 @@ in {
         RABBITMQ_ENABLED_PLUGINS_FILE = pkgs.writeText "enabled_plugins" ''
           [ ${concatStringsSep "," cfg.plugins} ].
         '';
-      } //  optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
+      } // optionalAttrs (cfg.config != "") { RABBITMQ_ADVANCED_CONFIG_FILE = advanced_config_file; };
 
       serviceConfig = {
         ExecStart = "${cfg.package}/sbin/rabbitmq-server";
diff --git a/nixpkgs/nixos/modules/services/audio/hqplayerd.nix b/nixpkgs/nixos/modules/services/audio/hqplayerd.nix
new file mode 100644
index 000000000000..d549ac77e0e5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/hqplayerd.nix
@@ -0,0 +1,142 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.hqplayerd;
+  pkg = pkgs.hqplayerd;
+  # XXX: This is hard-coded in the distributed binary, don't try to change it.
+  stateDir = "/var/lib/hqplayer";
+  configDir = "/etc/hqplayer";
+in
+{
+  options = {
+    services.hqplayerd = {
+      enable = mkEnableOption "HQPlayer Embedded";
+
+      auth = {
+        username = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Username used for HQPlayer's WebUI.
+
+            Without this you will need to manually create the credentials after
+            first start by going to http://your.ip/8088/auth
+          '';
+        };
+
+        password = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            Password used for HQPlayer's WebUI.
+
+            Without this you will need to manually create the credentials after
+            first start by going to http://your.ip/8088/auth
+          '';
+        };
+      };
+
+      licenseFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to the HQPlayer license key file.
+
+          Without this, the service will run in trial mode and restart every 30
+          minutes.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Opens ports needed for the WebUI and controller API.
+        '';
+      };
+
+      config = mkOption {
+        type = types.nullOr types.lines;
+        default = null;
+        description = ''
+          HQplayer daemon configuration, written to /etc/hqplayer/hqplayerd.xml.
+
+          Refer to ${pkg}/share/doc/hqplayerd/readme.txt for possible values.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.auth.username != null -> cfg.auth.password != null)
+                 && (cfg.auth.password != null -> cfg.auth.username != null);
+        message = "You must set either both services.hqplayer.auth.username and password, or neither.";
+      }
+    ];
+
+    environment = {
+      etc = {
+        "hqplayer/hqplayerd.xml" = mkIf (cfg.config != null) { source = pkgs.writeText "hqplayerd.xml" cfg.config; };
+        "hqplayer/hqplayerd4-key.xml" = mkIf (cfg.licenseFile != null) { source = cfg.licenseFile; };
+        "modules-load.d/taudio2.conf".source = "${pkg}/etc/modules-load.d/taudio2.conf";
+      };
+      systemPackages = [ pkg ];
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 8088 4321 ];
+    };
+
+    services.udev.packages = [ pkg ];
+
+    systemd = {
+      tmpfiles.rules = [
+        "d ${configDir}      0755 hqplayer hqplayer - -"
+        "d ${stateDir}       0755 hqplayer hqplayer - -"
+        "d ${stateDir}/home  0755 hqplayer hqplayer - -"
+      ];
+
+      packages = [ pkg ];
+
+      services.hqplayerd = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "systemd-tmpfiles-setup.service" ];
+
+        environment.HOME = "${stateDir}/home";
+
+        unitConfig.ConditionPathExists = [ configDir stateDir ];
+
+        restartTriggers = optionals (cfg.config != null) [ config.environment.etc."hqplayer/hqplayerd.xml".source ];
+
+        preStart = ''
+          cp -r "${pkg}/var/lib/hqplayer/web" "${stateDir}"
+          chmod -R u+wX "${stateDir}/web"
+
+          if [ ! -f "${configDir}/hqplayerd.xml" ]; then
+            echo "creating initial config file"
+            install -m 0644 "${pkg}/etc/hqplayer/hqplayerd.xml" "${configDir}/hqplayerd.xml"
+          fi
+        '' + optionalString (cfg.auth.username != null && cfg.auth.password != null) ''
+          ${pkg}/bin/hqplayerd -s ${cfg.auth.username} ${cfg.auth.password}
+        '';
+      };
+    };
+
+    users.groups = {
+      hqplayer.gid = config.ids.gids.hqplayer;
+    };
+
+    users.users = {
+      hqplayer = {
+        description = "hqplayer daemon user";
+        extraGroups = [ "audio" ];
+        group = "hqplayer";
+        uid = config.ids.uids.hqplayer;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/navidrome.nix b/nixpkgs/nixos/modules/services/audio/navidrome.nix
new file mode 100644
index 000000000000..c2fe429f9844
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/navidrome.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.navidrome;
+  settingsFormat = pkgs.formats.json {};
+in {
+  options = {
+    services.navidrome = {
+
+      enable = mkEnableOption pkgs.navidrome.meta.description;
+
+      settings = mkOption rec {
+        type = settingsFormat.type;
+        apply = recursiveUpdate default;
+        default = {
+          Address = "127.0.0.1";
+          Port = 4533;
+        };
+        example = {
+          MusicFolder = "/mnt/music";
+        };
+        description = ''
+          Configuration for Navidrome, see <link xlink:href="https://www.navidrome.org/docs/usage/configuration-options/"/> for supported values.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.navidrome = {
+      description = "Navidrome Media Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.navidrome}/bin/navidrome --configfile ${settingsFormat.generate "navidrome.json" cfg.settings}
+        '';
+        DynamicUser = true;
+        StateDirectory = "navidrome";
+        WorkingDirectory = "/var/lib/navidrome";
+        RuntimeDirectory = "navidrome";
+        RootDirectory = "/run/navidrome";
+        ReadWritePaths = "";
+        BindReadOnlyPaths = [
+          builtins.storeDir
+        ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder;
+        CapabilityBoundingSet = "";
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+        RestrictRealtime = true;
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        UMask = "0066";
+        ProtectHostname = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/networkaudiod.nix b/nixpkgs/nixos/modules/services/audio/networkaudiod.nix
new file mode 100644
index 000000000000..265a4e1d95d6
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/audio/networkaudiod.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  name = "networkaudiod";
+  cfg = config.services.networkaudiod;
+in {
+  options = {
+    services.networkaudiod = {
+      enable = mkEnableOption "Networkaudiod (NAA)";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.packages = [ pkgs.networkaudiod ];
+    systemd.services.networkaudiod.wantedBy = [ "multi-user.target" ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
index 85273a2039c3..e08f8a4f9e7a 100644
--- a/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
+++ b/nixpkgs/nixos/modules/services/audio/roon-bridge.nix
@@ -14,9 +14,6 @@ in {
         default = false;
         description = ''
           Open ports in the firewall for the bridge.
-
-          UDP: 9003
-          TCP: 9100 - 9200
         '';
       };
       user = mkOption {
@@ -54,10 +51,15 @@ in {
     };
 
     networking.firewall = mkIf cfg.openFirewall {
-      allowedTCPPortRanges = [
-        { from = 9100; to = 9200; }
-      ];
+      allowedTCPPortRanges = [{ from = 9100; to = 9200; }];
       allowedUDPPorts = [ 9003 ];
+      extraCommands = ''
+        iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type broadcast -j ACCEPT
+      '';
     };
 
 
diff --git a/nixpkgs/nixos/modules/services/audio/roon-server.nix b/nixpkgs/nixos/modules/services/audio/roon-server.nix
index eceb65044c5b..42da5a100170 100644
--- a/nixpkgs/nixos/modules/services/audio/roon-server.nix
+++ b/nixpkgs/nixos/modules/services/audio/roon-server.nix
@@ -14,9 +14,6 @@ in {
         default = false;
         description = ''
           Open ports in the firewall for the server.
-
-          UDP: 9003
-          TCP: 9100 - 9200
         '';
       };
       user = mkOption {
@@ -54,10 +51,15 @@ in {
     };
 
     networking.firewall = mkIf cfg.openFirewall {
-      allowedTCPPortRanges = [
-        { from = 9100; to = 9200; }
-      ];
+      allowedTCPPortRanges = [{ from = 9100; to = 9200; }];
       allowedUDPPorts = [ 9003 ];
+      extraCommands = ''
+        iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT
+        iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT
+        iptables -A INPUT -m pkttype --pkt-type broadcast -j ACCEPT
+      '';
     };
 
 
diff --git a/nixpkgs/nixos/modules/services/backup/borgbackup.nix b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
index 18fb29fd72a5..ccbc7726392d 100644
--- a/nixpkgs/nixos/modules/services/backup/borgbackup.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgbackup.nix
@@ -102,7 +102,7 @@ let
   mkWrapperDrv = {
       original, name, set ? {}
     }:
-    pkgs.runCommandNoCC "${name}-wrapper" {
+    pkgs.runCommand "${name}-wrapper" {
       buildInputs = [ pkgs.makeWrapper ];
     } (with lib; ''
       makeWrapper "${original}" "$out/bin/${name}" \
diff --git a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
index f658eb756f7b..bcc135005e16 100644
--- a/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixpkgs/nixos/modules/services/backup/postgresql-backup.nix
@@ -7,28 +7,49 @@ let
   cfg = config.services.postgresqlBackup;
 
   postgresqlBackupService = db: dumpCmd:
-    {
+    let
+      compressSuffixes = {
+        "none" = "";
+        "gzip" = ".gz";
+        "zstd" = ".zstd";
+      };
+      compressSuffix = getAttr cfg.compression compressSuffixes;
+
+      compressCmd = getAttr cfg.compression {
+        "none" = "cat";
+        "gzip" = "${pkgs.gzip}/bin/gzip -c";
+        "zstd" = "${pkgs.zstd}/bin/zstd -c";
+      };
+
+      mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
+      curFile = mkSqlPath "" compressSuffix;
+      prevFile = mkSqlPath ".prev" compressSuffix;
+      prevFiles = map (mkSqlPath ".prev") (attrValues compressSuffixes);
+      inProgressFile = mkSqlPath ".in-progress" compressSuffix;
+    in {
       enable = true;
 
       description = "Backup of ${db} database(s)";
 
       requires = [ "postgresql.service" ];
 
-      path = [ pkgs.coreutils pkgs.gzip config.services.postgresql.package ];
+      path = [ pkgs.coreutils config.services.postgresql.package ];
 
       script = ''
         set -e -o pipefail
 
         umask 0077 # ensure backup is only readable by postgres user
 
-        if [ -e ${cfg.location}/${db}.sql.gz ]; then
-          mv ${cfg.location}/${db}.sql.gz ${cfg.location}/${db}.prev.sql.gz
+        if [ -e ${curFile} ]; then
+          rm -f ${toString prevFiles}
+          mv ${curFile} ${prevFile}
         fi
 
-        ${dumpCmd} | \
-          gzip -c > ${cfg.location}/${db}.in-progress.sql.gz
+        ${dumpCmd} \
+          | ${compressCmd} \
+          > ${inProgressFile}
 
-        mv ${cfg.location}/${db}.in-progress.sql.gz ${cfg.location}/${db}.sql.gz
+        mv ${inProgressFile} ${curFile}
       '';
 
       serviceConfig = {
@@ -87,7 +108,7 @@ in {
         default = "/var/backup/postgresql";
         type = types.path;
         description = ''
-          Location to put the gzipped PostgreSQL database dumps.
+          Path of directory where the PostgreSQL database dumps will be placed.
         '';
       };
 
@@ -101,6 +122,14 @@ in {
           when no databases where specified.
         '';
       };
+
+      compression = mkOption {
+        type = types.enum ["none" "gzip" "zstd"];
+        default = "gzip";
+        description = ''
+          The type of compression to use on the generated database dump.
+        '';
+      };
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/backup/sanoid.nix b/nixpkgs/nixos/modules/services/backup/sanoid.nix
index abc4def1c61f..41d0e2e1df68 100644
--- a/nixpkgs/nixos/modules/services/backup/sanoid.nix
+++ b/nixpkgs/nixos/modules/services/backup/sanoid.nix
@@ -52,7 +52,7 @@ let
     use_template = mkOption {
       description = "Names of the templates to use for this dataset.";
       type = types.listOf (types.enum (attrNames cfg.templates));
-      default = [];
+      default = [ ];
     };
     useTemplate = use_template;
 
@@ -70,116 +70,127 @@ let
     processChildrenOnly = process_children_only;
   };
 
-  # Extract pool names from configured datasets
-  pools = unique (map (d: head (builtins.match "([^/]+).*" d)) (attrNames cfg.datasets));
-
-  configFile = let
-    mkValueString = v:
-      if builtins.isList v then concatStringsSep "," v
-      else generators.mkValueStringDefault {} v;
-
-    mkKeyValue = k: v: if v == null then ""
-      else if k == "processChildrenOnly" then ""
-      else if k == "useTemplate" then ""
-      else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
-  in generators.toINI { inherit mkKeyValue; } cfg.settings;
-
-in {
-
-    # Interface
-
-    options.services.sanoid = {
-      enable = mkEnableOption "Sanoid ZFS snapshotting service";
-
-      interval = mkOption {
-        type = types.str;
-        default = "hourly";
-        example = "daily";
-        description = ''
-          Run sanoid at this interval. The default is to run hourly.
-
-          The format is described in
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>.
-        '';
-      };
+  # Extract unique dataset names
+  datasets = unique (attrNames cfg.datasets);
+
+  # Function to build "zfs allow" and "zfs unallow" commands for the
+  # filesystems we've delegated permissions to.
+  buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
+    # Here we explicitly use the booted system to guarantee the stable API needed by ZFS
+    "-+/run/booted-system/sw/bin/zfs"
+    zfsAction
+    "sanoid"
+    (concatStringsSep "," permissions)
+    dataset
+  ];
+
+  configFile =
+    let
+      mkValueString = v:
+        if builtins.isList v then concatStringsSep "," v
+        else generators.mkValueStringDefault { } v;
+
+      mkKeyValue = k: v:
+        if v == null then ""
+        else if k == "processChildrenOnly" then ""
+        else if k == "useTemplate" then ""
+        else generators.mkKeyValueDefault { inherit mkValueString; } "=" k v;
+    in
+    generators.toINI { inherit mkKeyValue; } cfg.settings;
+
+in
+{
+
+  # Interface
+
+  options.services.sanoid = {
+    enable = mkEnableOption "Sanoid ZFS snapshotting service";
+
+    interval = mkOption {
+      type = types.str;
+      default = "hourly";
+      example = "daily";
+      description = ''
+        Run sanoid at this interval. The default is to run hourly.
+
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
 
-      datasets = mkOption {
-        type = types.attrsOf (types.submodule ({config, options, ...}: {
-          freeformType = datasetSettingsType;
-          options = commonOptions // datasetOptions;
-          config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or {});
-          config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or {});
-        }));
-        default = {};
-        description = "Datasets to snapshot.";
-      };
+    datasets = mkOption {
+      type = types.attrsOf (types.submodule ({ config, options, ... }: {
+        freeformType = datasetSettingsType;
+        options = commonOptions // datasetOptions;
+        config.use_template = mkAliasDefinitions (mkDefault options.useTemplate or { });
+        config.process_children_only = mkAliasDefinitions (mkDefault options.processChildrenOnly or { });
+      }));
+      default = { };
+      description = "Datasets to snapshot.";
+    };
 
-      templates = mkOption {
-        type = types.attrsOf (types.submodule {
-          freeformType = datasetSettingsType;
-          options = commonOptions;
-        });
-        default = {};
-        description = "Templates for datasets.";
-      };
+    templates = mkOption {
+      type = types.attrsOf (types.submodule {
+        freeformType = datasetSettingsType;
+        options = commonOptions;
+      });
+      default = { };
+      description = "Templates for datasets.";
+    };
 
-      settings = mkOption {
-        type = types.attrsOf datasetSettingsType;
-        description = ''
-          Free-form settings written directly to the config file. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/>
-          for allowed values.
-        '';
-      };
+    settings = mkOption {
+      type = types.attrsOf datasetSettingsType;
+      description = ''
+        Free-form settings written directly to the config file. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/blob/master/sanoid.defaults.conf"/>
+        for allowed values.
+      '';
+    };
 
-      extraArgs = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "--verbose" "--readonly" "--debug" ];
-        description = ''
-          Extra arguments to pass to sanoid. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/>
-          for allowed options.
-        '';
-      };
+    extraArgs = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--verbose" "--readonly" "--debug" ];
+      description = ''
+        Extra arguments to pass to sanoid. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/#sanoid-command-line-options"/>
+        for allowed options.
+      '';
     };
+  };
 
-    # Implementation
-
-    config = mkIf cfg.enable {
-      services.sanoid.settings = mkMerge [
-        (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates)
-        (mapAttrs (d: v: v) cfg.datasets)
-      ];
-
-      systemd.services.sanoid = {
-        description = "Sanoid snapshot service";
-        serviceConfig = {
-          ExecStartPre = map (pool: lib.escapeShellArgs [
-            "+/run/booted-system/sw/bin/zfs" "allow"
-            "sanoid" "snapshot,mount,destroy" pool
-          ]) pools;
-          ExecStart = lib.escapeShellArgs ([
-            "${pkgs.sanoid}/bin/sanoid"
-            "--cron"
-            "--configdir" (pkgs.writeTextDir "sanoid.conf" configFile)
-          ] ++ cfg.extraArgs);
-          ExecStopPost = map (pool: lib.escapeShellArgs [
-            "+/run/booted-system/sw/bin/zfs" "unallow" "sanoid" pool
-          ]) pools;
-          User = "sanoid";
-          Group = "sanoid";
-          DynamicUser = true;
-          RuntimeDirectory = "sanoid";
-          CacheDirectory = "sanoid";
-        };
-        # Prevents missing snapshots during DST changes
-        environment.TZ = "UTC";
-        after = [ "zfs.target" ];
-        startAt = cfg.interval;
+  # Implementation
+
+  config = mkIf cfg.enable {
+    services.sanoid.settings = mkMerge [
+      (mapAttrs' (d: v: nameValuePair ("template_" + d) v) cfg.templates)
+      (mapAttrs (d: v: v) cfg.datasets)
+    ];
+
+    systemd.services.sanoid = {
+      description = "Sanoid snapshot service";
+      serviceConfig = {
+        ExecStartPre = (map (buildAllowCommand "allow" [ "snapshot" "mount" "destroy" ]) datasets);
+        ExecStopPost = (map (buildAllowCommand "unallow" [ "snapshot" "mount" "destroy" ]) datasets);
+        ExecStart = lib.escapeShellArgs ([
+          "${pkgs.sanoid}/bin/sanoid"
+          "--cron"
+          "--configdir"
+          (pkgs.writeTextDir "sanoid.conf" configFile)
+        ] ++ cfg.extraArgs);
+        User = "sanoid";
+        Group = "sanoid";
+        DynamicUser = true;
+        RuntimeDirectory = "sanoid";
+        CacheDirectory = "sanoid";
       };
+      # Prevents missing snapshots during DST changes
+      environment.TZ = "UTC";
+      after = [ "zfs.target" ];
+      startAt = cfg.interval;
     };
+  };
 
-    meta.maintainers = with maintainers; [ lopsided98 ];
-  }
+  meta.maintainers = with maintainers; [ lopsided98 ];
+}
diff --git a/nixpkgs/nixos/modules/services/backup/syncoid.nix b/nixpkgs/nixos/modules/services/backup/syncoid.nix
index b764db1f14e4..3ad8d279a36d 100644
--- a/nixpkgs/nixos/modules/services/backup/syncoid.nix
+++ b/nixpkgs/nixos/modules/services/backup/syncoid.nix
@@ -5,212 +5,366 @@ with lib;
 let
   cfg = config.services.syncoid;
 
-  # Extract pool names of local datasets (ones that don't contain "@") that
-  # have the specified type (either "source" or "target")
-  getPools = type: unique (map (d: head (builtins.match "([^/]+).*" d)) (
-    # Filter local datasets
-    filter (d: !hasInfix "@" d)
-    # Get datasets of the specified type
-    (catAttrs type (attrValues cfg.commands))
-  ));
-in {
-
-    # Interface
-
-    options.services.syncoid = {
-      enable = mkEnableOption "Syncoid ZFS synchronization service";
-
-      interval = mkOption {
-        type = types.str;
-        default = "hourly";
-        example = "*-*-* *:15:00";
-        description = ''
-          Run syncoid at this interval. The default is to run hourly.
-
-          The format is described in
-          <citerefentry><refentrytitle>systemd.time</refentrytitle>
-          <manvolnum>7</manvolnum></citerefentry>.
-        '';
-      };
+  # Extract local dasaset names (so no datasets containing "@")
+  localDatasetName = d: optionals (d != null) (
+    let m = builtins.match "([^/@]+[^@]*)" d; in
+    optionals (m != null) m
+  );
 
-      user = mkOption {
-        type = types.str;
-        default = "syncoid";
-        example = "backup";
-        description = ''
-          The user for the service. ZFS privilege delegation will be
-          automatically configured for any local pools used by syncoid if this
-          option is set to a user other than root. The user will be given the
-          "hold" and "send" privileges on any pool that has datasets being sent
-          and the "create", "mount", "receive", and "rollback" privileges on
-          any pool that has datasets being received.
-        '';
-      };
+  # Escape as required by: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
+  escapeUnitName = name:
+    lib.concatMapStrings (s: if lib.isList s then "-" else s)
+      (builtins.split "[^a-zA-Z0-9_.\\-]+" name);
 
-      group = mkOption {
-        type = types.str;
-        default = "syncoid";
-        example = "backup";
-        description = "The group for the service.";
-      };
+  # Function to build "zfs allow" and "zfs unallow" commands for the
+  # filesystems we've delegated permissions to.
+  buildAllowCommand = zfsAction: permissions: dataset: lib.escapeShellArgs [
+    # Here we explicitly use the booted system to guarantee the stable API needed by ZFS
+    "-+/run/booted-system/sw/bin/zfs"
+    zfsAction
+    cfg.user
+    (concatStringsSep "," permissions)
+    dataset
+  ];
+in
+{
 
-      sshKey = mkOption {
-        type = types.nullOr types.path;
-        # Prevent key from being copied to store
-        apply = mapNullable toString;
-        default = null;
-        description = ''
-          SSH private key file to use to login to the remote system. Can be
-          overridden in individual commands.
-        '';
-      };
+  # Interface
 
-      commonArgs = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "--no-sync-snap" ];
-        description = ''
-          Arguments to add to every syncoid command, unless disabled for that
-          command. See
-          <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/>
-          for available options.
-        '';
-      };
+  options.services.syncoid = {
+    enable = mkEnableOption "Syncoid ZFS synchronization service";
 
-      commands = mkOption {
-        type = types.attrsOf (types.submodule ({ name, ... }: {
-          options = {
-            source = mkOption {
-              type = types.str;
-              example = "pool/dataset";
-              description = ''
-                Source ZFS dataset. Can be either local or remote. Defaults to
-                the attribute name.
-              '';
-            };
+    interval = mkOption {
+      type = types.str;
+      default = "hourly";
+      example = "*-*-* *:15:00";
+      description = ''
+        Run syncoid at this interval. The default is to run hourly.
 
-            target = mkOption {
-              type = types.str;
-              example = "user@server:pool/dataset";
-              description = ''
-                Target ZFS dataset. Can be either local
-                (<replaceable>pool/dataset</replaceable>) or remote
-                (<replaceable>user@server:pool/dataset</replaceable>).
-              '';
-            };
+        The format is described in
+        <citerefentry><refentrytitle>systemd.time</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
 
-            recursive = mkOption {
-              type = types.bool;
-              default = false;
-              description = ''
-                Whether to also transfer child datasets.
-              '';
-            };
+    user = mkOption {
+      type = types.str;
+      default = "syncoid";
+      example = "backup";
+      description = ''
+        The user for the service. ZFS privilege delegation will be
+        automatically configured for any local pools used by syncoid if this
+        option is set to a user other than root. The user will be given the
+        "hold" and "send" privileges on any pool that has datasets being sent
+        and the "create", "mount", "receive", and "rollback" privileges on
+        any pool that has datasets being received.
+      '';
+    };
 
-            sshKey = mkOption {
-              type = types.nullOr types.path;
-              # Prevent key from being copied to store
-              apply = mapNullable toString;
-              description = ''
-                SSH private key file to use to login to the remote system.
-                Defaults to <option>services.syncoid.sshKey</option> option.
-              '';
-            };
+    group = mkOption {
+      type = types.str;
+      default = "syncoid";
+      example = "backup";
+      description = "The group for the service.";
+    };
 
-            sendOptions = mkOption {
-              type = types.separatedString " ";
-              default = "";
-              example = "Lc e";
-              description = ''
-                Advanced options to pass to zfs send. Options are specified
-                without their leading dashes and separated by spaces.
-              '';
-            };
+    sshKey = mkOption {
+      type = types.nullOr types.path;
+      # Prevent key from being copied to store
+      apply = mapNullable toString;
+      default = null;
+      description = ''
+        SSH private key file to use to login to the remote system. Can be
+        overridden in individual commands.
+      '';
+    };
 
-            recvOptions = mkOption {
-              type = types.separatedString " ";
-              default = "";
-              example = "ux recordsize o compression=lz4";
-              description = ''
-                Advanced options to pass to zfs recv. Options are specified
-                without their leading dashes and separated by spaces.
-              '';
-            };
+    localSourceAllow = mkOption {
+      type = types.listOf types.str;
+      # Permissions snapshot and destroy are in case --no-sync-snap is not used
+      default = [ "bookmark" "hold" "send" "snapshot" "destroy" ];
+      description = ''
+        Permissions granted for the <option>services.syncoid.user</option> user
+        for local source datasets. See
+        <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+        for available permissions.
+      '';
+    };
 
-            useCommonArgs = mkOption {
-              type = types.bool;
-              default = true;
-              description = ''
-                Whether to add the configured common arguments to this command.
-              '';
-            };
+    localTargetAllow = mkOption {
+      type = types.listOf types.str;
+      default = [ "change-key" "compression" "create" "mount" "mountpoint" "receive" "rollback" ];
+      example = [ "create" "mount" "receive" "rollback" ];
+      description = ''
+        Permissions granted for the <option>services.syncoid.user</option> user
+        for local target datasets. See
+        <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+        for available permissions.
+        Make sure to include the <literal>change-key</literal> permission if you send raw encrypted datasets,
+        the <literal>compression</literal> permission if you send raw compressed datasets, and so on.
+        For remote target datasets you'll have to set your remote user permissions by yourself.
+      '';
+    };
 
-            extraArgs = mkOption {
-              type = types.listOf types.str;
-              default = [];
-              example = [ "--sshport 2222" ];
-              description = "Extra syncoid arguments for this command.";
-            };
+    commonArgs = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--no-sync-snap" ];
+      description = ''
+        Arguments to add to every syncoid command, unless disabled for that
+        command. See
+        <link xlink:href="https://github.com/jimsalterjrs/sanoid/#syncoid-command-line-options"/>
+        for available options.
+      '';
+    };
+
+    service = mkOption {
+      type = types.attrs;
+      default = { };
+      description = ''
+        Systemd configuration common to all syncoid services.
+      '';
+    };
+
+    commands = mkOption {
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options = {
+          source = mkOption {
+            type = types.str;
+            example = "pool/dataset";
+            description = ''
+              Source ZFS dataset. Can be either local or remote. Defaults to
+              the attribute name.
+            '';
           };
-          config = {
-            source = mkDefault name;
-            sshKey = mkDefault cfg.sshKey;
+
+          target = mkOption {
+            type = types.str;
+            example = "user@server:pool/dataset";
+            description = ''
+              Target ZFS dataset. Can be either local
+              (<replaceable>pool/dataset</replaceable>) or remote
+              (<replaceable>user@server:pool/dataset</replaceable>).
+            '';
           };
-        }));
-        default = {};
-        example = literalExample ''
-          {
-            "pool/test".target = "root@target:pool/test";
-          }
-        '';
-        description = "Syncoid commands to run.";
-      };
-    };
 
-    # Implementation
+          recursive = mkEnableOption ''the transfer of child datasets'';
+
+          sshKey = mkOption {
+            type = types.nullOr types.path;
+            # Prevent key from being copied to store
+            apply = mapNullable toString;
+            description = ''
+              SSH private key file to use to login to the remote system.
+              Defaults to <option>services.syncoid.sshKey</option> option.
+            '';
+          };
 
-    config = mkIf cfg.enable {
-      users =  {
-        users = mkIf (cfg.user == "syncoid") {
-          syncoid = {
-            group = cfg.group;
-            isSystemUser = true;
+          localSourceAllow = mkOption {
+            type = types.listOf types.str;
+            description = ''
+              Permissions granted for the <option>services.syncoid.user</option> user
+              for local source datasets. See
+              <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+              for available permissions.
+              Defaults to <option>services.syncoid.localSourceAllow</option> option.
+            '';
+          };
+
+          localTargetAllow = mkOption {
+            type = types.listOf types.str;
+            description = ''
+              Permissions granted for the <option>services.syncoid.user</option> user
+              for local target datasets. See
+              <link xlink:href="https://openzfs.github.io/openzfs-docs/man/8/zfs-allow.8.html"/>
+              for available permissions.
+              Make sure to include the <literal>change-key</literal> permission if you send raw encrypted datasets,
+              the <literal>compression</literal> permission if you send raw compressed datasets, and so on.
+              For remote target datasets you'll have to set your remote user permissions by yourself.
+            '';
+          };
+
+          sendOptions = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            example = "Lc e";
+            description = ''
+              Advanced options to pass to zfs send. Options are specified
+              without their leading dashes and separated by spaces.
+            '';
+          };
+
+          recvOptions = mkOption {
+            type = types.separatedString " ";
+            default = "";
+            example = "ux recordsize o compression=lz4";
+            description = ''
+              Advanced options to pass to zfs recv. Options are specified
+              without their leading dashes and separated by spaces.
+            '';
+          };
+
+          useCommonArgs = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Whether to add the configured common arguments to this command.
+            '';
+          };
+
+          service = mkOption {
+            type = types.attrs;
+            default = { };
+            description = ''
+              Systemd configuration specific to this syncoid service.
+            '';
+          };
+
+          extraArgs = mkOption {
+            type = types.listOf types.str;
+            default = [ ];
+            example = [ "--sshport 2222" ];
+            description = "Extra syncoid arguments for this command.";
           };
         };
-        groups = mkIf (cfg.group == "syncoid") {
-          syncoid = {};
+        config = {
+          source = mkDefault name;
+          sshKey = mkDefault cfg.sshKey;
+          localSourceAllow = mkDefault cfg.localSourceAllow;
+          localTargetAllow = mkDefault cfg.localTargetAllow;
         };
-      };
+      }));
+      default = { };
+      example = literalExample ''
+        {
+          "pool/test".target = "root@target:pool/test";
+        }
+      '';
+      description = "Syncoid commands to run.";
+    };
+  };
+
+  # Implementation
 
-      systemd.services.syncoid = {
-        description = "Syncoid ZFS synchronization service";
-        script = concatMapStringsSep "\n" (c: lib.escapeShellArgs
-          ([ "${pkgs.sanoid}/bin/syncoid" ]
-            ++ (optionals c.useCommonArgs cfg.commonArgs)
-            ++ (optional c.recursive "-r")
-            ++ (optionals (c.sshKey != null) [ "--sshkey" c.sshKey ])
-            ++ c.extraArgs
-            ++ [ "--sendoptions" c.sendOptions
-                 "--recvoptions" c.recvOptions
-                 "--no-privilege-elevation"
-                 c.source c.target
-               ])) (attrValues cfg.commands);
-        after = [ "zfs.target" ];
-        serviceConfig = {
-          ExecStartPre = let
-            allowCmd = permissions: pool: lib.escapeShellArgs [
-              "+/run/booted-system/sw/bin/zfs" "allow"
-              cfg.user (concatStringsSep "," permissions) pool
-            ];
-          in
-            (map (allowCmd [ "hold" "send" "snapshot" "destroy" ]) (getPools "source")) ++
-            (map (allowCmd [ "create" "mount" "receive" "rollback" ]) (getPools "target"));
-          User = cfg.user;
-          Group = cfg.group;
+  config = mkIf cfg.enable {
+    users = {
+      users = mkIf (cfg.user == "syncoid") {
+        syncoid = {
+          group = cfg.group;
+          isSystemUser = true;
+          # For syncoid to be able to create /var/lib/syncoid/.ssh/
+          # and to use custom ssh_config or known_hosts.
+          home = "/var/lib/syncoid";
+          createHome = false;
         };
-        startAt = cfg.interval;
+      };
+      groups = mkIf (cfg.group == "syncoid") {
+        syncoid = { };
       };
     };
 
-    meta.maintainers = with maintainers; [ lopsided98 ];
-  }
+    systemd.services = mapAttrs'
+      (name: c:
+        nameValuePair "syncoid-${escapeUnitName name}" (mkMerge [
+          {
+            description = "Syncoid ZFS synchronization from ${c.source} to ${c.target}";
+            after = [ "zfs.target" ];
+            startAt = cfg.interval;
+            # syncoid may need zpool to get feature@extensible_dataset
+            path = [ "/run/booted-system/sw/bin/" ];
+            serviceConfig = {
+              ExecStartPre =
+                (map (buildAllowCommand "allow" c.localSourceAllow) (localDatasetName c.source)) ++
+                (map (buildAllowCommand "allow" c.localTargetAllow) (localDatasetName c.target));
+              ExecStopPost =
+                (map (buildAllowCommand "unallow" c.localSourceAllow) (localDatasetName c.source)) ++
+                (map (buildAllowCommand "unallow" c.localTargetAllow) (localDatasetName c.target));
+              ExecStart = lib.escapeShellArgs ([ "${pkgs.sanoid}/bin/syncoid" ]
+                ++ optionals c.useCommonArgs cfg.commonArgs
+                ++ optional c.recursive "-r"
+                ++ optionals (c.sshKey != null) [ "--sshkey" c.sshKey ]
+                ++ c.extraArgs
+                ++ [
+                "--sendoptions"
+                c.sendOptions
+                "--recvoptions"
+                c.recvOptions
+                "--no-privilege-elevation"
+                c.source
+                c.target
+              ]);
+              User = cfg.user;
+              Group = cfg.group;
+              StateDirectory = [ "syncoid" ];
+              StateDirectoryMode = "700";
+              # Prevent SSH control sockets of different syncoid services from interfering
+              PrivateTmp = true;
+              # Permissive access to /proc because syncoid
+              # calls ps(1) to detect ongoing `zfs receive`.
+              ProcSubset = "all";
+              ProtectProc = "default";
+
+              # The following options are only for optimizing:
+              # systemd-analyze security | grep syncoid-'*'
+              AmbientCapabilities = "";
+              CapabilityBoundingSet = "";
+              DeviceAllow = [ "/dev/zfs" ];
+              LockPersonality = true;
+              MemoryDenyWriteExecute = true;
+              NoNewPrivileges = true;
+              PrivateDevices = true;
+              PrivateMounts = true;
+              PrivateNetwork = mkDefault false;
+              PrivateUsers = true;
+              ProtectClock = true;
+              ProtectControlGroups = true;
+              ProtectHome = true;
+              ProtectHostname = true;
+              ProtectKernelLogs = true;
+              ProtectKernelModules = true;
+              ProtectKernelTunables = true;
+              ProtectSystem = "strict";
+              RemoveIPC = true;
+              RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+              RestrictNamespaces = true;
+              RestrictRealtime = true;
+              RestrictSUIDSGID = true;
+              RootDirectory = "/run/syncoid/${escapeUnitName name}";
+              RootDirectoryStartOnly = true;
+              BindPaths = [ "/dev/zfs" ];
+              BindReadOnlyPaths = [ builtins.storeDir "/etc" "/run" "/bin/sh" ];
+              # Avoid useless mounting of RootDirectory= in the own RootDirectory= of ExecStart='s mount namespace.
+              InaccessiblePaths = [ "-+/run/syncoid/${escapeUnitName name}" ];
+              MountAPIVFS = true;
+              # Create RootDirectory= in the host's mount namespace.
+              RuntimeDirectory = [ "syncoid/${escapeUnitName name}" ];
+              RuntimeDirectoryMode = "700";
+              SystemCallFilter = [
+                "@system-service"
+                # Groups in @system-service which do not contain a syscall listed by:
+                # perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' syncoid …
+                # awk >perf.syscalls -F "," '$1 > 0 {sub("syscalls:sys_enter_","",$3); print $3}' perf.log
+                # systemd-analyze syscall-filter | grep -v -e '#' | sed -e ':loop; /^[^ ]/N; s/\n //; t loop' | grep $(printf ' -e \\<%s\\>' $(cat perf.syscalls)) | cut -f 1 -d ' '
+                "~@aio"
+                "~@chown"
+                "~@keyring"
+                "~@memlock"
+                "~@privileged"
+                "~@resources"
+                "~@setuid"
+                "~@timer"
+              ];
+              SystemCallArchitectures = "native";
+              # This is for BindPaths= and BindReadOnlyPaths=
+              # to allow traversal of directories they create in RootDirectory=.
+              UMask = "0066";
+            };
+          }
+          cfg.service
+          c.service
+        ]))
+      cfg.commands;
+  };
+
+  meta.maintainers = with maintainers; [ julm lopsided98 ];
+}
diff --git a/nixpkgs/nixos/modules/services/backup/znapzend.nix b/nixpkgs/nixos/modules/services/backup/znapzend.nix
index 0ca71b413cee..debb2a397050 100644
--- a/nixpkgs/nixos/modules/services/backup/znapzend.nix
+++ b/nixpkgs/nixos/modules/services/backup/znapzend.nix
@@ -279,7 +279,7 @@ let
     src_plan = plan;
     tsformat = timestampFormat;
     zend_delay = toString sendDelay;
-  } // fold (a: b: a // b) {} (
+  } // foldr (a: b: a // b) {} (
     map mkDestAttrs (builtins.attrValues destinations)
   );
 
diff --git a/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix b/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix
index be3f40f6bd86..6c2df95886e7 100644
--- a/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix
+++ b/nixpkgs/nixos/modules/services/blockchain/ethereum/geth.nix
@@ -83,8 +83,8 @@ let
       };
 
       syncmode = mkOption {
-        type = types.enum [ "fast" "full" "light" ];
-        default = "fast";
+        type = types.enum [ "snap" "fast" "full" "light" ];
+        default = "snap";
         description = "Blockchain sync mode.";
       };
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix
index 1378b5ccfb7a..821f1aa54604 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/addon-manager.nix
@@ -84,6 +84,9 @@ in
         Restart = "on-failure";
         RestartSec = 10;
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
     };
 
     services.kubernetes.addonManager.bootstrapAddons = mkIf isRBACEnabled
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix
index 24d86628b211..8f937a13231b 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/addons/dns.nix
@@ -60,6 +60,45 @@ in {
         sha256 = "02r440xcdsgi137k5lmmvp0z5w5fmk8g9mysq5pnysq1wl8sj6mw";
       };
     };
+
+    corefile = mkOption {
+      description = ''
+        Custom coredns corefile configuration.
+
+        See: <link xlink:href="https://coredns.io/manual/toc/#configuration"/>.
+      '';
+      type = types.str;
+      default = ''
+        .:${toString ports.dns} {
+          errors
+          health :${toString ports.health}
+          kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa {
+            pods insecure
+            fallthrough in-addr.arpa ip6.arpa
+          }
+          prometheus :${toString ports.metrics}
+          forward . /etc/resolv.conf
+          cache 30
+          loop
+          reload
+          loadbalance
+        }'';
+      defaultText = ''
+        .:${toString ports.dns} {
+          errors
+          health :${toString ports.health}
+          kubernetes ''${config.services.kubernetes.addons.dns.clusterDomain} in-addr.arpa ip6.arpa {
+            pods insecure
+            fallthrough in-addr.arpa ip6.arpa
+          }
+          prometheus :${toString ports.metrics}
+          forward . /etc/resolv.conf
+          cache 30
+          loop
+          reload
+          loadbalance
+        }'';
+    };
   };
 
   config = mkIf cfg.enable {
@@ -151,20 +190,7 @@ in {
           namespace = "kube-system";
         };
         data = {
-          Corefile = ".:${toString ports.dns} {
-            errors
-            health :${toString ports.health}
-            kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa {
-              pods insecure
-              fallthrough in-addr.arpa ip6.arpa
-            }
-            prometheus :${toString ports.metrics}
-            forward . /etc/resolv.conf
-            cache 30
-            loop
-            reload
-            loadbalance
-          }";
+          Corefile = cfg.corefile;
         };
       };
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix
index f1531caa7544..2c89310beb5a 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/apiserver.nix
@@ -190,12 +190,6 @@ in
       type = nullOr path;
     };
 
-    kubeletHttps = mkOption {
-      description = "Whether to use https for connections to kubelet.";
-      default = true;
-      type = bool;
-    };
-
     preferredAddressTypes = mkOption {
       description = "List of the preferred NodeAddressTypes to use for kubelet connections.";
       type = nullOr str;
@@ -365,7 +359,6 @@ in
                 "--feature-gates=${concatMapStringsSep "," (feature: "${feature}=true") cfg.featureGates}"} \
               ${optionalString (cfg.basicAuthFile != null)
                 "--basic-auth-file=${cfg.basicAuthFile}"} \
-              --kubelet-https=${boolToString cfg.kubeletHttps} \
               ${optionalString (cfg.kubeletClientCaFile != null)
                 "--kubelet-certificate-authority=${cfg.kubeletClientCaFile}"} \
               ${optionalString (cfg.kubeletClientCertFile != null)
@@ -405,6 +398,10 @@ in
             Restart = "on-failure";
             RestartSec = 5;
           };
+
+          unitConfig = {
+            StartLimitIntervalSec = 0;
+          };
         };
 
         services.etcd = {
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix
index 0c81fa9ae492..7128b5f70b1a 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/controller-manager.nix
@@ -146,6 +146,9 @@ in
         User = "kubernetes";
         Group = "kubernetes";
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
       path = top.path;
     };
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix
index 3f55719027f0..fecea7a15f3d 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/flannel.nix
@@ -58,7 +58,7 @@ in
     services.kubernetes.addonManager.bootstrapAddons = mkIf ((storageBackend == "kubernetes") && (elem "RBAC" top.apiserver.authorizationMode)) {
 
       flannel-cr = {
-        apiVersion = "rbac.authorization.k8s.io/v1beta1";
+        apiVersion = "rbac.authorization.k8s.io/v1";
         kind = "ClusterRole";
         metadata = { name = "flannel"; };
         rules = [{
@@ -79,7 +79,7 @@ in
       };
 
       flannel-crb = {
-        apiVersion = "rbac.authorization.k8s.io/v1beta1";
+        apiVersion = "rbac.authorization.k8s.io/v1";
         kind = "ClusterRoleBinding";
         metadata = { name = "flannel"; };
         roleRef = {
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix
index fcfcc8435477..08f5cdfdf334 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -337,6 +337,9 @@ in
           '';
           WorkingDirectory = top.dataDir;
         };
+        unitConfig = {
+          StartLimitIntervalSec = 0;
+        };
       };
 
       # Allways include cni plugins
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
index d9311d3e3a04..faf951d81574 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
@@ -189,7 +189,7 @@ in
         # manually paste it in place. Just symlink.
         # otherwise, create the target file, ready for users to insert the token
 
-        mkdir -p $(dirname ${certmgrAPITokenPath})
+        mkdir -p "$(dirname "${certmgrAPITokenPath}")"
         if [ -f "${cfsslAPITokenPath}" ]; then
           ln -fs "${cfsslAPITokenPath}" "${certmgrAPITokenPath}"
         else
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix
index 42729f54643b..a92043d52597 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/proxy.nix
@@ -77,6 +77,9 @@ in
         Restart = "on-failure";
         RestartSec = 5;
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
     };
 
     services.kubernetes.proxy.hostname = with config.networking; mkDefault hostName;
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix
index 454c689759df..1b0c22a11426 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/scheduler.nix
@@ -79,6 +79,9 @@ in
         Restart = "on-failure";
         RestartSec = 5;
       };
+      unitConfig = {
+        StartLimitIntervalSec = 0;
+      };
     };
 
     services.kubernetes.pki.certs = {
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix
index 9627b723f8f1..f951c1553235 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/github-runner.nix
@@ -98,6 +98,14 @@ in
       '';
       default = [ ];
     };
+
+    package = mkOption {
+      type = types.package;
+      description = ''
+        Which github-runner derivation to use.
+      '';
+      default = pkgs.github-runner;
+    };
   };
 
   config = mkIf cfg.enable {
@@ -131,7 +139,7 @@ in
       ] ++ cfg.extraPackages;
 
       serviceConfig = rec {
-        ExecStart = "${pkgs.github-runner}/bin/runsvc.sh";
+        ExecStart = "${cfg.package}/bin/runsvc.sh";
 
         # Does the following, sequentially:
         # - Copy the current and the previous `tokenFile` to the $RUNTIME_DIRECTORY
@@ -208,7 +216,7 @@ in
               if [[ -z "$empty" ]]; then
                 echo "Configuring GitHub Actions Runner"
                 token=$(< "$RUNTIME_DIRECTORY"/${newConfigTokenFilename})
-                RUNNER_ROOT="$STATE_DIRECTORY" ${pkgs.github-runner}/bin/config.sh \
+                RUNNER_ROOT="$STATE_DIRECTORY" ${cfg.package}/bin/config.sh \
                   --unattended \
                   --work "$RUNTIME_DIRECTORY" \
                   --url ${escapeShellArg cfg.url} \
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 2c6d9530a6b8..15c37c2bc76d 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -339,6 +339,9 @@ in
               <literal>CI_SERVER_URL=&lt;CI server URL&gt;</literal>
 
               <literal>REGISTRATION_TOKEN=&lt;registration secret&gt;</literal>
+
+              WARNING: make sure to use quoted absolute path,
+              or it is going to be copied to Nix Store.
             '';
           };
           registrationFlags = mkOption {
@@ -523,7 +526,10 @@ in
     };
   };
   config = mkIf cfg.enable {
-    warnings = optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`.";
+    warnings = (mapAttrsToList
+      (n: v: "services.gitlab-runner.services.${n}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.")
+      (filterAttrs (n: v: isStorePath v.registrationConfigFile) cfg.services))
+    ++ optional (cfg.configFile != null) "services.gitlab-runner.`configFile` is deprecated, please use services.gitlab-runner.`services`.";
     environment.systemPackages = [ cfg.package ];
     systemd.services.gitlab-runner = {
       description = "Gitlab Runner";
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
index 889688a26853..98ef1e2c691b 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -61,7 +61,7 @@ in {
 
       port = mkOption {
         default = 8080;
-        type = types.int;
+        type = types.port;
         description = ''
           Specifies port number on which the jenkins HTTP interface listens.
           The default is 8080.
diff --git a/nixpkgs/nixos/modules/services/databases/influxdb2.nix b/nixpkgs/nixos/modules/services/databases/influxdb2.nix
new file mode 100644
index 000000000000..df7bac4261b5
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/databases/influxdb2.nix
@@ -0,0 +1,53 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  format = pkgs.formats.json { };
+  cfg = config.services.influxdb2;
+  configFile = format.generate "config.json" cfg.settings;
+in
+{
+  options = {
+    services.influxdb2 = {
+      enable = mkEnableOption "the influxdb2 server";
+      package = mkOption {
+        default = pkgs.influxdb2;
+        defaultText = "pkgs.influxdb2";
+        description = "influxdb2 derivation to use.";
+        type = types.package;
+      };
+      settings = mkOption {
+        default = { };
+        description = "configuration options for influxdb2, see https://docs.influxdata.com/influxdb/v2.0/reference/config-options for details.";
+        type = format.type;
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [{
+      assertion = !(builtins.hasAttr "bolt-path" cfg.settings) && !(builtins.hasAttr "engine-path" cfg.settings);
+      message = "services.influxdb2.config: bolt-path and engine-path should not be set as they are managed by systemd";
+    }];
+    systemd.services.influxdb2 = {
+      description = "InfluxDB is an open-source, distributed, time series database";
+      documentation = [ "https://docs.influxdata.com/influxdb/" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        INFLUXD_CONFIG_PATH = "${configFile}";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/influxd --bolt-path \${STATE_DIRECTORY}/influxd.bolt --engine-path \${STATE_DIRECTORY}/engine";
+        StateDirectory = "influxdb2";
+        DynamicUser = true;
+        CapabilityBoundingSet = "";
+        SystemCallFilter = "@system-service";
+        LimitNOFILE = 65536;
+        KillMode = "control-group";
+        Restart = "on-failure";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ nickcao ];
+}
diff --git a/nixpkgs/nixos/modules/services/databases/postgresql.nix b/nixpkgs/nixos/modules/services/databases/postgresql.nix
index effc9182472e..fd4a195787f3 100644
--- a/nixpkgs/nixos/modules/services/databases/postgresql.nix
+++ b/nixpkgs/nixos/modules/services/databases/postgresql.nix
@@ -293,7 +293,8 @@ in
       # Note: when changing the default, make it conditional on
       # ‘system.stateVersion’ to maintain compatibility with existing
       # systems!
-      mkDefault (if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11
+      mkDefault (if versionAtLeast config.system.stateVersion "21.11" then pkgs.postgresql_13
+            else if versionAtLeast config.system.stateVersion "20.03" then pkgs.postgresql_11
             else if versionAtLeast config.system.stateVersion "17.09" then pkgs.postgresql_9_6
             else throw "postgresql_9_5 was removed, please upgrade your postgresql version.");
 
diff --git a/nixpkgs/nixos/modules/services/databases/redis.nix b/nixpkgs/nixos/modules/services/databases/redis.nix
index 9c0740f28c9b..8873f6d00e0b 100644
--- a/nixpkgs/nixos/modules/services/databases/redis.nix
+++ b/nixpkgs/nixos/modules/services/databases/redis.nix
@@ -272,7 +272,7 @@ in {
       }
       (mkIf (cfg.bind != null) { bind = cfg.bind; })
       (mkIf (cfg.unixSocket != null) { unixsocket = cfg.unixSocket; unixsocketperm = "${toString cfg.unixSocketPerm}"; })
-      (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${cfg.slaveOf.port}"; })
+      (mkIf (cfg.slaveOf != null) { slaveof = "${cfg.slaveOf.ip} ${toString cfg.slaveOf.port}"; })
       (mkIf (cfg.masterAuth != null) { masterauth = cfg.masterAuth; })
       (mkIf (cfg.requirePass != null) { requirepass = cfg.requirePass; })
     ];
diff --git a/nixpkgs/nixos/modules/services/databases/victoriametrics.nix b/nixpkgs/nixos/modules/services/databases/victoriametrics.nix
index 5b09115bb2fb..9e2c79e61a39 100644
--- a/nixpkgs/nixos/modules/services/databases/victoriametrics.nix
+++ b/nixpkgs/nixos/modules/services/databases/victoriametrics.nix
@@ -53,6 +53,14 @@ let cfg = config.services.victoriametrics; in
               -retentionPeriod ${toString cfg.retentionPeriod} \
               ${lib.escapeShellArgs cfg.extraOptions}
         '';
+        # victoriametrics 1.59 with ~7GB of data seems to eventually panic when merging files and then
+        # begins restart-looping forever. Set LimitNOFILE= to a large number to work around this issue.
+        #
+        # panic: FATAL: unrecoverable error when merging small parts in the partition "/var/lib/victoriametrics/data/small/2021_08":
+        # cannot open source part for merging: cannot open values file in stream mode:
+        # cannot open file "/var/lib/victoriametrics/data/small/2021_08/[...]/values.bin":
+        # open /var/lib/victoriametrics/data/small/2021_08/[...]/values.bin: too many open files
+        LimitNOFILE = 1048576;
       };
       wantedBy = [ "multi-user.target" ];
 
diff --git a/nixpkgs/nixos/modules/services/desktops/bamf.nix b/nixpkgs/nixos/modules/services/desktops/bamf.nix
index 37121c219a37..13de3a44328f 100644
--- a/nixpkgs/nixos/modules/services/desktops/bamf.nix
+++ b/nixpkgs/nixos/modules/services/desktops/bamf.nix
@@ -5,8 +5,8 @@
 with lib;
 
 {
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   ###### interface
diff --git a/nixpkgs/nixos/modules/services/desktops/geoclue2.nix b/nixpkgs/nixos/modules/services/desktops/geoclue2.nix
index e9ec787e5ada..cb5c948ecf78 100644
--- a/nixpkgs/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixpkgs/nixos/modules/services/desktops/geoclue2.nix
@@ -266,5 +266,7 @@ in
       } // mapAttrs' appConfigToINICompatible cfg.appConfig);
   };
 
-  meta.maintainers = with lib.maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
+  };
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
index 7c527b292158..46697ece4483 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/bluez-hardware.conf.json
@@ -28,6 +28,12 @@
       ]
     },
     {
+      "name": "BAA 100",
+      "no-features": [
+        "hw-volume"
+      ]
+    },
+    {
       "name": "JBL Endurance RUN BT",
       "no-features": [
         "msbc-alt1",
@@ -191,6 +197,46 @@
       ]
     },
     {
+      "sysname": "Linux",
+      "release": "~^5\\.10\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50)($|[^0-9])"
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.10\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.12\\.(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17)($|[^0-9])"
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.12\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.13\\.(1|2)($|[^0-9])"
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.13\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
+      "sysname": "Linux",
+      "release": "~^5\\.14\\.",
+      "no-features": [
+        "msbc-alt1"
+      ]
+    },
+    {
       "no-features": []
     }
   ]
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json
index e36e04fffcf2..128178bfa027 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/jack.conf.json
@@ -24,5 +24,15 @@
       "name": "libpipewire-module-metadata"
     }
   ],
-  "jack.properties": {}
+  "jack.properties": {},
+  "jack.rules": [
+    {
+      "matches": [
+        {}
+      ],
+      "actions": {
+        "update-props": {}
+      }
+    }
+  ]
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json
index 24906e767d6d..4b4e302af387 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/media-session.conf.json
@@ -59,6 +59,7 @@
     "with-pulseaudio": [
       "with-audio",
       "bluez5",
+      "bluez5-autoswitch",
       "logind",
       "restore-stream",
       "streams-follow-default"
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json
index 17bbbdef1179..3ed994f11145 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire-pulse.conf.json
@@ -37,5 +37,6 @@
       }
     }
   ],
+  "context.exec": [],
   "stream.properties": {}
 }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
index dbd6c5d87e1a..bc75aa2717a9 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -194,7 +194,7 @@ in {
     };
 
     environment.sessionVariables.LD_LIBRARY_PATH =
-      lib.optional cfg.jack.enable "/run/current-system/sw/lib/pipewire";
+      lib.optional cfg.jack.enable "${cfg.package.jack}/lib";
 
     # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/464#note_723554
     systemd.user.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1";
diff --git a/nixpkgs/nixos/modules/services/desktops/tumbler.nix b/nixpkgs/nixos/modules/services/desktops/tumbler.nix
index 8d9248cb9839..f5341df2f7a4 100644
--- a/nixpkgs/nixos/modules/services/desktops/tumbler.nix
+++ b/nixpkgs/nixos/modules/services/desktops/tumbler.nix
@@ -18,8 +18,8 @@ in
       "")
   ];
 
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   ###### interface
diff --git a/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix b/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix
index fb0218da3045..297fd1d3ff2f 100644
--- a/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix
+++ b/nixpkgs/nixos/modules/services/desktops/zeitgeist.nix
@@ -6,8 +6,8 @@ with lib;
 
 {
 
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   ###### interface
diff --git a/nixpkgs/nixos/modules/services/development/distccd.nix b/nixpkgs/nixos/modules/services/development/distccd.nix
new file mode 100644
index 000000000000..8790ea08d0c1
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/development/distccd.nix
@@ -0,0 +1,155 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.distccd;
+in
+{
+  options = {
+    services.distccd = {
+      enable = mkEnableOption "distccd";
+
+      allowedClients = mkOption {
+        type = types.listOf types.str;
+        default = [ "127.0.0.1" ];
+        example = [ "127.0.0.1" "192.168.0.0/24" "10.0.0.0/24" ];
+        description = ''
+          Client IPs which are allowed to connect to distccd in CIDR notation.
+
+          Anyone who can connect to the distccd server can run arbitrary
+          commands on that system as the distcc user, therefore you should use
+          this judiciously.
+        '';
+      };
+
+      jobTimeout = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Maximum duration, in seconds, of a single compilation request.
+        '';
+      };
+
+      logLevel = mkOption {
+        type = types.nullOr (types.enum [ "critical" "error" "warning" "notice" "info" "debug" ]);
+        default = "warning";
+        description = ''
+          Set the minimum severity of error that will be included in the log
+          file. Useful if you only want to see error messages rather than an
+          entry for each connection.
+        '';
+      };
+
+      maxJobs = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Maximum number of tasks distccd should execute at any time.
+        '';
+      };
+
+
+      nice = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        description = ''
+          Niceness of the compilation tasks.
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Opens the specified TCP port for distcc.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.distcc;
+        example = "pkgs.distcc";
+        description = ''
+          The distcc package to use.
+        '';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 3632;
+        description = ''
+          The TCP port which distccd will listen on.
+        '';
+      };
+
+      stats = {
+        enable = mkEnableOption "statistics reporting via HTTP server";
+        port = mkOption {
+          type = types.port;
+          default = 3633;
+          description = ''
+            The TCP port which the distccd statistics HTTP server will listen
+            on.
+          '';
+        };
+      };
+
+      zeroconf = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to register via mDNS/DNS-SD
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ]
+        ++ optionals cfg.stats.enable [ cfg.stats.port ];
+    };
+
+    systemd.services.distccd = {
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      description = "Distributed C, C++ and Objective-C compiler";
+      documentation = [ "man:distccd(1)" ];
+
+      serviceConfig = {
+        User = "distcc";
+        Group = "distcc";
+        # FIXME: I'd love to get rid of `--enable-tcp-insecure` here, but I'm
+        # not sure how I'm supposed to get distccd to "accept" running a binary
+        # (the compiler) that's outside of /usr/lib.
+        ExecStart = pkgs.writeShellScript "start-distccd" ''
+          export PATH="${pkgs.distccMasquerade}/bin"
+          ${cfg.package}/bin/distccd \
+            --no-detach \
+            --daemon \
+            --enable-tcp-insecure \
+            --port ${toString cfg.port} \
+            ${optionalString (cfg.jobTimeout != null) "--job-lifetime ${toString cfg.jobTimeout}"} \
+            ${optionalString (cfg.logLevel != null) "--log-level ${cfg.logLevel}"} \
+            ${optionalString (cfg.maxJobs != null) "--jobs ${toString cfg.maxJobs}"} \
+            ${optionalString (cfg.nice != null) "--nice ${toString cfg.nice}"} \
+            ${optionalString cfg.stats.enable "--stats"} \
+            ${optionalString cfg.stats.enable "--stats-port ${toString cfg.stats.port}"} \
+            ${optionalString cfg.zeroconf "--zeroconf"} \
+            ${concatMapStrings (c: "--allow ${c} ") cfg.allowedClients}
+        '';
+      };
+    };
+
+    users = {
+      groups.distcc.gid = config.ids.gids.distcc;
+      users.distcc = {
+        description = "distccd user";
+        group = "distcc";
+        uid = config.ids.uids.distcc;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/development/hoogle.nix b/nixpkgs/nixos/modules/services/development/hoogle.nix
index 6d6c88b9b2aa..a6693013b73c 100644
--- a/nixpkgs/nixos/modules/services/development/hoogle.nix
+++ b/nixpkgs/nixos/modules/services/development/hoogle.nix
@@ -17,7 +17,7 @@ in {
     enable = mkEnableOption "Haskell documentation server";
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 8080;
       description = ''
         Port number Hoogle will be listening to.
diff --git a/nixpkgs/nixos/modules/services/hardware/sane.nix b/nixpkgs/nixos/modules/services/hardware/sane.nix
index 8c1bde7b4158..ccf726bd182b 100644
--- a/nixpkgs/nixos/modules/services/hardware/sane.nix
+++ b/nixpkgs/nixos/modules/services/hardware/sane.nix
@@ -4,7 +4,10 @@ with lib;
 
 let
 
-  pkg = pkgs.sane-backends;
+  pkg = pkgs.sane-backends.override {
+    scanSnapDriversUnfree = config.hardware.sane.drivers.scanSnap.enable;
+    scanSnapDriversPackage = config.hardware.sane.drivers.scanSnap.package;
+  };
 
   sanedConf = pkgs.writeTextFile {
     name = "saned.conf";
@@ -98,6 +101,28 @@ in
       '';
     };
 
+    hardware.sane.drivers.scanSnap.enable = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = ''
+        Whether to enable drivers for the Fujitsu ScanSnap scanners.
+
+        The driver files are unfree and extracted from the Windows driver image.
+      '';
+    };
+
+    hardware.sane.drivers.scanSnap.package = mkOption {
+      type = types.package;
+      default = pkgs.sane-drivers.epjitsu;
+      description = ''
+        Epjitsu driver package to use. Useful if you want to extract the driver files yourself.
+
+        The process is described in the <literal>/etc/sane.d/epjitsu.conf</literal> file in
+        the <literal>sane-backends</literal> package.
+      '';
+    };
+
     services.saned.enable = mkOption {
       type = types.bool;
       default = false;
diff --git a/nixpkgs/nixos/modules/services/mail/dovecot.nix b/nixpkgs/nixos/modules/services/mail/dovecot.nix
index 1ccfb357750b..f3500f46e355 100644
--- a/nixpkgs/nixos/modules/services/mail/dovecot.nix
+++ b/nixpkgs/nixos/modules/services/mail/dovecot.nix
@@ -429,6 +429,7 @@ in
 
       startLimitIntervalSec = 60;  # 1 min
       serviceConfig = {
+        Type = "notify";
         ExecStart = "${dovecotPkg}/sbin/dovecot -F";
         ExecReload = "${dovecotPkg}/sbin/doveadm reload";
         Restart = "on-failure";
@@ -468,10 +469,6 @@ in
 
     assertions = [
       {
-        assertion = intersectLists cfg.protocols [ "pop3" "imap" ] != [];
-        message = "dovecot needs at least one of the IMAP or POP3 listeners enabled";
-      }
-      {
         assertion = (cfg.sslServerCert == null) == (cfg.sslServerKey == null)
         && (cfg.sslCACert != null -> !(cfg.sslServerCert == null || cfg.sslServerKey == null));
         message = "dovecot needs both sslServerCert and sslServerKey defined for working crypto";
diff --git a/nixpkgs/nixos/modules/services/mail/nullmailer.nix b/nixpkgs/nixos/modules/services/mail/nullmailer.nix
index 09874ca0ed75..f9c345669978 100644
--- a/nixpkgs/nixos/modules/services/mail/nullmailer.nix
+++ b/nixpkgs/nixos/modules/services/mail/nullmailer.nix
@@ -220,7 +220,7 @@ with lib;
       after = [ "network.target" ];
 
       preStart = ''
-        mkdir -p /var/spool/nullmailer/{queue,tmp}
+        mkdir -p /var/spool/nullmailer/{queue,tmp,failed}
         rm -f /var/spool/nullmailer/trigger && mkfifo -m 660 /var/spool/nullmailer/trigger
       '';
 
diff --git a/nixpkgs/nixos/modules/services/mail/postfix.nix b/nixpkgs/nixos/modules/services/mail/postfix.nix
index 35639e1bbc83..9b0a5bba2feb 100644
--- a/nixpkgs/nixos/modules/services/mail/postfix.nix
+++ b/nixpkgs/nixos/modules/services/mail/postfix.nix
@@ -194,7 +194,7 @@ let
       # We need to handle the last column specially here, because it's
       # open-ended (command + args).
       lines = [ labels labelDefaults ] ++ (map (l: init l ++ [""]) masterCf);
-    in fold foldLine (genList (const 0) (length labels)) lines;
+    in foldr foldLine (genList (const 0) (length labels)) lines;
 
     # Pad a string with spaces from the right (opposite of fixedWidthString).
     pad = width: str: let
@@ -203,7 +203,7 @@ let
     in str + optionalString (padWidth > 0) padding;
 
     # It's + 2 here, because that's the amount of spacing between columns.
-    fullWidth = fold (width: acc: acc + width + 2) 0 maxWidths;
+    fullWidth = foldr (width: acc: acc + width + 2) 0 maxWidths;
 
     formatLine = line: concatStringsSep "  " (zipListsWith pad maxWidths line);
 
diff --git a/nixpkgs/nixos/modules/services/mail/postfixadmin.nix b/nixpkgs/nixos/modules/services/mail/postfixadmin.nix
new file mode 100644
index 000000000000..f5c8efb3076c
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/mail/postfixadmin.nix
@@ -0,0 +1,199 @@
+{ lib, config, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.postfixadmin;
+  fpm = config.services.phpfpm.pools.postfixadmin;
+  localDB = cfg.database.host == "localhost";
+  user = if localDB then cfg.database.username else "nginx";
+in
+{
+  options.services.postfixadmin = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to enable postfixadmin.
+
+        Also enables nginx virtual host management.
+        Further nginx configuration can be done by adapting <literal>services.nginx.virtualHosts.&lt;name&gt;</literal>.
+        See <xref linkend="opt-services.nginx.virtualHosts"/> for further information.
+      '';
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      example = "postfixadmin.example.com";
+      description = "Hostname to use for the nginx vhost";
+    };
+
+    adminEmail = mkOption {
+      type = types.str;
+      example = "postmaster@example.com";
+      description = ''
+        Defines the Site Admin's email address.
+        This will be used to send emails from to create mailboxes and
+        from Send Email / Broadcast message pages.
+      '';
+    };
+
+    setupPasswordFile = mkOption {
+      type = types.path;
+      description = ''
+        Password file for the admin.
+        Generate with <literal>php -r "echo password_hash('some password here', PASSWORD_DEFAULT);"</literal>
+      '';
+    };
+
+    database = {
+      username = mkOption {
+        type = types.str;
+        default = "postfixadmin";
+        description = ''
+          Username for the postgresql connection.
+          If <literal>database.host</literal> is set to <literal>localhost</literal>, a unix user and group of the same name will be created as well.
+        '';
+      };
+      host = mkOption {
+        type = types.str;
+        default = "localhost";
+        description = ''
+          Host of the postgresql server. If this is not set to
+          <literal>localhost</literal>, you have to create the
+          postgresql user and database yourself, with appropriate
+          permissions.
+        '';
+      };
+      passwordFile = mkOption {
+        type = types.path;
+        description = "Password file for the postgresql connection. Must be readable by user <literal>nginx</literal>.";
+      };
+      dbname = mkOption {
+        type = types.str;
+        default = "postfixadmin";
+        description = "Name of the postgresql database";
+      };
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = "Extra configuration for the postfixadmin instance, see postfixadmin's config.inc.php for available options.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."postfixadmin/config.local.php".text = ''
+      <?php
+
+      $CONF['setup_password'] = file_get_contents('${cfg.setupPasswordFile}');
+
+      $CONF['database_type'] = 'pgsql';
+      $CONF['database_host'] = ${if localDB then "null" else "'${cfg.database.host}'"};
+      ${optionalString localDB "$CONF['database_user'] = '${cfg.database.username}';"}
+      $CONF['database_password'] = ${if localDB then "'dummy'" else "file_get_contents('${cfg.database.passwordFile}')"};
+      $CONF['database_name'] = '${cfg.database.dbname}';
+      $CONF['configured'] = true;
+
+      ${cfg.extraConfig}
+    '';
+
+    systemd.tmpfiles.rules = [ "d /var/cache/postfixadmin/templates_c 700 ${user} ${user}" ];
+
+    services.nginx = {
+      enable = true;
+      virtualHosts = {
+        ${cfg.hostName} = {
+          forceSSL = mkDefault true;
+          enableACME = mkDefault true;
+          locations."/" = {
+            root = "${pkgs.postfixadmin}/public";
+            index = "index.php";
+            extraConfig = ''
+              location ~* \.php$ {
+                fastcgi_split_path_info ^(.+\.php)(/.+)$;
+                fastcgi_pass unix:${fpm.socket};
+                include ${pkgs.nginx}/conf/fastcgi_params;
+                include ${pkgs.nginx}/conf/fastcgi.conf;
+              }
+            '';
+          };
+        };
+      };
+    };
+
+    services.postgresql = mkIf localDB {
+      enable = true;
+      ensureUsers = [ {
+        name = cfg.database.username;
+      } ];
+    };
+    # The postgresql module doesn't currently support concepts like
+    # objects owners and extensions; for now we tack on what's needed
+    # here.
+    systemd.services.postfixadmin-postgres = let pgsql = config.services.postgresql; in mkIf localDB {
+      after = [ "postgresql.service" ];
+      bindsTo = [ "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [
+        pgsql.package
+        pkgs.util-linux
+      ];
+      script = ''
+        set -eu
+
+        PSQL() {
+            psql --port=${toString pgsql.port} "$@"
+        }
+
+        PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${cfg.database.dbname}'" | grep -q 1 || PSQL -tAc 'CREATE DATABASE "${cfg.database.dbname}" OWNER "${cfg.database.username}"'
+        current_owner=$(PSQL -tAc "SELECT pg_catalog.pg_get_userbyid(datdba) FROM pg_catalog.pg_database WHERE datname = '${cfg.database.dbname}'")
+        if [[ "$current_owner" != "${cfg.database.username}" ]]; then
+            PSQL -tAc 'ALTER DATABASE "${cfg.database.dbname}" OWNER TO "${cfg.database.username}"'
+            if [[ -e "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}" ]]; then
+                echo "Reassigning ownership of database ${cfg.database.dbname} to user ${cfg.database.username} failed on last boot. Failing..."
+                exit 1
+            fi
+            touch "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}"
+            PSQL "${cfg.database.dbname}" -tAc "REASSIGN OWNED BY \"$current_owner\" TO \"${cfg.database.username}\""
+            rm "${config.services.postgresql.dataDir}/.reassigning_${cfg.database.dbname}"
+        fi
+      '';
+
+      serviceConfig = {
+        User = pgsql.superUser;
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+    };
+
+    users.users.${user} = mkIf localDB {
+      group = user;
+      isSystemUser = true;
+      createHome = false;
+    };
+    users.groups.${user} = mkIf localDB {};
+
+    services.phpfpm.pools.postfixadmin = {
+      user = user;
+      phpPackage = pkgs.php74;
+      phpOptions = ''
+        error_log = 'stderr'
+        log_errors = on
+      '';
+      settings = mapAttrs (name: mkDefault) {
+        "listen.owner" = "nginx";
+        "listen.group" = "nginx";
+        "listen.mode" = "0660";
+        "pm" = "dynamic";
+        "pm.max_children" = 75;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 1;
+        "pm.max_spare_servers" = 20;
+        "pm.max_requests" = 500;
+        "catch_workers_output" = true;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/mail/rspamd.nix b/nixpkgs/nixos/modules/services/mail/rspamd.nix
index 473ddd52357d..c78f464235aa 100644
--- a/nixpkgs/nixos/modules/services/mail/rspamd.nix
+++ b/nixpkgs/nixos/modules/services/mail/rspamd.nix
@@ -371,8 +371,9 @@ in
     };
     services.postfix.config = mkIf cfg.postfix.enable cfg.postfix.config;
 
-    systemd.services.postfix.serviceConfig.SupplementaryGroups =
-      mkIf cfg.postfix.enable [ postfixCfg.group ];
+    systemd.services.postfix = mkIf cfg.postfix.enable {
+      serviceConfig.SupplementaryGroups = [ postfixCfg.group ];
+    };
 
     # Allow users to run 'rspamc' and 'rspamadm'.
     environment.systemPackages = [ pkgs.rspamd ];
diff --git a/nixpkgs/nixos/modules/services/misc/airsonic.nix b/nixpkgs/nixos/modules/services/misc/airsonic.nix
index a572f1f6d6f5..490f6c5a5c06 100644
--- a/nixpkgs/nixos/modules/services/misc/airsonic.nix
+++ b/nixpkgs/nixos/modules/services/misc/airsonic.nix
@@ -82,6 +82,25 @@ in {
         '';
       };
 
+      jre = mkOption {
+        type = types.package;
+        default = pkgs.jre8;
+        defaultText = literalExample "pkgs.jre8";
+        description = ''
+          JRE package to use.
+
+          Airsonic only supports Java 8, airsonic-advanced requires at least
+          Java 11.
+        '';
+      };
+
+      war = mkOption {
+        type = types.path;
+        default = "${pkgs.airsonic}/webapps/airsonic.war";
+        defaultText = "\${pkgs.airsonic}/webapps/airsonic.war";
+        description = "Airsonic war file to use.";
+      };
+
       jvmOptions = mkOption {
         description = ''
           Extra command line options for the JVM running AirSonic.
@@ -118,7 +137,7 @@ in {
       '';
       serviceConfig = {
         ExecStart = ''
-          ${pkgs.jre8}/bin/java -Xmx${toString cfg.maxMemory}m \
+          ${cfg.jre}/bin/java -Xmx${toString cfg.maxMemory}m \
           -Dairsonic.home=${cfg.home} \
           -Dserver.address=${cfg.listenAddress} \
           -Dserver.port=${toString cfg.port} \
@@ -128,7 +147,7 @@ in {
             "-Dserver.use-forward-headers=true"} \
           ${toString cfg.jvmOptions} \
           -verbose:gc \
-          -jar ${pkgs.airsonic}/webapps/airsonic.war
+          -jar ${cfg.war}
         '';
         Restart = "always";
         User = "airsonic";
diff --git a/nixpkgs/nixos/modules/services/misc/gitea.nix b/nixpkgs/nixos/modules/services/misc/gitea.nix
index b6c1ca3e61a9..8322b7c09022 100644
--- a/nixpkgs/nixos/modules/services/misc/gitea.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitea.nix
@@ -522,20 +522,16 @@ in
           (umask 027; gitea_setup)
         ''}
 
+        # run migrations/init the database
+        ${gitea}/bin/gitea migrate
+
         # update all hooks' binary paths
-        HOOKS=$(find ${cfg.repositoryRoot} -mindepth 4 -maxdepth 6 -type f -wholename "*git/hooks/*")
-        if [ "$HOOKS" ]
-        then
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' $HOOKS
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/env,${pkgs.coreutils}/bin/env,g' $HOOKS
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/bash,${pkgs.bash}/bin/bash,g' $HOOKS
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/perl,${pkgs.perl}/bin/perl,g' $HOOKS
-        fi
+        ${gitea}/bin/gitea admin regenerate hooks
 
         # update command option in authorized_keys
         if [ -r ${cfg.stateDir}/.ssh/authorized_keys ]
         then
-          sed -ri 's,/nix/store/[a-z0-9.-]+/bin/gitea,${gitea}/bin/gitea,g' ${cfg.stateDir}/.ssh/authorized_keys
+          ${gitea}/bin/gitea admin regenerate keys
         fi
       '';
 
diff --git a/nixpkgs/nixos/modules/services/misc/gitlab.nix b/nixpkgs/nixos/modules/services/misc/gitlab.nix
index 1514cc0665df..805deeee0c04 100644
--- a/nixpkgs/nixos/modules/services/misc/gitlab.nix
+++ b/nixpkgs/nixos/modules/services/misc/gitlab.nix
@@ -117,6 +117,7 @@ let
       shared.path = "${cfg.statePath}/shared";
       gitaly.client_path = "${cfg.packages.gitaly}/bin";
       backup = {
+        gitaly_backup_path = "${cfg.packages.gitaly}/bin/gitaly-backup";
         path = cfg.backup.path;
         keep_time = cfg.backup.keepTime;
       } // (optionalAttrs (cfg.backup.uploadOptions != {}) {
@@ -1299,7 +1300,7 @@ in {
         Restart = "on-failure";
         WorkingDirectory = gitlabEnv.HOME;
         ExecStart =
-          "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse "
+          "${cfg.packages.gitlab-workhorse}/bin/workhorse "
           + "-listenUmask 0 "
           + "-listenNetwork unix "
           + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
@@ -1352,9 +1353,8 @@ in {
         procps
         gnupg
       ];
-
       serviceConfig = {
-        Type = "simple";
+        Type = "notify";
         User = cfg.user;
         Group = cfg.group;
         TimeoutSec = "infinity";
diff --git a/nixpkgs/nixos/modules/services/misc/home-assistant.nix b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
index dcd825bba433..73ec3b9a17a2 100644
--- a/nixpkgs/nixos/modules/services/misc/home-assistant.nix
+++ b/nixpkgs/nixos/modules/services/misc/home-assistant.nix
@@ -78,7 +78,7 @@ in {
 
     port = mkOption {
       default = 8123;
-      type = types.int;
+      type = types.port;
       description = "The port on which to listen.";
     };
 
@@ -285,6 +285,7 @@ in {
           "alarmdecoder"
           "arduino"
           "blackbird"
+          "deconz"
           "dsmr"
           "edl21"
           "elkm1"
diff --git a/nixpkgs/nixos/modules/services/misc/klipper.nix b/nixpkgs/nixos/modules/services/misc/klipper.nix
index 4930648ba8e3..e6b9dd234a9b 100644
--- a/nixpkgs/nixos/modules/services/misc/klipper.nix
+++ b/nixpkgs/nixos/modules/services/misc/klipper.nix
@@ -2,7 +2,13 @@
 with lib;
 let
   cfg = config.services.klipper;
-  format = pkgs.formats.ini { mkKeyValue = generators.mkKeyValueDefault {} ":"; };
+  format = pkgs.formats.ini {
+    # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
+    listToValue = l:
+      if builtins.length l == 1 then generators.mkValueStringDefault {} (head l)
+      else lib.concatMapStrings (s: "\n  ${generators.mkValueStringDefault {} s}") l;
+    mkKeyValue = generators.mkKeyValueDefault {} ":";
+  };
 in
 {
   ##### interface
@@ -24,8 +30,7 @@ in
 
       apiSocket = mkOption {
         type = types.nullOr types.path;
-        default = null;
-        example = "/run/klipper/api";
+        default = "/run/klipper/api";
         description = "Path of the API socket to create.";
       };
 
diff --git a/nixpkgs/nixos/modules/services/misc/libreddit.nix b/nixpkgs/nixos/modules/services/misc/libreddit.nix
new file mode 100644
index 000000000000..77b34a856204
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/libreddit.nix
@@ -0,0 +1,66 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+  let
+    cfg = config.services.libreddit;
+
+    args = concatStringsSep " " ([
+      "--port ${toString cfg.port}"
+      "--address ${cfg.address}"
+    ] ++ optional cfg.redirect "--redirect-https");
+
+in
+{
+  options = {
+    services.libreddit = {
+      enable = mkEnableOption "Private front-end for Reddit";
+
+      address = mkOption {
+        default = "0.0.0.0";
+        example = "127.0.0.1";
+        type =  types.str;
+        description = "The address to listen on";
+      };
+
+      port = mkOption {
+        default = 8080;
+        example = 8000;
+        type = types.port;
+        description = "The port to listen on";
+      };
+
+      redirect = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable the redirecting to HTTPS";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for the libreddit web interface";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.libreddit = {
+        description = "Private front-end for Reddit";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          ExecStart = "${pkgs.libreddit}/bin/libreddit ${args}";
+          AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+          Restart = "on-failure";
+          RestartSec = "2s";
+        };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix b/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix
index a0a5973d30f2..02627e51c932 100644
--- a/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix
+++ b/nixpkgs/nixos/modules/services/misc/matrix-appservice-irc.nix
@@ -10,7 +10,7 @@ let
 
   jsonType = (pkgs.formats.json {}).type;
 
-  configFile = pkgs.runCommandNoCC "matrix-appservice-irc.yml" {
+  configFile = pkgs.runCommand "matrix-appservice-irc.yml" {
     # Because this program will be run at build time, we need `nativeBuildInputs`
     nativeBuildInputs = [ (pkgs.python3.withPackages (ps: [ ps.pyyaml ps.jsonschema ])) ];
     preferLocalBuild = true;
diff --git a/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix b/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix
index 3c734a948198..e150a1aaaad1 100644
--- a/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix
+++ b/nixpkgs/nixos/modules/services/misc/matrix-synapse.nix
@@ -221,9 +221,10 @@ in {
         default = config.networking.hostName;
         description = ''
           The domain name of the server, with optional explicit port.
-          This is used by remote servers to connect to this server,
-          e.g. matrix.org, localhost:8080, etc.
+          This is used by remote servers to look up the server address.
           This is also the last part of your UserID.
+
+          The server_name cannot be changed later so it is important to configure this correctly before you start Synapse.
         '';
       };
       public_baseurl = mkOption {
diff --git a/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix b/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix
index 0ae5797fea04..717cf7936ead 100644
--- a/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix
+++ b/nixpkgs/nixos/modules/services/misc/mautrix-telegram.nix
@@ -128,7 +128,7 @@ in {
         # https://github.com/tulir/mautrix-telegram/issues/584
         [ -f ${settingsFile} ] && rm -f ${settingsFile}
         old_umask=$(umask)
-        umask 0277
+        umask 0177
         ${pkgs.envsubst}/bin/envsubst \
           -o ${settingsFile} \
           -i ${settingsFileUnsubstituted}
diff --git a/nixpkgs/nixos/modules/services/misc/moonraker.nix b/nixpkgs/nixos/modules/services/misc/moonraker.nix
new file mode 100644
index 000000000000..de8668a0c066
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/moonraker.nix
@@ -0,0 +1,135 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  pkg = pkgs.moonraker;
+  cfg = config.services.moonraker;
+  format = pkgs.formats.ini {
+    # https://github.com/NixOS/nixpkgs/pull/121613#issuecomment-885241996
+    listToValue = l:
+      if builtins.length l == 1 then generators.mkValueStringDefault {} (head l)
+      else lib.concatMapStrings (s: "\n  ${generators.mkValueStringDefault {} s}") l;
+    mkKeyValue = generators.mkKeyValueDefault {} ":";
+  };
+in {
+  options = {
+    services.moonraker = {
+      enable = mkEnableOption "Moonraker, an API web server for Klipper";
+
+      klipperSocket = mkOption {
+        type = types.path;
+        default = config.services.klipper.apiSocket;
+        description = "Path to Klipper's API socket.";
+      };
+
+      stateDir = mkOption {
+        type = types.path;
+        default = "/var/lib/moonraker";
+        description = "The directory containing the Moonraker databases.";
+      };
+
+      configDir = mkOption {
+        type = types.path;
+        default = cfg.stateDir + "/config";
+        description = ''
+          The directory containing client-writable configuration files.
+
+          Clients will be able to edit files in this directory via the API. This directory must be writable.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "moonraker";
+        description = "User account under which Moonraker runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "moonraker";
+        description = "Group account under which Moonraker runs.";
+      };
+
+      address = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        example = "0.0.0.0";
+        description = "The IP or host to listen on.";
+      };
+
+      port = mkOption {
+        type = types.ints.unsigned;
+        default = 7125;
+        description = "The port to listen on.";
+      };
+
+      settings = mkOption {
+        type = format.type;
+        default = { };
+        example = {
+          authorization = {
+            trusted_clients = [ "10.0.0.0/24" ];
+            cors_domains = [ "https://app.fluidd.xyz" ];
+          };
+        };
+        description = ''
+          Configuration for Moonraker. See the <link xlink:href="https://moonraker.readthedocs.io/en/latest/configuration/">documentation</link>
+          for supported values.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    warnings = optional (cfg.settings ? update_manager)
+      ''Enabling update_manager is not supported on NixOS and will lead to non-removable warnings in some clients.'';
+
+    users.users = optionalAttrs (cfg.user == "moonraker") {
+      moonraker = {
+        group = cfg.group;
+        uid = config.ids.uids.moonraker;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == "moonraker") {
+      moonraker.gid = config.ids.gids.moonraker;
+    };
+
+    environment.etc."moonraker.cfg".source = let
+      forcedConfig = {
+        server = {
+          host = cfg.address;
+          port = cfg.port;
+          klippy_uds_address = cfg.klipperSocket;
+          config_path = cfg.configDir;
+          database_path = "${cfg.stateDir}/database";
+        };
+      };
+      fullConfig = recursiveUpdate cfg.settings forcedConfig;
+    in format.generate "moonraker.cfg" fullConfig;
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
+      "d '${cfg.configDir}' - ${cfg.user} ${cfg.group} - -"
+    ];
+
+    systemd.services.moonraker = {
+      description = "Moonraker, an API web server for Klipper";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ]
+        ++ optional config.services.klipper.enable "klipper.service";
+
+      # Moonraker really wants its own config to be writable...
+      script = ''
+        cp /etc/moonraker.cfg ${cfg.configDir}/moonraker-temp.cfg
+        chmod u+w ${cfg.configDir}/moonraker-temp.cfg
+        exec ${pkg}/bin/moonraker -c ${cfg.configDir}/moonraker-temp.cfg
+      '';
+
+      serviceConfig = {
+        WorkingDirectory = cfg.stateDir;
+        Group = cfg.group;
+        User = cfg.user;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix b/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix
new file mode 100644
index 000000000000..11116f7c3489
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/mx-puppet-discord.nix
@@ -0,0 +1,120 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  dataDir = "/var/lib/mx-puppet-discord";
+  registrationFile = "${dataDir}/discord-registration.yaml";
+  cfg = config.services.mx-puppet-discord;
+  settingsFormat = pkgs.formats.json {};
+  settingsFile = settingsFormat.generate "mx-puppet-discord-config.json" cfg.settings;
+
+in {
+  options = {
+    services.mx-puppet-discord = {
+      enable = mkEnableOption ''
+        mx-puppet-discord is a discord puppeting bridge for matrix.
+        It handles bridging private and group DMs, as well as Guilds (servers)
+      '';
+
+      settings = mkOption rec {
+        apply = recursiveUpdate default;
+        inherit (settingsFormat) type;
+        default = {
+          bridge.port = 8434;
+          presence = {
+            enabled = true;
+            interval = 500;
+          };
+          provisioning.whitelist = [ ];
+          relay.whitelist = [ ];
+
+          # variables are preceded by a colon.
+          namePatterns = {
+            user = ":name";
+            userOverride = ":displayname";
+            room = ":name";
+            group = ":name";
+          };
+
+          #defaults to sqlite but can be configured to use postgresql with
+          #connstring
+          database.filename = "${dataDir}/mx-puppet-discord/database.db";
+          logging = {
+            console = "info";
+            lineDateFormat = "MMM-D HH:mm:ss.SSS";
+          };
+        };
+        example = literalExample ''
+          {
+            bridge = {
+              bindAddress = "localhost";
+              domain = "example.com";
+              homeserverUrl = "https://example.com";
+            };
+
+            provisioning.whitelist = [ "@admin:example.com" ];
+            relay.whitelist = [ "@.*:example.com" ];
+          }
+        '';
+        description = ''
+          <filename>config.yaml</filename> configuration as a Nix attribute set.
+          Configuration options should match those described in
+          <link xlink:href="https://github.com/matrix-discord/mx-puppet-discord/blob/master/sample.config.yaml">
+          sample.config.yaml</link>.
+        '';
+      };
+      serviceDependencies = mkOption {
+        type = with types; listOf str;
+        default = optional config.services.matrix-synapse.enable "matrix-synapse.service";
+        description = ''
+          List of Systemd services to require and wait for when starting the application service.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.mx-puppet-discord = {
+      description = ''
+        mx-puppet-discord is a discord puppeting bridge for matrix.
+        It handles bridging private and group DMs, as well as Guilds (servers).
+      '';
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
+      preStart = ''
+        # generate the appservice's registration file if absent
+        if [ ! -f '${registrationFile}' ]; then
+          ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -r -c ${settingsFile} \
+          -f ${registrationFile}
+        fi
+      '';
+
+      serviceConfig = {
+        Type = "simple";
+        Restart = "always";
+
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectControlGroups = true;
+
+        DynamicUser = true;
+        PrivateTmp = true;
+        WorkingDirectory = pkgs.mx-puppet-discord;
+        StateDirectory = baseNameOf dataDir;
+        UMask = 0027;
+
+        ExecStart = ''
+          ${pkgs.mx-puppet-discord}/bin/mx-puppet-discord -c ${settingsFile}
+        '';
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ govanify ];
+}
diff --git a/nixpkgs/nixos/modules/services/misc/nitter.nix b/nixpkgs/nixos/modules/services/misc/nitter.nix
new file mode 100644
index 000000000000..301af76c336a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/nitter.nix
@@ -0,0 +1,351 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.nitter;
+  configFile = pkgs.writeText "nitter.conf" ''
+    ${generators.toINI {
+      # String values need to be quoted
+      mkKeyValue = generators.mkKeyValueDefault {
+        mkValueString = v:
+          if isString v then "\"" + (strings.escape ["\""] (toString v)) + "\""
+          else generators.mkValueStringDefault {} v;
+      } " = ";
+    } (lib.recursiveUpdate {
+      Server = cfg.server;
+      Cache = cfg.cache;
+      Config = cfg.config // { hmacKey = "@hmac@"; };
+      Preferences = cfg.preferences;
+    } cfg.settings)}
+  '';
+  # `hmac` is a secret used for cryptographic signing of video URLs.
+  # Generate it on first launch, then copy configuration and replace
+  # `@hmac@` with this value.
+  # We are not using sed as it would leak the value in the command line.
+  preStart = pkgs.writers.writePython3 "nitter-prestart" {} ''
+    import os
+    import secrets
+
+    state_dir = os.environ.get("STATE_DIRECTORY")
+    if not os.path.isfile(f"{state_dir}/hmac"):
+        # Generate hmac on first launch
+        hmac = secrets.token_hex(32)
+        with open(f"{state_dir}/hmac", "w") as f:
+            f.write(hmac)
+    else:
+        # Load previously generated hmac
+        with open(f"{state_dir}/hmac", "r") as f:
+            hmac = f.read()
+
+    configFile = "${configFile}"
+    with open(configFile, "r") as f_in:
+        with open(f"{state_dir}/nitter.conf", "w") as f_out:
+            f_out.write(f_in.read().replace("@hmac@", hmac))
+  '';
+in
+{
+  options = {
+    services.nitter = {
+      enable = mkEnableOption "If enabled, start Nitter.";
+
+      server = {
+        address = mkOption {
+          type =  types.str;
+          default = "0.0.0.0";
+          example = "127.0.0.1";
+          description = "The address to listen on.";
+        };
+
+        port = mkOption {
+          type = types.port;
+          default = 8080;
+          example = 8000;
+          description = "The port to listen on.";
+        };
+
+        https = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Set secure attribute on cookies. Keep it disabled to enable cookies when not using HTTPS.";
+        };
+
+        httpMaxConnections = mkOption {
+          type = types.int;
+          default = 100;
+          description = "Maximum number of HTTP connections.";
+        };
+
+        staticDir = mkOption {
+          type = types.path;
+          default = "${pkgs.nitter}/share/nitter/public";
+          defaultText = "\${pkgs.nitter}/share/nitter/public";
+          description = "Path to the static files directory.";
+        };
+
+        title = mkOption {
+          type = types.str;
+          default = "nitter";
+          description = "Title of the instance.";
+        };
+
+        hostname = mkOption {
+          type = types.str;
+          default = "localhost";
+          example = "nitter.net";
+          description = "Hostname of the instance.";
+        };
+      };
+
+      cache = {
+        listMinutes = mkOption {
+          type = types.int;
+          default = 240;
+          description = "How long to cache list info (not the tweets, so keep it high).";
+        };
+
+        rssMinutes = mkOption {
+          type = types.int;
+          default = 10;
+          description = "How long to cache RSS queries.";
+        };
+
+        redisHost = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Redis host.";
+        };
+
+        redisPort = mkOption {
+          type = types.port;
+          default = 6379;
+          description = "Redis port.";
+        };
+
+        redisConnections = mkOption {
+          type = types.int;
+          default = 20;
+          description = "Redis connection pool size.";
+        };
+
+        redisMaxConnections = mkOption {
+          type = types.int;
+          default = 30;
+          description = ''
+            Maximum number of connections to Redis.
+
+            New connections are opened when none are available, but if the
+            pool size goes above this, they are closed when released, do not
+            worry about this unless you receive tons of requests per second.
+          '';
+        };
+      };
+
+      config = {
+        base64Media = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Use base64 encoding for proxied media URLs.";
+        };
+
+        tokenCount = mkOption {
+          type = types.int;
+          default = 10;
+          description = ''
+            Minimum amount of usable tokens.
+
+            Tokens are used to authorize API requests, but they expire after
+            ~1 hour, and have a limit of 187 requests. The limit gets reset
+            every 15 minutes, and the pool is filled up so there is always at
+            least tokenCount usable tokens. Only increase this if you receive
+            major bursts all the time.
+          '';
+        };
+      };
+
+      preferences = {
+        replaceTwitter = mkOption {
+          type = types.str;
+          default = "";
+          example = "nitter.net";
+          description = "Replace Twitter links with links to this instance (blank to disable).";
+        };
+
+        replaceYouTube = mkOption {
+          type = types.str;
+          default = "";
+          example = "piped.kavin.rocks";
+          description = "Replace YouTube links with links to this instance (blank to disable).";
+        };
+
+        replaceInstagram = mkOption {
+          type = types.str;
+          default = "";
+          description = "Replace Instagram links with links to this instance (blank to disable).";
+        };
+
+        mp4Playback = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Enable MP4 video playback.";
+        };
+
+        hlsPlayback = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Enable HLS video streaming (requires JavaScript).";
+        };
+
+        proxyVideos = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Proxy video streaming through the server (might be slow).";
+        };
+
+        muteVideos = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Mute videos by default.";
+        };
+
+        autoplayGifs = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Autoplay GIFs.";
+        };
+
+        theme = mkOption {
+          type = types.str;
+          default = "Nitter";
+          description = "Instance theme.";
+        };
+
+        infiniteScroll = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Infinite scrolling (requires JavaScript, experimental!).";
+        };
+
+        stickyProfile = mkOption {
+          type = types.bool;
+          default = true;
+          description = "Make profile sidebar stick to top.";
+        };
+
+        bidiSupport = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Support bidirectional text (makes clicking on tweets harder).";
+        };
+
+        hideTweetStats = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide tweet stats (replies, retweets, likes).";
+        };
+
+        hideBanner = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide profile banner.";
+        };
+
+        hidePins = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide pinned tweets.";
+        };
+
+        hideReplies = mkOption {
+          type = types.bool;
+          default = false;
+          description = "Hide tweet replies.";
+        };
+      };
+
+      settings = mkOption {
+        type = types.attrs;
+        default = {};
+        description = ''
+          Add settings here to override NixOS module generated settings.
+
+          Check the official repository for the available settings:
+          https://github.com/zedeus/nitter/blob/master/nitter.conf
+        '';
+      };
+
+      redisCreateLocally = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Configure local Redis server for Nitter.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for Nitter web interface.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !cfg.redisCreateLocally || (cfg.cache.redisHost == "localhost" && cfg.cache.redisPort == 6379);
+        message = "When services.nitter.redisCreateLocally is enabled, you need to use localhost:6379 as a cache server.";
+      }
+    ];
+
+    systemd.services.nitter = {
+        description = "Nitter (An alternative Twitter front-end)";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "syslog.target" "network.target" ];
+        serviceConfig = {
+          DynamicUser = true;
+          StateDirectory = "nitter";
+          Environment = [ "NITTER_CONF_FILE=/var/lib/nitter/nitter.conf" ];
+          # Some parts of Nitter expect `public` folder in working directory,
+          # see https://github.com/zedeus/nitter/issues/414
+          WorkingDirectory = "${pkgs.nitter}/share/nitter";
+          ExecStart = "${pkgs.nitter}/bin/nitter";
+          ExecStartPre = "${preStart}";
+          AmbientCapabilities = lib.mkIf (cfg.server.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+          Restart = "on-failure";
+          RestartSec = "5s";
+          # Hardening
+          CapabilityBoundingSet = if (cfg.server.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
+          DeviceAllow = [ "" ];
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          # A private user cannot have process capabilities on the host's user
+          # namespace and thus CAP_NET_BIND_SERVICE has no effect.
+          PrivateUsers = (cfg.server.port >= 1024);
+          ProcSubset = "pid";
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+          UMask = "0077";
+        };
+    };
+
+    services.redis = lib.mkIf (cfg.redisCreateLocally) {
+      enable = true;
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.server.port ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
index 133e96da0ec8..70b27b7d3d09 100644
--- a/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixpkgs/nixos/modules/services/misc/nix-daemon.nix
@@ -458,7 +458,7 @@ in
                 description = "The flake reference to which <option>from></option> is to be rewritten.";
               };
               flake = mkOption {
-                type = types.unspecified;
+                type = types.nullOr types.attrs;
                 default = null;
                 example = literalExample "nixpkgs";
                 description = ''
diff --git a/nixpkgs/nixos/modules/services/misc/nzbget.nix b/nixpkgs/nixos/modules/services/misc/nzbget.nix
index 715ec891cd68..27c5f2e395f6 100644
--- a/nixpkgs/nixos/modules/services/misc/nzbget.nix
+++ b/nixpkgs/nixos/modules/services/misc/nzbget.nix
@@ -7,24 +7,12 @@ let
   pkg = pkgs.nzbget;
   stateDir = "/var/lib/nzbget";
   configFile = "${stateDir}/nzbget.conf";
-  configOpts = concatStringsSep " " (mapAttrsToList (name: value: "-o ${name}=${value}") nixosOpts);
-
-  nixosOpts = {
-    # allows nzbget to run as a "simple" service
-    OutputMode = "loggable";
-    # use journald for logging
-    WriteLog = "none";
-    ErrorTarget = "screen";
-    WarningTarget = "screen";
-    InfoTarget = "screen";
-    DetailTarget = "screen";
-    # required paths
-    ConfigTemplate = "${pkg}/share/nzbget/nzbget.conf";
-    WebDir = "${pkg}/share/nzbget/webui";
-    # nixos handles package updates
-    UpdateCheck = "none";
-  };
-
+  configOpts = concatStringsSep " " (mapAttrsToList (name: value: "-o ${name}=${escapeShellArg (toStr value)}") cfg.settings);
+  toStr = v:
+    if v == true then "yes"
+    else if v == false then "no"
+    else if isInt v then toString v
+    else v;
 in
 {
   imports = [
@@ -50,12 +38,41 @@ in
         default = "nzbget";
         description = "Group under which NZBGet runs";
       };
+
+      settings = mkOption {
+        type = with types; attrsOf (oneOf [ bool int str ]);
+        default = {};
+        description = ''
+          NZBGet configuration, passed via command line using switch -o. Refer to
+          <link xlink:href="https://github.com/nzbget/nzbget/blob/master/nzbget.conf"/>
+          for details on supported values.
+        '';
+        example = {
+          MainDir = "/data";
+        };
+      };
     };
   };
 
   # implementation
 
   config = mkIf cfg.enable {
+    services.nzbget.settings = {
+      # allows nzbget to run as a "simple" service
+      OutputMode = "loggable";
+      # use journald for logging
+      WriteLog = "none";
+      ErrorTarget = "screen";
+      WarningTarget = "screen";
+      InfoTarget = "screen";
+      DetailTarget = "screen";
+      # required paths
+      ConfigTemplate = "${pkg}/share/nzbget/nzbget.conf";
+      WebDir = "${pkg}/share/nzbget/webui";
+      # nixos handles package updates
+      UpdateCheck = "none";
+    };
+
     systemd.services.nzbget = {
       description = "NZBGet Daemon";
       after = [ "network.target" ];
@@ -64,6 +81,7 @@ in
         unrar
         p7zip
       ];
+
       preStart = ''
         if [ ! -f ${configFile} ]; then
           ${pkgs.coreutils}/bin/install -m 0700 ${pkg}/share/nzbget/nzbget.conf ${configFile}
diff --git a/nixpkgs/nixos/modules/services/misc/octoprint.nix b/nixpkgs/nixos/modules/services/misc/octoprint.nix
index c926d889b37a..7129ac69527f 100644
--- a/nixpkgs/nixos/modules/services/misc/octoprint.nix
+++ b/nixpkgs/nixos/modules/services/misc/octoprint.nix
@@ -122,6 +122,9 @@ in
         ExecStart = "${pluginsEnv}/bin/octoprint serve -b ${cfg.stateDir}";
         User = cfg.user;
         Group = cfg.group;
+        SupplementaryGroups = [
+          "dialout"
+        ];
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/misc/paperless-ng.nix b/nixpkgs/nixos/modules/services/misc/paperless-ng.nix
new file mode 100644
index 000000000000..4b7087e17f96
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/misc/paperless-ng.nix
@@ -0,0 +1,308 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  cfg = config.services.paperless-ng;
+
+  defaultUser = "paperless";
+
+  env = {
+    PAPERLESS_DATA_DIR = cfg.dataDir;
+    PAPERLESS_MEDIA_ROOT = cfg.mediaDir;
+    PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
+    GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}";
+  } // lib.mapAttrs (_: toString) cfg.extraConfig;
+
+  manage = let
+    setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env);
+  in pkgs.writeShellScript "manage" ''
+    ${setupEnv}
+    exec ${cfg.package}/bin/paperless-ng "$@"
+  '';
+
+  # Secure the services
+  defaultServiceConfig = {
+    TemporaryFileSystem = "/:ro";
+    BindReadOnlyPaths = [
+      "/nix/store"
+      "-/etc/resolv.conf"
+      "-/etc/nsswitch.conf"
+      "-/etc/hosts"
+      "-/etc/localtime"
+      "-/run/postgresql"
+    ];
+    BindPaths = [
+      cfg.consumptionDir
+      cfg.dataDir
+      cfg.mediaDir
+    ];
+    CapabilityBoundingSet = "";
+    # ProtectClock adds DeviceAllow=char-rtc r
+    DeviceAllow = "";
+    LockPersonality = true;
+    MemoryDenyWriteExecute = true;
+    NoNewPrivileges = true;
+    PrivateDevices = true;
+    PrivateMounts = true;
+    # Needs to connect to redis
+    # PrivateNetwork = true;
+    PrivateTmp = true;
+    PrivateUsers = true;
+    ProcSubset = "pid";
+    ProtectClock = true;
+    # Breaks if the home dir of the user is in /home
+    # Also does not add much value in combination with the TemporaryFileSystem.
+    # ProtectHome = true;
+    ProtectHostname = true;
+    # Would re-mount paths ignored by temporary root
+    #ProtectSystem = "strict";
+    ProtectControlGroups = true;
+    ProtectKernelLogs = true;
+    ProtectKernelModules = true;
+    ProtectKernelTunables = true;
+    ProtectProc = "invisible";
+    RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+    RestrictNamespaces = true;
+    RestrictRealtime = true;
+    RestrictSUIDSGID = true;
+    SystemCallArchitectures = "native";
+    SystemCallFilter = [ "@system-service" "~@privileged @resources @setuid @keyring" ];
+    # Does not work well with the temporary root
+    #UMask = "0066";
+  };
+in
+{
+  meta.maintainers = with maintainers; [ earvstedt Flakebi ];
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "paperless"] ''
+      The paperless module has been removed as the upstream project died.
+      Users should migrate to the paperless-ng module (services.paperless-ng).
+      More information can be found in the NixOS 21.11 release notes.
+    '')
+  ];
+
+  options.services.paperless-ng = {
+    enable = mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enable Paperless-ng.
+
+        When started, the Paperless database is automatically created if it doesn't
+        exist and updated if the Paperless package has changed.
+        Both tasks are achieved by running a Django migration.
+
+        A script to manage the Paperless instance (by wrapping Django's manage.py) is linked to
+        <literal>''${dataDir}/paperless-ng-manage</literal>.
+      '';
+    };
+
+    dataDir = mkOption {
+      type = types.str;
+      default = "/var/lib/paperless";
+      description = "Directory to store the Paperless data.";
+    };
+
+    mediaDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/media";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory to store the Paperless documents.";
+    };
+
+    consumptionDir = mkOption {
+      type = types.str;
+      default = "${cfg.dataDir}/consume";
+      defaultText = "\${dataDir}/consume";
+      description = "Directory from which new documents are imported.";
+    };
+
+    consumptionDirIsPublic = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether all users can write to the consumption dir.";
+    };
+
+    passwordFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/keys/paperless-ng-password";
+      description = ''
+        A file containing the superuser password.
+
+        A superuser is required to access the web interface.
+        If unset, you can create a superuser manually by running
+        <literal>''${dataDir}/paperless-ng-manage createsuperuser</literal>.
+
+        The default superuser name is <literal>admin</literal>. To change it, set
+        option <option>extraConfig.PAPERLESS_ADMIN_USER</option>.
+        WARNING: When changing the superuser name after the initial setup, the old superuser
+        will continue to exist.
+
+        To disable login for the web interface, set the following:
+        <literal>extraConfig.PAPERLESS_AUTO_LOGIN_USERNAME = "admin";</literal>.
+        WARNING: Only use this on a trusted system without internet access to Paperless.
+      '';
+    };
+
+    address = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Web interface address.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 28981;
+      description = "Web interface port.";
+    };
+
+    extraConfig = mkOption {
+      type = types.attrs;
+      default = {};
+      description = ''
+        Extra paperless-ng config options.
+
+        See <link xlink:href="https://paperless-ng.readthedocs.io/en/latest/configuration.html">the documentation</link>
+        for available options.
+      '';
+      example = literalExample ''
+        {
+          PAPERLESS_OCR_LANGUAGE = "deu+eng";
+        }
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = "User under which Paperless runs.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.paperless-ng;
+      defaultText = "pkgs.paperless-ng";
+      description = "The Paperless package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # Enable redis if no special url is set
+    services.redis.enable = mkIf (!hasAttr "PAPERLESS_REDIS" env) true;
+
+    systemd.tmpfiles.rules = [
+      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      "d '${cfg.mediaDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      (if cfg.consumptionDirIsPublic then
+        "d '${cfg.consumptionDir}' 777 - - - -"
+      else
+        "d '${cfg.consumptionDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+      )
+    ];
+
+    systemd.services.paperless-ng-server = {
+      description = "Paperless document server";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = "${cfg.package}/bin/paperless-ng qcluster";
+        Restart = "on-failure";
+      };
+      environment = env;
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "paperless-ng-consumer.service" "paperless-ng-web.service" ];
+
+      preStart = ''
+        ln -sf ${manage} ${cfg.dataDir}/paperless-ng-manage
+
+        # Auto-migrate on first run or if the package has changed
+        versionFile="${cfg.dataDir}/src-version"
+        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
+          ${cfg.package}/bin/paperless-ng migrate
+          echo ${cfg.package} > "$versionFile"
+        fi
+      ''
+      + optionalString (cfg.passwordFile != null) ''
+        export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}"
+        export PAPERLESS_ADMIN_PASSWORD=$(cat "${cfg.dataDir}/superuser-password")
+        superuserState="$PAPERLESS_ADMIN_USER:$PAPERLESS_ADMIN_PASSWORD"
+        superuserStateFile="${cfg.dataDir}/superuser-state"
+
+        if [[ $(cat "$superuserStateFile" 2>/dev/null) != $superuserState ]]; then
+          ${cfg.package}/bin/paperless-ng manage_superuser
+          echo "$superuserState" > "$superuserStateFile"
+        fi
+      '';
+    };
+
+    # Password copying can't be implemented as a privileged preStart script
+    # in 'paperless-ng-server' because 'defaultServiceConfig' limits the filesystem
+    # paths accessible by the service.
+    systemd.services.paperless-ng-copy-password = mkIf (cfg.passwordFile != null) {
+      requiredBy = [ "paperless-ng-server.service" ];
+      before = [ "paperless-ng-server.service" ];
+      serviceConfig = {
+        ExecStart = ''
+          ${pkgs.coreutils}/bin/install --mode 600 --owner '${cfg.user}' --compare \
+            '${cfg.passwordFile}' '${cfg.dataDir}/superuser-password'
+        '';
+        Type = "oneshot";
+      };
+    };
+
+    systemd.services.paperless-ng-consumer = {
+      description = "Paperless document consumer";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = "${cfg.package}/bin/paperless-ng document_consumer";
+        Restart = "on-failure";
+      };
+      environment = env;
+      # Bind to `paperless-ng-server` so that the consumer never runs
+      # during migrations
+      bindsTo = [ "paperless-ng-server.service" ];
+      after = [ "paperless-ng-server.service" ];
+    };
+
+    systemd.services.paperless-ng-web = {
+      description = "Paperless web server";
+      serviceConfig = defaultServiceConfig // {
+        User = cfg.user;
+        ExecStart = ''
+          ${pkgs.python3Packages.gunicorn}/bin/gunicorn \
+            -c ${cfg.package}/lib/paperless-ng/gunicorn.conf.py paperless.asgi:application
+        '';
+        Restart = "on-failure";
+
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        CapabilityBoundingSet = "CAP_NET_BIND_SERVICE";
+        # gunicorn needs setuid
+        SystemCallFilter = defaultServiceConfig.SystemCallFilter ++ [ "@setuid" ];
+      };
+      environment = env // {
+        PATH = mkForce cfg.package.path;
+        PYTHONPATH = "${cfg.package.pythonPath}:${cfg.package}/lib/paperless-ng/src";
+      };
+      # Allow the web interface to access the private /tmp directory of the server.
+      # This is required to support uploading files via the web interface.
+      unitConfig.JoinsNamespaceOf = "paperless-ng-server.service";
+      # Bind to `paperless-ng-server` so that the web server never runs
+      # during migrations
+      bindsTo = [ "paperless-ng-server.service" ];
+      after = [ "paperless-ng-server.service" ];
+    };
+
+    users = optionalAttrs (cfg.user == defaultUser) {
+      users.${defaultUser} = {
+        group = defaultUser;
+        uid = config.ids.uids.paperless;
+        home = cfg.dataDir;
+      };
+
+      groups.${defaultUser} = {
+        gid = config.ids.gids.paperless;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix
deleted file mode 100644
index 43730b80eb2c..000000000000
--- a/nixpkgs/nixos/modules/services/misc/paperless.nix
+++ /dev/null
@@ -1,183 +0,0 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
-let
-  cfg = config.services.paperless;
-
-  defaultUser = "paperless";
-
-  manage = cfg.package.withConfig {
-    config = {
-      PAPERLESS_CONSUMPTION_DIR = cfg.consumptionDir;
-      PAPERLESS_INLINE_DOC = "true";
-      PAPERLESS_DISABLE_LOGIN = "true";
-    } // cfg.extraConfig;
-    inherit (cfg) dataDir ocrLanguages;
-    paperlessPkg = cfg.package;
-  };
-in
-{
-  options.services.paperless = {
-    enable = mkOption {
-      type = lib.types.bool;
-      default = false;
-      description = ''
-        Enable Paperless.
-
-        When started, the Paperless database is automatically created if it doesn't
-        exist and updated if the Paperless package has changed.
-        Both tasks are achieved by running a Django migration.
-      '';
-    };
-
-    dataDir = mkOption {
-      type = types.str;
-      default = "/var/lib/paperless";
-      description = "Directory to store the Paperless data.";
-    };
-
-    consumptionDir = mkOption {
-      type = types.str;
-      default = "${cfg.dataDir}/consume";
-      defaultText = "\${dataDir}/consume";
-      description = "Directory from which new documents are imported.";
-    };
-
-    consumptionDirIsPublic = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Whether all users can write to the consumption dir.";
-    };
-
-    ocrLanguages = mkOption {
-      type = with types; nullOr (listOf str);
-      default = null;
-      description = ''
-        Languages available for OCR via Tesseract, specified as
-        <literal>ISO 639-2/T</literal> language codes.
-        If unset, defaults to all available languages.
-      '';
-      example = [ "eng" "spa" "jpn" ];
-    };
-
-    address = mkOption {
-      type = types.str;
-      default = "localhost";
-      description = "Server listening address.";
-    };
-
-    port = mkOption {
-      type = types.port;
-      default = 28981;
-      description = "Server port to listen on.";
-    };
-
-    extraConfig = mkOption {
-      type = types.attrs;
-      default = {};
-      description = ''
-        Extra paperless config options.
-
-        The config values are evaluated as double-quoted Bash string literals.
-
-        See <literal>paperless-src/paperless.conf.example</literal> for available options.
-
-        To enable user authentication, set <literal>PAPERLESS_DISABLE_LOGIN = "false"</literal>
-        and run the shell command <literal>$dataDir/paperless-manage createsuperuser</literal>.
-
-        To define secret options without storing them in /nix/store, use the following pattern:
-        <literal>PAPERLESS_PASSPHRASE = "$(&lt; /etc/my_passphrase_file)"</literal>
-      '';
-      example = literalExample ''
-        {
-          PAPERLESS_OCR_LANGUAGE = "deu";
-        }
-      '';
-    };
-
-    user = mkOption {
-      type = types.str;
-      default = defaultUser;
-      description = "User under which Paperless runs.";
-    };
-
-    package = mkOption {
-      type = types.package;
-      default = pkgs.paperless;
-      defaultText = "pkgs.paperless";
-      description = "The Paperless package to use.";
-    };
-
-    manage = mkOption {
-      type = types.package;
-      readOnly = true;
-      default = manage;
-      description = ''
-        A script to manage the Paperless instance.
-        It wraps Django's manage.py and is also available at
-        <literal>$dataDir/manage-paperless</literal>
-      '';
-    };
-  };
-
-  config = mkIf cfg.enable {
-
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
-    ] ++ (optional cfg.consumptionDirIsPublic
-      "d '${cfg.consumptionDir}' 777 - - - -"
-      # If the consumption dir is not created here, it's automatically created by
-      # 'manage' with the default permissions.
-    );
-
-    systemd.services.paperless-consumer = {
-      description = "Paperless document consumer";
-      serviceConfig = {
-        User = cfg.user;
-        ExecStart = "${manage} document_consumer";
-        Restart = "always";
-      };
-      after = [ "systemd-tmpfiles-setup.service" ];
-      wantedBy = [ "multi-user.target" ];
-      preStart = ''
-        if [[ $(readlink ${cfg.dataDir}/paperless-manage) != ${manage} ]]; then
-          ln -sf ${manage} ${cfg.dataDir}/paperless-manage
-        fi
-
-        ${manage.setupEnv}
-        # Auto-migrate on first run or if the package has changed
-        versionFile="$PAPERLESS_DBDIR/src-version"
-        if [[ $(cat "$versionFile" 2>/dev/null) != ${cfg.package} ]]; then
-          python $paperlessSrc/manage.py migrate
-          echo ${cfg.package} > "$versionFile"
-        fi
-      '';
-    };
-
-    systemd.services.paperless-server = {
-      description = "Paperless document server";
-      serviceConfig = {
-        User = cfg.user;
-        ExecStart = "${manage} runserver --noreload ${cfg.address}:${toString cfg.port}";
-        Restart = "always";
-      };
-      # Bind to `paperless-consumer` so that the server never runs
-      # during migrations
-      bindsTo = [ "paperless-consumer.service" ];
-      after = [ "paperless-consumer.service" ];
-      wantedBy = [ "multi-user.target" ];
-    };
-
-    users = optionalAttrs (cfg.user == defaultUser) {
-      users.${defaultUser} = {
-        group = defaultUser;
-        uid = config.ids.uids.paperless;
-        home = cfg.dataDir;
-      };
-
-      groups.${defaultUser} = {
-        gid = config.ids.gids.paperless;
-      };
-    };
-  };
-}
diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix
index a17a1010dbf7..e446f08284f7 100644
--- a/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix
+++ b/nixpkgs/nixos/modules/services/misc/sourcehut/builds.nix
@@ -84,7 +84,7 @@ in
             (rev: archs:
               lib.attrsets.mapAttrsToList
                 (arch: image:
-                  pkgs.runCommandNoCC "buildsrht-images" { } ''
+                  pkgs.runCommand "buildsrht-images" { } ''
                     mkdir -p $out/${distro}/${rev}/${arch}
                     ln -s ${image}/*.qcow2 $out/${distro}/${rev}/${arch}/root.img.qcow2
                   '')
@@ -97,7 +97,7 @@ in
         "${pkgs.sourcehut.buildsrht}/lib/images"
       ];
     };
-    image_dir = pkgs.runCommandNoCC "builds.sr.ht-worker-images" { } ''
+    image_dir = pkgs.runCommand "builds.sr.ht-worker-images" { } ''
       mkdir -p $out/images
       cp -Lr ${image_dir_pre}/* $out/images
     '';
diff --git a/nixpkgs/nixos/modules/services/misc/uhub.nix b/nixpkgs/nixos/modules/services/misc/uhub.nix
index d1b388310280..da2613e6db17 100644
--- a/nixpkgs/nixos/modules/services/misc/uhub.nix
+++ b/nixpkgs/nixos/modules/services/misc/uhub.nix
@@ -3,178 +3,110 @@
 with lib;
 
 let
-
-  cfg = config.services.uhub;
-
-  uhubPkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; };
-
-  pluginConfig = ""
-  + optionalString cfg.plugins.authSqlite.enable ''
-    plugin ${uhubPkg.mod_auth_sqlite}/mod_auth_sqlite.so "file=${cfg.plugins.authSqlite.file}"
-  ''
-  + optionalString cfg.plugins.logging.enable ''
-    plugin ${uhubPkg.mod_logging}/mod_logging.so ${if cfg.plugins.logging.syslog then "syslog=true" else "file=${cfg.plugins.logging.file}"}
-  ''
-  + optionalString cfg.plugins.welcome.enable ''
-    plugin ${uhubPkg.mod_welcome}/mod_welcome.so "motd=${pkgs.writeText "motd.txt"  cfg.plugins.welcome.motd} rules=${pkgs.writeText "rules.txt" cfg.plugins.welcome.rules}"
-  ''
-  + optionalString cfg.plugins.history.enable ''
-    plugin ${uhubPkg.mod_chat_history}/mod_chat_history.so "history_max=${toString cfg.plugins.history.max} history_default=${toString cfg.plugins.history.default} history_connect=${toString cfg.plugins.history.connect}"
-  '';
-
-  uhubConfigFile = pkgs.writeText "uhub.conf" ''
-    file_acl=${pkgs.writeText "users.conf" cfg.aclConfig}
-    file_plugins=${pkgs.writeText "plugins.conf" pluginConfig}
-    server_bind_addr=${cfg.address}
-    server_port=${toString cfg.port}
-    ${lib.optionalString cfg.enableTLS "tls_enable=yes"}
-    ${cfg.hubConfig}
-  '';
-
-in
-
-{
+  settingsFormat = {
+    type = with lib.types; attrsOf (oneOf [ bool int str ]);
+    generate = name: attrs:
+      pkgs.writeText name (lib.strings.concatStringsSep "\n"
+        (lib.attrsets.mapAttrsToList
+          (key: value: "${key}=${builtins.toJSON value}") attrs));
+  };
+in {
   options = {
 
-    services.uhub = {
-
-      enable = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to enable the uhub ADC hub.";
-      };
-
-      port = mkOption {
-        type = types.int;
-        default = 1511;
-        description = "TCP port to bind the hub to.";
-      };
-
-      address = mkOption {
-        type = types.str;
-        default = "any";
-        description = "Address to bind the hub to.";
-      };
-
-      enableTLS = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Whether to enable TLS support.";
-      };
+    services.uhub = mkOption {
+      default = { };
+      description = "Uhub ADC hub instances";
+      type = types.attrsOf (types.submodule {
+        options = {
 
-      hubConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = "Contents of uhub configuration file.";
-      };
+          enable = mkEnableOption "hub instance" // { default = true; };
 
-      aclConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = "Contents of user ACL configuration file.";
-      };
-
-      plugins = {
-
-        authSqlite = {
-          enable = mkOption {
+          enableTLS = mkOption {
             type = types.bool;
             default = false;
-            description = "Whether to enable the Sqlite authentication database plugin";
-          };
-          file = mkOption {
-            type = types.path;
-            example = "/var/db/uhub-users";
-            description = "Path to user database. Use the uhub-passwd utility to create the database and add/remove users.";
+            description = "Whether to enable TLS support.";
           };
-        };
 
-        logging = {
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = "Whether to enable the logging plugin.";
-          };
-          file = mkOption {
-            type = types.str;
-            default = "";
-            description = "Path of log file.";
-          };
-          syslog = mkOption {
-            type = types.bool;
-            default = false;
-            description = "If true then the system log is used instead of writing to file.";
-          };
-        };
-
-        welcome = {
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = "Whether to enable the welcome plugin.";
-          };
-          motd = mkOption {
-            default = "";
-            type = types.lines;
+          settings = mkOption {
+            inherit (settingsFormat) type;
             description = ''
-              Welcome message displayed to clients after connecting
-              and with the <literal>!motd</literal> command.
+              Configuration of uhub.
+              See https://www.uhub.org/doc/config.php for a list of options.
             '';
+            default = { };
+            example = {
+              server_bind_addr = "any";
+              server_port = 1511;
+              hub_name = "My Public Hub";
+              hub_description = "Yet another ADC hub";
+              max_users = 150;
+            };
           };
-          rules = mkOption {
-            default = "";
-            type = types.lines;
-            description = ''
-              Rules message, displayed to clients with the <literal>!rules</literal> command.
-            '';
-          };
-        };
 
-        history = {
-          enable = mkOption {
-            type = types.bool;
-            default = false;
-            description = "Whether to enable the history plugin.";
+          plugins = mkOption {
+            description = "Uhub plugin configuration.";
+            type = with types;
+              listOf (submodule {
+                options = {
+                  plugin = mkOption {
+                    type = path;
+                    example = literalExample
+                      "$${pkgs.uhub}/plugins/mod_auth_sqlite.so";
+                    description = "Path to plugin file.";
+                  };
+                  settings = mkOption {
+                    description = "Settings specific to this plugin.";
+                    type = with types; attrsOf str;
+                    example = { file = "/etc/uhub/users.db"; };
+                  };
+                };
+              });
+            default = [ ];
           };
-          max = mkOption {
-            type = types.int;
-            default = 200;
-            description = "The maximum number of messages to keep in history";
-          };
-          default = mkOption {
-            type = types.int;
-            default = 10;
-            description = "When !history is provided without arguments, then this default number of messages are returned.";
-          };
-          connect = mkOption {
-            type = types.int;
-            default = 5;
-            description = "The number of chat history messages to send when users connect (0 = do not send any history).";
-          };
-        };
 
-      };
+        };
+      });
     };
 
   };
 
-  config = mkIf cfg.enable {
-
-    users = {
-      users.uhub.uid = config.ids.uids.uhub;
-      groups.uhub.gid = config.ids.gids.uhub;
-    };
-
-    systemd.services.uhub = {
-      description = "high performance peer-to-peer hub for the ADC network";
-      after = [ "network.target" ];
-      wantedBy = [ "multi-user.target" ];
-      serviceConfig = {
-        Type = "notify";
-        ExecStart  = "${uhubPkg}/bin/uhub -c ${uhubConfigFile} -u uhub -g uhub -L";
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+  config = let
+    hubs = lib.attrsets.filterAttrs (_: cfg: cfg.enable) config.services.uhub;
+  in {
+
+    environment.etc = lib.attrsets.mapAttrs' (name: cfg:
+      let
+        settings' = cfg.settings // {
+          tls_enable = cfg.enableTLS;
+          file_plugins = pkgs.writeText "uhub-plugins.conf"
+            (lib.strings.concatStringsSep "\n" (map ({ plugin, settings }:
+              "plugin ${plugin} ${
+                toString
+                (lib.attrsets.mapAttrsToList (key: value: ''"${key}=${value}"'')
+                  settings)
+              }") cfg.plugins));
+        };
+      in {
+        name = "uhub/${name}.conf";
+        value.source = settingsFormat.generate "uhub-${name}.conf" settings';
+      }) hubs;
+
+    systemd.services = lib.attrsets.mapAttrs' (name: cfg: {
+      name = "uhub-${name}";
+      value = let pkg = pkgs.uhub.override { tlsSupport = cfg.enableTLS; };
+      in {
+        description = "high performance peer-to-peer hub for the ADC network";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        reloadIfChanged = true;
+        serviceConfig = {
+          Type = "notify";
+          ExecStart = "${pkg}/bin/uhub -c /etc/uhub/${name}.conf -L";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+          DynamicUser = true;
+        };
       };
-    };
+    }) hubs;
   };
 
 }
diff --git a/nixpkgs/nixos/modules/services/monitoring/grafana.nix b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
index e0b2624b6cac..fb67bbfb8420 100644
--- a/nixpkgs/nixos/modules/services/monitoring/grafana.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/grafana.nix
@@ -6,6 +6,8 @@ let
   cfg = config.services.grafana;
   opt = options.services.grafana;
   declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins);
+  useMysql = cfg.database.type == "mysql";
+  usePostgresql = cfg.database.type == "postgres";
 
   envOptions = {
     PATHS_DATA = cfg.dataDir;
@@ -635,7 +637,7 @@ in {
     systemd.services.grafana = {
       description = "Grafana Service Daemon";
       wantedBy = ["multi-user.target"];
-      after = ["networking.target"];
+      after = ["networking.target"] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
       environment = {
         QT_QPA_PLATFORM = "offscreen";
       } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
diff --git a/nixpkgs/nixos/modules/services/monitoring/nagios.nix b/nixpkgs/nixos/modules/services/monitoring/nagios.nix
index 61214508a9c6..0afaefe04e18 100644
--- a/nixpkgs/nixos/modules/services/monitoring/nagios.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/nagios.nix
@@ -102,8 +102,8 @@ in
 
       plugins = mkOption {
         type = types.listOf types.package;
-        default = with pkgs; [ nagiosPluginsOfficial ssmtp mailutils ];
-        defaultText = "[pkgs.nagiosPluginsOfficial pkgs.ssmtp pkgs.mailutils]";
+        default = with pkgs; [ monitoring-plugins ssmtp mailutils ];
+        defaultText = "[pkgs.monitoring-plugins pkgs.ssmtp pkgs.mailutils]";
         description = "
           Packages to be added to the Nagios <envar>PATH</envar>.
           Typically used to add plugins, but can be anything.
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
index 3be247ffb24e..1161d18ab14b 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/default.nix
@@ -10,7 +10,7 @@ let
   # a wrapper that verifies that the configuration is valid
   promtoolCheck = what: name: file:
     if cfg.checkConfig then
-      pkgs.runCommandNoCCLocal
+      pkgs.runCommandLocal
         "${name}-${replaceStrings [" "] [""] what}-checked"
         { buildInputs = [ cfg.package ]; } ''
       ln -s ${file} $out
@@ -19,7 +19,7 @@ let
 
   # Pretty-print JSON to a file
   writePrettyJSON = name: x:
-    pkgs.runCommandNoCCLocal name {} ''
+    pkgs.runCommandLocal name {} ''
       echo '${builtins.toJSON x}' | ${pkgs.jq}/bin/jq . > $out
     '';
 
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
index d648de6a4148..9182c2f2ed87 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -33,6 +33,7 @@ let
     "domain"
     "dovecot"
     "fritzbox"
+    "influxdb"
     "json"
     "jitsi"
     "kea"
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
new file mode 100644
index 000000000000..ba45173e946a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.influxdb;
+in
+{
+  port = 9122;
+  extraOpts = {
+    sampleExpiry = mkOption {
+      type = types.str;
+      default = "5m";
+      example = "10m";
+      description = "How long a sample is valid for";
+    };
+    udpBindAddress = mkOption {
+      type = types.str;
+      default = ":9122";
+      example = "192.0.2.1:9122";
+      description = "Address on which to listen for udp packets";
+    };
+  };
+  serviceOpts = {
+    serviceConfig = {
+      RuntimeDirectory = "prometheus-influxdb-exporter";
+      ExecStart = ''
+        ${pkgs.prometheus-influxdb-exporter}/bin/influxdb_exporter \
+        --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+        --influxdb.sample-expiry ${cfg.sampleExpiry} ${concatStringsSep " " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/thanos.nix b/nixpkgs/nixos/modules/services/monitoring/thanos.nix
index 474ea4b25054..96addf392bd7 100644
--- a/nixpkgs/nixos/modules/services/monitoring/thanos.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/thanos.nix
@@ -63,7 +63,7 @@ let
     };
   };
 
-  toYAML = name: attrs: pkgs.runCommandNoCC name {
+  toYAML = name: attrs: pkgs.runCommand name {
     preferLocalBuild = true;
     json = builtins.toFile "${name}.json" (builtins.toJSON attrs);
     nativeBuildInputs = [ pkgs.remarshal ];
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix b/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix
index 2748571be1f7..57f5f6b006c8 100644
--- a/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixpkgs/nixos/modules/services/network-filesystems/ipfs.nix
@@ -5,36 +5,41 @@ let
   opt = options.services.ipfs;
 
   ipfsFlags = toString ([
-    (optionalString  cfg.autoMount                   "--mount")
-    (optionalString  cfg.enableGC                    "--enable-gc")
-    (optionalString (cfg.serviceFdlimit != null)     "--manage-fdlimit=false")
-    (optionalString (cfg.defaultMode == "offline")   "--offline")
+    (optionalString cfg.autoMount "--mount")
+    (optionalString cfg.enableGC "--enable-gc")
+    (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false")
+    (optionalString (cfg.defaultMode == "offline") "--offline")
     (optionalString (cfg.defaultMode == "norouting") "--routing=none")
   ] ++ cfg.extraFlags);
 
   splitMulitaddr = addrRaw: lib.tail (lib.splitString "/" addrRaw);
 
-  multiaddrToListenStream = addrRaw: let
+  multiaddrToListenStream = addrRaw:
+    let
       addr = splitMulitaddr addrRaw;
       s = builtins.elemAt addr;
-    in if s 0 == "ip4" && s 2 == "tcp"
-      then "${s 1}:${s 3}"
+    in
+    if s 0 == "ip4" && s 2 == "tcp"
+    then "${s 1}:${s 3}"
     else if s 0 == "ip6" && s 2 == "tcp"
-      then "[${s 1}]:${s 3}"
+    then "[${s 1}]:${s 3}"
     else if s 0 == "unix"
-      then "/${lib.concatStringsSep "/" (lib.tail addr)}"
+    then "/${lib.concatStringsSep "/" (lib.tail addr)}"
     else null; # not valid for listen stream, skip
 
-  multiaddrToListenDatagram = addrRaw: let
+  multiaddrToListenDatagram = addrRaw:
+    let
       addr = splitMulitaddr addrRaw;
       s = builtins.elemAt addr;
-    in if s 0 == "ip4" && s 2 == "udp"
-      then "${s 1}:${s 3}"
+    in
+    if s 0 == "ip4" && s 2 == "udp"
+    then "${s 1}:${s 3}"
     else if s 0 == "ip6" && s 2 == "udp"
-      then "[${s 1}]:${s 3}"
+    then "[${s 1}]:${s 3}"
     else null; # not valid for listen datagram, skip
 
-in {
+in
+{
 
   ###### interface
 
@@ -65,9 +70,10 @@ in {
 
       dataDir = mkOption {
         type = types.str;
-        default = if versionAtLeast config.system.stateVersion "17.09"
-                  then "/var/lib/ipfs"
-                  else "/var/lib/ipfs/.ipfs";
+        default =
+          if versionAtLeast config.system.stateVersion "17.09"
+          then "/var/lib/ipfs"
+          else "/var/lib/ipfs/.ipfs";
         description = "The data dir for IPFS";
       };
 
@@ -83,6 +89,12 @@ in {
         description = "Whether IPFS should try to mount /ipfs and /ipns at startup.";
       };
 
+      autoMigrate = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Whether IPFS should try to run the fs-repo-migration at startup.";
+      };
+
       ipfsMountDir = mkOption {
         type = types.str;
         default = "/ipfs";
@@ -137,7 +149,7 @@ in {
           These are applied last, so may override configuration set by other options in this module.
           Keep in mind that this configuration is stateful; i.e., unsetting anything in here does not reset the value to the default!
         '';
-        default = {};
+        default = { };
         example = {
           Datastore.StorageMax = "100GB";
           Discovery.MDNS.Enabled = false;
@@ -153,7 +165,7 @@ in {
       extraFlags = mkOption {
         type = types.listOf types.str;
         description = "Extra flags passed to the IPFS daemon";
-        default = [];
+        default = [ ];
       };
 
       localDiscovery = mkOption {
@@ -168,7 +180,7 @@ in {
         type = types.nullOr types.int;
         default = null;
         description = "The fdlimit for the IPFS systemd unit or <literal>null</literal> to have the daemon attempt to manage it";
-        example = 64*1024;
+        example = 64 * 1024;
       };
 
       startWhenNeeded = mkOption {
@@ -186,6 +198,9 @@ in {
     environment.systemPackages = [ cfg.package ];
     environment.variables.IPFS_PATH = cfg.dataDir;
 
+    # https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size
+    boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000;
+
     programs.fuse = mkIf cfg.autoMount {
       userAllowOther = true;
     };
@@ -226,33 +241,36 @@ in {
             ${optionalString (! cfg.localDiscovery) "--profile=server"}
         else
           ${if cfg.localDiscovery
-            then "ipfs config profile apply local-discovery"
-            else "ipfs config profile apply server"
+            then "ipfs --offline config profile apply local-discovery"
+            else "ipfs --offline config profile apply server"
           }
         fi
       '' + optionalString cfg.autoMount ''
         ipfs --offline config Mounts.FuseAllowOther --json true
         ipfs --offline config Mounts.IPFS ${cfg.ipfsMountDir}
         ipfs --offline config Mounts.IPNS ${cfg.ipnsMountDir}
+      '' + optionalString cfg.autoMigrate ''
+        ${pkgs.ipfs-migrator}/bin/fs-repo-migrations -y
       '' + concatStringsSep "\n" (collect
-            isString
-            (mapAttrsRecursive
-              (path: value:
-              # Using heredoc below so that the value is never improperly quoted
-              ''
-                read value <<EOF
-                ${builtins.toJSON value}
-                EOF
-                ipfs --offline config --json "${concatStringsSep "." path}" "$value"
-              '')
-              ({ Addresses.API = cfg.apiAddress;
-                 Addresses.Gateway = cfg.gatewayAddress;
-                 Addresses.Swarm = cfg.swarmAddress;
-              } //
-              cfg.extraConfig))
-          );
+        isString
+        (mapAttrsRecursive
+          (path: value:
+            # Using heredoc below so that the value is never improperly quoted
+            ''
+              read value <<EOF
+              ${builtins.toJSON value}
+              EOF
+              ipfs --offline config --json "${concatStringsSep "." path}" "$value"
+            '')
+          ({
+            Addresses.API = cfg.apiAddress;
+            Addresses.Gateway = cfg.gatewayAddress;
+            Addresses.Swarm = cfg.swarmAddress;
+          } //
+          cfg.extraConfig))
+      );
       serviceConfig = {
-        ExecStart = ["" "${cfg.package}/bin/ipfs daemon ${ipfsFlags}"];
+        ExecStart = [ "" "${cfg.package}/bin/ipfs daemon ${ipfsFlags}" ];
         User = cfg.user;
         Group = cfg.group;
       } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; };
@@ -263,12 +281,16 @@ in {
     systemd.sockets.ipfs-gateway = {
       wantedBy = [ "sockets.target" ];
       socketConfig = {
-        ListenStream = let
+        ListenStream =
+          let
             fromCfg = multiaddrToListenStream cfg.gatewayAddress;
-          in [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
-        ListenDatagram = let
+          in
+          [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
+        ListenDatagram =
+          let
             fromCfg = multiaddrToListenDatagram cfg.gatewayAddress;
-          in [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
+          in
+          [ "" ] ++ lib.optional (fromCfg != null) fromCfg;
       };
     };
 
@@ -276,9 +298,11 @@ in {
       wantedBy = [ "sockets.target" ];
       # We also include "%t/ipfs.sock" because there is no way to put the "%t"
       # in the multiaddr.
-      socketConfig.ListenStream = let
+      socketConfig.ListenStream =
+        let
           fromCfg = multiaddrToListenStream cfg.apiAddress;
-        in [ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg;
+        in
+        [ "" "%t/ipfs.sock" ] ++ lib.optional (fromCfg != null) fromCfg;
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix b/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix
new file mode 100644
index 000000000000..f1806c5af0a9
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/network-filesystems/litestream/default.nix
@@ -0,0 +1,100 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.litestream;
+  settingsFormat = pkgs.formats.yaml {};
+in
+{
+  options.services.litestream = {
+    enable = mkEnableOption "litestream";
+
+    package = mkOption {
+      description = "Package to use.";
+      default = pkgs.litestream;
+      defaultText = "pkgs.litestream";
+      type = types.package;
+    };
+
+    settings = mkOption {
+      description = ''
+        See the <link xlink:href="https://litestream.io/reference/config/">documentation</link>.
+      '';
+      type = settingsFormat.type;
+      example = {
+        dbs = [
+          {
+            path = "/var/lib/db1";
+            replicas = [
+              {
+                url = "s3://mybkt.litestream.io/db1";
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    environmentFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/secrets/litestream";
+      description = ''
+        Environment file as defined in <citerefentry>
+        <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
+        </citerefentry>.
+
+        Secrets may be passed to the service without adding them to the
+        world-readable Nix store, by specifying placeholder variables as
+        the option value in Nix and setting these variables accordingly in the
+        environment file.
+
+        By default, Litestream will perform environment variable expansion
+        within the config file before reading it. Any references to ''$VAR or
+        ''${VAR} formatted variables will be replaced with their environment
+        variable values. If no value is set then it will be replaced with an
+        empty string.
+
+        <programlisting>
+          # Content of the environment file
+          LITESTREAM_ACCESS_KEY_ID=AKIAxxxxxxxxxxxxxxxx
+          LITESTREAM_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxxxxxxxx
+        </programlisting>
+
+        Note that this file needs to be available on the host on which
+        this exporter is running.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    environment.etc = {
+      "litestream.yml" = {
+        source = settingsFormat.generate "litestream-config.yaml" cfg.settings;
+      };
+    };
+
+    systemd.services.litestream = {
+      description = "Litestream";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ];
+      serviceConfig = {
+        EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
+        ExecStart = "${cfg.package}/bin/litestream replicate";
+        Restart = "always";
+        User = "litestream";
+        Group = "litestream";
+      };
+    };
+
+    users.users.litestream = {
+      description = "Litestream user";
+      group = "litestream";
+      isSystemUser = true;
+    };
+    users.groups.litestream = {};
+  };
+  meta.doc = ./litestream.xml;
+}
diff --git a/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml b/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml
new file mode 100644
index 000000000000..598f9be8cf63
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/network-filesystems/litestream/litestream.xml
@@ -0,0 +1,65 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-litestream">
+ <title>Litestream</title>
+ <para>
+  <link xlink:href="https://litestream.io/">Litestream</link> is a standalone streaming
+  replication tool for SQLite.
+ </para>
+
+ <section xml:id="module-services-litestream-configuration">
+  <title>Configuration</title>
+
+  <para>
+   Litestream service is managed by a dedicated user named <literal>litestream</literal>
+   which needs permission to the database file. Here's an example config which gives
+   required permissions to access <link linkend="opt-services.grafana.database.path">
+   grafana database</link>:
+<programlisting>
+{ pkgs, ... }:
+{
+  users.users.litestream.extraGroups = [ "grafana" ];
+
+  systemd.services.grafana.serviceConfig.ExecStartPost = "+" + pkgs.writeShellScript "grant-grafana-permissions" ''
+    timeout=10
+
+    while [ ! -f /var/lib/grafana/data/grafana.db ];
+    do
+      if [ "$timeout" == 0 ]; then
+        echo "ERROR: Timeout while waiting for /var/lib/grafana/data/grafana.db."
+        exit 1
+      fi
+
+      sleep 1
+
+      ((timeout--))
+    done
+
+    find /var/lib/grafana -type d -exec chmod -v 775 {} \;
+    find /var/lib/grafana -type f -exec chmod -v 660 {} \;
+  '';
+
+  services.litestream = {
+    enable = true;
+
+    environmentFile = "/run/secrets/litestream";
+
+    settings = {
+      dbs = [
+        {
+          path = "/var/lib/grafana/data/grafana.db";
+          replicas = [{
+            url = "s3://mybkt.litestream.io/grafana";
+          }];
+        }
+      ];
+    };
+  };
+}
+</programlisting>
+  </para>
+ </section>
+
+</chapter>
diff --git a/nixpkgs/nixos/modules/services/networking/autossh.nix b/nixpkgs/nixos/modules/services/networking/autossh.nix
index a8d9a027e9fa..245f2bfc2cf3 100644
--- a/nixpkgs/nixos/modules/services/networking/autossh.nix
+++ b/nixpkgs/nixos/modules/services/networking/autossh.nix
@@ -79,7 +79,7 @@ in
 
     systemd.services =
 
-      lib.fold ( s : acc : acc //
+      lib.foldr ( s : acc : acc //
         {
           "autossh-${s.name}" =
             let
diff --git a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
index 0b7d5575c11f..020a817f2596 100644
--- a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix
@@ -240,8 +240,8 @@ in
 
     system.nssModules = optional cfg.nssmdns pkgs.nssmdns;
     system.nssDatabases.hosts = optionals cfg.nssmdns (mkMerge [
-      (mkOrder 900 [ "mdns_minimal [NOTFOUND=return]" ]) # must be before resolve
-      (mkOrder 1501 [ "mdns" ]) # 1501 to ensure it's after dns
+      (mkBefore [ "mdns_minimal [NOTFOUND=return]" ]) # before resolve
+      (mkAfter [ "mdns" ]) # after dns
     ]);
 
     environment.systemPackages = [ pkgs.avahi ];
diff --git a/nixpkgs/nixos/modules/services/networking/bird.nix b/nixpkgs/nixos/modules/services/networking/bird.nix
index 1923afdf83f2..c14adbda3c5a 100644
--- a/nixpkgs/nixos/modules/services/networking/bird.nix
+++ b/nixpkgs/nixos/modules/services/networking/bird.nix
@@ -10,8 +10,8 @@ let
       birdBin = if variant == "bird6" then "bird6" else "bird";
       birdc = if variant == "bird6" then "birdc6" else "birdc";
       descr =
-        { bird = "1.9.x with IPv4 suport";
-          bird6 = "1.9.x with IPv6 suport";
+        { bird = "1.6.x with IPv4 support";
+          bird6 = "1.6.x with IPv6 support";
           bird2 = "2.x";
         }.${variant};
     in {
diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix
index f1a504b3e3f4..ca95d00c2ff8 100644
--- a/nixpkgs/nixos/modules/services/networking/cjdns.nix
+++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix
@@ -39,7 +39,7 @@ let
   };
 
   # Additional /etc/hosts entries for peers with an associated hostname
-  cjdnsExtraHosts = pkgs.runCommandNoCC "cjdns-hosts" {} ''
+  cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" {} ''
     exec >$out
     ${concatStringsSep "\n" (mapAttrsToList (k: v:
         optionalString (v.hostname != "")
diff --git a/nixpkgs/nixos/modules/services/networking/connman.nix b/nixpkgs/nixos/modules/services/networking/connman.nix
index 11f66b05df12..608672c6446c 100644
--- a/nixpkgs/nixos/modules/services/networking/connman.nix
+++ b/nixpkgs/nixos/modules/services/networking/connman.nix
@@ -150,6 +150,7 @@ in {
       useDHCP = false;
       wireless = {
         enable = mkIf (!enableIwd) true;
+        dbusControlled = true;
         iwd = mkIf enableIwd {
           enable = true;
         };
diff --git a/nixpkgs/nixos/modules/services/networking/epmd.nix b/nixpkgs/nixos/modules/services/networking/epmd.nix
index f7cdc0fe79c0..3899d164f16a 100644
--- a/nixpkgs/nixos/modules/services/networking/epmd.nix
+++ b/nixpkgs/nixos/modules/services/networking/epmd.nix
@@ -4,9 +4,7 @@ with lib;
 
 let
   cfg = config.services.epmd;
-
 in
-
 {
   ###### interface
   options.services.epmd = {
@@ -27,16 +25,31 @@ in
         an Erlang runtime that is already installed for other purposes.
       '';
     };
+    listenStream = mkOption
+      {
+        type = types.str;
+        default = "[::]:4369";
+        description = ''
+          the listenStream used by the systemd socket.
+          see https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream= for more informations.
+          use this to change the port epmd will run on.
+          if not defined, epmd will use "[::]:4369"
+        '';
+      };
   };
 
   ###### implementation
   config = mkIf cfg.enable {
+    assertions = [{
+      assertion = cfg.listenStream == "[::]:4369" -> config.networking.enableIPv6;
+      message = "epmd listens by default on ipv6, enable ipv6 or change config.services.epmd.listenStream";
+    }];
     systemd.sockets.epmd = rec {
       description = "Erlang Port Mapper Daemon Activation Socket";
       wantedBy = [ "sockets.target" ];
       before = wantedBy;
       socketConfig = {
-        ListenStream = "4369";
+        ListenStream = cfg.listenStream;
         Accept = "false";
       };
     };
diff --git a/nixpkgs/nixos/modules/services/networking/firewall.nix b/nixpkgs/nixos/modules/services/networking/firewall.nix
index cdc3a172ea70..f982621e2328 100644
--- a/nixpkgs/nixos/modules/services/networking/firewall.nix
+++ b/nixpkgs/nixos/modules/services/networking/firewall.nix
@@ -339,6 +339,8 @@ in
         description =
           ''
             Whether to log rejected or dropped incoming connections.
+            Note: The logs are found in the kernel logs, i.e. dmesg
+            or journalctl -k.
           '';
       };
 
@@ -350,6 +352,8 @@ in
             Whether to log all rejected or dropped incoming packets.
             This tends to give a lot of log messages, so it's mostly
             useful for debugging.
+            Note: The logs are found in the kernel logs, i.e. dmesg
+            or journalctl -k.
           '';
       };
 
diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix
index 93a21fd4c97e..fba0d817006e 100644
--- a/nixpkgs/nixos/modules/services/networking/i2pd.nix
+++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix
@@ -32,9 +32,9 @@ let
       description = "Bind address for ${name} endpoint.";
     };
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = port;
-      description = "Bind port for ${name} endoint.";
+      description = "Bind port for ${name} endpoint.";
     };
   };
 
diff --git a/nixpkgs/nixos/modules/services/networking/iwd.nix b/nixpkgs/nixos/modules/services/networking/iwd.nix
index 99e5e78badd2..8835f7f9372d 100644
--- a/nixpkgs/nixos/modules/services/networking/iwd.nix
+++ b/nixpkgs/nixos/modules/services/networking/iwd.nix
@@ -4,8 +4,31 @@ with lib;
 
 let
   cfg = config.networking.wireless.iwd;
+  ini = pkgs.formats.ini { };
+  configFile = ini.generate "main.conf" cfg.settings;
 in {
-  options.networking.wireless.iwd.enable = mkEnableOption "iwd";
+  options.networking.wireless.iwd = {
+    enable = mkEnableOption "iwd";
+
+    settings = mkOption {
+      type = ini.type;
+      default = { };
+
+      example = {
+        Settings.AutoConnect = true;
+
+        Network = {
+          EnableIPv6 = true;
+          RoutePriorityOffset = 300;
+        };
+      };
+
+      description = ''
+        Options passed to iwd.
+        See <link xlink:href="https://iwd.wiki.kernel.org/networkconfigurationsettings">here</link> for supported options.
+      '';
+    };
+  };
 
   config = mkIf cfg.enable {
     assertions = [{
@@ -15,6 +38,8 @@ in {
       '';
     }];
 
+    environment.etc."iwd/main.conf".source = configFile;
+
     # for iwctl
     environment.systemPackages =  [ pkgs.iwd ];
 
@@ -27,7 +52,10 @@ in {
       linkConfig.NamePolicy = "keep kernel";
     };
 
-    systemd.services.iwd.wantedBy = [ "multi-user.target" ];
+    systemd.services.iwd = {
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [ configFile ];
+    };
   };
 
   meta.maintainers = with lib.maintainers; [ mic92 dtzWill ];
diff --git a/nixpkgs/nixos/modules/services/networking/kea.nix b/nixpkgs/nixos/modules/services/networking/kea.nix
index 72773b83a496..b11402204aec 100644
--- a/nixpkgs/nixos/modules/services/networking/kea.nix
+++ b/nixpkgs/nixos/modules/services/networking/kea.nix
@@ -238,6 +238,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        ctrlAgentConfig
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
         KillMode = "process";
@@ -269,6 +273,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        dhcp4Config
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-dhcp4 -c /etc/kea/dhcp4-server.conf ${lib.escapeShellArgs cfg.dhcp4.extraArgs}";
         # Kea does not request capabilities by itself
@@ -307,6 +315,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        dhcp6Config
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-dhcp6 -c /etc/kea/dhcp6-server.conf ${lib.escapeShellArgs cfg.dhcp6.extraArgs}";
         # Kea does not request capabilities by itself
@@ -343,6 +355,10 @@ in
         KEA_PIDFILE_DIR = "/run/kea";
       };
 
+      restartTriggers = [
+        dhcpDdnsConfig
+      ];
+
       serviceConfig = {
         ExecStart = "${package}/bin/kea-dhcp-ddns -c /etc/kea/dhcp-ddns.conf ${lib.escapeShellArgs cfg.dhcp-ddns.extraArgs}";
         AmbientCapabilites = [
diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
index 8ce71f26b3ee..9ec1ddc929e1 100644
--- a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
+++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
@@ -9,6 +9,7 @@ with lib;
     default = false;
     description = ''
       This option enables Mullvad VPN daemon.
+      This sets <option>networking.firewall.checkReversePath</option> to "loose", which might be undesirable for security.
     '';
   };
 
@@ -18,6 +19,9 @@ with lib;
     # mullvad-daemon writes to /etc/iproute2/rt_tables
     networking.iproute2.enable = true;
 
+    # See https://github.com/NixOS/nixpkgs/issues/113589
+    networking.firewall.checkReversePath = "loose";
+
     systemd.services.mullvad-daemon = {
       description = "Mullvad VPN daemon";
       wantedBy = [ "multi-user.target" ];
@@ -42,5 +46,5 @@ with lib;
     };
   };
 
-  meta.maintainers = [ maintainers.xfix ];
+  meta.maintainers = with maintainers; [ ymarkus ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/nats.nix b/nixpkgs/nixos/modules/services/networking/nats.nix
new file mode 100644
index 000000000000..eb0c65bc6561
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/nats.nix
@@ -0,0 +1,159 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.nats;
+
+  format = pkgs.formats.json { };
+
+  configFile = format.generate "nats.conf" cfg.settings;
+
+in {
+
+  ### Interface
+
+  options = {
+    services.nats = {
+      enable = mkEnableOption "NATS messaging system";
+
+      user = mkOption {
+        type = types.str;
+        default = "nats";
+        description = "User account under which NATS runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "nats";
+        description = "Group under which NATS runs.";
+      };
+
+      serverName = mkOption {
+        default = "nats";
+        example = "n1-c3";
+        type = types.str;
+        description = ''
+          Name of the NATS server, must be unique if clustered.
+        '';
+      };
+
+      jetstream = mkEnableOption "JetStream";
+
+      port = mkOption {
+        default = 4222;
+        example = 4222;
+        type = types.port;
+        description = ''
+          Port on which to listen.
+        '';
+      };
+
+      dataDir = mkOption {
+        default = "/var/lib/nats";
+        type = types.path;
+        description = ''
+          The NATS data directory. Only used if JetStream is enabled, for
+          storing stream metadata and messages.
+
+          If left as the default value this directory will automatically be
+          created before the NATS server starts, otherwise the sysadmin is
+          responsible for ensuring the directory exists with appropriate
+          ownership and permissions.
+        '';
+      };
+
+      settings = mkOption {
+        default = { };
+        type = format.type;
+        example = literalExample ''
+          {
+            jetstream = {
+              max_mem = "1G";
+              max_file = "10G";
+            };
+          };
+        '';
+        description = ''
+          Declarative NATS configuration. See the
+          <link xlink:href="https://docs.nats.io/nats-server/configuration">
+          NATS documentation</link> for a list of options.
+        '';
+      };
+    };
+  };
+
+  ### Implementation
+
+  config = mkIf cfg.enable {
+    services.nats.settings = {
+      server_name = cfg.serverName;
+      port = cfg.port;
+      jetstream = optionalAttrs cfg.jetstream { store_dir = cfg.dataDir; };
+    };
+
+    systemd.services.nats = {
+      description = "NATS messaging system";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = mkMerge [
+        (mkIf (cfg.dataDir == "/var/lib/nats") {
+          StateDirectory = "nats";
+          StateDirectoryMode = "0750";
+        })
+        {
+          Type = "simple";
+          ExecStart = "${pkgs.nats-server}/bin/nats-server -c ${configFile}";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+          ExecStop = "${pkgs.coreutils}/bin/kill -SIGINT $MAINPID";
+          Restart = "on-failure";
+
+          User = cfg.user;
+          Group = cfg.group;
+
+          # Hardening
+          CapabilityBoundingSet = "";
+          LimitNOFILE = 800000; # JetStream requires 2 FDs open per stream.
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProcSubset = "pid";
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          ReadOnlyPaths = [ ];
+          ReadWritePaths = [ cfg.dataDir ];
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+          UMask = "0077";
+        }
+      ];
+    };
+
+    users.users = mkIf (cfg.user == "nats") {
+      nats = {
+        description = "NATS daemon user";
+        isSystemUser = true;
+        group = cfg.group;
+        home = cfg.dataDir;
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "nats") { nats = { }; };
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
index 064018057cdb..c8861171dd6c 100644
--- a/nixpkgs/nixos/modules/services/networking/networkmanager.nix
+++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix
@@ -6,7 +6,6 @@ let
   cfg = config.networking.networkmanager;
 
   basePackages = with pkgs; [
-    crda
     modemmanager
     networkmanager
     networkmanager-fortisslvpn
@@ -49,6 +48,7 @@ let
       rc-manager =
         if config.networking.resolvconf.enable then "resolvconf"
         else "unmanaged";
+      firewall-backend = cfg.firewallBackend;
     })
     (mkSection "keyfile" {
       unmanaged-devices =
@@ -244,6 +244,15 @@ in {
         '';
       };
 
+      firewallBackend = mkOption {
+        type = types.enum [ "iptables" "nftables" "none" ];
+        default = "iptables";
+        description = ''
+          Which firewall backend should be used for configuring masquerading with shared mode.
+          If set to none, NetworkManager doesn't manage the configuration at all.
+        '';
+      };
+
       logLevel = mkOption {
         type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ];
         default = "WARN";
@@ -404,6 +413,8 @@ in {
       }
     ];
 
+    hardware.wirelessRegulatoryDatabase = true;
+
     environment.etc = with pkgs; {
       "NetworkManager/NetworkManager.conf".source = configFile;
 
diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix
index cb75142965ea..72f37c32253e 100644
--- a/nixpkgs/nixos/modules/services/networking/nftables.nix
+++ b/nixpkgs/nixos/modules/services/networking/nftables.nix
@@ -103,6 +103,7 @@ in
     }];
     boot.blacklistedKernelModules = [ "ip_tables" ];
     environment.systemPackages = [ pkgs.nftables ];
+    networking.networkmanager.firewallBackend = mkDefault "nftables";
     systemd.services.nftables = {
       description = "nftables firewall";
       before = [ "network-pre.target" ];
diff --git a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
index 96c6444c23a1..ed61c178c685 100644
--- a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
+++ b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix
@@ -81,17 +81,26 @@ in
         '';
       };
 
-      initstepslew = mkOption {
-        type = types.attrsOf (types.either types.bool types.int);
-        default = {
-          enabled = true;
-          threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s)
+      initstepslew = {
+        enabled = mkOption {
+          type = types.bool;
+          default = true;
+          description = ''
+            Allow chronyd to make a rapid measurement of the system clock error
+            at boot time, and to correct the system clock by stepping before
+            normal operation begins.
+          '';
+        };
+
+        threshold = mkOption {
+          type = types.either types.float types.int;
+          default = 1000; # by default, same threshold as 'ntpd -g' (1000s)
+          description = ''
+            The threshold of system clock error (in seconds) above which the
+            clock will be stepped. If the correction required is less than the
+            threshold, a slew is used instead.
+          '';
         };
-        description = ''
-          Allow chronyd to make a rapid measurement of the system clock error at
-          boot time, and to correct the system clock by stepping before normal
-          operation begins.
-        '';
       };
 
       directory = mkOption {
@@ -148,7 +157,7 @@ in
         wantedBy = [ "multi-user.target" ];
         wants    = [ "time-sync.target" ];
         before   = [ "time-sync.target" ];
-        after    = [ "network.target" ];
+        after    = [ "network.target" "nss-lookup.target" ];
         conflicts = [ "ntpd.service" "systemd-timesyncd.service" ];
 
         path = [ chronyPkg ];
diff --git a/nixpkgs/nixos/modules/services/networking/nylon.nix b/nixpkgs/nixos/modules/services/networking/nylon.nix
index bfc358cb12fb..a20fa615af80 100644
--- a/nixpkgs/nixos/modules/services/networking/nylon.nix
+++ b/nixpkgs/nixos/modules/services/networking/nylon.nix
@@ -160,7 +160,7 @@ in
 
     users.groups.nylon.gid = config.ids.gids.nylon;
 
-    systemd.services = fold (a: b: a // b) {} nylonUnits;
+    systemd.services = foldr (a: b: a // b) {} nylonUnits;
 
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/quicktun.nix b/nixpkgs/nixos/modules/services/networking/quicktun.nix
index fb783c836464..438e67d5ebb6 100644
--- a/nixpkgs/nixos/modules/services/networking/quicktun.nix
+++ b/nixpkgs/nixos/modules/services/networking/quicktun.nix
@@ -87,7 +87,7 @@ with lib;
   };
 
   config = mkIf (cfg != []) {
-    systemd.services = fold (a: b: a // b) {} (
+    systemd.services = foldr (a: b: a // b) {} (
       mapAttrsToList (name: qtcfg: {
         "quicktun-${name}" = {
           wantedBy = [ "multi-user.target" ];
diff --git a/nixpkgs/nixos/modules/services/networking/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix
index a808a7f39d05..405808491ea4 100644
--- a/nixpkgs/nixos/modules/services/networking/shout.nix
+++ b/nixpkgs/nixos/modules/services/networking/shout.nix
@@ -41,7 +41,7 @@ in {
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 9000;
       description = "TCP port to listen on for http connections.";
     };
diff --git a/nixpkgs/nixos/modules/services/networking/soju.nix b/nixpkgs/nixos/modules/services/networking/soju.nix
new file mode 100644
index 000000000000..68a33e9dccba
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/soju.nix
@@ -0,0 +1,113 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.soju;
+  stateDir = "/var/lib/soju";
+  listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") cfg.listen;
+  tlsCfg = optionalString (cfg.tlsCertificate != null)
+    "tls ${cfg.tlsCertificate} ${cfg.tlsCertificateKey}";
+  logCfg = optionalString cfg.enableMessageLogging
+    "log fs ${stateDir}/logs";
+
+  configFile = pkgs.writeText "soju.conf" ''
+    ${listenCfg}
+    hostname ${cfg.hostName}
+    ${tlsCfg}
+    db sqlite3 ${stateDir}/soju.db
+    ${logCfg}
+    http-origin ${concatStringsSep " " cfg.httpOrigins}
+    accept-proxy-ip ${concatStringsSep " " cfg.acceptProxyIP}
+
+    ${cfg.extraConfig}
+  '';
+in
+{
+  ###### interface
+
+  options.services.soju = {
+    enable = mkEnableOption "soju";
+
+    listen = mkOption {
+      type = types.listOf types.str;
+      default = [ ":6697" ];
+      description = ''
+        Where soju should listen for incoming connections. See the
+        <literal>listen</literal> directive in
+        <citerefentry><refentrytitle>soju</refentrytitle>
+        <manvolnum>1</manvolnum></citerefentry>.
+      '';
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      default = config.networking.hostName;
+      description = "Server hostname.";
+    };
+
+    tlsCertificate = mkOption {
+      type = types.nullOr types.path;
+      example = "/var/host.cert";
+      description = "Path to server TLS certificate.";
+    };
+
+    tlsCertificateKey = mkOption {
+      type = types.nullOr types.path;
+      example = "/var/host.key";
+      description = "Path to server TLS certificate key.";
+    };
+
+    enableMessageLogging = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to enable message logging.";
+    };
+
+    httpOrigins = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        List of allowed HTTP origins for WebSocket listeners. The parameters are
+        interpreted as shell patterns, see
+        <citerefentry><refentrytitle>glob</refentrytitle>
+        <manvolnum>7</manvolnum></citerefentry>.
+      '';
+    };
+
+    acceptProxyIP = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = ''
+        Allow the specified IPs to act as a proxy. Proxys have the ability to
+        overwrite the remote and local connection addresses (via the X-Forwarded-\*
+        HTTP header fields). The special name "localhost" accepts the loopback
+        addresses 127.0.0.0/8 and ::1/128. By default, all IPs are rejected.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = "Lines added verbatim to the configuration file.";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.soju = {
+      description = "soju IRC bouncer";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      serviceConfig = {
+        DynamicUser = true;
+        Restart = "always";
+        ExecStart = "${pkgs.soju}/bin/soju -config ${configFile}";
+        StateDirectory = "soju";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ malvo ];
+}
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index d804c017f5d6..04879eb7d82d 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -531,11 +531,7 @@ in
             XAuthLocation ${pkgs.xorg.xauth}/bin/xauth
         ''}
 
-        ${if cfg.forwardX11 then ''
-          X11Forwarding yes
-        '' else ''
-          X11Forwarding no
-        ''}
+        X11Forwarding ${if cfg.forwardX11 then "yes" else "no"}
 
         ${optionalString cfg.allowSFTP ''
           Subsystem sftp ${cfg.sftpServerExecutable} ${concatStringsSep " " cfg.sftpFlags}
@@ -568,11 +564,7 @@ in
 
         LogLevel ${cfg.logLevel}
 
-        ${if cfg.useDns then ''
-          UseDNS yes
-        '' else ''
-          UseDNS no
-        ''}
+        UseDNS ${if cfg.useDns then "yes" else "no"}
 
         StrictModes ${if cfg.strictModes then "yes" else "no"}
 
diff --git a/nixpkgs/nixos/modules/services/networking/syncplay.nix b/nixpkgs/nixos/modules/services/networking/syncplay.nix
index e3147c10502c..27a16fb2e29f 100644
--- a/nixpkgs/nixos/modules/services/networking/syncplay.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncplay.nix
@@ -21,7 +21,7 @@ in
       };
 
       port = mkOption {
-        type = types.int;
+        type = types.port;
         default = 8999;
         description = ''
           TCP port to bind to.
diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix
index 28348c7893a0..ebe4d89a0e7f 100644
--- a/nixpkgs/nixos/modules/services/networking/syncthing.nix
+++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix
@@ -5,15 +5,16 @@ with lib;
 let
   cfg = config.services.syncthing;
   defaultUser = "syncthing";
+  defaultGroup = defaultUser;
 
   devices = mapAttrsToList (name: device: {
     deviceID = device.id;
-    inherit (device) name addresses introducer;
-  }) cfg.declarative.devices;
+    inherit (device) name addresses introducer autoAcceptFolders;
+  }) cfg.devices;
 
   folders = mapAttrsToList ( _: folder: {
     inherit (folder) path id label type;
-    devices = map (device: { deviceId = cfg.declarative.devices.${device}.id; }) folder.devices;
+    devices = map (device: { deviceId = cfg.devices.${device}.id; }) folder.devices;
     rescanIntervalS = folder.rescanInterval;
     fsWatcherEnabled = folder.watch;
     fsWatcherDelayS = folder.watchDelay;
@@ -23,215 +24,227 @@ let
   }) (filterAttrs (
     _: folder:
     folder.enable
-  ) cfg.declarative.folders);
-
-  # get the api key by parsing the config.xml
-  getApiKey = pkgs.writers.writeDash "getAPIKey" ''
-    ${pkgs.libxml2}/bin/xmllint \
-      --xpath 'string(configuration/gui/apikey)'\
-      ${cfg.configDir}/config.xml
-  '';
+  ) cfg.folders);
 
   updateConfig = pkgs.writers.writeDash "merge-syncthing-config" ''
     set -efu
-    # wait for syncthing port to open
-    until ${pkgs.curl}/bin/curl -Ss ${cfg.guiAddress} -o /dev/null; do
-      sleep 1
-    done
-
-    API_KEY=$(${getApiKey})
-    OLD_CFG=$(${pkgs.curl}/bin/curl -Ss \
-      -H "X-API-Key: $API_KEY" \
-      ${cfg.guiAddress}/rest/system/config)
-
-    # generate the new config by merging with the nixos config options
-    NEW_CFG=$(echo "$OLD_CFG" | ${pkgs.jq}/bin/jq -s '.[] as $in | $in * {
-      "devices": (${builtins.toJSON devices}${optionalString (! cfg.declarative.overrideDevices) " + $in.devices"}),
-      "folders": (${builtins.toJSON folders}${optionalString (! cfg.declarative.overrideFolders) " + $in.folders"})
-    }')
-
-    # POST the new config to syncthing
-    echo "$NEW_CFG" | ${pkgs.curl}/bin/curl -Ss \
-      -H "X-API-Key: $API_KEY" \
-      ${cfg.guiAddress}/rest/system/config -d @-
-
-    # restart syncthing after sending the new config
-    ${pkgs.curl}/bin/curl -Ss \
-      -H "X-API-Key: $API_KEY" \
-      -X POST \
-      ${cfg.guiAddress}/rest/system/restart
+
+    # get the api key by parsing the config.xml
+    while
+        ! api_key=$(${pkgs.libxml2}/bin/xmllint \
+            --xpath 'string(configuration/gui/apikey)' \
+            ${cfg.configDir}/config.xml)
+    do sleep 1; done
+
+    curl() {
+        ${pkgs.curl}/bin/curl -sSLk -H "X-API-Key: $api_key" \
+            --retry 1000 --retry-delay 1 --retry-all-errors \
+            "$@"
+    }
+
+    # query the old config
+    old_cfg=$(curl ${cfg.guiAddress}/rest/config)
+
+    # generate the new config by merging with the NixOS config options
+    new_cfg=$(printf '%s\n' "$old_cfg" | ${pkgs.jq}/bin/jq -c '. * {
+        "devices": (${builtins.toJSON devices}${optionalString (! cfg.overrideDevices) " + .devices"}),
+        "folders": (${builtins.toJSON folders}${optionalString (! cfg.overrideFolders) " + .folders"})
+    } * ${builtins.toJSON cfg.extraOptions}')
+
+    # send the new config
+    curl -X PUT -d "$new_cfg" ${cfg.guiAddress}/rest/config
+
+    # restart Syncthing if required
+    if curl ${cfg.guiAddress}/rest/config/restart-required |
+       ${pkgs.jq}/bin/jq -e .requiresRestart > /dev/null; then
+        curl -X POST ${cfg.guiAddress}/rest/system/restart
+    fi
   '';
 in {
   ###### interface
   options = {
     services.syncthing = {
 
-      enable = mkEnableOption ''
-        Syncthing - the self-hosted open-source alternative
-        to Dropbox and Bittorrent Sync. Initial interface will be
-        available on http://127.0.0.1:8384/.
-      '';
-
-      declarative = {
-        cert = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Path to users cert.pem file, will be copied into the syncthing's
-            <literal>configDir</literal>
-          '';
-        };
+      enable = mkEnableOption
+        "Syncthing, a self-hosted open-source alternative to Dropbox and Bittorrent Sync";
 
-        key = mkOption {
-          type = types.nullOr types.str;
-          default = null;
-          description = ''
-            Path to users key.pem file, will be copied into the syncthing's
-            <literal>configDir</literal>
-          '';
-        };
+      cert = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Path to the <literal>cert.pem</literal> file, which will be copied into Syncthing's
+          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        '';
+      };
 
-        overrideDevices = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            Whether to delete the devices which are not configured via the
-            <literal>declarative.devices</literal> option.
-            If set to false, devices added via the webinterface will
-            persist but will have to be deleted manually.
-          '';
+      key = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Path to the <literal>key.pem</literal> file, which will be copied into Syncthing's
+          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        '';
+      };
+
+      overrideDevices = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to delete the devices which are not configured via the
+          <link linkend="opt-services.syncthing.devices">devices</link> option.
+          If set to <literal>false</literal>, devices added via the web
+          interface will persist and will have to be deleted manually.
+        '';
+      };
+
+      devices = mkOption {
+        default = {};
+        description = ''
+          Peers/devices which Syncthing should communicate with.
+
+          Note that you can still add devices manually, but those changes
+          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          is enabled.
+        '';
+        example = {
+          bigbox = {
+            id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
+            addresses = [ "tcp://192.168.0.10:51820" ];
+          };
         };
+        type = types.attrsOf (types.submodule ({ name, ... }: {
+          options = {
+
+            name = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The name of the device.
+              '';
+            };
 
-        devices = mkOption {
-          default = {};
-          description = ''
-            Peers/devices which syncthing should communicate with.
-          '';
-          example = {
-            bigbox = {
-              id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU";
-              addresses = [ "tcp://192.168.0.10:51820" ];
+            addresses = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                The addresses used to connect to the device.
+                If this is left empty, dynamic configuration is attempted.
+              '';
+            };
+
+            id = mkOption {
+              type = types.str;
+              description = ''
+                The device ID. See <link xlink:href="https://docs.syncthing.net/dev/device-ids.html"/>.
+              '';
+            };
+
+            introducer = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether the device should act as an introducer and be allowed
+                to add folders on this computer.
+                See <link xlink:href="https://docs.syncthing.net/users/introducer.html"/>.
+              '';
             };
+
+            autoAcceptFolders = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Automatically create or share folders that this device advertises at the default path.
+                See <link xlink:href="https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format"/>.
+              '';
+            };
+
           };
-          type = types.attrsOf (types.submodule ({ name, ... }: {
-            options = {
-
-              name = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  Name of the device
-                '';
-              };
-
-              addresses = mkOption {
-                type = types.listOf types.str;
-                default = [];
-                description = ''
-                  The addresses used to connect to the device.
-                  If this is let empty, dynamic configuration is attempted
-                '';
-              };
-
-              id = mkOption {
-                type = types.str;
-                description = ''
-                  The id of the other peer, this is mandatory. It's documented at
-                  https://docs.syncthing.net/dev/device-ids.html
-                '';
-              };
-
-              introducer = mkOption {
-                type = types.bool;
-                default = false;
-                description = ''
-                  If the device should act as an introducer and be allowed
-                  to add folders on this computer.
-                '';
-              };
+        }));
+      };
 
+      overrideFolders = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to delete the folders which are not configured via the
+          <link linkend="opt-services.syncthing.folders">folders</link> option.
+          If set to <literal>false</literal>, folders added via the web
+          interface will persist and will have to be deleted manually.
+        '';
+      };
+
+      folders = mkOption {
+        default = {};
+        description = ''
+          Folders which should be shared by Syncthing.
+
+          Note that you can still add devices manually, but those changes
+          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          is enabled.
+        '';
+        example = literalExample ''
+          {
+            "/home/user/sync" = {
+              id = "syncme";
+              devices = [ "bigbox" ];
+            };
+          }
+        '';
+        type = types.attrsOf (types.submodule ({ name, ... }: {
+          options = {
+
+            enable = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether to share this folder.
+                This option is useful when you want to define all folders
+                in one place, but not every machine should share all folders.
+              '';
             };
-          }));
-        };
 
-        overrideFolders = mkOption {
-          type = types.bool;
-          default = true;
-          description = ''
-            Whether to delete the folders which are not configured via the
-            <literal>declarative.folders</literal> option.
-            If set to false, folders added via the webinterface will persist
-            but will have to be deleted manually.
-          '';
-        };
+            path = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The path to the folder which should be shared.
+              '';
+            };
 
-        folders = mkOption {
-          default = {};
-          description = ''
-            folders which should be shared by syncthing.
-          '';
-          example = literalExample ''
-            {
-              "/home/user/sync" = {
-                id = "syncme";
-                devices = [ "bigbox" ];
-              };
-            }
-          '';
-          type = types.attrsOf (types.submodule ({ name, ... }: {
-            options = {
-
-              enable = mkOption {
-                type = types.bool;
-                default = true;
-                description = ''
-                  share this folder.
-                  This option is useful when you want to define all folders
-                  in one place, but not every machine should share all folders.
-                '';
-              };
-
-              path = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  The path to the folder which should be shared.
-                '';
-              };
-
-              id = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  The id of the folder. Must be the same on all devices.
-                '';
-              };
-
-              label = mkOption {
-                type = types.str;
-                default = name;
-                description = ''
-                  The label of the folder.
-                '';
-              };
-
-              devices = mkOption {
-                type = types.listOf types.str;
-                default = [];
-                description = ''
-                  The devices this folder should be shared with. Must be defined
-                  in the <literal>declarative.devices</literal> attribute.
-                '';
-              };
-
-              versioning = mkOption {
-                default = null;
-                description = ''
-                  How to keep changed/deleted files with syncthing.
-                  There are 4 different types of versioning with different parameters.
-                  See https://docs.syncthing.net/users/versioning.html
-                '';
-                example = [
+            id = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The ID of the folder. Must be the same on all devices.
+              '';
+            };
+
+            label = mkOption {
+              type = types.str;
+              default = name;
+              description = ''
+                The label of the folder.
+              '';
+            };
+
+            devices = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              description = ''
+                The devices this folder should be shared with. Each device must
+                be defined in the <link linkend="opt-services.syncthing.devices">devices</link> option.
+              '';
+            };
+
+            versioning = mkOption {
+              default = null;
+              description = ''
+                How to keep changed/deleted files with Syncthing.
+                There are 4 different types of versioning with different parameters.
+                See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+              '';
+              example = literalExample ''
+                [
                   {
                     versioning = {
                       type = "simple";
@@ -257,87 +270,99 @@ in {
                   {
                     versioning = {
                       type = "external";
-                      params.versionsPath = pkgs.writers.writeBash "backup" ''
+                      params.versionsPath = pkgs.writers.writeBash "backup" '''
                         folderpath="$1"
                         filepath="$2"
                         rm -rf "$folderpath/$filepath"
-                      '';
+                      ''';
                     };
                   }
-                ];
-                type = with types; nullOr (submodule {
-                  options = {
-                    type = mkOption {
-                      type = enum [ "external" "simple" "staggered" "trashcan" ];
-                      description = ''
-                        Type of versioning.
-                        See https://docs.syncthing.net/users/versioning.html
-                      '';
-                    };
-                    params = mkOption {
-                      type = attrsOf (either str path);
-                      description = ''
-                        Parameters for versioning. Structure depends on versioning.type.
-                        See https://docs.syncthing.net/users/versioning.html
-                      '';
-                    };
+                ]
+              '';
+              type = with types; nullOr (submodule {
+                options = {
+                  type = mkOption {
+                    type = enum [ "external" "simple" "staggered" "trashcan" ];
+                    description = ''
+                      The type of versioning.
+                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                    '';
+                  };
+                  params = mkOption {
+                    type = attrsOf (either str path);
+                    description = ''
+                      The parameters for versioning. Structure depends on
+                      <link linkend="opt-services.syncthing.folders._name_.versioning.type">versioning.type</link>.
+                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                    '';
                   };
-                });
-              };
-
-              rescanInterval = mkOption {
-                type = types.int;
-                default = 3600;
-                description = ''
-                  How often the folders should be rescaned for changes.
-                '';
-              };
-
-              type = mkOption {
-                type = types.enum [ "sendreceive" "sendonly" "receiveonly" ];
-                default = "sendreceive";
-                description = ''
-                  Whether to send only changes from this folder, only receive them
-                  or propagate both.
-                '';
-              };
-
-              watch = mkOption {
-                type = types.bool;
-                default = true;
-                description = ''
-                  Whether the folder should be watched for changes by inotify.
-                '';
-              };
-
-              watchDelay = mkOption {
-                type = types.int;
-                default = 10;
-                description = ''
-                  The delay after an inotify event is triggered.
-                '';
-              };
-
-              ignorePerms = mkOption {
-                type = types.bool;
-                default = true;
-                description = ''
-                  Whether to propagate permission changes.
-                '';
-              };
-
-              ignoreDelete = mkOption {
-                type = types.bool;
-                default = false;
-                description = ''
-                  Whether to delete files in destination. See <link
-                  xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html">
-                  upstream's docs</link>.
-                '';
-              };
+                };
+              });
+            };
+
+            rescanInterval = mkOption {
+              type = types.int;
+              default = 3600;
+              description = ''
+                How often the folder should be rescanned for changes.
+              '';
+            };
 
+            type = mkOption {
+              type = types.enum [ "sendreceive" "sendonly" "receiveonly" ];
+              default = "sendreceive";
+              description = ''
+                Whether to only send changes for this folder, only receive them
+                or both.
+              '';
             };
-          }));
+
+            watch = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether the folder should be watched for changes by inotify.
+              '';
+            };
+
+            watchDelay = mkOption {
+              type = types.int;
+              default = 10;
+              description = ''
+                The delay after an inotify event is triggered.
+              '';
+            };
+
+            ignorePerms = mkOption {
+              type = types.bool;
+              default = true;
+              description = ''
+                Whether to ignore permission changes.
+              '';
+            };
+
+            ignoreDelete = mkOption {
+              type = types.bool;
+              default = false;
+              description = ''
+                Whether to skip deleting files that are deleted by peers.
+                See <link xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"/>.
+              '';
+            };
+          };
+        }));
+      };
+
+      extraOptions = mkOption {
+        type = types.addCheck (pkgs.formats.json {}).type isAttrs;
+        default = {};
+        description = ''
+          Extra configuration options for Syncthing.
+          See <link xlink:href="https://docs.syncthing.net/users/config.html"/>.
+        '';
+        example = {
+          options.localAnnounceEnabled = false;
+          gui.theme = "black";
         };
       };
 
@@ -345,31 +370,35 @@ in {
         type = types.str;
         default = "127.0.0.1:8384";
         description = ''
-          Address to serve the GUI.
+          The address to serve the web interface at.
         '';
       };
 
       systemService = mkOption {
         type = types.bool;
         default = true;
-        description = "Auto launch Syncthing as a system service.";
+        description = ''
+          Whether to auto-launch Syncthing as a system service.
+        '';
       };
 
       user = mkOption {
         type = types.str;
         default = defaultUser;
+        example = "yourUser";
         description = ''
-          Syncthing will be run under this user (user will be created if it doesn't exist.
-          This can be your user name).
+          The user to run Syncthing as.
+          By default, a user named <literal>${defaultUser}</literal> will be created.
         '';
       };
 
       group = mkOption {
         type = types.str;
-        default = defaultUser;
+        default = defaultGroup;
+        example = "yourGroup";
         description = ''
-          Syncthing will be run under this group (group will not be created if it doesn't exist.
-          This can be your user name).
+          The group to run Syncthing under.
+          By default, a group named <literal>${defaultGroup}</literal> will be created.
         '';
       };
 
@@ -378,63 +407,76 @@ in {
         default = null;
         example = "socks5://address.com:1234";
         description = ''
-          Overwrites all_proxy environment variable for the syncthing process to
-          the given value. This is normaly used to let relay client connect
-          through SOCKS5 proxy server.
+          Overwrites the all_proxy environment variable for the Syncthing process to
+          the given value. This is normally used to let Syncthing connect
+          through a SOCKS5 proxy server.
+          See <link xlink:href="https://docs.syncthing.net/users/proxying.html"/>.
         '';
       };
 
       dataDir = mkOption {
         type = types.path;
         default = "/var/lib/syncthing";
+        example = "/home/yourUser";
         description = ''
-          Path where synced directories will exist.
+          The path where synchronised directories will exist.
         '';
       };
 
-      configDir = mkOption {
+      configDir = let
+        cond = versionAtLeast config.system.stateVersion "19.03";
+      in mkOption {
         type = types.path;
         description = ''
-          Path where the settings and keys will exist.
+          The path where the settings and keys will exist.
+        '';
+        default = cfg.dataDir + (optionalString cond "/.config/syncthing");
+        defaultText = literalExample "dataDir${optionalString cond " + \"/.config/syncthing\""}";
+      };
+
+      extraFlags = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "--reset-deltas" ];
+        description = ''
+          Extra flags passed to the syncthing command in the service definition.
         '';
-        default =
-          let
-            nixos = config.system.stateVersion;
-            cond  = versionAtLeast nixos "19.03";
-          in cfg.dataDir + (optionalString cond "/.config/syncthing");
       };
 
       openDefaultPorts = mkOption {
         type = types.bool;
         default = false;
-        example = literalExample "true";
+        example = true;
         description = ''
-          Open the default ports in the firewall:
-            - TCP 22000 for transfers
-            - UDP 21027 for discovery
-          If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled.
-          Alternatively, if are running only a single instance on this machine using the default ports, enable this.
+          Whether to open the default ports in the firewall: TCP 22000 for transfers
+          and UDP 21027 for discovery.
+
+          If multiple users are running Syncthing on this machine, you will need
+          to manually open a set of ports for each instance and leave this disabled.
+          Alternatively, if you are running only a single instance on this machine
+          using the default ports, enable this.
         '';
       };
 
       package = mkOption {
         type = types.package;
         default = pkgs.syncthing;
-        defaultText = "pkgs.syncthing";
-        example = literalExample "pkgs.syncthing";
+        defaultText = literalExample "pkgs.syncthing";
         description = ''
-          Syncthing package to use.
+          The Syncthing package to use.
         '';
       };
     };
   };
 
   imports = [
-    (mkRemovedOptionModule ["services" "syncthing" "useInotify"] ''
-      This option was removed because syncthing now has the inotify functionality included under the name "fswatcher".
-      It can be enabled on a per-folder basis through the webinterface.
+    (mkRemovedOptionModule [ "services" "syncthing" "useInotify" ] ''
+      This option was removed because Syncthing now has the inotify functionality included under the name "fswatcher".
+      It can be enabled on a per-folder basis through the web interface.
     '')
-  ];
+  ] ++ map (o:
+    mkRenamedOptionModule [ "services" "syncthing" "declarative" o ] [ "services" "syncthing" o ]
+  ) [ "cert" "key" "devices" "folders" "overrideDevices" "overrideFolders" "extraOptions"];
 
   ###### implementation
 
@@ -457,8 +499,8 @@ in {
         };
     };
 
-    users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) {
-      ${defaultUser}.gid =
+    users.groups = mkIf (cfg.systemService && cfg.group == defaultGroup) {
+      ${defaultGroup}.gid =
         config.ids.gids.syncthing;
     };
 
@@ -478,14 +520,14 @@ in {
           RestartForceExitStatus="3 4";
           User = cfg.user;
           Group = cfg.group;
-          ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null)
+          ExecStartPre = mkIf (cfg.cert != null || cfg.key != null)
             "+${pkgs.writers.writeBash "syncthing-copy-keys" ''
               install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir}
-              ${optionalString (cfg.declarative.cert != null) ''
-                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem
+              ${optionalString (cfg.cert != null) ''
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.cert} ${cfg.configDir}/cert.pem
               ''}
-              ${optionalString (cfg.declarative.key != null) ''
-                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem
+              ${optionalString (cfg.key != null) ''
+                install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.key} ${cfg.configDir}/key.pem
               ''}
             ''}"
           ;
@@ -493,7 +535,7 @@ in {
             ${cfg.package}/bin/syncthing \
               -no-browser \
               -gui-address=${cfg.guiAddress} \
-              -home=${cfg.configDir}
+              -home=${cfg.configDir} ${escapeShellArgs cfg.extraFlags}
           '';
           MemoryDenyWriteExecute = true;
           NoNewPrivileges = true;
@@ -516,8 +558,10 @@ in {
         };
       };
       syncthing-init = mkIf (
-        cfg.declarative.devices != {} || cfg.declarative.folders != {}
+        cfg.devices != {} || cfg.folders != {} || cfg.extraOptions != {}
       ) {
+        description = "Syncthing configuration updater";
+        requisite = [ "syncthing.service" ];
         after = [ "syncthing.service" ];
         wantedBy = [ "multi-user.target" ];
 
diff --git a/nixpkgs/nixos/modules/services/networking/tinc.nix b/nixpkgs/nixos/modules/services/networking/tinc.nix
index b6afd83a9abd..22caf9f4ec56 100644
--- a/nixpkgs/nixos/modules/services/networking/tinc.nix
+++ b/nixpkgs/nixos/modules/services/networking/tinc.nix
@@ -351,7 +351,7 @@ in
 
   config = mkIf (cfg.networks != { }) {
 
-    environment.etc = fold (a: b: a // b) { }
+    environment.etc = foldr (a: b: a // b) { }
       (flip mapAttrsToList cfg.networks (network: data:
         flip mapAttrs' data.hosts (host: text: nameValuePair
           ("tinc/${network}/hosts/${host}")
@@ -427,9 +427,12 @@ in
       nameValuePair ("tinc.${network}") ({
         description = "Tinc daemon user for ${network}";
         isSystemUser = true;
+        group = "tinc.${network}";
       })
     );
-
+    users.groups = flip mapAttrs' cfg.networks (network: _:
+      nameValuePair "tinc.${network}" {}
+    );
   };
 
   meta.maintainers = with maintainers; [ minijackson ];
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 62bcf7a14972..2e320378cc9a 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -173,6 +173,41 @@ in
         User = "unifi";
         UMask = "0077";
         WorkingDirectory = "${stateDir}";
+
+        # Hardening
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+        # ProtectClock= adds DeviceAllow=char-rtc r
+        DeviceAllow = "";
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" ];
+
+        # Required for ProtectSystem=strict
+        BindPaths = [ stateDir ];
+
+        # Needs network access
+        PrivateNetwork = false;
+        # Cannot be true due to OpenJDK
+        MemoryDenyWriteExecute = false;
       };
     };
 
diff --git a/nixpkgs/nixos/modules/services/networking/v2ray.nix b/nixpkgs/nixos/modules/services/networking/v2ray.nix
index 6a924a16449a..0b8b5b56e25b 100644
--- a/nixpkgs/nixos/modules/services/networking/v2ray.nix
+++ b/nixpkgs/nixos/modules/services/networking/v2ray.nix
@@ -25,7 +25,7 @@ with lib;
 
           Either <literal>configFile</literal> or <literal>config</literal> must be specified.
 
-          See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>.
+          See <link xlink:href="https://www.v2fly.org/en_US/config/overview.html"/>.
         '';
       };
 
@@ -47,7 +47,7 @@ with lib;
 
           Either `configFile` or `config` must be specified.
 
-          See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>.
+          See <link xlink:href="https://www.v2fly.org/en_US/config/overview.html"/>.
         '';
       };
     };
diff --git a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
index 35ff67937fc7..c6291366b0f1 100644
--- a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
+++ b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix
@@ -19,7 +19,7 @@ let
     ${ethtool} -s ${interface} ${methodParameter {inherit method password;}}
   '';
 
-  concatStrings = fold (x: y: x + y) "";
+  concatStrings = foldr (x: y: x + y) "";
   lines = concatStrings (map (l: line l) interfaces);
 
 in
@@ -31,6 +31,20 @@ in
 
     services.wakeonlan.interfaces = mkOption {
       default = [ ];
+      type = types.listOf (types.submodule { options = {
+        interface = mkOption {
+          type = types.str;
+          description = "Interface to enable for Wake-On-Lan.";
+        };
+        method = mkOption {
+          type = types.enum [ "magicpacket" "password"];
+          description = "Wake-On-Lan method for this interface.";
+        };
+        password = mkOption {
+          type = types.strMatching "[a-fA-F0-9]{2}:([a-fA-F0-9]{2}:){4}[a-fA-F0-9]{2}";
+          description = "The password has the shape of six bytes in hexadecimal separated by a colon each.";
+        };
+      };});
       example = [
         {
           interface = "eth0";
diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
index c0a4ce40760a..155c6fdd0ab0 100644
--- a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix
@@ -8,28 +8,108 @@ let
     else pkgs.wpa_supplicant;
 
   cfg = config.networking.wireless;
-  configFile = if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable then pkgs.writeText "wpa_supplicant.conf" ''
-    ${optionalString cfg.userControlled.enable ''
-      ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group}
-      update_config=1''}
-    ${cfg.extraConfig}
-    ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let
-      key = if psk != null
-        then ''"${psk}"''
-        else pskRaw;
-      baseAuth = if key != null
-        then "psk=${key}"
-        else "key_mgmt=NONE";
-    in ''
-      network={
-        ssid="${ssid}"
-        ${optionalString (priority != null) ''priority=${toString priority}''}
-        ${optionalString hidden "scan_ssid=1"}
-        ${if (auth != null) then auth else baseAuth}
-        ${extraConfig}
-      }
-    '') cfg.networks)}
-  '' else "/etc/wpa_supplicant.conf";
+
+  # Content of wpa_supplicant.conf
+  generatedConfig = concatStringsSep "\n" (
+    (mapAttrsToList mkNetwork cfg.networks)
+    ++ optional cfg.userControlled.enable (concatStringsSep "\n"
+      [ "ctrl_interface=/run/wpa_supplicant"
+        "ctrl_interface_group=${cfg.userControlled.group}"
+        "update_config=1"
+      ])
+    ++ optional cfg.scanOnLowSignal ''bgscan="simple:30:-70:3600"''
+    ++ optional (cfg.extraConfig != "") cfg.extraConfig);
+
+  configFile =
+    if cfg.networks != {} || cfg.extraConfig != "" || cfg.userControlled.enable
+      then pkgs.writeText "wpa_supplicant.conf" generatedConfig
+      else "/etc/wpa_supplicant.conf";
+
+  # Creates a network block for wpa_supplicant.conf
+  mkNetwork = ssid: opts:
+  let
+    quote = x: ''"${x}"'';
+    indent = x: "  " + x;
+
+    pskString = if opts.psk != null
+      then quote opts.psk
+      else opts.pskRaw;
+
+    options = [
+      "ssid=${quote ssid}"
+      (if pskString != null || opts.auth != null
+        then "key_mgmt=${concatStringsSep " " opts.authProtocols}"
+        else "key_mgmt=NONE")
+    ] ++ optional opts.hidden "scan_ssid=1"
+      ++ optional (pskString != null) "psk=${pskString}"
+      ++ optionals (opts.auth != null) (filter (x: x != "") (splitString "\n" opts.auth))
+      ++ optional (opts.priority != null) "priority=${toString opts.priority}"
+      ++ optional (opts.extraConfig != "") opts.extraConfig;
+  in ''
+    network={
+    ${concatMapStringsSep "\n" indent options}
+    }
+  '';
+
+  # Creates a systemd unit for wpa_supplicant bound to a given (or any) interface
+  mkUnit = iface:
+    let
+      deviceUnit = optional (iface != null) "sys-subsystem-net-devices-${utils.escapeSystemdPath iface}.device";
+      configStr = if cfg.allowAuxiliaryImperativeNetworks
+        then "-c /etc/wpa_supplicant.conf -I ${configFile}"
+        else "-c ${configFile}";
+    in {
+      description = "WPA Supplicant instance" + optionalString (iface != null) " for interface ${iface}";
+
+      after = deviceUnit;
+      before = [ "network.target" ];
+      wants = [ "network.target" ];
+      requires = deviceUnit;
+      wantedBy = [ "multi-user.target" ];
+      stopIfChanged = false;
+
+      path = [ package ];
+
+      script =
+      ''
+        if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]; then
+          echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
+        fi
+
+        iface_args="-s ${optionalString cfg.dbusControlled "-u"} -D${cfg.driver} ${configStr}"
+
+        ${if iface == null then ''
+          # detect interfaces automatically
+
+          # check if there are no wireless interfaces
+          if ! find -H /sys/class/net/* -name wireless | grep -q .; then
+            # if so, wait until one appears
+            echo "Waiting for wireless interfaces"
+            grep -q '^ACTION=add' < <(stdbuf -oL -- udevadm monitor -s net/wlan -pu)
+            # Note: the above line has been carefully written:
+            # 1. The process substitution avoids udevadm hanging (after grep has quit)
+            #    until it tries to write to the pipe again. Not even pipefail works here.
+            # 2. stdbuf is needed because udevadm output is buffered by default and grep
+            #    may hang until more udev events enter the pipe.
+          fi
+
+          # add any interface found to the daemon arguments
+          for name in $(find -H /sys/class/net/* -name wireless | cut -d/ -f 5); do
+            echo "Adding interface $name"
+            args+="''${args:+ -N} -i$name $iface_args"
+          done
+        '' else ''
+          # add known interface to the daemon arguments
+          args="-i${iface} $iface_args"
+        ''}
+
+        # finally start daemon
+        exec wpa_supplicant $args
+      '';
+    };
+
+  systemctl = "/run/current-system/systemd/bin/systemctl";
+
 in {
   options = {
     networking.wireless = {
@@ -42,11 +122,10 @@ in {
         description = ''
           The interfaces <command>wpa_supplicant</command> will use. If empty, it will
           automatically use all wireless interfaces.
-          <warning><para>
-            The automatic discovery of interfaces does not work reliably on boot:
-            it may fail and leave the system without network. When possible, specify
-            a known interface name.
-          </para></warning>
+
+          <note><para>
+            A separate wpa_supplicant instance will be started for each interface.
+          </para></note>
         '';
       };
 
@@ -66,6 +145,16 @@ in {
         '';
       };
 
+      scanOnLowSignal = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to periodically scan for (better) networks when the signal of
+          the current one is low. This will make roaming between access points
+          faster, but will consume more power.
+        '';
+      };
+
       networks = mkOption {
         type = types.attrsOf (types.submodule {
           options = {
@@ -94,11 +183,52 @@ in {
               '';
             };
 
+            authProtocols = mkOption {
+              default = [
+                # WPA2 and WPA3
+                "WPA-PSK" "WPA-EAP" "SAE"
+                # 802.11r variants of the above
+                "FT-PSK" "FT-EAP" "FT-SAE"
+              ];
+              # The list can be obtained by running this command
+              # awk '
+              #   /^# key_mgmt: /{ run=1 }
+              #   /^#$/{ run=0 }
+              #   /^# [A-Z0-9-]{2,}/{ if(run){printf("\"%s\"\n", $2)} }
+              # ' /run/current-system/sw/share/doc/wpa_supplicant/wpa_supplicant.conf.example
+              type = types.listOf (types.enum [
+                "WPA-PSK"
+                "WPA-EAP"
+                "IEEE8021X"
+                "NONE"
+                "WPA-NONE"
+                "FT-PSK"
+                "FT-EAP"
+                "FT-EAP-SHA384"
+                "WPA-PSK-SHA256"
+                "WPA-EAP-SHA256"
+                "SAE"
+                "FT-SAE"
+                "WPA-EAP-SUITE-B"
+                "WPA-EAP-SUITE-B-192"
+                "OSEN"
+                "FILS-SHA256"
+                "FILS-SHA384"
+                "FT-FILS-SHA256"
+                "FT-FILS-SHA384"
+                "OWE"
+                "DPP"
+              ]);
+              description = ''
+                The list of authentication protocols accepted by this network.
+                This corresponds to the <literal>key_mgmt</literal> option in wpa_supplicant.
+              '';
+            };
+
             auth = mkOption {
               type = types.nullOr types.str;
               default = null;
               example = ''
-                key_mgmt=WPA-EAP
                 eap=PEAP
                 identity="user@example.com"
                 password="secret"
@@ -205,6 +335,16 @@ in {
           description = "Members of this group can control wpa_supplicant.";
         };
       };
+
+      dbusControlled = mkOption {
+        type = types.bool;
+        default = lib.length cfg.interfaces < 2;
+        description = ''
+          Whether to enable the DBus control interface.
+          This is only needed when using NetworkManager or connman.
+        '';
+      };
+
       extraConfig = mkOption {
         type = types.str;
         default = "";
@@ -228,73 +368,47 @@ in {
     assertions = flip mapAttrsToList cfg.networks (name: cfg: {
       assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1;
       message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive'';
-    });
+    }) ++ [
+      {
+        assertion = length cfg.interfaces > 1 -> !cfg.dbusControlled;
+        message =
+          let daemon = if config.networking.networkmanager.enable then "NetworkManager" else
+                       if config.services.connman.enable then "connman" else null;
+              n = toString (length cfg.interfaces);
+          in ''
+            It's not possible to run multiple wpa_supplicant instances with DBus support.
+            Note: you're seeing this error because `networking.wireless.interfaces` has
+            ${n} entries, implying an equal number of wpa_supplicant instances.
+          '' + optionalString (daemon != null) ''
+            You don't need to change `networking.wireless.interfaces` when using ${daemon}:
+            in this case the interfaces will be configured automatically for you.
+          '';
+      }
+    ];
 
-    warnings =
-      optional (cfg.interfaces == [] && config.systemd.services.wpa_supplicant.wantedBy != [])
-      ''
-        No network interfaces for wpa_supplicant have been configured: the service
-        may randomly fail to start at boot. You should specify at least one using the option
-        networking.wireless.interfaces.
-      '';
+    hardware.wirelessRegulatoryDatabase = true;
 
     environment.systemPackages = [ package ];
-
-    services.dbus.packages = [ package ];
-    services.udev.packages = [ pkgs.crda ];
-
-    # FIXME: start a separate wpa_supplicant instance per interface.
-    systemd.services.wpa_supplicant = let
-      ifaces = cfg.interfaces;
-      deviceUnit = interface: [ "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device" ];
-    in {
-      description = "WPA Supplicant";
-
-      after = lib.concatMap deviceUnit ifaces;
-      before = [ "network.target" ];
-      wants = [ "network.target" ];
-      requires = lib.concatMap deviceUnit ifaces;
-      wantedBy = [ "multi-user.target" ];
-      stopIfChanged = false;
-
-      path = [ package ];
-
-      script = let
-        configStr = if cfg.allowAuxiliaryImperativeNetworks
-          then "-c /etc/wpa_supplicant.conf -I ${configFile}"
-          else "-c ${configFile}";
-      in ''
-        if [ -f /etc/wpa_supplicant.conf -a "/etc/wpa_supplicant.conf" != "${configFile}" ]
-        then echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
-        fi
-        iface_args="-s -u -D${cfg.driver} ${configStr}"
-        ${if ifaces == [] then ''
-          for i in $(cd /sys/class/net && echo *); do
-            DEVTYPE=
-            UEVENT_PATH=/sys/class/net/$i/uevent
-            if [ -e "$UEVENT_PATH" ]; then
-              source "$UEVENT_PATH"
-              if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then
-                args+="''${args:+ -N} -i$i $iface_args"
-              fi
-            fi
-          done
-        '' else ''
-          args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}"
-        ''}
-        exec wpa_supplicant $args
-      '';
-    };
-
-    powerManagement.resumeCommands = ''
-      /run/current-system/systemd/bin/systemctl try-restart wpa_supplicant
-    '';
-
-    # Restart wpa_supplicant when a wlan device appears or disappears.
-    services.udev.extraRules = ''
-      ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="/run/current-system/systemd/bin/systemctl try-restart wpa_supplicant.service"
+    services.dbus.packages = optional cfg.dbusControlled package;
+
+    systemd.services =
+      if cfg.interfaces == []
+        then { wpa_supplicant = mkUnit null; }
+        else listToAttrs (map (i: nameValuePair "wpa_supplicant-${i}" (mkUnit i)) cfg.interfaces);
+
+    # Restart wpa_supplicant after resuming from sleep
+    powerManagement.resumeCommands = concatStringsSep "\n" (
+      optional (cfg.interfaces == []) "${systemctl} try-restart wpa_supplicant"
+      ++ map (i: "${systemctl} try-restart wpa_supplicant-${i}") cfg.interfaces
+    );
+
+    # Restart wpa_supplicant when a wlan device appears or disappears. This is
+    # only needed when an interface hasn't been specified by the user.
+    services.udev.extraRules = optionalString (cfg.interfaces == []) ''
+      ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", \
+      RUN+="${systemctl} try-restart wpa_supplicant.service"
     '';
   };
 
-  meta.maintainers = with lib.maintainers; [ globin ];
+  meta.maintainers = with lib.maintainers; [ globin rnhmjoj ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/zeronet.nix b/nixpkgs/nixos/modules/services/networking/zeronet.nix
index f354a9d42c79..a34b2d871541 100644
--- a/nixpkgs/nixos/modules/services/networking/zeronet.nix
+++ b/nixpkgs/nixos/modules/services/networking/zeronet.nix
@@ -32,7 +32,7 @@ in with lib; {
     };
 
     port = mkOption {
-      type = types.int;
+      type = types.port;
       default = 43110;
       example = 43110;
       description = "Optional zeronet web UI port.";
@@ -41,7 +41,7 @@ in with lib; {
     fileserverPort = mkOption {
       # Not optional: when absent zeronet tries to write one to the
       # read-only config file and crashes
-      type = types.int;
+      type = types.port;
       default = 12261;
       example = 12261;
       description = "Zeronet fileserver port.";
diff --git a/nixpkgs/nixos/modules/services/search/elasticsearch.nix b/nixpkgs/nixos/modules/services/search/elasticsearch.nix
index 91d8f544e16b..440f34b3dc5c 100644
--- a/nixpkgs/nixos/modules/services/search/elasticsearch.nix
+++ b/nixpkgs/nixos/modules/services/search/elasticsearch.nix
@@ -5,8 +5,6 @@ with lib;
 let
   cfg = config.services.elasticsearch;
 
-  es6 = builtins.compareVersions cfg.package.version "6" >= 0;
-
   esConfig = ''
     network.host: ${cfg.listenAddress}
     cluster.name: ${cfg.cluster_name}
@@ -36,7 +34,8 @@ let
     postBuild = "${pkgs.coreutils}/bin/mkdir -p $out/plugins";
   };
 
-in {
+in
+{
 
   ###### interface
 
@@ -116,20 +115,20 @@ in {
 
     extraCmdLineOptions = mkOption {
       description = "Extra command line options for the elasticsearch launcher.";
-      default = [];
+      default = [ ];
       type = types.listOf types.str;
     };
 
     extraJavaOptions = mkOption {
       description = "Extra command line options for Java.";
-      default = [];
+      default = [ ];
       type = types.listOf types.str;
       example = [ "-Djava.net.preferIPv4Stack=true" ];
     };
 
     plugins = mkOption {
       description = "Extra elasticsearch plugins";
-      default = [];
+      default = [ ];
       type = types.listOf types.package;
       example = lib.literalExample "[ pkgs.elasticsearchPlugins.discovery-ec2 ]";
     };
@@ -146,9 +145,7 @@ in {
       path = [ pkgs.inetutils ];
       environment = {
         ES_HOME = cfg.dataDir;
-        ES_JAVA_OPTS = toString ( optional (!es6) [ "-Des.path.conf=${configDir}" ]
-                                  ++ cfg.extraJavaOptions);
-      } // optionalAttrs es6 {
+        ES_JAVA_OPTS = toString cfg.extraJavaOptions;
         ES_PATH_CONF = configDir;
       };
       serviceConfig = {
@@ -187,7 +184,10 @@ in {
         rm -f "${configDir}/logging.yml"
         cp ${loggingConfigFile} ${configDir}/${loggingConfigFilename}
         mkdir -p ${configDir}/scripts
-        ${optionalString es6 "cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options"}
+        cp ${cfg.package}/config/jvm.options ${configDir}/jvm.options
+        # redirect jvm logs to the data directory
+        mkdir -m 0700 -p ${cfg.dataDir}/logs
+        ${pkgs.sd}/bin/sd 'logs/gc.log' '${cfg.dataDir}/logs/gc.log' ${configDir}/jvm.options \
 
         if [ "$(id -u)" = 0 ]; then chown -R elasticsearch:elasticsearch ${cfg.dataDir}; fi
       '';
diff --git a/nixpkgs/nixos/modules/services/system/uptimed.nix b/nixpkgs/nixos/modules/services/system/uptimed.nix
index 1e256c51408e..67a03876e19f 100644
--- a/nixpkgs/nixos/modules/services/system/uptimed.nix
+++ b/nixpkgs/nixos/modules/services/system/uptimed.nix
@@ -4,7 +4,7 @@ with lib;
 
 let
   cfg = config.services.uptimed;
-  stateDir = "/var/spool/uptimed";
+  stateDir = "/var/lib/uptimed";
 in
 {
   options = {
@@ -21,12 +21,16 @@ in
   };
 
   config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.uptimed ];
+
     users.users.uptimed = {
       description = "Uptimed daemon user";
       home        = stateDir;
-      createHome  = true;
       uid         = config.ids.uids.uptimed;
+      group       = "uptimed";
     };
+    users.groups.uptimed = {};
 
     systemd.services.uptimed = {
       unitConfig.Documentation = "man:uptimed(8) man:uprecords(1)";
@@ -41,7 +45,7 @@ in
         PrivateTmp              = "yes";
         PrivateNetwork          = "yes";
         NoNewPrivileges         = "yes";
-        ReadWriteDirectories    = stateDir;
+        StateDirectory          = [ "uptimed" ];
         InaccessibleDirectories = "/home";
         ExecStart               = "${pkgs.uptimed}/sbin/uptimed -f -p ${stateDir}/pid";
       };
diff --git a/nixpkgs/nixos/modules/services/torrent/deluge.nix b/nixpkgs/nixos/modules/services/torrent/deluge.nix
index 7ca4fdcf64d4..151a1dd638d1 100644
--- a/nixpkgs/nixos/modules/services/torrent/deluge.nix
+++ b/nixpkgs/nixos/modules/services/torrent/deluge.nix
@@ -149,7 +149,7 @@ in {
 
         package = mkOption {
           type = types.package;
-          example = literalExample "pkgs.deluge-1_x";
+          example = literalExample "pkgs.deluge-2_x";
           description = ''
             Deluge package to use.
           '';
@@ -184,6 +184,13 @@ in {
       if versionAtLeast config.system.stateVersion "20.09" then
         pkgs.deluge-2_x
       else
+        # deluge-1_x is no longer packaged and this will resolve to an error
+        # thanks to the alias for this name.  This is left here so that anyone
+        # using NixOS older than 20.09 receives that error when they upgrade
+        # and is forced to make an intentional choice to switch to deluge-2_x.
+        # That might be slightly inconvenient but there is no path to
+        # downgrade from 2.x to 1.x so NixOS should not automatically perform
+        # this state migration.
         pkgs.deluge-1_x
     );
 
diff --git a/nixpkgs/nixos/modules/services/video/replay-sorcery.nix b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
new file mode 100644
index 000000000000..d78e782c7968
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/video/replay-sorcery.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.replay-sorcery;
+  configFile = generators.toKeyValue {} cfg.settings;
+in
+{
+  options = with types; {
+    services.replay-sorcery = {
+      enable = mkEnableOption "the ReplaySorcery service for instant-replays";
+
+      enableSysAdminCapability = mkEnableOption ''
+        the system admin capability to support hardware accelerated
+        video capture. This is equivalent to running ReplaySorcery as
+        root, so use with caution'';
+
+      autoStart = mkOption {
+        type = bool;
+        default = false;
+        description = "Automatically start ReplaySorcery when graphical-session.target starts.";
+      };
+
+      settings = mkOption {
+        type = attrsOf (oneOf [ str int ]);
+        default = {};
+        description = "System-wide configuration for ReplaySorcery (/etc/replay-sorcery.conf).";
+        example = literalExample ''
+          {
+            videoInput = "hwaccel"; # requires `services.replay-sorcery.enableSysAdminCapability = true`
+            videoFramerate = 60;
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ pkgs.replay-sorcery ];
+      etc."replay-sorcery.conf".text = configFile;
+    };
+
+    security.wrappers = mkIf cfg.enableSysAdminCapability {
+      replay-sorcery = {
+        source = "${pkgs.replay-sorcery}/bin/replay-sorcery";
+        capabilities = "cap_sys_admin+ep";
+      };
+    };
+
+    systemd = {
+      packages = [ pkgs.replay-sorcery ];
+      user.services.replay-sorcery = {
+        wantedBy = mkIf cfg.autoStart [ "graphical-session.target" ];
+        partOf = mkIf cfg.autoStart [ "graphical-session.target" ];
+        serviceConfig = {
+          ExecStart = mkIf cfg.enableSysAdminCapability [
+            "" # Tell systemd to clear the existing ExecStart list, to prevent appending to it.
+            "${config.security.wrapperDir}/replay-sorcery"
+          ];
+        };
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/wayland/cage.nix b/nixpkgs/nixos/modules/services/wayland/cage.nix
index 2e71abb69fc4..bd97a674eb86 100644
--- a/nixpkgs/nixos/modules/services/wayland/cage.nix
+++ b/nixpkgs/nixos/modules/services/wayland/cage.nix
@@ -82,7 +82,7 @@ in {
       auth    required pam_unix.so nullok
       account required pam_unix.so
       session required pam_unix.so
-      session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+      session required pam_env.so conffile=/etc/pam/environment readenv=0
       session required ${pkgs.systemd}/lib/security/pam_systemd.so
     '';
 
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.nix b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
index 8d5302ba267b..050e4ee3d329 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.nix
@@ -771,7 +771,6 @@ in
           "tmp"
           "assets/javascripts/plugins"
           "public"
-          "plugins"
           "sockets"
         ];
         RuntimeDirectoryMode = 0750;
diff --git a/nixpkgs/nixos/modules/services/web-apps/discourse.xml b/nixpkgs/nixos/modules/services/web-apps/discourse.xml
index 1d6866e7b352..184c9c6363e5 100644
--- a/nixpkgs/nixos/modules/services/web-apps/discourse.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/discourse.xml
@@ -284,12 +284,23 @@ services.discourse = {
       Ruby dependencies are listed in its
       <filename>plugin.rb</filename> file as function calls to
       <literal>gem</literal>. To construct the corresponding
-      <filename>Gemfile</filename>, run <command>bundle
+      <filename>Gemfile</filename> manually, run <command>bundle
       init</command>, then add the <literal>gem</literal> lines to it
       verbatim.
     </para>
 
     <para>
+      Much of the packaging can be done automatically by the
+      <filename>nixpkgs/pkgs/servers/web-apps/discourse/update.py</filename>
+      script - just add the plugin to the <literal>plugins</literal>
+      list in the <function>update_plugins</function> function and run
+      the script:
+      <programlisting language="bash">
+./update.py update-plugins
+</programlisting>.
+    </para>
+
+    <para>
       Some plugins provide <link
       linkend="module-services-discourse-site-settings">site
       settings</link>. Their defaults can be configured using <xref
diff --git a/nixpkgs/nixos/modules/services/web-apps/fluidd.nix b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
new file mode 100644
index 000000000000..c632b8ff7199
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/fluidd.nix
@@ -0,0 +1,64 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.fluidd;
+  moonraker = config.services.moonraker;
+in
+{
+  options.services.fluidd = {
+    enable = mkEnableOption "Fluidd, a Klipper web interface for managing your 3d printer";
+
+    package = mkOption {
+      type = types.package;
+      description = "Fluidd package to be used in the module";
+      default = pkgs.fluidd;
+      defaultText = "pkgs.fluidd";
+    };
+
+    hostName = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Hostname to serve fluidd on";
+    };
+
+    nginx = mkOption {
+      type = types.submodule
+        (import ../web-servers/nginx/vhost-options.nix { inherit config lib; });
+      default = { };
+      example = {
+        serverAliases = [ "fluidd.\${config.networking.domain}" ];
+      };
+      description = "Extra configuration for the nginx virtual host of fluidd.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.nginx = {
+      enable = true;
+      upstreams.fluidd-apiserver.servers."${moonraker.address}:${toString moonraker.port}" = { };
+      virtualHosts."${cfg.hostName}" = mkMerge [
+        cfg.nginx
+        {
+          root = mkForce "${cfg.package}/share/fluidd/htdocs";
+          locations = {
+            "/" = {
+              index = "index.html";
+              tryFiles = "$uri $uri/ /index.html";
+            };
+            "/index.html".extraConfig = ''
+              add_header Cache-Control "no-store, no-cache, must-revalidate";
+            '';
+            "/websocket" = {
+              proxyWebsockets = true;
+              proxyPass = "http://fluidd-apiserver/websocket";
+            };
+            "~ ^/(printer|api|access|machine|server)/" = {
+              proxyWebsockets = true;
+              proxyPass = "http://fluidd-apiserver$request_uri";
+            };
+          };
+        }
+      ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/isso.nix b/nixpkgs/nixos/modules/services/web-apps/isso.nix
new file mode 100644
index 000000000000..d05a99a3eedc
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/isso.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkEnableOption mkIf mkOption types literalExample;
+
+  cfg = config.services.isso;
+
+  settingsFormat = pkgs.formats.ini { };
+  configFile = settingsFormat.generate "isso.conf" cfg.settings;
+in {
+
+  options = {
+    services.isso = {
+      enable = mkEnableOption ''
+        A commenting server similar to Disqus.
+
+        Note: The application's author suppose to run isso behind a reverse proxy.
+        The embedded solution offered by NixOS is also only suitable for small installations
+        below 20 requests per second.
+      '';
+
+      settings = mkOption {
+        description = ''
+          Configuration for <package>isso</package>.
+
+          See <link xlink:href="https://posativ.org/isso/docs/configuration/server/">Isso Server Configuration</link>
+          for supported values.
+        '';
+
+        type = types.submodule {
+          freeformType = settingsFormat.type;
+        };
+
+        example = literalExample ''
+          {
+            general = {
+              host = "http://localhost";
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.isso.settings.general.dbpath = lib.mkDefault "/var/lib/isso/comments.db";
+
+    systemd.services.isso = {
+      description = "isso, a commenting server similar to Disqus";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        User = "isso";
+        Group = "isso";
+
+        DynamicUser = true;
+
+        StateDirectory = "isso";
+
+        ExecStart = ''
+          ${pkgs.isso}/bin/isso -c ${configFile}
+        '';
+
+        Restart = "on-failure";
+        RestartSec = 1;
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
index dc66c2966564..b1bea222c7f7 100644
--- a/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/keycloak.nix
@@ -281,7 +281,7 @@ in
       createLocalPostgreSQL = databaseActuallyCreateLocally && cfg.database.type == "postgresql";
       createLocalMySQL = databaseActuallyCreateLocally && cfg.database.type == "mysql";
 
-      mySqlCaKeystore = pkgs.runCommandNoCC "mysql-ca-keystore" {} ''
+      mySqlCaKeystore = pkgs.runCommand "mysql-ca-keystore" {} ''
         ${pkgs.jre}/bin/keytool -importcert -trustcacerts -alias MySQLCACert -file ${cfg.database.caCert} -keystore $out -storepass notsosecretpassword -noprompt
       '';
 
@@ -553,7 +553,7 @@ in
 
       jbossCliScript = pkgs.writeText "jboss-cli-script" (mkJbossScript keycloakConfig');
 
-      keycloakConfig = pkgs.runCommandNoCC "keycloak-config" {
+      keycloakConfig = pkgs.runCommand "keycloak-config" {
         nativeBuildInputs = [ cfg.package ];
       } ''
         export JBOSS_BASE_DIR="$(pwd -P)";
diff --git a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
index 01710b1bd59c..1bbadafa2078 100644
--- a/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/miniflux.nix
@@ -98,6 +98,29 @@ in
         EnvironmentFile = if cfg.adminCredentialsFile == null
         then defaultCredentials
         else cfg.adminCredentialsFile;
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
+        UMask = "0077";
       };
 
       environment = cfg.config;
diff --git a/nixpkgs/nixos/modules/services/web-apps/moodle.nix b/nixpkgs/nixos/modules/services/web-apps/moodle.nix
index ad1e55d62d1d..c854e084e14d 100644
--- a/nixpkgs/nixos/modules/services/web-apps/moodle.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/moodle.nix
@@ -56,7 +56,7 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  phpExt = pkgs.php.withExtensions
+  phpExt = pkgs.php74.withExtensions
         ({ enabled, all }: with all; [ iconv mbstring curl openssl tokenizer xmlrpc soap ctype zip gd simplexml dom  intl json sqlite3 pgsql pdo_sqlite pdo_pgsql pdo_odbc pdo_mysql pdo mysqli session zlib xmlreader fileinfo filter ]);
 in
 {
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
index 111b31734696..ba5f6582cbec 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.nix
@@ -502,8 +502,6 @@ in {
               ${if c.dbport != null then "--database-port" else null} = ''"${toString c.dbport}"'';
               ${if c.dbuser != null then "--database-user" else null} = ''"${c.dbuser}"'';
               "--database-pass" = dbpass;
-              ${if c.dbtableprefix != null
-                then "--database-table-prefix" else null} = ''"${toString c.dbtableprefix}"'';
               "--admin-user" = ''"${c.adminuser}"'';
               "--admin-pass" = adminpass;
               "--data-dir" = ''"${cfg.home}/data"'';
@@ -699,7 +697,6 @@ in {
         };
         extraConfig = ''
           index index.php index.html /index.php$request_uri;
-          expires 1m;
           add_header X-Content-Type-Options nosniff;
           add_header X-XSS-Protection "1; mode=block";
           add_header X-Robots-Tag none;
diff --git a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
index 3af37b15dd56..ed84487d233a 100644
--- a/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
+++ b/nixpkgs/nixos/modules/services/web-apps/nextcloud.xml
@@ -84,47 +84,93 @@
 </para>
 
  </section>
- <section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
-  <title>Pitfalls</title>
-
-  <para>
-   Unfortunately Nextcloud appears to be very stateful when it comes to
-   managing its own configuration. The config file lives in the home directory
-   of the <literal>nextcloud</literal> user (by default
-   <literal>/var/lib/nextcloud/config/config.php</literal>) and is also used to
-   track several states of the application (e.g. whether installed or not).
-  </para>
 
-  <para>
-   All configuration parameters are also stored in
-   <literal>/var/lib/nextcloud/config/override.config.php</literal> which is generated by
-   the module and linked from the store to ensure that all values from <literal>config.php</literal>
-   can be modified by the module.
-   However <literal>config.php</literal> manages the application's state and shouldn't be touched
-   manually because of that.
-  </para>
-
-  <warning>
-   <para>Don't delete <literal>config.php</literal>! This file
-   tracks the application's state and a deletion can cause unwanted
-   side-effects!</para>
-  </warning>
-
-  <warning>
-   <para>Don't rerun <literal>nextcloud-occ
-   maintenance:install</literal>! This command tries to install the application
-   and can cause unwanted side-effects!</para>
-  </warning>
+ <section xml:id="module-services-nextcloud-pitfalls-during-upgrade">
+  <title>Common problems</title>
+  <itemizedlist>
+   <listitem>
+    <formalpara>
+     <title>General notes</title>
+     <para>
+      Unfortunately Nextcloud appears to be very stateful when it comes to
+      managing its own configuration. The config file lives in the home directory
+      of the <literal>nextcloud</literal> user (by default
+      <literal>/var/lib/nextcloud/config/config.php</literal>) and is also used to
+      track several states of the application (e.g., whether installed or not).
+     </para>
+    </formalpara>
+    <para>
+     All configuration parameters are also stored in
+     <filename>/var/lib/nextcloud/config/override.config.php</filename> which is generated by
+     the module and linked from the store to ensure that all values from
+     <filename>config.php</filename> can be modified by the module.
+     However <filename>config.php</filename> manages the application's state and shouldn't be
+     touched manually because of that.
+    </para>
+    <warning>
+     <para>Don't delete <filename>config.php</filename>! This file
+     tracks the application's state and a deletion can cause unwanted
+     side-effects!</para>
+    </warning>
 
-  <para>
-   Nextcloud doesn't allow to move more than one major-version forward. If you're e.g. on
-   <literal>v16</literal>, you cannot upgrade to <literal>v18</literal>, you need to upgrade to
-   <literal>v17</literal> first. This is ensured automatically as long as the
-   <link linkend="opt-system.stateVersion">stateVersion</link> is declared properly. In that case
-   the oldest version available (one major behind the one from the previous NixOS
-   release) will be selected by default and the module will generate a warning that reminds
-   the user to upgrade to latest Nextcloud <emphasis>after</emphasis> that deploy.
-  </para>
+    <warning>
+     <para>Don't rerun <literal>nextcloud-occ
+     maintenance:install</literal>! This command tries to install the application
+     and can cause unwanted side-effects!</para>
+    </warning>
+   </listitem>
+   <listitem>
+    <formalpara>
+     <title>Multiple version upgrades</title>
+     <para>
+      Nextcloud doesn't allow to move more than one major-version forward. E.g., if you're on
+      <literal>v16</literal>, you cannot upgrade to <literal>v18</literal>, you need to upgrade to
+      <literal>v17</literal> first. This is ensured automatically as long as the
+      <link linkend="opt-system.stateVersion">stateVersion</link> is declared properly. In that case
+      the oldest version available (one major behind the one from the previous NixOS
+      release) will be selected by default and the module will generate a warning that reminds
+      the user to upgrade to latest Nextcloud <emphasis>after</emphasis> that deploy.
+     </para>
+    </formalpara>
+   </listitem>
+   <listitem>
+    <formalpara>
+     <title><literal>Error: Command "upgrade" is not defined.</literal></title>
+     <para>
+      This error usually occurs if the initial installation
+      (<command>nextcloud-occ maintenance:install</command>) has failed. After that, the application
+      is not installed, but the upgrade is attempted to be executed. Further context can
+      be found in <link xlink:href="https://github.com/NixOS/nixpkgs/issues/111175">NixOS/nixpkgs#111175</link>.
+     </para>
+    </formalpara>
+    <para>
+     First of all, it makes sense to find out what went wrong by looking at the logs
+     of the installation via <command>journalctl -u nextcloud-setup</command> and try to fix
+     the underlying issue.
+    </para>
+    <itemizedlist>
+     <listitem>
+      <para>
+       If this occurs on an <emphasis>existing</emphasis> setup, this is most likely because
+       the maintenance mode is active. It can be deactivated by running
+       <command>nextcloud-occ maintenance:mode --off</command>. It's advisable though to
+       check the logs first on why the maintenance mode was activated.
+      </para>
+     </listitem>
+     <listitem>
+      <warning><para>Only perform the following measures on
+      <emphasis>freshly installed instances!</emphasis></para></warning>
+      <para>
+       A re-run of the installer can be forced by <emphasis>deleting</emphasis>
+       <filename>/var/lib/nextcloud/config/config.php</filename>. This is the only time
+       advisable because the fresh install doesn't have any state that can be lost.
+       In case that doesn't help, an entire re-creation can be forced via
+       <command>rm -rf ~nextcloud/</command>.
+      </para>
+     </listitem>
+    </itemizedlist>
+   </listitem>
+  </itemizedlist>
  </section>
 
  <section xml:id="module-services-nextcloud-httpd">
diff --git a/nixpkgs/nixos/modules/services/web-apps/node-red.nix b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
new file mode 100644
index 000000000000..4f6850ace214
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/node-red.nix
@@ -0,0 +1,148 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.node-red;
+  defaultUser = "node-red";
+  finalPackage = if cfg.withNpmAndGcc then node-red_withNpmAndGcc else cfg.package;
+  node-red_withNpmAndGcc = pkgs.runCommand "node-red" {
+    nativeBuildInputs = [ pkgs.makeWrapper ];
+  }
+  ''
+    mkdir -p $out/bin
+    makeWrapper ${pkgs.nodePackages.node-red}/bin/node-red $out/bin/node-red \
+      --set PATH '${lib.makeBinPath [ pkgs.nodePackages.npm pkgs.gcc ]}:$PATH' \
+  '';
+in
+{
+  options.services.node-red = {
+    enable = mkEnableOption "the Node-RED service";
+
+    package = mkOption {
+      default = pkgs.nodePackages.node-red;
+      defaultText = "pkgs.nodePackages.node-red";
+      type = types.package;
+      description = "Node-RED package to use.";
+    };
+
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Open ports in the firewall for the server.
+      '';
+    };
+
+    withNpmAndGcc = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Give Node-RED access to NPM and GCC at runtime, so 'Nodes' can be
+        downloaded and managed imperatively via the 'Palette Manager'.
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.path;
+      default = "${cfg.package}/lib/node_modules/node-red/settings.js";
+      defaultText = "\${cfg.package}/lib/node_modules/node-red/settings.js";
+      description = ''
+        Path to the JavaScript configuration file.
+        See <link
+        xlink:href="https://github.com/node-red/node-red/blob/master/packages/node_modules/node-red/settings.js"/>
+        for a configuration example.
+      '';
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 1880;
+      description = "Listening port.";
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = ''
+        User under which Node-RED runs.If left as the default value this user
+        will automatically be created on system activation, otherwise the
+        sysadmin is responsible for ensuring the user exists.
+      '';
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = ''
+        Group under which Node-RED runs.If left as the default value this group
+        will automatically be created on system activation, otherwise the
+        sysadmin is responsible for ensuring the group exists.
+      '';
+    };
+
+    userDir = mkOption {
+      type = types.path;
+      default = "/var/lib/node-red";
+      description = ''
+        The directory to store all user data, such as flow and credential files and all library data. If left
+        as the default value this directory will automatically be created before the node-red service starts,
+        otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership
+        and permissions.
+      '';
+    };
+
+    safe = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Whether to launch Node-RED in --safe mode.";
+    };
+
+    define = mkOption {
+      type = types.attrs;
+      default = {};
+      description = "List of settings.js overrides to pass via -D to Node-RED.";
+      example = literalExample ''
+        {
+          "logging.console.level" = "trace";
+        }
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users = optionalAttrs (cfg.user == defaultUser) {
+      ${defaultUser} = {
+        isSystemUser = true;
+      };
+    };
+
+    users.groups = optionalAttrs (cfg.group == defaultUser) {
+      ${defaultUser} = { };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+
+    systemd.services.node-red = {
+      description = "Node-RED Service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "networking.target" ];
+      environment = {
+        HOME = cfg.userDir;
+      };
+      serviceConfig = mkMerge [
+        {
+          User = cfg.user;
+          Group = cfg.group;
+          ExecStart = "${finalPackage}/bin/node-red ${pkgs.lib.optionalString cfg.safe "--safe"} --settings ${cfg.configFile} --port ${toString cfg.port} --userDir ${cfg.userDir} ${concatStringsSep " " (mapAttrsToList (name: value: "-D ${name}=${value}") cfg.define)}";
+          PrivateTmp = true;
+          Restart = "always";
+          WorkingDirectory = cfg.userDir;
+        }
+        (mkIf (cfg.userDir == "/var/lib/node-red") { StateDirectory = "node-red"; })
+      ];
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
index b78487cc9281..bc18c824f394 100644
--- a/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/tt-rss.nix
@@ -6,10 +6,6 @@ let
 
   configVersion = 26;
 
-  cacheDir = "cache";
-  lockDir = "lock";
-  feedIconsDir = "feed-icons";
-
   dbPort = if cfg.database.port == null
     then (if cfg.database.type == "pgsql" then 5432 else 3306)
     else cfg.database.port;
@@ -19,86 +15,104 @@ let
   mysqlLocal = cfg.database.createLocally && cfg.database.type == "mysql";
   pgsqlLocal = cfg.database.createLocally && cfg.database.type == "pgsql";
 
-  tt-rss-config = pkgs.writeText "config.php" ''
+  tt-rss-config = let
+    password =
+      if (cfg.database.password != null) then
+        "${(escape ["'" "\\"] cfg.database.password)}"
+      else if (cfg.database.passwordFile != null) then
+        "file_get_contents('${cfg.database.passwordFile}'"
+      else
+        ""
+      ;
+  in pkgs.writeText "config.php" ''
     <?php
+      putenv('TTRSS_PHP_EXECUTABLE=${pkgs.php}/bin/php');
 
-      define('PHP_EXECUTABLE', '${pkgs.php}/bin/php');
-
-      define('LOCK_DIRECTORY', '${lockDir}');
-      define('CACHE_DIR', '${cacheDir}');
-      define('ICONS_DIR', '${feedIconsDir}');
-      define('ICONS_URL', '${feedIconsDir}');
-      define('SELF_URL_PATH', '${cfg.selfUrlPath}');
+      putenv('TTRSS_LOCK_DIRECTORY=${cfg.root}/lock');
+      putenv('TTRSS_CACHE_DIR=${cfg.root}/cache');
+      putenv('TTRSS_ICONS_DIR=${cfg.root}/feed-icons');
+      putenv('TTRSS_ICONS_URL=feed-icons');
+      putenv('TTRSS_SELF_URL_PATH=${cfg.selfUrlPath}');
 
-      define('MYSQL_CHARSET', 'UTF8');
+      putenv('TTRSS_MYSQL_CHARSET=UTF8');
 
-      define('DB_TYPE', '${cfg.database.type}');
-      define('DB_HOST', '${optionalString (cfg.database.host != null) cfg.database.host}');
-      define('DB_USER', '${cfg.database.user}');
-      define('DB_NAME', '${cfg.database.name}');
-      define('DB_PASS', ${
-        if (cfg.database.password != null) then
-          "'${(escape ["'" "\\"] cfg.database.password)}'"
-        else if (cfg.database.passwordFile != null) then
-          "file_get_contents('${cfg.database.passwordFile}')"
-        else
-          "''"
-      });
-      define('DB_PORT', '${toString dbPort}');
+      putenv('TTRSS_DB_TYPE=${cfg.database.type}');
+      putenv('TTRSS_DB_HOST=${optionalString (cfg.database.host != null) cfg.database.host}');
+      putenv('TTRSS_DB_USER=${cfg.database.user}');
+      putenv('TTRSS_DB_NAME=${cfg.database.name}');
+      putenv('TTRSS_DB_PASS=${password}');
+      putenv('TTRSS_DB_PORT=${toString dbPort}');
 
-      define('AUTH_AUTO_CREATE', ${boolToString cfg.auth.autoCreate});
-      define('AUTH_AUTO_LOGIN', ${boolToString cfg.auth.autoLogin});
+      putenv('TTRSS_AUTH_AUTO_CREATE=${boolToString cfg.auth.autoCreate}');
+      putenv('TTRSS_AUTH_AUTO_LOGIN=${boolToString cfg.auth.autoLogin}');
 
-      define('FEED_CRYPT_KEY', '${escape ["'" "\\"] cfg.feedCryptKey}');
+      putenv('TTRSS_FEED_CRYPT_KEY=${escape ["'" "\\"] cfg.feedCryptKey}');
 
 
-      define('SINGLE_USER_MODE', ${boolToString cfg.singleUserMode});
+      putenv('TTRSS_SINGLE_USER_MODE=${boolToString cfg.singleUserMode}');
 
-      define('SIMPLE_UPDATE_MODE', ${boolToString cfg.simpleUpdateMode});
+      putenv('TTRSS_SIMPLE_UPDATE_MODE=${boolToString cfg.simpleUpdateMode}');
 
-      // Never check for updates - the running version of the code should be
-      // controlled entirely by the version of TT-RSS active in the current Nix
-      // profile. If TT-RSS updates itself to a version requiring a database
-      // schema upgrade, and then the SystemD tt-rss.service is restarted, the
-      // old code copied from the Nix store will overwrite the updated version,
-      // causing the code to detect the need for a schema "upgrade" (since the
-      // schema version in the database is different than in the code), but the
-      // update schema operation in TT-RSS will do nothing because the schema
-      // version in the database is newer than that in the code.
-      define('CHECK_FOR_UPDATES', false);
+      # Never check for updates - the running version of the code should
+      # be controlled entirely by the version of TT-RSS active in the
+      # current Nix profile. If TT-RSS updates itself to a version
+      # requiring a database schema upgrade, and then the SystemD
+      # tt-rss.service is restarted, the old code copied from the Nix
+      # store will overwrite the updated version, causing the code to
+      # detect the need for a schema "upgrade" (since the schema version
+      # in the database is different than in the code), but the update
+      # schema operation in TT-RSS will do nothing because the schema
+      # version in the database is newer than that in the code.
+      putenv('TTRSS_CHECK_FOR_UPDATES=false');
 
-      define('FORCE_ARTICLE_PURGE', ${toString cfg.forceArticlePurge});
-      define('SESSION_COOKIE_LIFETIME', ${toString cfg.sessionCookieLifetime});
-      define('ENABLE_GZIP_OUTPUT', ${boolToString cfg.enableGZipOutput});
+      putenv('TTRSS_FORCE_ARTICLE_PURGE=${toString cfg.forceArticlePurge}');
+      putenv('TTRSS_SESSION_COOKIE_LIFETIME=${toString cfg.sessionCookieLifetime}');
+      putenv('TTRSS_ENABLE_GZIP_OUTPUT=${boolToString cfg.enableGZipOutput}');
 
-      define('PLUGINS', '${builtins.concatStringsSep "," cfg.plugins}');
+      putenv('TTRSS_PLUGINS=${builtins.concatStringsSep "," cfg.plugins}');
 
-      define('LOG_DESTINATION', '${cfg.logDestination}');
-      define('CONFIG_VERSION', ${toString configVersion});
+      putenv('TTRSS_LOG_DESTINATION=${cfg.logDestination}');
+      putenv('TTRSS_CONFIG_VERSION=${toString configVersion}');
 
 
-      define('PUBSUBHUBBUB_ENABLED', ${boolToString cfg.pubSubHubbub.enable});
-      define('PUBSUBHUBBUB_HUB', '${cfg.pubSubHubbub.hub}');
+      putenv('TTRSS_PUBSUBHUBBUB_ENABLED=${boolToString cfg.pubSubHubbub.enable}');
+      putenv('TTRSS_PUBSUBHUBBUB_HUB=${cfg.pubSubHubbub.hub}');
 
-      define('SPHINX_SERVER', '${cfg.sphinx.server}');
-      define('SPHINX_INDEX', '${builtins.concatStringsSep "," cfg.sphinx.index}');
+      putenv('TTRSS_SPHINX_SERVER=${cfg.sphinx.server}');
+      putenv('TTRSS_SPHINX_INDEX=${builtins.concatStringsSep "," cfg.sphinx.index}');
 
-      define('ENABLE_REGISTRATION', ${boolToString cfg.registration.enable});
-      define('REG_NOTIFY_ADDRESS', '${cfg.registration.notifyAddress}');
-      define('REG_MAX_USERS', ${toString cfg.registration.maxUsers});
+      putenv('TTRSS_ENABLE_REGISTRATION=${boolToString cfg.registration.enable}');
+      putenv('TTRSS_REG_NOTIFY_ADDRESS=${cfg.registration.notifyAddress}');
+      putenv('TTRSS_REG_MAX_USERS=${toString cfg.registration.maxUsers}');
 
-      define('SMTP_SERVER', '${cfg.email.server}');
-      define('SMTP_LOGIN', '${cfg.email.login}');
-      define('SMTP_PASSWORD', '${escape ["'" "\\"] cfg.email.password}');
-      define('SMTP_SECURE', '${cfg.email.security}');
+      putenv('TTRSS_SMTP_SERVER=${cfg.email.server}');
+      putenv('TTRSS_SMTP_LOGIN=${cfg.email.login}');
+      putenv('TTRSS_SMTP_PASSWORD=${escape ["'" "\\"] cfg.email.password}');
+      putenv('TTRSS_SMTP_SECURE=${cfg.email.security}');
 
-      define('SMTP_FROM_NAME', '${escape ["'" "\\"] cfg.email.fromName}');
-      define('SMTP_FROM_ADDRESS', '${escape ["'" "\\"] cfg.email.fromAddress}');
-      define('DIGEST_SUBJECT', '${escape ["'" "\\"] cfg.email.digestSubject}');
+      putenv('TTRSS_SMTP_FROM_NAME=${escape ["'" "\\"] cfg.email.fromName}');
+      putenv('TTRSS_SMTP_FROM_ADDRESS=${escape ["'" "\\"] cfg.email.fromAddress}');
+      putenv('TTRSS_DIGEST_SUBJECT=${escape ["'" "\\"] cfg.email.digestSubject}');
 
       ${cfg.extraConfig}
   '';
 
+  # tt-rss and plugins and themes and config.php
+  servedRoot = pkgs.runCommand "tt-rss-served-root" {} ''
+    cp --no-preserve=mode -r ${pkgs.tt-rss} $out
+    cp ${tt-rss-config} $out/config.php
+    ${optionalString (cfg.pluginPackages != []) ''
+    for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
+    cp -r "$plugin"/* "$out/plugins.local/"
+    done
+    ''}
+    ${optionalString (cfg.themePackages != []) ''
+    for theme in ${concatStringsSep " " cfg.themePackages}; do
+    cp -r "$theme"/* "$out/themes.local/"
+    done
+    ''}
+  '';
+
  in {
 
   ###### interface
@@ -542,12 +556,16 @@ let
       enable = true;
       virtualHosts = {
         ${cfg.virtualHost} = {
-          root = "${cfg.root}";
+          root = "${cfg.root}/www";
 
           locations."/" = {
             index = "index.php";
           };
 
+          locations."^~ /feed-icons" = {
+            root = "${cfg.root}";
+          };
+
           locations."~ \\.php$" = {
             extraConfig = ''
               fastcgi_split_path_info ^(.+\.php)(/.+)$;
@@ -560,13 +578,22 @@ let
     };
 
     systemd.tmpfiles.rules = [
-      "d '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
-      "Z '${cfg.root}' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}' 0555 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/lock' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache/upload' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache/images' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/cache/export' 0755 ${cfg.user} tt_rss - -"
+      "d '${cfg.root}/feed-icons' 0755 ${cfg.user} tt_rss - -"
+      "L+ '${cfg.root}/www' - - - - ${servedRoot}"
     ];
 
-    systemd.services.tt-rss =
-      {
+    systemd.services = {
+      phpfpm-tt-rss = mkIf (cfg.pool == "${poolName}") {
+        restartTriggers = [ servedRoot ];
+      };
 
+      tt-rss = {
         description = "Tiny Tiny RSS feeds update daemon";
 
         preStart = let
@@ -589,24 +616,7 @@ let
 
               else "";
 
-        in ''
-          rm -rf "${cfg.root}/*"
-          cp -r "${pkgs.tt-rss}/"* "${cfg.root}"
-          ${optionalString (cfg.pluginPackages != []) ''
-            for plugin in ${concatStringsSep " " cfg.pluginPackages}; do
-              cp -r "$plugin"/* "${cfg.root}/plugins.local/"
-            done
-          ''}
-          ${optionalString (cfg.themePackages != []) ''
-            for theme in ${concatStringsSep " " cfg.themePackages}; do
-              cp -r "$theme"/* "${cfg.root}/themes.local/"
-            done
-          ''}
-          ln -sf "${tt-rss-config}" "${cfg.root}/config.php"
-          chmod -R 755 "${cfg.root}"
-        ''
-
-        + (optionalString (cfg.database.type == "pgsql") ''
+        in (optionalString (cfg.database.type == "pgsql") ''
           exists=$(${callSql "select count(*) > 0 from pg_tables where tableowner = user"} \
           | tail -n+3 | head -n-2 | sed -e 's/[ \n\t]*//')
 
@@ -631,7 +641,7 @@ let
         serviceConfig = {
           User = "${cfg.user}";
           Group = "tt_rss";
-          ExecStart = "${pkgs.php}/bin/php ${cfg.root}/update.php --daemon --quiet";
+          ExecStart = "${pkgs.php}/bin/php ${cfg.root}/www/update.php --daemon --quiet";
           Restart = "on-failure";
           RestartSec = "60";
           SyslogIdentifier = "tt-rss";
@@ -640,6 +650,7 @@ let
         wantedBy = [ "multi-user.target" ];
         requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
         after = [ "network.target" ] ++ optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
+      };
     };
 
     services.mysql = mkIf mysqlLocal {
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
index df7035c03cc2..ceb199870975 100644
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -36,11 +36,12 @@ let
   dependentCertNames = unique (map (hostOpts: hostOpts.certName) acmeEnabledVhosts);
 
   mkListenInfo = hostOpts:
-    if hostOpts.listen != [] then hostOpts.listen
-    else (
-      optional (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) { ip = "*"; port = 443; ssl = true; } ++
-      optional (!hostOpts.onlySSL) { ip = "*"; port = 80; ssl = false; }
-    );
+    if hostOpts.listen != [] then
+      hostOpts.listen
+    else
+      optionals (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) (map (addr: { ip = addr; port = 443; ssl = true; }) hostOpts.listenAddresses) ++
+      optionals (!hostOpts.onlySSL) (map (addr: { ip = addr; port = 80; ssl = false; }) hostOpts.listenAddresses)
+    ;
 
   listenInfo = unique (concatMap mkListenInfo vhosts);
 
@@ -462,7 +463,7 @@ in
         default = "common";
         example = "combined";
         description = ''
-          Log format for log files. Possible values are: combined, common, referer, agent.
+          Log format for log files. Possible values are: combined, common, referer, agent, none.
           See <link xlink:href="https://httpd.apache.org/docs/2.4/logs.html"/> for more details.
         '';
       };
diff --git a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
index 394f9a305546..3f732a5c9f33 100644
--- a/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
@@ -47,10 +47,27 @@ in
       ];
       description = ''
         Listen addresses and ports for this virtual host.
-        <note><para>
+        <note>
+        <para>
           This option overrides <literal>addSSL</literal>, <literal>forceSSL</literal> and <literal>onlySSL</literal>.
-        </para></note>
+        </para>
+        <para>
+          If you only want to set the addresses manually and not the ports, take a look at <literal>listenAddresses</literal>.
+        </para>
+        </note>
+      '';
+    };
+
+    listenAddresses = mkOption {
+      type = with types; nonEmptyListOf str;
+
+      description = ''
+        Listen addresses for this virtual host.
+        Compared to <literal>listen</literal> this only sets the addreses
+        and the ports are chosen automatically.
       '';
+      default = [ "*" ];
+      example = [ "127.0.0.1" ];
     };
 
     enableSSL = mkOption {
diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
index 955b9756406d..fd7102096343 100644
--- a/nixpkgs/nixos/modules/services/web-servers/caddy.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/caddy/default.nix
@@ -4,42 +4,57 @@ with lib;
 
 let
   cfg = config.services.caddy;
-  configFile = pkgs.writeText "Caddyfile" cfg.config;
+  vhostToConfig = vhostName: vhostAttrs: ''
+    ${vhostName} ${builtins.concatStringsSep " " vhostAttrs.serverAliases} {
+      ${vhostAttrs.extraConfig}
+    }
+  '';
+  configFile = pkgs.writeText "Caddyfile" (builtins.concatStringsSep "\n"
+    ([ cfg.config ] ++ (mapAttrsToList vhostToConfig cfg.virtualHosts)));
+
+  formattedConfig = pkgs.runCommand "formattedCaddyFile" { } ''
+    ${cfg.package}/bin/caddy fmt ${configFile} > $out
+  '';
 
   tlsConfig = {
     apps.tls.automation.policies = [{
-      issuer = {
+      issuers = [{
         inherit (cfg) ca email;
         module = "acme";
-      };
+      }];
     }];
   };
 
   adaptedConfig = pkgs.runCommand "caddy-config-adapted.json" { } ''
     ${cfg.package}/bin/caddy adapt \
-      --config ${configFile} --adapter ${cfg.adapter} > $out
+      --config ${formattedConfig} --adapter ${cfg.adapter} > $out
   '';
   tlsJSON = pkgs.writeText "tls.json" (builtins.toJSON tlsConfig);
 
   # merge the TLS config options we expose with the ones originating in the Caddyfile
   configJSON =
-    let tlsConfigMerge = ''
-      {"apps":
-        {"tls":
-          {"automation":
-            {"policies":
-              (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies
-               then .[0].apps.tls.automation.policies
-               else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies)
-               end)
+    if cfg.ca != null then
+      let tlsConfigMerge = ''
+        {"apps":
+          {"tls":
+            {"automation":
+              {"policies":
+                (if .[0].apps.tls.automation.policies == .[1]?.apps.tls.automation.policies
+                 then .[0].apps.tls.automation.policies
+                 else (.[0].apps.tls.automation.policies + .[1]?.apps.tls.automation.policies)
+                 end)
+              }
             }
           }
-        }
-      }'';
-    in pkgs.runCommand "caddy-config.json" { } ''
-    ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out
-  '';
-in {
+        }'';
+      in
+      pkgs.runCommand "caddy-config.json" { } ''
+        ${pkgs.jq}/bin/jq -s '.[0] * ${tlsConfigMerge}' ${adaptedConfig} ${tlsJSON} > $out
+      ''
+    else
+      adaptedConfig;
+in
+{
   imports = [
     (mkRemovedOptionModule [ "services" "caddy" "agree" ] "this option is no longer necessary for Caddy 2")
   ];
@@ -63,6 +78,27 @@ in {
       '';
     };
 
+    virtualHosts = mkOption {
+      type = types.attrsOf (types.submodule (import ./vhost-options.nix {
+        inherit config lib;
+      }));
+      default = { };
+      example = literalExample ''
+        {
+          "hydra.example.com" = {
+            serverAliases = [ "www.hydra.example.com" ];
+            extraConfig = ''''''
+              encode gzip
+              log
+              root /srv/http
+            '''''';
+          };
+        };
+      '';
+      description = "Declarative vhost config";
+    };
+
+
     user = mkOption {
       default = "caddy";
       type = types.str;
@@ -85,11 +121,24 @@ in {
       '';
     };
 
+    resume = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Use saved config, if any (and prefer over configuration passed with <option>services.caddy.config</option>).
+      '';
+    };
+
     ca = mkOption {
       default = "https://acme-v02.api.letsencrypt.org/directory";
       example = "https://acme-staging-v02.api.letsencrypt.org/directory";
-      type = types.str;
-      description = "Certificate authority ACME server. The default (Let's Encrypt production server) should be fine for most people.";
+      type = types.nullOr types.str;
+      description = ''
+        Certificate authority ACME server. The default (Let's Encrypt
+        production server) should be fine for most people. Set it to null if
+        you don't want to include any authority (or if you want to write a more
+        fine-graned configuration manually)
+      '';
     };
 
     email = mkOption {
@@ -132,7 +181,7 @@ in {
       startLimitIntervalSec = 14400;
       startLimitBurst = 10;
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/caddy run --config ${configJSON}";
+        ExecStart = "${cfg.package}/bin/caddy run ${optionalString cfg.resume "--resume"} --config ${configJSON}";
         ExecReload = "${cfg.package}/bin/caddy reload --config ${configJSON}";
         Type = "simple";
         User = cfg.user;
diff --git a/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix
new file mode 100644
index 000000000000..1f74295fc9a2
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-servers/caddy/vhost-options.nix
@@ -0,0 +1,28 @@
+# This file defines the options that can be used both for the Nginx
+# main server configuration, and for the virtual hosts.  (The latter
+# has additional options that affect the web server as a whole, like
+# the user/group to run under.)
+
+{ lib, ... }:
+
+with lib;
+{
+  options = {
+    serverAliases = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "www.example.org" "example.org" ];
+      description = ''
+        Additional names of virtual hosts served by this virtual host configuration.
+      '';
+    };
+
+    extraConfig = mkOption {
+      type = types.lines;
+      default = "";
+      description = ''
+        These lines go into the vhost verbatim
+      '';
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-servers/minio.nix b/nixpkgs/nixos/modules/services/web-servers/minio.nix
index d075449012f7..6b10afad4991 100644
--- a/nixpkgs/nixos/modules/services/web-servers/minio.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/minio.nix
@@ -19,7 +19,13 @@ in
     listenAddress = mkOption {
       default = ":9000";
       type = types.str;
-      description = "Listen on a specific IP address and port.";
+      description = "IP address and port of the server.";
+    };
+
+    consoleAddress = mkOption {
+      default = ":9001";
+      type = types.str;
+      description = "IP address and port of the web UI (console).";
     };
 
     dataDir = mkOption {
@@ -99,7 +105,7 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --config-dir=${cfg.configDir} ${toString cfg.dataDir}";
+        ExecStart = "${cfg.package}/bin/minio server --json --address ${cfg.listenAddress} --console-address ${cfg.consoleAddress} --config-dir=${cfg.configDir} ${toString cfg.dataDir}";
         Type = "simple";
         User = "minio";
         Group = "minio";
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
index ebb3c38d6c25..6682472fdb8e 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -22,7 +22,9 @@ let
     } // (optionalAttrs (vhostConfig.enableACME || vhostConfig.useACMEHost != null) {
       sslCertificate = "${certs.${certName}.directory}/fullchain.pem";
       sslCertificateKey = "${certs.${certName}.directory}/key.pem";
-      sslTrustedCertificate = "${certs.${certName}.directory}/chain.pem";
+      sslTrustedCertificate = if vhostConfig.sslTrustedCertificate != null
+                              then vhostConfig.sslTrustedCertificate
+                              else "${certs.${certName}.directory}/chain.pem";
     })
   ) cfg.virtualHosts;
   enableIPv6 = config.networking.enableIPv6;
@@ -169,6 +171,14 @@ let
         map_hash_max_size ${toString cfg.mapHashMaxSize};
       ''}
 
+      ${optionalString (cfg.serverNamesHashBucketSize != null) ''
+        server_names_hash_bucket_size ${toString cfg.serverNamesHashBucketSize};
+      ''}
+
+      ${optionalString (cfg.serverNamesHashMaxSize != null) ''
+        server_names_hash_max_size ${toString cfg.serverNamesHashMaxSize};
+      ''}
+
       # $connection_upgrade is used for websocket proxying
       map $http_upgrade $connection_upgrade {
           default upgrade;
@@ -230,13 +240,13 @@ let
 
         defaultListen =
           if vhost.listen != [] then vhost.listen
-          else optionals (hasSSL || vhost.rejectSSL) (
-            singleton { addr = "0.0.0.0"; port = 443; ssl = true; }
-            ++ optional enableIPv6 { addr = "[::]"; port = 443; ssl = true; }
-          ) ++ optionals (!onlySSL) (
-            singleton { addr = "0.0.0.0"; port = 80; ssl = false; }
-            ++ optional enableIPv6 { addr = "[::]"; port = 80; ssl = false; }
-          );
+          else
+            let addrs = if vhost.listenAddresses != [] then vhost.listenAddresses else (
+              [ "0.0.0.0" ] ++ optional enableIPv6 "[::0]"
+            );
+            in
+          optionals (hasSSL || vhost.rejectSSL) (map (addr: { inherit addr; port = 443; ssl = true; }) addrs)
+          ++ optionals (!onlySSL) (map (addr: { inherit addr; port = 80; ssl = false; }) addrs);
 
         hostListen =
           if vhost.forceSSL
@@ -641,6 +651,23 @@ in
           '';
       };
 
+      serverNamesHashBucketSize = mkOption {
+        type = types.nullOr types.ints.positive;
+        default = null;
+        description = ''
+            Sets the bucket size for the server names hash tables. Default
+            value depends on the processor’s cache line size.
+          '';
+      };
+
+      serverNamesHashMaxSize = mkOption {
+        type = types.nullOr types.ints.positive;
+        default = null;
+        description = ''
+            Sets the maximum size of the server names hash tables.
+          '';
+      };
+
       resolver = mkOption {
         type = types.submodule {
           options = {
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
index bc18bcaa7b34..94645e927f86 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -43,7 +43,24 @@ with lib;
         IPv6 addresses must be enclosed in square brackets.
         Note: this option overrides <literal>addSSL</literal>
         and <literal>onlySSL</literal>.
+
+        If you only want to set the addresses manually and not
+        the ports, take a look at <literal>listenAddresses</literal>
+      '';
+    };
+
+    listenAddresses = mkOption {
+      type = with types; listOf str;
+
+      description = ''
+        Listen addresses for this virtual host.
+        Compared to <literal>listen</literal> this only sets the addreses
+        and the ports are choosen automatically.
+
+        Note: This option overrides <literal>enableIPv6</literal>
       '';
+      default = [];
+      example = [ "127.0.0.1" "::1" ];
     };
 
     enableACME = mkOption {
@@ -145,7 +162,7 @@ with lib;
     sslTrustedCertificate = mkOption {
       type = types.nullOr types.path;
       default = null;
-      example = "/var/root.cert";
+      example = "\${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
       description = "Path to root SSL certificate for stapling and client certificates.";
     };
 
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
index b0859321a525..4bc42525906c 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -372,6 +372,13 @@ in
       xdg.portal.enable = true;
       xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
 
+      # Harmonize Qt5 application style and also make them use the portal for file chooser dialog.
+      qt5 = {
+        enable = mkDefault true;
+        platformTheme = mkDefault "gnome";
+        style = mkDefault "adwaita";
+      };
+
       networking.networkmanager.enable = mkDefault true;
 
       services.xserver.updateDbusEnvironment = true;
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index b6be524aea66..aac905fea437 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -271,13 +271,14 @@ in
           kmenuedit
           kscreen
           kscreenlocker
-          ksysguard
+          ksystemstats
           kwayland
           kwin
           kwrited
           libkscreen
           libksysguard
           milou
+          plasma-systemmonitor
           plasma-browser-integration
           plasma-integration
           polkit-kde-agent
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix b/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix
index dec5c06cb3ca..861976d1186f 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/account-service-util.nix
@@ -39,6 +39,6 @@ python3.pkgs.buildPythonApplication {
   '';
 
   meta = with lib; {
-    maintainers = with maintainers; [ ];
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 }
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
index e04fcdaf4145..584dfb63c4dc 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/default.nix
@@ -18,7 +18,6 @@ let
 
   fontconfig = config.fonts.fontconfig;
   xresourcesXft = pkgs.writeText "Xresources-Xft" ''
-    ${optionalString (fontconfig.dpi != 0) ''Xft.dpi: ${toString fontconfig.dpi}''}
     Xft.antialias: ${if fontconfig.antialias then "1" else "0"}
     Xft.rgba: ${fontconfig.subpixel.rgba}
     Xft.lcdfilter: lcd${fontconfig.subpixel.lcdfilter}
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
index ef9ec438cc1c..5c4c6c67fd02 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/gdm.nix
@@ -164,6 +164,11 @@ in
     systemd.packages = with pkgs.gnome; [ gdm gnome-session gnome-shell ];
     environment.systemPackages = [ pkgs.gnome.adwaita-icon-theme ];
 
+    # We dont use the upstream gdm service
+    # it has to be disabled since the gdm package has it
+    # https://github.com/NixOS/nixpkgs/issues/108672
+    systemd.services.gdm.enable = false;
+
     systemd.services.display-manager.wants = [
       # Because sd_login_monitor_new requires /run/systemd/machines
       "systemd-machined.service"
@@ -309,7 +314,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = gdm
-        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+        session  required       pam_env.so conffile=/etc/pam/environment readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
index 76f16646cf5e..f18e4a914e57 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix
@@ -10,8 +10,8 @@ let
 
 in
 {
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   options = {
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix
index 3d497c9f25ee..41c1b635f5d6 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -69,8 +69,8 @@ let
 
 in
 {
-  meta = {
-    maintainers = with maintainers; [ ];
+  meta = with lib; {
+    maintainers = with maintainers; [ ] ++ teams.pantheon.members;
   };
 
   # Note: the order in which lightdm greeter modules are imported
@@ -284,7 +284,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = lightdm
-        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+        session  required       pam_env.so conffile=/etc/pam/environment readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
index 116994db1c14..d79b3cda2fcc 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
@@ -229,7 +229,7 @@ in
         password required       pam_deny.so
 
         session  required       pam_succeed_if.so audit quiet_success user = sddm
-        session  required       pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
+        session  required       pam_env.so conffile=/etc/pam/environment readenv=0
         session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
         session  optional       pam_keyinit.so force revoke
         session  optional       pam_permit.so
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py b/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py
index 0cca80af44e8..75940efe32b4 100755
--- a/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/set-session.py
@@ -72,11 +72,14 @@ def main():
                     f"Setting session name: {session}, as we found the existing wayland-session: {session_file}"
                 )
                 user.set_session(session)
+                user.set_session_type("wayland")
             elif is_session_xsession(session_file):
                 logging.debug(
                     f"Setting session name: {session}, as we found the existing xsession: {session_file}"
                 )
                 user.set_x_session(session)
+                user.set_session(session)
+                user.set_session_type("x11")
             else:
                 logging.error(f"Couldn't figure out session type for {session_file}")
                 sys.exit(1)
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix
new file mode 100644
index 000000000000..132531c0ddc0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sx.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xserver.displayManager.sx;
+
+in {
+  options = {
+    services.xserver.displayManager.sx = {
+      enable = mkEnableOption "sx pseudo-display manager" // {
+        description = ''
+          Whether to enable the "sx" pseudo-display manager, which allows users
+          to start manually via the "sx" command from a vt shell. The X server
+          runs under the user's id, not as root. The user must provide a
+          ~/.config/sx/sxrc file containing session startup commands, see
+          sx(1). This is not automatically generated from the desktopManager
+          and windowManager settings. sx doesn't have a way to directly set
+          X server flags, but it can be done by overriding its xorgserver
+          dependency.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.sx ];
+    services.xserver = {
+      exportConfiguration = true;
+      displayManager = {
+        job.execCmd = "";
+        lightdm.enable = mkForce false;
+      };
+      logFile = mkDefault null;
+    };
+    systemd.services.display-manager.enable = false;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix b/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix
index 089e9f769f0a..37a14e34f57e 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/awesome.nix
@@ -27,7 +27,7 @@ in
         default = [];
         type = types.listOf types.package;
         description = "List of lua packages available for being used in the Awesome configuration.";
-        example = literalExample "[ luaPackages.oocairo ]";
+        example = literalExample "[ pkgs.luaPackages.vicious ]";
       };
 
       package = mkOption {
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix
index 171660c53ac3..5015852db69f 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/clfswm.nix
@@ -8,17 +8,27 @@ in
 
 {
   options = {
-    services.xserver.windowManager.clfswm.enable = mkEnableOption "clfswm";
+    services.xserver.windowManager.clfswm = {
+      enable = mkEnableOption "clfswm";
+      package = mkOption {
+        type        = types.package;
+        default     = pkgs.lispPackages.clfswm;
+        defaultText = "pkgs.lispPackages.clfswm";
+        description = ''
+          clfswm package to use.
+        '';
+      };
+    };
   };
 
   config = mkIf cfg.enable {
     services.xserver.windowManager.session = singleton {
       name = "clfswm";
       start = ''
-        ${pkgs.lispPackages.clfswm}/bin/clfswm &
+        ${cfg.package}/bin/clfswm &
         waitPID=$!
       '';
     };
-    environment.systemPackages = [ pkgs.lispPackages.clfswm ];
+    environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/default.nix b/nixpkgs/nixos/modules/services/x11/window-managers/default.nix
index 53285fbce877..d71738ea633f 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/default.nix
@@ -26,6 +26,7 @@ in
     ./leftwm.nix
     ./lwm.nix
     ./metacity.nix
+    ./mlvwm.nix
     ./mwm.nix
     ./openbox.nix
     ./pekwm.nix
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix
new file mode 100644
index 000000000000..08dd04020296
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/mlvwm.nix
@@ -0,0 +1,41 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.xserver.windowManager.mlvwm;
+
+in
+{
+
+  options.services.xserver.windowManager.mlvwm = {
+    enable = mkEnableOption "Macintosh-like Virtual Window Manager";
+
+    configFile = mkOption {
+      default = null;
+      type = with types; nullOr path;
+      description = ''
+        Path to the mlvwm configuration file.
+        If left at the default value, $HOME/.mlvwmrc will be used.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.xserver.windowManager.session = [{
+      name = "mlvwm";
+      start = ''
+        ${pkgs.mlvwm}/bin/mlvwm ${optionalString (cfg.configFile != null)
+          "-f /etc/mlvwm/mlvwmrc"
+        } &
+        waitPID=$!
+      '';
+    }];
+
+    environment.etc."mlvwm/mlvwmrc" = mkIf (cfg.configFile != null) {
+      source = cfg.configFile;
+    };
+
+    environment.systemPackages = [ pkgs.mlvwm ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix
index cadc316bbc4f..835b41d4ada9 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/qtile.nix
@@ -15,7 +15,7 @@ in
     services.xserver.windowManager.session = [{
       name = "qtile";
       start = ''
-        ${pkgs.qtile}/bin/qtile &
+        ${pkgs.qtile}/bin/qtile start &
         waitPID=$!
       '';
     }];
diff --git a/nixpkgs/nixos/modules/services/x11/xserver.nix b/nixpkgs/nixos/modules/services/x11/xserver.nix
index 37e004ae80a7..ad9bd88f98aa 100644
--- a/nixpkgs/nixos/modules/services/x11/xserver.nix
+++ b/nixpkgs/nixos/modules/services/x11/xserver.nix
@@ -297,7 +297,11 @@ in
       dpi = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = "DPI resolution to use for X server.";
+        description = ''
+          Force global DPI resolution to use for X server. It's recommended to
+          use this only when DPI is detected incorrectly; also consider using
+          <literal>Monitor</literal> section in configuration file instead.
+        '';
       };
 
       updateDbusEnvironment = mkOption {
@@ -657,6 +661,7 @@ in
         pkgs.xterm
         pkgs.xdg-utils
         xorg.xf86inputevdev.out # get evdev.4 man page
+        pkgs.nixos-icons # needed for gnome and pantheon about dialog, nixos-manual and maybe more
       ]
       ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
 
@@ -680,7 +685,7 @@ in
     systemd.services.display-manager =
       { description = "X11 Server";
 
-        after = [ "acpid.service" "systemd-logind.service" ];
+        after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
 
         restartIfChanged = false;
 
diff --git a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
index 8bd85465472f..dd391c8b5d78 100644
--- a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
@@ -243,9 +243,13 @@ while (my ($unit, $state) = each %{$activePrev}) {
                             foreach my $socket (@sockets) {
                                 if (defined $activePrev->{$socket}) {
                                     $unitsToStop{$socket} = 1;
-                                    $unitsToStart{$socket} = 1;
-                                    recordUnit($startListFile, $socket);
-                                    $socketActivated = 1;
+                                    # Only restart sockets that actually
+                                    # exist in new configuration:
+                                    if (-e "$out/etc/systemd/system/$socket") {
+                                        $unitsToStart{$socket} = 1;
+                                        recordUnit($startListFile, $socket);
+                                        $socketActivated = 1;
+                                    }
                                 }
                             }
                         }
diff --git a/nixpkgs/nixos/modules/system/activation/top-level.nix b/nixpkgs/nixos/modules/system/activation/top-level.nix
index 4e2f25cd27fc..d3e4923a993f 100644
--- a/nixpkgs/nixos/modules/system/activation/top-level.nix
+++ b/nixpkgs/nixos/modules/system/activation/top-level.nix
@@ -125,7 +125,7 @@ let
     else showWarnings config.warnings baseSystem;
 
   # Replace runtime dependencies
-  system = fold ({ oldDependency, newDependency }: drv:
+  system = foldr ({ oldDependency, newDependency }: drv:
       pkgs.replaceDependency { inherit oldDependency newDependency drv; }
     ) baseSystemAssertWarn config.system.replaceRuntimeDependencies;
 
diff --git a/nixpkgs/nixos/modules/system/boot/binfmt.nix b/nixpkgs/nixos/modules/system/boot/binfmt.nix
index cbdf581d73a7..2408ecc80d22 100644
--- a/nixpkgs/nixos/modules/system/boot/binfmt.nix
+++ b/nixpkgs/nixos/modules/system/boot/binfmt.nix
@@ -1,6 +1,6 @@
 { config, lib, pkgs, ... }:
 let
-  inherit (lib) mkOption types optionalString;
+  inherit (lib) mkOption types optionalString stringAfter;
 
   cfg = config.boot.binfmt;
 
@@ -270,7 +270,7 @@ in {
 
     environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf"
       (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations));
-    system.activationScripts.binfmt = ''
+    system.activationScripts.binfmt = stringAfter [ "specialfs" ] ''
       mkdir -p -m 0755 /run/binfmt
       ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet config.boot.binfmt.registrations)}
     '';
diff --git a/nixpkgs/nixos/modules/system/boot/kexec.nix b/nixpkgs/nixos/modules/system/boot/kexec.nix
index 03312aa26edc..02c2713ede11 100644
--- a/nixpkgs/nixos/modules/system/boot/kexec.nix
+++ b/nixpkgs/nixos/modules/system/boot/kexec.nix
@@ -1,8 +1,8 @@
 { pkgs, lib, ... }:
 
 {
-  config = lib.mkIf (lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.kexectools) {
-    environment.systemPackages = [ pkgs.kexectools ];
+  config = lib.mkIf (lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.kexec-tools) {
+    environment.systemPackages = [ pkgs.kexec-tools ];
 
     systemd.services.prepare-kexec =
       { description = "Preparation for kexec";
@@ -10,7 +10,7 @@
         before = [ "systemd-kexec.service" ];
         unitConfig.DefaultDependencies = false;
         serviceConfig.Type = "oneshot";
-        path = [ pkgs.kexectools ];
+        path = [ pkgs.kexec-tools ];
         script =
           ''
             # Don't load the current system profile if we already have a kernel loaded
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix b/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
index c6ec9acd54c1..1be663670384 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/grub.nix
@@ -75,7 +75,7 @@ let
              else "${convertedFont}");
     });
 
-  bootDeviceCounters = fold (device: attr: attr // { ${device} = (attr.${device} or 0) + 1; }) {}
+  bootDeviceCounters = foldr (device: attr: attr // { ${device} = (attr.${device} or 0) + 1; }) {}
     (concatMap (args: args.devices) cfg.mirroredBoots);
 
   convertedFont = (pkgs.runCommand "grub-font-converted.pf2" {}
@@ -553,6 +553,8 @@ in
         apply = toString;
         description = ''
           Index of the default menu item to be booted.
+          Can also be set to "saved", which will make GRUB select
+          the menu item that was used at the last boot.
         '';
       };
 
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
index e0167654748e..4d8537d4c327 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -85,6 +85,7 @@ my $bootloaderId = get("bootloaderId");
 my $forceInstall = get("forceInstall");
 my $font = get("font");
 my $theme = get("theme");
+my $saveDefault = $defaultEntry eq "saved";
 $ENV{'PATH'} = get("path");
 
 die "unsupported GRUB version\n" if $grubVersion != 1 && $grubVersion != 2;
@@ -250,6 +251,8 @@ if ($copyKernels == 0) {
 my $conf .= "# Automatically generated.  DO NOT EDIT THIS FILE!\n";
 
 if ($grubVersion == 1) {
+    # $defaultEntry might be "saved", indicating that we want to use the last selected configuration as default.
+    # Incidentally this is already the correct value for the grub 1 config to achieve this behaviour.
     $conf .= "
     default $defaultEntry
     timeout $timeout
@@ -305,6 +308,10 @@ else {
         " . $grubStore->search;
     }
     # FIXME: should use grub-mkconfig.
+    my $defaultEntryText = $defaultEntry;
+    if ($saveDefault) {
+        $defaultEntryText = "\"\${saved_entry}\"";
+    }
     $conf .= "
     " . $grubBoot->search . "
     if [ -s \$prefix/grubenv ]; then
@@ -318,11 +325,19 @@ else {
     set next_entry=
     save_env next_entry
     set timeout=1
+    set boot_once=true
     else
-    set default=$defaultEntry
+    set default=$defaultEntryText
     set timeout=$timeout
     fi
 
+    function savedefault {
+        if [ -z \"\${boot_once}\"]; then
+        saved_entry=\"\${chosen}\"
+        save_env saved_entry
+        fi
+    }
+
     # Setup the graphics stack for bios and efi systems
     if [ \"\${grub_platform}\" = \"efi\" ]; then
     insmod efi_gop
@@ -468,9 +483,16 @@ sub addEntry {
         $conf .= "  $extraPerEntryConfig\n" if $extraPerEntryConfig;
         $conf .= "  kernel $xen $xenParams\n" if $xen;
         $conf .= "  " . ($xen ? "module" : "kernel") . " $kernel $kernelParams\n";
-        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n\n";
+        $conf .= "  " . ($xen ? "module" : "initrd") . " $initrd\n";
+        if ($saveDefault) {
+            $conf .= "  savedefault\n";
+        }
+        $conf .= "\n";
     } else {
         $conf .= "menuentry \"$name\" " . ($options||"") . " {\n";
+        if ($saveDefault) {
+            $conf .= "  savedefault\n";
+        }
         $conf .= $grubBoot->search . "\n";
         if ($copyKernels == 0) {
             $conf .= $grubStore->search . "\n";
@@ -605,6 +627,11 @@ my $efiTarget = getEfiTarget();
 
 # Append entries detected by os-prober
 if (get("useOSProber") eq "true") {
+    if ($saveDefault) {
+        # os-prober will read this to determine if "savedefault" should be added to generated entries
+        $ENV{'GRUB_SAVEDEFAULT'} = "true";
+    }
+
     my $targetpackage = ($efiTarget eq "no") ? $grub : $grubEfi;
     system(get("shell"), "-c", "pkgdatadir=$targetpackage/share/grub $targetpackage/etc/grub.d/30_os-prober >> $tmpFile");
 }
diff --git a/nixpkgs/nixos/modules/system/boot/resolved.nix b/nixpkgs/nixos/modules/system/boot/resolved.nix
index 84bc9b78076c..a6fc07da0abb 100644
--- a/nixpkgs/nixos/modules/system/boot/resolved.nix
+++ b/nixpkgs/nixos/modules/system/boot/resolved.nix
@@ -140,7 +140,8 @@ in
 
     # add resolve to nss hosts database if enabled and nscd enabled
     # system.nssModules is configured in nixos/modules/system/boot/systemd.nix
-    system.nssDatabases.hosts = optional config.services.nscd.enable "resolve [!UNAVAIL=return]";
+    # added with order 501 to allow modules to go before with mkBefore
+    system.nssDatabases.hosts = (mkOrder 501 ["resolve [!UNAVAIL=return]"]);
 
     systemd.additionalUpstreamSystemUnits = [
       "systemd-resolved.service"
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1-init.sh b/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
index ddaf985878e0..3dfcc010b64e 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
@@ -542,7 +542,7 @@ while read -u 3 mountPoint; do
     # If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs.
     if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then
       fsType=$(blkid -o value -s TYPE "$device")
-      fsSize=$(blockdev --getsize64 "$device")
+      fsSize=$(blockdev --getsize64 "$device" || stat -Lc '%s' "$device")
 
       mkdir -p /tmp-iso
       mount -t "$fsType" /dev/root /tmp-iso
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1.nix b/nixpkgs/nixos/modules/system/boot/stage-1.nix
index d606d473d91e..03133fa1bc43 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-1.nix
@@ -375,7 +375,7 @@ let
         }
         trap cleanup EXIT
 
-        tmp=$(mktemp -d initrd-secrets.XXXXXXXXXX)
+        tmp=$(mktemp -d ''${TMPDIR:-/tmp}/initrd-secrets.XXXXXXXXXX)
 
         ${lib.concatStringsSep "\n" (mapAttrsToList (dest: source:
             let source' = if source == null then dest else toString source; in
diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix
index adc226d89913..644ee9d2e46a 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -70,7 +70,10 @@ let
 
       # Journal.
       "systemd-journald.socket"
+      "systemd-journald@.socket"
+      "systemd-journald-varlink@.socket"
       "systemd-journald.service"
+      "systemd-journald@.service"
       "systemd-journal-flush.service"
       "systemd-journal-catalog-update.service"
       ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
@@ -925,9 +928,8 @@ in
     system.nssModules = [ systemd.out ];
     system.nssDatabases = {
       hosts = (mkMerge [
-        [ "mymachines" ]
-        (mkOrder 1600 [ "myhostname" ] # 1600 to ensure it's always the last
-      )
+        (mkOrder 400 ["mymachines"]) # 400 to ensure it comes before resolve (which is mkBefore'd)
+        (mkOrder 999 ["myhostname"]) # after files (which is 998), but before regular nss modules
       ]);
       passwd = (mkMerge [
         (mkAfter [ "systemd" ])
@@ -1045,7 +1047,7 @@ in
           done
         '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
           rm -f $out/${removePrefix "tmpfiles.d/" name}
-        '') config.system.build.etc.targets;
+        '') config.system.build.etc.passthru.targets;
       }) + "/*";
 
       "systemd/system-generators" = { source = hooks "generators" cfg.generators; };
@@ -1054,6 +1056,7 @@ in
 
     services.dbus.enable = true;
 
+    users.users.systemd-coredump.uid = config.ids.uids.systemd-coredump;
     users.users.systemd-network.uid = config.ids.uids.systemd-network;
     users.groups.systemd-network.gid = config.ids.gids.systemd-network;
     users.users.systemd-resolve.uid = config.ids.uids.systemd-resolve;
@@ -1130,6 +1133,7 @@ in
 
     users.groups.systemd-journal.gid = config.ids.gids.systemd-journal;
     users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
+    users.users.systemd-journal-gateway.group = "systemd-journal-gateway";
     users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
 
     # Generate timer units for all services that have a ‘startAt’ value.
@@ -1182,6 +1186,8 @@ in
     systemd.services."user-runtime-dir@".restartIfChanged = false;
     systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
     systemd.services.systemd-journald.stopIfChanged = false;
+    systemd.services."systemd-journald@".restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
+    systemd.services."systemd-journald@".stopIfChanged = false;
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
diff --git a/nixpkgs/nixos/modules/system/etc/etc.nix b/nixpkgs/nixos/modules/system/etc/etc.nix
index a450f303572e..84468ea31f74 100644
--- a/nixpkgs/nixos/modules/system/etc/etc.nix
+++ b/nixpkgs/nixos/modules/system/etc/etc.nix
@@ -6,23 +6,62 @@ with lib;
 
 let
 
-  etc' = filter (f: f.enable) (attrValues config.environment.etc);
-
-  etc = pkgs.stdenvNoCC.mkDerivation {
-    name = "etc";
-
-    builder = ./make-etc.sh;
-
-    preferLocalBuild = true;
-    allowSubstitutes = false;
-
-    /* !!! Use toXML. */
-    sources = map (x: x.source) etc';
-    targets = map (x: x.target) etc';
-    modes = map (x: x.mode) etc';
-    users  = map (x: x.user) etc';
-    groups  = map (x: x.group) etc';
-  };
+  # if the source is a local file, it should be imported to the store
+  localToStore = mapAttrs (name: value: if name == "source" then "${value}" else value);
+  etc' = map localToStore (filter (f: f.enable) (attrValues config.environment.etc));
+
+  etc = pkgs.runCommandLocal "etc" {
+    # This is needed for the systemd module
+    passthru.targets = map (x: x.target) etc';
+  } /* sh */ ''
+    set -euo pipefail
+
+    makeEtcEntry() {
+      src="$1"
+      target="$2"
+      mode="$3"
+      user="$4"
+      group="$5"
+
+      if [[ "$src" = *'*'* ]]; then
+        # If the source name contains '*', perform globbing.
+        mkdir -p "$out/etc/$target"
+        for fn in $src; do
+            ln -s "$fn" "$out/etc/$target/"
+        done
+      else
+
+        mkdir -p "$out/etc/$(dirname "$target")"
+        if ! [ -e "$out/etc/$target" ]; then
+          ln -s "$src" "$out/etc/$target"
+        else
+          echo "duplicate entry $target -> $src"
+          if [ "$(readlink "$out/etc/$target")" != "$src" ]; then
+            echo "mismatched duplicate entry $(readlink "$out/etc/$target") <-> $src"
+            ret=1
+
+            continue
+          fi
+        fi
+
+        if [ "$mode" != symlink ]; then
+          echo "$mode" > "$out/etc/$target.mode"
+          echo "$user" > "$out/etc/$target.uid"
+          echo "$group" > "$out/etc/$target.gid"
+        fi
+      fi
+    }
+
+    mkdir -p "$out/etc"
+    ${concatMapStringsSep "\n" (etcEntry: escapeShellArgs [
+      "makeEtcEntry"
+      etcEntry.source
+      etcEntry.target
+      etcEntry.mode
+      etcEntry.user
+      etcEntry.group
+    ]) etc'}
+  '';
 
 in
 
diff --git a/nixpkgs/nixos/modules/system/etc/make-etc.sh b/nixpkgs/nixos/modules/system/etc/make-etc.sh
deleted file mode 100644
index aabfb5e88a65..000000000000
--- a/nixpkgs/nixos/modules/system/etc/make-etc.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-source $stdenv/setup
-
-mkdir -p $out/etc
-
-set -f
-sources_=($sources)
-targets_=($targets)
-modes_=($modes)
-users_=($users)
-groups_=($groups)
-set +f
-
-for ((i = 0; i < ${#targets_[@]}; i++)); do
-    source="${sources_[$i]}"
-    target="${targets_[$i]}"
-
-    if [[ "$source" =~ '*' ]]; then
-
-        # If the source name contains '*', perform globbing.
-        mkdir -p $out/etc/$target
-        for fn in $source; do
-            ln -s "$fn" $out/etc/$target/
-        done
-
-    else
-
-        mkdir -p $out/etc/$(dirname $target)
-        if ! [ -e $out/etc/$target ]; then
-            ln -s $source $out/etc/$target
-        else
-            echo "duplicate entry $target -> $source"
-            if test "$(readlink $out/etc/$target)" != "$source"; then
-                echo "mismatched duplicate entry $(readlink $out/etc/$target) <-> $source"
-                exit 1
-            fi
-        fi
-
-        if test "${modes_[$i]}" != symlink; then
-            echo "${modes_[$i]}"  > $out/etc/$target.mode
-            echo "${users_[$i]}"  > $out/etc/$target.uid
-            echo "${groups_[$i]}" > $out/etc/$target.gid
-        fi
-
-    fi
-done
diff --git a/nixpkgs/nixos/modules/tasks/encrypted-devices.nix b/nixpkgs/nixos/modules/tasks/encrypted-devices.nix
index dd337de98698..06117d19af46 100644
--- a/nixpkgs/nixos/modules/tasks/encrypted-devices.nix
+++ b/nixpkgs/nixos/modules/tasks/encrypted-devices.nix
@@ -8,7 +8,7 @@ let
   keyedEncDevs = filter (dev: dev.encrypted.keyFile != null) encDevs;
   keylessEncDevs = filter (dev: dev.encrypted.keyFile == null) encDevs;
   anyEncrypted =
-    fold (j: v: v || j.encrypted.enable) false encDevs;
+    foldr (j: v: v || j.encrypted.enable) false encDevs;
 
   encryptedFSOptions = {
 
diff --git a/nixpkgs/nixos/modules/tasks/filesystems.nix b/nixpkgs/nixos/modules/tasks/filesystems.nix
index d274a38a270f..4f56504f45e7 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems.nix
@@ -255,7 +255,7 @@ in
         # https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
         escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
         swapOptions = sw: concatStringsSep "," (
-          [ "defaults" ]
+          sw.options
           ++ optional (sw.priority != null) "pri=${toString sw.priority}"
           ++ optional (sw.discardPolicy != null) "discard${optionalString (sw.discardPolicy != "both") "=${toString sw.discardPolicy}"}"
         );
@@ -264,6 +264,8 @@ in
         #
         # To make changes, edit the fileSystems and swapDevices NixOS options
         # in your /etc/nixos/configuration.nix file.
+        #
+        # <file system> <mount point>   <type>  <options>       <dump>  <pass>
 
         # Filesystems.
         ${concatMapStrings (fs:
@@ -324,28 +326,33 @@ in
       in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems)) // {
     # Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
     # This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
-    # Since the pstore filesystem is usually empty right after mounting because the backend isn't registered yet, and a path unit cannot detect files inside of it, the same service waits for that to happen. systemd's restart mechanism can't be used here because the first failure also fails all dependent units.
         "mount-pstore" = {
           serviceConfig = {
             Type = "oneshot";
-            ExecStart = "${pkgs.util-linux}/bin/mount -t pstore -o nosuid,noexec,nodev pstore /sys/fs/pstore";
-            ExecStartPost = pkgs.writeShellScript "wait-for-pstore.sh" ''
+            # skip on kernels without the pstore module
+            ExecCondition = "${pkgs.kmod}/bin/modprobe -b pstore";
+            ExecStart = pkgs.writeShellScript "mount-pstore.sh" ''
               set -eu
-              TRIES=0
-              while [ $TRIES -lt 20 ] && [ "$(cat /sys/module/pstore/parameters/backend)" = "(null)" ]; do
-                sleep 0.1
-                TRIES=$((TRIES+1))
+              # if the pstore module is builtin it will have mounted the persistent store automatically. it may also be already mounted for other reasons.
+              ${pkgs.util-linux}/bin/mountpoint -q /sys/fs/pstore || ${pkgs.util-linux}/bin/mount -t pstore -o nosuid,noexec,nodev pstore /sys/fs/pstore
+              # wait up to 1.5 seconds for the backend to be registered and the files to appear. a systemd path unit cannot detect this happening; and succeeding after a restart would not start dependent units.
+              TRIES=15
+              while [ "$(cat /sys/module/pstore/parameters/backend)" = "(null)" ]; do
+                if (( $TRIES )); then
+                  sleep 0.1
+                  TRIES=$((TRIES-1))
+                else
+                  echo "Persistent Storage backend was not registered in time." >&2
+                  break
+                fi
               done
             '';
             RemainAfterExit = true;
           };
           unitConfig = {
-            ConditionPathIsMountPoint = "!/sys/fs/pstore";
             ConditionVirtualization = "!container";
             DefaultDependencies = false; # needed to prevent a cycle
           };
-          after = [ "modprobe@pstore.service" ];
-          requires = [ "modprobe@pstore.service" ];
           before = [ "systemd-pstore.service" ];
           wantedBy = [ "systemd-pstore.service" ];
         };
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/exfat.nix b/nixpkgs/nixos/modules/tasks/filesystems/exfat.nix
index 1527f993fdd4..540b9b91c3ec 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/exfat.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/exfat.nix
@@ -4,8 +4,10 @@ with lib;
 
 {
   config = mkIf (any (fs: fs == "exfat") config.boot.supportedFilesystems) {
-
-    system.fsPackages = [ pkgs.exfat ];
-
+    system.fsPackages = if config.boot.kernelPackages.kernelOlder "5.7" then [
+      pkgs.exfat # FUSE
+    ] else [
+      pkgs.exfatprogs # non-FUSE
+    ];
   };
 }
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
index 376d6530f363..cb0e66402476 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
@@ -8,6 +8,7 @@ with lib;
 let
 
   cfgZfs = config.boot.zfs;
+  cfgExpandOnBoot = config.services.zfs.expandOnBoot;
   cfgSnapshots = config.services.zfs.autoSnapshot;
   cfgSnapFlags = cfgSnapshots.flags;
   cfgScrub = config.services.zfs.autoScrub;
@@ -200,7 +201,6 @@ in
           an interactive prompt (keylocation=prompt) and from a file (keylocation=file://).
         '';
       };
-
     };
 
     services.zfs.autoSnapshot = {
@@ -327,6 +327,23 @@ in
       };
     };
 
+    services.zfs.expandOnBoot = mkOption {
+      type = types.either (types.enum [ "disabled" "all" ]) (types.listOf types.str);
+      default = "disabled";
+      example = [ "tank" "dozer" ];
+      description = ''
+        After importing, expand each device in the specified pools.
+
+        Set the value to the plain string "all" to expand all pools on boot:
+
+            services.zfs.expandOnBoot = "all";
+
+        or set the value to a list of pools to expand the disks of specific pools:
+
+            services.zfs.expandOnBoot = [ "tank" "dozer" ];
+      '';
+    };
+
     services.zfs.zed = {
       enableMail = mkEnableOption "ZED's ability to send emails" // {
         default = cfgZfs.package.enableMail;
@@ -586,6 +603,7 @@ in
               ${cfgZfs.package}/sbin/zfs set nixos:shutdown-time="$(date)" "${pool}"
             '';
           };
+
         createZfsService = serv:
           nameValuePair serv {
             after = [ "systemd-modules-load.service" ];
@@ -609,6 +627,86 @@ in
       systemd.targets.zfs.wantedBy = [ "multi-user.target" ];
     })
 
+    (mkIf (cfgZfs.enabled && cfgExpandOnBoot != "disabled") {
+      systemd.services."zpool-expand@" = {
+        description = "Expand ZFS pools";
+        after = [ "zfs.target" ];
+
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+
+        scriptArgs = "%i";
+        path = [ pkgs.gawk cfgZfs.package ];
+
+        # ZFS has no way of enumerating just devices in a pool in a way
+        # that 'zpool online -e' supports. Thus, we've implemented a
+        # bit of a strange approach of highlighting just devices.
+        # See: https://github.com/openzfs/zfs/issues/12505
+        script = let
+          # This UUID has been chosen at random and is to provide a
+          # collision-proof, predictable token to search for
+          magicIdentifier = "NIXOS-ZFS-ZPOOL-DEVICE-IDENTIFIER-37108bec-aff6-4b58-9e5e-53c7c9766f05";
+          zpoolScripts = pkgs.writeShellScriptBin "device-highlighter" ''
+            echo "${magicIdentifier}"
+          '';
+        in ''
+          pool=$1
+
+          echo "Expanding all devices for $pool."
+
+          # Put our device-highlighter script it to the PATH
+          export ZPOOL_SCRIPTS_PATH=${zpoolScripts}/bin
+
+          # Enable running our precisely specified zpool script as root
+          export ZPOOL_SCRIPTS_AS_ROOT=1
+
+          devices() (
+            zpool status -c device-highlighter "$pool" \
+             | awk '($2 == "ONLINE" && $6 == "${magicIdentifier}") { print $1; }'
+          )
+
+          for device in $(devices); do
+            echo "Attempting to expand $device of $pool..."
+            if ! zpool online -e "$pool" "$device"; then
+              echo "Failed to expand '$device' of '$pool'."
+            fi
+          done
+        '';
+      };
+
+      systemd.services."zpool-expand-pools" =
+        let
+          # Create a string, to be interpolated in a bash script
+          # which enumerates all of the pools to expand.
+          # If the `pools` option is `true`, we want to dynamically
+          # expand every pool. Otherwise we want to enumerate
+          # just the specifically provided list of pools.
+          poolListProvider = if cfgExpandOnBoot == "all"
+            then "$(zpool list -H | awk '{print $1}')"
+            else lib.escapeShellArgs cfgExpandOnBoot;
+        in
+        {
+          description = "Expand specified ZFS pools";
+          wantedBy = [ "default.target" ];
+          after = [ "zfs.target" ];
+
+          serviceConfig = {
+            Type = "oneshot";
+            RemainAfterExit = true;
+          };
+
+          path = [ pkgs.gawk cfgZfs.package ];
+
+          script = ''
+            for pool in ${poolListProvider}; do
+              systemctl start --no-block "zpool-expand@$pool"
+            done
+          '';
+        };
+    })
+
     (mkIf (cfgZfs.enabled && cfgSnapshots.enable) {
       systemd.services = let
                            descr = name: if name == "frequent" then "15 mins"
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix b/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix
index 1c145e8ff477..225f9dc67fcc 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -33,9 +33,6 @@ in
       assertion = cfg.defaultGatewayWindowSize == null;
       message = "networking.defaultGatewayWindowSize is not supported by networkd.";
     } {
-      assertion = cfg.vswitches == {};
-      message = "networking.vswitches are not supported by networkd.";
-    } {
       assertion = cfg.defaultGateway == null || cfg.defaultGateway.interface == null;
       message = "networking.defaultGateway.interface is not supported by networkd.";
     } {
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces.nix b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
index 879f077332e3..8f9c66b01572 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
@@ -1296,14 +1296,14 @@ in
             '';
 
             # Udev script to execute for a new WLAN interface. The script configures the new WLAN interface.
-            newInterfaceScript = device: new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" ''
+            newInterfaceScript = new: pkgs.writeScript "udev-run-script-wlan-interfaces-${new._iName}.sh" ''
               #!${pkgs.runtimeShell}
               # Configure the new interface
               ${pkgs.iw}/bin/iw dev ${new._iName} set type ${new.type}
-              ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${device} set meshid ${new.meshID}"}
-              ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${device} set monitor ${new.flags}"}
-              ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${device} set 4addr ${if new.fourAddr then "on" else "off"}"}
-              ${optionalString (new.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${device} address ${new.mac}"}
+              ${optionalString (new.type == "mesh" && new.meshID!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set meshid ${new.meshID}"}
+              ${optionalString (new.type == "monitor" && new.flags!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set monitor ${new.flags}"}
+              ${optionalString (new.type == "managed" && new.fourAddr!=null) "${pkgs.iw}/bin/iw dev ${new._iName} set 4addr ${if new.fourAddr then "on" else "off"}"}
+              ${optionalString (new.mac != null) "${pkgs.iproute2}/bin/ip link set dev ${new._iName} address ${new.mac}"}
             '';
 
             # Udev attributes for systemd to name the device and to create a .device target.
@@ -1318,7 +1318,7 @@ in
             # It is important to have that rule first as overwriting the NAME attribute also prevents the
             # next rules from matching.
             ${flip (concatMapStringsSep "\n") (wlanListDeviceFirst device wlanDeviceInterfaces.${device}) (interface:
-            ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript device interface}"'')}
+            ''ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", ENV{INTERFACE}=="${interface._iName}", ${systemdAttrs interface._iName}, RUN+="${newInterfaceScript interface}"'')}
 
             # Add the required, new WLAN interfaces to the default WLAN interface with the
             # persistent, default name as assigned by udev.
diff --git a/nixpkgs/nixos/modules/virtualisation/amazon-image.nix b/nixpkgs/nixos/modules/virtualisation/amazon-image.nix
index 26297a7d0f1f..fe248a94488b 100644
--- a/nixpkgs/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixpkgs/nixos/modules/virtualisation/amazon-image.nix
@@ -18,7 +18,15 @@ let
 in
 
 {
-  imports = [ ../profiles/headless.nix ./ec2-data.nix ./amazon-init.nix ];
+  imports = [
+    ../profiles/headless.nix
+    # Note: While we do use the headless profile, we also explicitly
+    # turn on the serial console on ttyS0 below. This is because
+    # AWS does support accessing the serial console:
+    # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configure-access-to-serial-console.html
+    ./ec2-data.nix
+    ./amazon-init.nix
+  ];
 
   config = {
 
@@ -33,23 +41,29 @@ in
 
     boot.growPartition = cfg.hvm;
 
-    fileSystems."/" = {
+    fileSystems."/" = mkIf (!cfg.zfs.enable) {
       device = "/dev/disk/by-label/nixos";
       fsType = "ext4";
       autoResize = true;
     };
 
-    fileSystems."/boot" = mkIf cfg.efi {
+    fileSystems."/boot" = mkIf (cfg.efi || cfg.zfs.enable) {
+      # The ZFS image uses a partition labeled ESP whether or not we're
+      # booting with EFI.
       device = "/dev/disk/by-label/ESP";
       fsType = "vfat";
     };
 
+    services.zfs.expandOnBoot = mkIf cfg.zfs.enable "all";
+
+    boot.zfs.devNodes = mkIf cfg.zfs.enable "/dev/";
+
     boot.extraModulePackages = [
       config.boot.kernelPackages.ena
     ];
     boot.initrd.kernelModules = [ "xen-blkfront" "xen-netfront" ];
     boot.initrd.availableKernelModules = [ "ixgbevf" "ena" "nvme" ];
-    boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0" "random.trust_cpu=on" ];
+    boot.kernelParams = mkIf cfg.hvm [ "console=ttyS0,115200n8" "random.trust_cpu=on" ];
 
     # Prevent the nouveau kernel module from being loaded, as it
     # interferes with the nvidia/nvidia-uvm modules needed for CUDA.
@@ -63,7 +77,12 @@ in
     boot.loader.grub.extraPerEntryConfig = mkIf (!cfg.hvm) "root (hd0)";
     boot.loader.grub.efiSupport = cfg.efi;
     boot.loader.grub.efiInstallAsRemovable = cfg.efi;
-    boot.loader.timeout = 0;
+    boot.loader.timeout = 1;
+    boot.loader.grub.extraConfig = ''
+      serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1
+      terminal_output console serial
+      terminal_input console serial
+    '';
 
     boot.initrd.network.enable = true;
 
@@ -127,15 +146,14 @@ in
         copy_bin_and_libs ${pkgs.util-linux}/sbin/swapon
       '';
 
-    # Don't put old configurations in the GRUB menu.  The user has no
-    # way to select them anyway.
-    boot.loader.grub.configurationLimit = 0;
-
     # Allow root logins only using the SSH key that the user specified
     # at instance creation time.
     services.openssh.enable = true;
     services.openssh.permitRootLogin = "prohibit-password";
 
+    # Enable the serial console on ttyS0
+    systemd.services."serial-getty@ttyS0".enable = true;
+
     # Creates symlinks for block device names.
     services.udev.packages = [ pkgs.ec2-utils ];
 
diff --git a/nixpkgs/nixos/modules/virtualisation/amazon-options.nix b/nixpkgs/nixos/modules/virtualisation/amazon-options.nix
index 2e807131e938..698edcd835a6 100644
--- a/nixpkgs/nixos/modules/virtualisation/amazon-options.nix
+++ b/nixpkgs/nixos/modules/virtualisation/amazon-options.nix
@@ -1,7 +1,46 @@
 { config, lib, pkgs, ... }:
-{
+let
+  inherit (lib) types;
+in {
   options = {
     ec2 = {
+      zfs = {
+        enable = lib.mkOption {
+          default = false;
+          internal = true;
+          description = ''
+            Whether the EC2 instance uses a ZFS root.
+          '';
+        };
+
+        datasets = lib.mkOption {
+          description = ''
+            Datasets to create under the `tank` and `boot` zpools.
+
+            **NOTE:** This option is used only at image creation time, and
+            does not attempt to declaratively create or manage datasets
+            on an existing system.
+          '';
+
+          default = {};
+
+          type = types.attrsOf (types.submodule {
+            options = {
+              mount = lib.mkOption {
+                description = "Where to mount this dataset.";
+                type = types.nullOr types.string;
+                default = null;
+              };
+
+              properties = lib.mkOption {
+                description = "Properties to set on this dataset.";
+                type = types.attrsOf types.string;
+                default = {};
+              };
+            };
+          });
+        };
+      };
       hvm = lib.mkOption {
         default = lib.versionAtLeast config.system.stateVersion "17.03";
         internal = true;
@@ -18,4 +57,17 @@
       };
     };
   };
+
+  config = lib.mkIf config.ec2.zfs.enable {
+    networking.hostId = lib.mkDefault "00000000";
+
+    fileSystems = let
+      mountable = lib.filterAttrs (_: value: ((value.mount or null) != null)) config.ec2.zfs.datasets;
+    in lib.mapAttrs'
+      (dataset: opts: lib.nameValuePair opts.mount {
+        device = dataset;
+        fsType = "zfs";
+      })
+      mountable;
+  };
 }
diff --git a/nixpkgs/nixos/modules/virtualisation/containerd.nix b/nixpkgs/nixos/modules/virtualisation/containerd.nix
index c7ceb816a311..43cb6273f253 100644
--- a/nixpkgs/nixos/modules/virtualisation/containerd.nix
+++ b/nixpkgs/nixos/modules/virtualisation/containerd.nix
@@ -86,6 +86,7 @@ in
 
         StateDirectory = "containerd";
         RuntimeDirectory = "containerd";
+        RuntimeDirectoryPreserve = "yes";
       };
       unitConfig = {
         StartLimitBurst = "16";
diff --git a/nixpkgs/nixos/modules/virtualisation/google-compute-image.nix b/nixpkgs/nixos/modules/virtualisation/google-compute-image.nix
index 79c3921669ed..0c72696f802b 100644
--- a/nixpkgs/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixpkgs/nixos/modules/virtualisation/google-compute-image.nix
@@ -36,6 +36,14 @@ in
         `<nixpkgs/nixos/modules/virtualisation/google-compute-image.nix>`.
       '';
     };
+
+    virtualisation.googleComputeImage.compressionLevel = mkOption {
+      type = types.int;
+      default = 6;
+      description = ''
+        GZIP compression level of the resulting disk image (1-9).
+      '';
+    };
   };
 
   #### implementation
@@ -47,7 +55,8 @@ in
         PATH=$PATH:${with pkgs; lib.makeBinPath [ gnutar gzip ]}
         pushd $out
         mv $diskImage disk.raw
-        tar -Szcf nixos-image-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}.raw.tar.gz disk.raw
+        tar -Sc disk.raw | gzip -${toString cfg.compressionLevel} > \
+          nixos-image-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}.raw.tar.gz
         rm $out/disk.raw
         popd
       '';
diff --git a/nixpkgs/nixos/modules/virtualisation/podman.nix b/nixpkgs/nixos/modules/virtualisation/podman.nix
index e245004e04a6..893afee4c32d 100644
--- a/nixpkgs/nixos/modules/virtualisation/podman.nix
+++ b/nixpkgs/nixos/modules/virtualisation/podman.nix
@@ -9,7 +9,7 @@ let
   podmanPackage = (pkgs.podman.override { inherit (cfg) extraPackages; });
 
   # Provides a fake "docker" binary mapping to podman
-  dockerCompat = pkgs.runCommandNoCC "${podmanPackage.pname}-docker-compat-${podmanPackage.version}" {
+  dockerCompat = pkgs.runCommand "${podmanPackage.pname}-docker-compat-${podmanPackage.version}" {
     outputs = [ "out" "man" ];
     inherit (podmanPackage) meta;
   } ''
diff --git a/nixpkgs/nixos/modules/virtualisation/virtualbox-image.nix b/nixpkgs/nixos/modules/virtualisation/virtualbox-image.nix
index 272c696807aa..1a0c4df42cb3 100644
--- a/nixpkgs/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixpkgs/nixos/modules/virtualisation/virtualbox-image.nix
@@ -18,6 +18,13 @@ in {
           The size of the VirtualBox base image in MiB.
         '';
       };
+      baseImageFreeSpace = mkOption {
+        type = with types; int;
+        default = 30 * 1024;
+        description = ''
+          Free space in the VirtualBox base image in MiB.
+        '';
+      };
       memorySize = mkOption {
         type = types.int;
         default = 1536;
@@ -129,6 +136,7 @@ in {
       inherit pkgs lib config;
       partitionTableType = "legacy";
       diskSize = cfg.baseImageSize;
+      additionalSpace = "${toString cfg.baseImageFreeSpace}M";
 
       postVM =
         ''