about summary refs log tree commit diff
path: root/nixpkgs/nixos
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2024-02-20 12:16:56 +0100
committerAlyssa Ross <hi@alyssa.is>2024-02-20 12:16:56 +0100
commitb24d64b3b1ef897f07cd072a88a9881cb330aa7f (patch)
treea87bb2eed9af3ef1efd51dd65221d91f0c949041 /nixpkgs/nixos
parent73338df7473bb3810e70a16b8b0cba4f0f606f2b (diff)
parentfa15b53dbea5028db38d6e09b4cef6eba42aeebb (diff)
downloadnixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.tar
nixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.tar.gz
nixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.tar.bz2
nixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.tar.lz
nixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.tar.xz
nixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.tar.zst
nixlib-b24d64b3b1ef897f07cd072a88a9881cb330aa7f.zip
Merge branch 'nixos-unstable-small' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/nixos')
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md8
-rw-r--r--nixpkgs/nixos/lib/systemd-unit-options.nix2
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix19
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix23
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix7
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix3
-rw-r--r--nixpkgs/nixos/modules/config/ldso.nix6
-rw-r--r--nixpkgs/nixos/modules/config/no-x-libs.nix1
-rw-r--r--nixpkgs/nixos/modules/config/users-groups.nix22
-rw-r--r--nixpkgs/nixos/modules/config/vte.nix4
-rw-r--r--nixpkgs/nixos/modules/config/xdg/portal.nix28
-rw-r--r--nixpkgs/nixos/modules/module-list.nix2
-rw-r--r--nixpkgs/nixos/modules/programs/chromium.nix2
-rw-r--r--nixpkgs/nixos/modules/programs/quark-goldleaf.nix18
-rw-r--r--nixpkgs/nixos/modules/programs/yazi.nix4
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix3
-rw-r--r--nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix3
-rw-r--r--nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/databases/mysql.nix15
-rw-r--r--nixpkgs/nixos/modules/services/databases/pgbouncer.nix19
-rw-r--r--nixpkgs/nixos/modules/services/display-managers/greetd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/games/teeworlds.nix310
-rw-r--r--nixpkgs/nixos/modules/services/hardware/asusd.nix10
-rw-r--r--nixpkgs/nixos/modules/services/hardware/bolt.nix19
-rw-r--r--nixpkgs/nixos/modules/services/hardware/brltty.nix1
-rw-r--r--nixpkgs/nixos/modules/services/hardware/undervolt.nix9
-rw-r--r--nixpkgs/nixos/modules/services/mail/stalwart-mail.nix13
-rw-r--r--nixpkgs/nixos/modules/services/misc/sourcehut/default.nix22
-rw-r--r--nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix13
-rw-r--r--nixpkgs/nixos/modules/services/networking/go-camo.nix73
-rw-r--r--nixpkgs/nixos/modules/services/networking/hostapd.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/knot.nix109
-rw-r--r--nixpkgs/nixos/modules/services/networking/libreswan.nix7
-rw-r--r--nixpkgs/nixos/modules/services/security/opensnitch.nix102
-rw-r--r--nixpkgs/nixos/modules/services/system/automatic-timezoned.nix2
-rw-r--r--nixpkgs/nixos/modules/services/torrent/transmission.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mastodon.nix46
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/photoprism.nix19
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/garage.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/ttyd.nix92
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix10
-rw-r--r--nixpkgs/nixos/modules/services/x11/window-managers/icewm.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/kernel.nix7
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1.nix5
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/oomd.nix6
-rw-r--r--nixpkgs/nixos/modules/system/etc/build-composefs-dump.py3
-rw-r--r--nixpkgs/nixos/modules/virtualisation/docker.nix14
-rw-r--r--nixpkgs/nixos/modules/virtualisation/incus.nix12
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix2
-rw-r--r--nixpkgs/nixos/tests/docker-tools.nix54
-rw-r--r--nixpkgs/nixos/tests/go-camo.nix30
-rw-r--r--nixpkgs/nixos/tests/incus/default.nix1
-rw-r--r--nixpkgs/nixos/tests/incus/ui.nix63
-rw-r--r--nixpkgs/nixos/tests/keepalived.nix2
-rw-r--r--nixpkgs/nixos/tests/knot.nix11
-rw-r--r--nixpkgs/nixos/tests/lemmy.nix3
-rw-r--r--nixpkgs/nixos/tests/opensnitch.nix2
-rw-r--r--nixpkgs/nixos/tests/prometheus-exporters.nix3
-rw-r--r--nixpkgs/nixos/tests/qownnotes.nix16
-rw-r--r--nixpkgs/nixos/tests/slurm.nix2
-rw-r--r--nixpkgs/nixos/tests/sourcehut.nix252
-rw-r--r--nixpkgs/nixos/tests/sourcehut/builds.nix54
-rw-r--r--nixpkgs/nixos/tests/sourcehut/default.nix6
-rw-r--r--nixpkgs/nixos/tests/sourcehut/git.nix96
-rw-r--r--nixpkgs/nixos/tests/sourcehut/nodes/common.nix107
-rw-r--r--nixpkgs/nixos/tests/stalwart-mail.nix11
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/default.nix2
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix (renamed from nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix)38
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/script.nix1
-rw-r--r--nixpkgs/nixos/tests/web-apps/mastodon/standard.nix7
-rw-r--r--nixpkgs/nixos/tests/web-servers/ttyd.nix20
71 files changed, 1366 insertions, 522 deletions
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md
index f6a77036674e..be754f99713b 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -71,6 +71,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [TigerBeetle](https://tigerbeetle.com/), a distributed financial accounting database designed for mission critical safety and performance. Available as [services.tigerbeetle](#opt-services.tigerbeetle.enable).
 
+- [go-camo](https://github.com/cactus/go-camo), a secure image proxy server. Available as [services.go-camo](#opt-services.go-camo.enable).
+
 - [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
 
 - [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable).
@@ -167,6 +169,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `xxd` has been moved from `vim` default output to its own output to reduce closure size. The canonical way to reference it across all platforms is `unixtools.xxd`.
 
+- The `stalwart-mail` package has been updated to v0.5.3, which includes [breaking changes](https://github.com/stalwartlabs/mail-server/blob/v0.5.3/UPGRADING.md).
+
 - `services.avahi.nssmdns` got split into `services.avahi.nssmdns4` and `services.avahi.nssmdns6` which enable the mDNS NSS switch for IPv4 and IPv6 respectively.
   Since most mDNS responders only register IPv4 addresses, most users want to keep the IPv6 support disabled to avoid long timeouts.
 
@@ -175,6 +179,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   `wants`), because the dependency that `multi-user.target` has on
   `network-online.target` is planned for removal.
 
+- `services.pgbouncer` now has systemd support enabled and will log to journald. The default setting for `services.pgbouncer.logFile` is now `null` to disable logging to a separate log file.
+
 - `services.archisteamfarm` no longer uses the abbreviation `asf` for its state directory (`/var/lib/asf`), user and group (both `asf`). Instead the long name `archisteamfarm` is used.
   Configurations with `system.stateVersion` 23.11 or earlier, default to the old stateDirectory until the 24.11 release and must either set the option explicitly or move the data to the new directory.
 
@@ -335,6 +341,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - The `services.paperless` module no longer uses the previously downloaded NLTK data stored in `/var/cache/paperless/nltk`. This directory can be removed.
 
+- The `services.teeworlds` module now has a wealth of configuration options, including a new `package` option.
+
 - The `hardware.pulseaudio` module now sets permission of pulse user home directory to 755 when running in "systemWide" mode. It fixes [issue 114399](https://github.com/NixOS/nixpkgs/issues/114399).
 
 - The module `services.github-runner` has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration.
diff --git a/nixpkgs/nixos/lib/systemd-unit-options.nix b/nixpkgs/nixos/lib/systemd-unit-options.nix
index df05d165d9e8..bc7880da9fe0 100644
--- a/nixpkgs/nixos/lib/systemd-unit-options.nix
+++ b/nixpkgs/nixos/lib/systemd-unit-options.nix
@@ -6,7 +6,7 @@ with lib;
 let
   checkService = checkUnitConfig "Service" [
     (assertValueOneOf "Type" [
-      "exec" "simple" "forking" "oneshot" "dbus" "notify" "idle"
+      "exec" "simple" "forking" "oneshot" "dbus" "notify" "notify-reload" "idle"
     ])
     (assertValueOneOf "Restart" [
       "no" "on-success" "on-failure" "on-abnormal" "on-abort" "always"
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix
index ef00c6f86cbd..4698971de8ff 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix
@@ -13,8 +13,23 @@
       ./lxd.nix
     ];
 
-  networking.useDHCP = false;
-  networking.interfaces.eth0.useDHCP = true;
+  networking = {
+    dhcpcd.enable = false;
+    useDHCP = false;
+    useHostResolvConf = false;
+  };
+
+  systemd.network = {
+    enable = true;
+    networks."50-eth0" = {
+      matchConfig.Name = "eth0";
+      networkConfig = {
+        DHCP = "ipv4";
+        IPv6AcceptRA = true;
+      };
+      linkConfig.RequiredForOnline = "routable";
+    };
+  };
 
   system.stateVersion = "@stateVersion@"; # Did you read the comment?
 }
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix
index b77f9f5aabe0..930ab34af385 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix
@@ -20,12 +20,25 @@
     };
   in ''
     if [ ! -e /etc/nixos/configuration.nix ]; then
-      mkdir -p /etc/nixos
-      cp ${config} /etc/nixos/configuration.nix
+      install -m 644 -D ${config} /etc/nixos/configuration.nix
     fi
   '';
 
-  # Network
-  networking.useDHCP = false;
-  networking.interfaces.eth0.useDHCP = true;
+  networking = {
+    dhcpcd.enable = false;
+    useDHCP = false;
+    useHostResolvConf = false;
+  };
+
+  systemd.network = {
+    enable = true;
+    networks."50-eth0" = {
+      matchConfig.Name = "eth0";
+      networkConfig = {
+        DHCP = "ipv4";
+        IPv6AcceptRA = true;
+      };
+      linkConfig.RequiredForOnline = "routable";
+    };
+  };
 }
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix
index 2d1761401fcd..d1264fa04304 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix
@@ -14,14 +14,15 @@
     ];
 
   networking = {
-    dhcdpcd.enable = false;
+    dhcpcd.enable = false;
     useDHCP = false;
+    useHostResolvConf = false;
   };
 
   systemd.network = {
     enable = true;
-    networks."50-eth0" = {
-      matchConfig.Name = "eth0";
+    networks."50-enp5s0" = {
+      matchConfig.Name = "enp5s0";
       networkConfig = {
         DHCP = "ipv4";
         IPv6AcceptRA = true;
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
index a58579914465..bbbdd1f3fa17 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
@@ -27,8 +27,9 @@
 
   # Network
   networking = {
-    dhcdpcd.enable = false;
+    dhcpcd.enable = false;
     useDHCP = false;
+    useHostResolvConf = false;
   };
 
   systemd.network = {
diff --git a/nixpkgs/nixos/modules/config/ldso.nix b/nixpkgs/nixos/modules/config/ldso.nix
index 72ae3958d886..bd6f0dc5a83b 100644
--- a/nixpkgs/nixos/modules/config/ldso.nix
+++ b/nixpkgs/nixos/modules/config/ldso.nix
@@ -6,9 +6,9 @@ let
   libDir = pkgs.stdenv.hostPlatform.libDir;
   ldsoBasename = builtins.unsafeDiscardStringContext (last (splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker));
 
-  pkgs32 = pkgs.pkgsi686Linux;
-  libDir32 = pkgs32.stdenv.hostPlatform.libDir;
-  ldsoBasename32 = builtins.unsafeDiscardStringContext (last (splitString "/" pkgs32.stdenv.cc.bintools.dynamicLinker));
+  # Hard-code to avoid creating another instance of nixpkgs. Also avoids eval errors in some cases.
+  libDir32 = "lib"; # pkgs.pkgsi686Linux.stdenv.hostPlatform.libDir
+  ldsoBasename32 = "ld-linux.so.2"; # last (splitString "/" pkgs.pkgsi686Linux.stdenv.cc.bintools.dynamicLinker)
 in {
   options = {
     environment.ldso = mkOption {
diff --git a/nixpkgs/nixos/modules/config/no-x-libs.nix b/nixpkgs/nixos/modules/config/no-x-libs.nix
index 2f763290e32d..32b17f6059ef 100644
--- a/nixpkgs/nixos/modules/config/no-x-libs.nix
+++ b/nixpkgs/nixos/modules/config/no-x-libs.nix
@@ -83,6 +83,7 @@ with lib;
       # translateManpages -> perlPackages.po4a -> texlive-combined-basic -> texlive-core-big -> libX11
       util-linux = super.util-linux.override { translateManpages = false; };
       vim-full = super.vim-full.override { guiSupport = false; };
+      vte = super.vte.override { gtkVersion = null; };
       zbar = super.zbar.override { enableVideo = false; withXorg = false; };
     }));
   };
diff --git a/nixpkgs/nixos/modules/config/users-groups.nix b/nixpkgs/nixos/modules/config/users-groups.nix
index 967ad0846d75..dd34771c0b42 100644
--- a/nixpkgs/nixos/modules/config/users-groups.nix
+++ b/nixpkgs/nixos/modules/config/users-groups.nix
@@ -649,7 +649,6 @@ in {
         home = "/root";
         shell = mkDefault cfg.defaultUserShell;
         group = "root";
-        initialHashedPassword = mkDefault "!";
       };
       nobody = {
         uid = ids.uids.nobody;
@@ -897,7 +896,26 @@ in {
     ));
 
     warnings =
-      builtins.filter (x: x != null) (
+      flip concatMap (attrValues cfg.users) (user: let
+        unambiguousPasswordConfiguration = 1 >= length (filter (x: x != null) ([
+          user.hashedPassword
+          user.hashedPasswordFile
+          user.password
+        ] ++ optionals cfg.mutableUsers [
+          # For immutable users, initialHashedPassword is set to hashedPassword,
+          # so using these options would always trigger the assertion.
+          user.initialHashedPassword
+          user.initialPassword
+        ]));
+      in optional (!unambiguousPasswordConfiguration) ''
+        The user '${user.name}' has multiple of the options
+        `hashedPassword`, `password`, `hashedPasswordFile`, `initialPassword`
+        & `initialHashedPassword` set to a non-null value.
+        The options silently discard others by the order of precedence
+        given above which can lead to surprising results. To resolve this warning,
+        set at most one of the options above to a non-`null` value.
+      '')
+      ++ builtins.filter (x: x != null) (
         flip mapAttrsToList cfg.users (_: user:
         # This regex matches a subset of the Modular Crypto Format (MCF)[1]
         # informal standard. Since this depends largely on the OS or the
diff --git a/nixpkgs/nixos/modules/config/vte.nix b/nixpkgs/nixos/modules/config/vte.nix
index a969607f6e0b..48f85246560a 100644
--- a/nixpkgs/nixos/modules/config/vte.nix
+++ b/nixpkgs/nixos/modules/config/vte.nix
@@ -1,5 +1,3 @@
-# VTE
-
 { config, pkgs, lib, ... }:
 
 with lib;
@@ -9,7 +7,7 @@ let
   vteInitSnippet = ''
     # Show current working directory in VTE terminals window title.
     # Supports both bash and zsh, requires interactive shell.
-    . ${pkgs.vte}/etc/profile.d/vte.sh
+    . ${pkgs.vte.override { gtkVersion = null; }}/etc/profile.d/vte.sh
   '';
 
 in
diff --git a/nixpkgs/nixos/modules/config/xdg/portal.nix b/nixpkgs/nixos/modules/config/xdg/portal.nix
index 07d4fa76c2e8..5aa23377f9ff 100644
--- a/nixpkgs/nixos/modules/config/xdg/portal.nix
+++ b/nixpkgs/nixos/modules/config/xdg/portal.nix
@@ -119,19 +119,6 @@ in
     let
       cfg = config.xdg.portal;
       packages = [ pkgs.xdg-desktop-portal ] ++ cfg.extraPortals;
-      configPackages = cfg.configPackages;
-
-      joinedPortals = pkgs.buildEnv {
-        name = "xdg-portals";
-        paths = packages;
-        pathsToLink = [ "/share/xdg-desktop-portal/portals" "/share/applications" ];
-      };
-
-      joinedPortalConfigs = pkgs.buildEnv {
-        name = "xdg-portal-configs";
-        paths = configPackages;
-        pathsToLink = [ "/share/xdg-desktop-portal" ];
-      };
     in
     mkIf cfg.enable {
       warnings = lib.optional (cfg.configPackages == [ ] && cfg.config == { }) ''
@@ -158,17 +145,18 @@ in
       systemd.packages = packages;
 
       environment = {
-        # fixes screen sharing on plasmawayland on non-chromium apps by linking
-        # share/applications/*.desktop files
-        # see https://github.com/NixOS/nixpkgs/issues/145174
-        systemPackages = [ joinedPortals ];
-        pathsToLink = [ "/share/applications" ];
+        systemPackages = packages ++ cfg.configPackages;
+        pathsToLink = [
+          # Portal definitions and upstream desktop environment portal configurations.
+          "/share/xdg-desktop-portal"
+          # .desktop files to register fallback icon and app name.
+          "/share/applications"
+        ];
 
         sessionVariables = {
           GTK_USE_PORTAL = mkIf cfg.gtkUsePortal "1";
           NIXOS_XDG_OPEN_USE_PORTAL = mkIf cfg.xdgOpenUsePortal "1";
-          XDG_DESKTOP_PORTAL_DIR = "${joinedPortals}/share/xdg-desktop-portal/portals";
-          NIXOS_XDG_DESKTOP_PORTAL_CONFIG_DIR = mkIf (cfg.configPackages != [ ]) "${joinedPortalConfigs}/share/xdg-desktop-portal";
+          NIX_XDG_DESKTOP_PORTAL_DIR = "/run/current-system/sw/share/xdg-desktop-portal/portals";
         };
 
         etc = lib.concatMapAttrs
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index e81754414ea4..a64efec046a4 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -241,6 +241,7 @@
   ./programs/proxychains.nix
   ./programs/qdmr.nix
   ./programs/qt5ct.nix
+  ./programs/quark-goldleaf.nix
   ./programs/regreet.nix
   ./programs/rog-control-center.nix
   ./programs/rust-motd.nix
@@ -963,6 +964,7 @@
   ./services/networking/gns3-server.nix
   ./services/networking/gnunet.nix
   ./services/networking/go-autoconfig.nix
+  ./services/networking/go-camo.nix
   ./services/networking/go-neb.nix
   ./services/networking/go-shadowsocks2.nix
   ./services/networking/gobgpd.nix
diff --git a/nixpkgs/nixos/modules/programs/chromium.nix b/nixpkgs/nixos/modules/programs/chromium.nix
index 287d93c82cad..45a9e9e2a689 100644
--- a/nixpkgs/nixos/modules/programs/chromium.nix
+++ b/nixpkgs/nixos/modules/programs/chromium.nix
@@ -23,7 +23,7 @@ in
 
       enablePlasmaBrowserIntegration = mkEnableOption (lib.mdDoc "Native Messaging Host for Plasma Browser Integration");
 
-      plasmaBrowserIntegrationPackage = mkPackageOption pkgs "plasma5Packages.plasma-browser-integration" { };
+      plasmaBrowserIntegrationPackage = mkPackageOption pkgs [ "plasma5Packages" "plasma-browser-integration" ] { };
 
       extensions = mkOption {
         type = with types; nullOr (listOf str);
diff --git a/nixpkgs/nixos/modules/programs/quark-goldleaf.nix b/nixpkgs/nixos/modules/programs/quark-goldleaf.nix
new file mode 100644
index 000000000000..71aadc8c594e
--- /dev/null
+++ b/nixpkgs/nixos/modules/programs/quark-goldleaf.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.programs.quark-goldleaf;
+in
+{
+  options = {
+    programs.quark-goldleaf = {
+      enable = lib.mkEnableOption "quark-goldleaf with udev rules applied";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.quark-goldleaf ];
+    services.udev.packages = [ pkgs.quark-goldleaf ];
+  };
+
+  meta.maintainers = pkgs.quark-goldleaf.meta.maintainers;
+}
diff --git a/nixpkgs/nixos/modules/programs/yazi.nix b/nixpkgs/nixos/modules/programs/yazi.nix
index 273a7eeed05f..338eddb60d80 100644
--- a/nixpkgs/nixos/modules/programs/yazi.nix
+++ b/nixpkgs/nixos/modules/programs/yazi.nix
@@ -22,7 +22,7 @@ in
             description = lib.mdDoc ''
               Configuration included in `${name}.toml`.
 
-              See https://github.com/sxyazi/yazi/blob/v${cfg.package.version}/config/docs/${name}.md for documentation.
+              See https://yazi-rs.github.io/docs/configuration/${name}/ for documentation.
             '';
           }))
           names);
@@ -47,7 +47,5 @@ in
   };
   meta = {
     maintainers = with lib.maintainers; [ linsui ];
-    # The version of the package is used in the doc.
-    buildDocsInSandbox = false;
   };
 }
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix
index 3fb916c76971..a920b6cb1268 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/default.nix
@@ -285,7 +285,7 @@ in {
       systemd.tmpfiles.rules = [
         "d /opt/cni/bin 0755 root root -"
         "d /run/kubernetes 0755 kubernetes kubernetes -"
-        "d /var/lib/kubernetes 0755 kubernetes kubernetes -"
+        "d ${cfg.dataDir} 0755 kubernetes kubernetes -"
       ];
 
       users.users.kubernetes = {
@@ -294,6 +294,7 @@ in {
         group = "kubernetes";
         home = cfg.dataDir;
         createHome = true;
+        homeMode = "755";
       };
       users.groups.kubernetes.gid = config.ids.gids.kubernetes;
 
diff --git a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
index 9a01238c2391..a4b5cb8eda86 100644
--- a/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
+++ b/nixpkgs/nixos/modules/services/cluster/kubernetes/pki.nix
@@ -192,7 +192,8 @@ in
         mkdir -p "$(dirname "${certmgrAPITokenPath}")"
         if [ -f "${cfsslAPITokenPath}" ]; then
           ln -fs "${cfsslAPITokenPath}" "${certmgrAPITokenPath}"
-        else
+        elif [ ! -f "${certmgrAPITokenPath}" ]; then
+          # Don't remove the token if it already exists
           install -m 600 /dev/null "${certmgrAPITokenPath}"
         fi
       ''
diff --git a/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix b/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix
index 54bbe69703f9..b1d44e67658b 100644
--- a/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixpkgs/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -39,7 +39,7 @@ let
 
   hydra-package =
   let
-    makeWrapperArgs = concatStringsSep " " (mapAttrsToList (key: value: "--set \"${key}\" \"${value}\"") hydraEnv);
+    makeWrapperArgs = concatStringsSep " " (mapAttrsToList (key: value: "--set-default \"${key}\" \"${value}\"") hydraEnv);
   in pkgs.buildEnv rec {
     name = "hydra-env";
     nativeBuildInputs = [ pkgs.makeWrapper ];
diff --git a/nixpkgs/nixos/modules/services/databases/mysql.nix b/nixpkgs/nixos/modules/services/databases/mysql.nix
index 128bb0862175..a6d71cca88de 100644
--- a/nixpkgs/nixos/modules/services/databases/mysql.nix
+++ b/nixpkgs/nixos/modules/services/databases/mysql.nix
@@ -7,6 +7,9 @@ let
   cfg = config.services.mysql;
 
   isMariaDB = lib.getName cfg.package == lib.getName pkgs.mariadb;
+  isOracle = lib.getName cfg.package == lib.getName pkgs.mysql80;
+  # Oracle MySQL has supported "notify" service type since 8.0
+  hasNotify = isMariaDB || (isOracle && versionAtLeast cfg.package.version "8.0");
 
   mysqldOptions =
     "--user=${cfg.user} --datadir=${cfg.dataDir} --basedir=${cfg.package}";
@@ -377,19 +380,11 @@ in
         # The super user account to use on *first* run of MySQL server
         superUser = if isMariaDB then cfg.user else "root";
       in ''
-        ${optionalString (!isMariaDB) ''
+        ${optionalString (!hasNotify) ''
           # Wait until the MySQL server is available for use
-          count=0
           while [ ! -e /run/mysqld/mysqld.sock ]
           do
-              if [ $count -eq 30 ]
-              then
-                  echo "Tried 30 times, giving up..."
-                  exit 1
-              fi
-
               echo "MySQL daemon not yet started. Waiting for 1 second..."
-              count=$((count++))
               sleep 1
           done
         ''}
@@ -477,7 +472,7 @@ in
 
       serviceConfig = mkMerge [
         {
-          Type = if isMariaDB then "notify" else "simple";
+          Type = if hasNotify then "notify" else "simple";
           Restart = "on-abort";
           RestartSec = "5s";
 
diff --git a/nixpkgs/nixos/modules/services/databases/pgbouncer.nix b/nixpkgs/nixos/modules/services/databases/pgbouncer.nix
index 65b287e84442..157d49c13161 100644
--- a/nixpkgs/nixos/modules/services/databases/pgbouncer.nix
+++ b/nixpkgs/nixos/modules/services/databases/pgbouncer.nix
@@ -66,9 +66,6 @@ let
       ${optionalString (cfg.adminUsers != null) "admin_users = ${cfg.adminUsers}"}
       ${optionalString (cfg.statsUsers != null) "stats_users = ${cfg.statsUsers}"}
 
-      # linux
-      pidfile = /run/pgbouncer/pgbouncer.pid
-
       # extra
       ${cfg.extraConfig}
     '';
@@ -96,10 +93,9 @@ in {
 
     logFile = mkOption {
       type = types.nullOr types.str;
-      default = "pgbouncer.log";
+      default = null;
       description = lib.mdDoc ''
-        Specifies the log file.
-        Either this or syslog has to be specified.
+        Specifies a log file in addition to journald.
       '';
     };
 
@@ -601,22 +597,21 @@ in {
 
     systemd.services.pgbouncer = {
       description = "PgBouncer - PostgreSQL connection pooler";
-      wants    = [ "postgresql.service" ];
-      after    = [ "postgresql.service" ];
+      wants    = [ "network-online.target" ] ++ lib.optional config.services.postgresql.enable "postgresql.service";
+      after    = [ "network-online.target" ] ++ lib.optional config.services.postgresql.enable "postgresql.service";
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        Type = "forking";
+        Type = "notify";
         User = cfg.user;
         Group = cfg.group;
-        ExecStart = "${pkgs.pgbouncer}/bin/pgbouncer -d ${confFile}";
+        ExecStart = "${lib.getExe pkgs.pgbouncer} ${confFile}";
         ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
         RuntimeDirectory = "pgbouncer";
-        PIDFile = "/run/pgbouncer/pgbouncer.pid";
         LimitNOFILE = cfg.openFilesLimit;
       };
     };
 
-    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
+    networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.listenPort;
 
   };
 
diff --git a/nixpkgs/nixos/modules/services/display-managers/greetd.nix b/nixpkgs/nixos/modules/services/display-managers/greetd.nix
index 2212f97a9ffe..c2d345152de9 100644
--- a/nixpkgs/nixos/modules/services/display-managers/greetd.nix
+++ b/nixpkgs/nixos/modules/services/display-managers/greetd.nix
@@ -78,7 +78,7 @@ in
       serviceConfig = {
         ExecStart = "${pkgs.greetd.greetd}/bin/greetd --config ${settingsFormat.generate "greetd.toml" cfg.settings}";
 
-        Restart = mkIf cfg.restart "always";
+        Restart = mkIf cfg.restart "on-success";
 
         # Defaults from greetd upstream configuration
         IgnoreSIGPIPE = false;
diff --git a/nixpkgs/nixos/modules/services/games/teeworlds.nix b/nixpkgs/nixos/modules/services/games/teeworlds.nix
index bd0df1ffca57..04b611fb3cb1 100644
--- a/nixpkgs/nixos/modules/services/games/teeworlds.nix
+++ b/nixpkgs/nixos/modules/services/games/teeworlds.nix
@@ -6,13 +6,86 @@ let
   cfg = config.services.teeworlds;
   register = cfg.register;
 
+  bool = b: if b != null && b then "1" else "0";
+  optionalSetting = s: setting: optionalString (s != null) "${setting} ${s}";
+  lookup = attrs: key: default: if attrs ? key then attrs."${key}" else default;
+
+  inactivePenaltyOptions = {
+    "spectator" = "1";
+    "spectator/kick" = "2";
+    "kick" = "3";
+  };
+  skillLevelOptions = {
+    "casual" = "0";
+    "normal" = "1";
+    "competitive" = "2";
+  };
+  tournamentModeOptions = {
+    "disable" = "0";
+    "enable"  = "1";
+    "restrictSpectators" = "2";
+  };
+
   teeworldsConf = pkgs.writeText "teeworlds.cfg" ''
     sv_port ${toString cfg.port}
-    sv_register ${if cfg.register then "1" else "0"}
-    ${optionalString (cfg.name != null) "sv_name ${cfg.name}"}
-    ${optionalString (cfg.motd != null) "sv_motd ${cfg.motd}"}
-    ${optionalString (cfg.password != null) "password ${cfg.password}"}
-    ${optionalString (cfg.rconPassword != null) "sv_rcon_password ${cfg.rconPassword}"}
+    sv_register ${bool cfg.register}
+    sv_name ${cfg.name}
+    ${optionalSetting cfg.motd "sv_motd"}
+    ${optionalSetting cfg.password "password"}
+    ${optionalSetting cfg.rconPassword "sv_rcon_password"}
+
+    ${optionalSetting cfg.server.bindAddr "bindaddr"}
+    ${optionalSetting cfg.server.hostName "sv_hostname"}
+    sv_high_bandwidth ${bool cfg.server.enableHighBandwidth}
+    sv_inactivekick ${lookup inactivePenaltyOptions cfg.server.inactivePenalty "spectator/kick"}
+    sv_inactivekick_spec ${bool cfg.server.kickInactiveSpectators}
+    sv_inactivekick_time ${toString cfg.server.inactiveTime}
+    sv_max_clients ${toString cfg.server.maxClients}
+    sv_max_clients_per_ip ${toString cfg.server.maxClientsPerIP}
+    sv_skill_level ${lookup skillLevelOptions cfg.server.skillLevel "normal"}
+    sv_spamprotection ${bool cfg.server.enableSpamProtection}
+
+    sv_gametype ${cfg.game.gameType}
+    sv_map ${cfg.game.map}
+    sv_match_swap ${bool cfg.game.swapTeams}
+    sv_player_ready_mode ${bool cfg.game.enableReadyMode}
+    sv_player_slots ${toString cfg.game.playerSlots}
+    sv_powerups ${bool cfg.game.enablePowerups}
+    sv_scorelimit ${toString cfg.game.scoreLimit}
+    sv_strict_spectate_mode ${bool cfg.game.restrictSpectators}
+    sv_teamdamage ${bool cfg.game.enableTeamDamage}
+    sv_timelimit ${toString cfg.game.timeLimit}
+    sv_tournament_mode ${lookup tournamentModeOptions cfg.server.tournamentMode "disable"}
+    sv_vote_kick ${bool cfg.game.enableVoteKick}
+    sv_vote_kick_bantime ${toString cfg.game.voteKickBanTime}
+    sv_vote_kick_min ${toString cfg.game.voteKickMinimumPlayers}
+
+    ${optionalSetting cfg.server.bindAddr "bindaddr"}
+    ${optionalSetting cfg.server.hostName "sv_hostname"}
+    sv_high_bandwidth ${bool cfg.server.enableHighBandwidth}
+    sv_inactivekick ${lookup inactivePenaltyOptions cfg.server.inactivePenalty "spectator/kick"}
+    sv_inactivekick_spec ${bool cfg.server.kickInactiveSpectators}
+    sv_inactivekick_time ${toString cfg.server.inactiveTime}
+    sv_max_clients ${toString cfg.server.maxClients}
+    sv_max_clients_per_ip ${toString cfg.server.maxClientsPerIP}
+    sv_skill_level ${lookup skillLevelOptions cfg.server.skillLevel "normal"}
+    sv_spamprotection ${bool cfg.server.enableSpamProtection}
+
+    sv_gametype ${cfg.game.gameType}
+    sv_map ${cfg.game.map}
+    sv_match_swap ${bool cfg.game.swapTeams}
+    sv_player_ready_mode ${bool cfg.game.enableReadyMode}
+    sv_player_slots ${toString cfg.game.playerSlots}
+    sv_powerups ${bool cfg.game.enablePowerups}
+    sv_scorelimit ${toString cfg.game.scoreLimit}
+    sv_strict_spectate_mode ${bool cfg.game.restrictSpectators}
+    sv_teamdamage ${bool cfg.game.enableTeamDamage}
+    sv_timelimit ${toString cfg.game.timeLimit}
+    sv_tournament_mode ${lookup tournamentModeOptions cfg.server.tournamentMode "disable"}
+    sv_vote_kick ${bool cfg.game.enableVoteKick}
+    sv_vote_kick_bantime ${toString cfg.game.voteKickBanTime}
+    sv_vote_kick_min ${toString cfg.game.voteKickMinimumPlayers}
+
     ${concatStringsSep "\n" cfg.extraOptions}
   '';
 
@@ -22,17 +95,19 @@ in
     services.teeworlds = {
       enable = mkEnableOption (lib.mdDoc "Teeworlds Server");
 
+      package = mkPackageOptionMD pkgs "teeworlds-server" { };
+
       openPorts = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Whether to open firewall ports for Teeworlds";
+        description = lib.mdDoc "Whether to open firewall ports for Teeworlds.";
       };
 
       name = mkOption {
-        type = types.nullOr types.str;
-        default = null;
+        type = types.str;
+        default = "unnamed server";
         description = lib.mdDoc ''
-          Name of the server. Defaults to 'unnamed server'.
+          Name of the server.
         '';
       };
 
@@ -41,7 +116,7 @@ in
         example = true;
         default = false;
         description = lib.mdDoc ''
-          Whether the server registers as public server in the global server list. This is disabled by default because of privacy.
+          Whether the server registers as a public server in the global server list. This is disabled by default for privacy reasons.
         '';
       };
 
@@ -49,7 +124,7 @@ in
         type = types.nullOr types.str;
         default = null;
         description = lib.mdDoc ''
-          Set the server message of the day text.
+          The server's message of the day text.
         '';
       };
 
@@ -85,6 +160,217 @@ in
         '';
         example = [ "sv_map dm1" "sv_gametype dm" ];
       };
+
+      server = {
+        bindAddr = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = lib.mdDoc ''
+            The address the server will bind to.
+          '';
+        };
+
+        enableHighBandwidth = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Whether to enable high bandwidth mode on LAN servers. This will double the amount of bandwidth required for running the server.
+          '';
+        };
+
+        hostName = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = lib.mdDoc ''
+            Hostname for the server.
+          '';
+        };
+
+        inactivePenalty = mkOption {
+          type = types.enum [ "spectator" "spectator/kick" "kick" ];
+          example = "spectator";
+          default = "spectator/kick";
+          description = lib.mdDoc ''
+            Specify what to do when a client goes inactive (see [](#opt-services.teeworlds.server.inactiveTime)).
+
+            - `spectator`: send the client into spectator mode
+
+            - `spectator/kick`: send the client into a free spectator slot, otherwise kick the client
+
+            - `kick`: kick the client
+          '';
+        };
+
+        kickInactiveSpectators = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Whether to kick inactive spectators.
+          '';
+        };
+
+        inactiveTime = mkOption {
+          type = types.ints.unsigned;
+          default = 3;
+          description = lib.mdDoc ''
+            The amount of minutes a client has to idle before it is considered inactive.
+          '';
+        };
+
+        maxClients = mkOption {
+          type = types.ints.unsigned;
+          default = 12;
+          description = lib.mdDoc ''
+            The maximum amount of clients that can be connected to the server at the same time.
+          '';
+        };
+
+        maxClientsPerIP = mkOption {
+          type = types.ints.unsigned;
+          default = 12;
+          description = lib.mdDoc ''
+            The maximum amount of clients with the same IP address that can be connected to the server at the same time.
+          '';
+        };
+
+        skillLevel = mkOption {
+          type = types.enum [ "casual" "normal" "competitive" ];
+          default = "normal";
+          description = lib.mdDoc ''
+            The skill level shown in the server browser.
+          '';
+        };
+
+        enableSpamProtection = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Whether to enable chat spam protection.
+          '';
+        };
+      };
+
+      game = {
+        gameType = mkOption {
+          type = types.str;
+          example = "ctf";
+          default = "dm";
+          description = lib.mdDoc ''
+            The game type to use on the server.
+
+            The default gametypes are `dm`, `tdm`, `ctf`, `lms`, and `lts`.
+          '';
+        };
+
+        map = mkOption {
+          type = types.str;
+          example = "ctf5";
+          default = "dm1";
+          description = lib.mdDoc ''
+            The map to use on the server.
+          '';
+        };
+
+        swapTeams = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Whether to swap teams each round.
+          '';
+        };
+
+        enableReadyMode = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Whether to enable "ready mode"; where players can pause/unpause the game
+            and start the game in warmup, using their ready state.
+          '';
+        };
+
+        playerSlots = mkOption {
+          type = types.ints.unsigned;
+          default = 8;
+          description = lib.mdDoc ''
+            The amount of slots to reserve for players (as opposed to spectators).
+          '';
+        };
+
+        enablePowerups = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Whether to allow powerups such as the ninja.
+          '';
+        };
+
+        scoreLimit = mkOption {
+          type = types.ints.unsigned;
+          example = 400;
+          default = 20;
+          description = lib.mdDoc ''
+            The score limit needed to win a round.
+          '';
+        };
+
+        restrictSpectators = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Whether to restrict access to information such as health, ammo and armour in spectator mode.
+          '';
+        };
+
+        enableTeamDamage = mkOption {
+          type = types.bool;
+          default = false;
+          description = lib.mdDoc ''
+            Whether to enable team damage; whether to allow team mates to inflict damage on one another.
+          '';
+        };
+
+        timeLimit = mkOption {
+          type = types.ints.unsigned;
+          default = 0;
+          description = lib.mdDoc ''
+            Time limit of the game. In cases of equal points, there will be sudden death.
+            Setting this to 0 disables a time limit.
+          '';
+        };
+
+        tournamentMode = mkOption {
+          type = types.enum [ "disable" "enable" "restrictSpectators" ];
+          default = "disable";
+          description = lib.mdDoc ''
+            Whether to enable tournament mode. In tournament mode, players join as spectators.
+            If this is set to `restrictSpectators`, tournament mode is enabled but spectator chat is restricted.
+          '';
+        };
+
+        enableVoteKick = mkOption {
+          type = types.bool;
+          default = true;
+          description = lib.mdDoc ''
+            Whether to enable voting to kick players.
+          '';
+        };
+
+        voteKickBanTime = mkOption {
+          type = types.ints.unsigned;
+          default = 5;
+          description = lib.mdDoc ''
+            The amount of minutes that a player is banned for if they get kicked by a vote.
+          '';
+        };
+
+        voteKickMinimumPlayers = mkOption {
+          type = types.ints.unsigned;
+          default = 5;
+          description = lib.mdDoc ''
+            The minimum amount of players required to start a kick vote.
+          '';
+        };
+      };
     };
   };
 
@@ -100,7 +386,7 @@ in
 
       serviceConfig = {
         DynamicUser = true;
-        ExecStart = "${pkgs.teeworlds-server}/bin/teeworlds_srv -f ${teeworldsConf}";
+        ExecStart = "${cfg.package}/bin/teeworlds_srv -f ${teeworldsConf}";
 
         # Hardening
         CapabilityBoundingSet = false;
diff --git a/nixpkgs/nixos/modules/services/hardware/asusd.nix b/nixpkgs/nixos/modules/services/hardware/asusd.nix
index ebbdea26c051..ff9a751e5be8 100644
--- a/nixpkgs/nixos/modules/services/hardware/asusd.nix
+++ b/nixpkgs/nixos/modules/services/hardware/asusd.nix
@@ -8,6 +8,8 @@ in
     services.asusd = {
       enable = lib.mkEnableOption (lib.mdDoc "the asusd service for ASUS ROG laptops");
 
+      package = lib.mkPackageOption pkgs "asusctl" { };
+
       enableUserService = lib.mkOption {
         type = lib.types.bool;
         default = false;
@@ -73,7 +75,7 @@ in
   };
 
   config = lib.mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.asusctl ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc =
       let
@@ -92,9 +94,9 @@ in
       };
 
     services.dbus.enable = true;
-    systemd.packages = [ pkgs.asusctl ];
-    services.dbus.packages = [ pkgs.asusctl ];
-    services.udev.packages = [ pkgs.asusctl ];
+    systemd.packages = [ cfg.package ];
+    services.dbus.packages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
     services.supergfxd.enable = lib.mkDefault true;
 
     systemd.user.services.asusd-user.enable = cfg.enableUserService;
diff --git a/nixpkgs/nixos/modules/services/hardware/bolt.nix b/nixpkgs/nixos/modules/services/hardware/bolt.nix
index 6990a9ea63b3..3bdf67cc1758 100644
--- a/nixpkgs/nixos/modules/services/hardware/bolt.nix
+++ b/nixpkgs/nixos/modules/services/hardware/bolt.nix
@@ -1,14 +1,13 @@
-# Thunderbolt 3 device manager
-
 { config, lib, pkgs, ...}:
 
 with lib;
 
+let
+  cfg = config.services.hardware.bolt;
+in
 {
   options = {
-
     services.hardware.bolt = {
-
       enable = mkOption {
         type = types.bool;
         default = false;
@@ -20,15 +19,13 @@ with lib;
         '';
       };
 
+      package = mkPackageOption pkgs "bolt" { };
     };
-
   };
 
-  config = mkIf config.services.hardware.bolt.enable {
-
-    environment.systemPackages = [ pkgs.bolt ];
-    services.udev.packages = [ pkgs.bolt ];
-    systemd.packages = [ pkgs.bolt ];
-
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+    systemd.packages = [ cfg.package ];
   };
 }
diff --git a/nixpkgs/nixos/modules/services/hardware/brltty.nix b/nixpkgs/nixos/modules/services/hardware/brltty.nix
index 3133804f485f..f96760e92c57 100644
--- a/nixpkgs/nixos/modules/services/hardware/brltty.nix
+++ b/nixpkgs/nixos/modules/services/hardware/brltty.nix
@@ -34,6 +34,7 @@ in {
     users.users.brltty = {
       description = "BRLTTY daemon user";
       group = "brltty";
+      isSystemUser = true;
     };
     users.groups = {
       brltty = { };
diff --git a/nixpkgs/nixos/modules/services/hardware/undervolt.nix b/nixpkgs/nixos/modules/services/hardware/undervolt.nix
index 67d8171587bb..c4d4c6791a21 100644
--- a/nixpkgs/nixos/modules/services/hardware/undervolt.nix
+++ b/nixpkgs/nixos/modules/services/hardware/undervolt.nix
@@ -12,6 +12,7 @@ let
     inherit (cfg)
       verbose
       temp
+      turbo
       ;
     # `core` and `cache` are both intentionally set to `cfg.coreOffset` as according to the undervolt docs:
     #
@@ -105,6 +106,14 @@ in
       '';
     };
 
+    turbo = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      description = lib.mdDoc ''
+        Changes the Intel Turbo feature status (1 is disabled and 0 is enabled).
+      '';
+    };
+
     p1.limit = mkOption {
       type = with types; nullOr int;
       default = null;
diff --git a/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix b/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix
index f576a426b318..8ab3497f7a17 100644
--- a/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix
+++ b/nixpkgs/nixos/modules/services/mail/stalwart-mail.nix
@@ -32,10 +32,15 @@ in {
       global.tracing.level = mkDefault "info";
       queue.path = mkDefault "${dataDir}/queue";
       report.path = mkDefault "${dataDir}/reports";
+      store.db.type = mkDefault "sqlite";
       store.db.path = mkDefault "${dataDir}/data/index.sqlite3";
-      store.blob.type = mkDefault "local";
-      store.blob.local.path = mkDefault "${dataDir}/data/blobs";
+      store.blob.type = mkDefault "fs";
+      store.blob.path = mkDefault "${dataDir}/data/blobs";
+      storage.data = mkDefault "db";
+      storage.fts = mkDefault "db";
+      storage.blob = mkDefault "blob";
       resolver.type = mkDefault "system";
+      resolver.public-suffix = mkDefault ["https://publicsuffix.org/list/public_suffix_list.dat"];
     };
 
     systemd.services.stalwart-mail = {
@@ -57,8 +62,8 @@ in {
         KillSignal = "SIGINT";
         Restart = "on-failure";
         RestartSec = 5;
-        StandardOutput = "syslog";
-        StandardError = "syslog";
+        StandardOutput = "journal";
+        StandardError = "journal";
         SyslogIdentifier = "stalwart-mail";
 
         DynamicUser = true;
diff --git a/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix b/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
index 80a6162b2168..557d6d7e7168 100644
--- a/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
+++ b/nixpkgs/nixos/modules/services/misc/sourcehut/default.nix
@@ -790,13 +790,21 @@ in
         '';
       };
       systemd.tmpfiles.settings."10-sourcehut-gitsrht" = mkIf cfg.git.enable (
-        builtins.listToAttrs (map (name: {
-          name = "/var/log/sourcehut/gitsrht-${name}";
-          value.f = {
-            inherit (cfg.git) user group;
-            mode = "0644";
-          };
-        }) [ "keys" "shell" "update-hook" ])
+        mkMerge [
+          (builtins.listToAttrs (map (name: {
+            name = "/var/log/sourcehut/gitsrht-${name}";
+            value.f = {
+              inherit (cfg.git) user group;
+              mode = "0644";
+            };
+          }) [ "keys" "shell" "update-hook" ]))
+          {
+            ${cfg.settings."git.sr.ht".repos}.d = {
+              inherit (cfg.git) user group;
+              mode = "0644";
+            };
+          }
+        ]
       );
       systemd.services.sshd = {
         preStart = mkIf cfg.hg.enable ''
diff --git a/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix b/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix
index 6c51a9bb8555..a8300ecd5233 100644
--- a/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix
+++ b/nixpkgs/nixos/modules/services/misc/tandoor-recipes.nix
@@ -17,14 +17,11 @@ let
     lib.mapAttrs (_: toString) cfg.extraConfig
   );
 
-  manage =
-    let
-      setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env);
-    in
-    pkgs.writeShellScript "manage" ''
-      ${setupEnv}
-      exec ${pkg}/bin/tandoor-recipes "$@"
-    '';
+  manage = pkgs.writeShellScript "manage" ''
+    set -o allexport # Export the following env vars
+    ${lib.toShellVars env}
+    exec ${pkg}/bin/tandoor-recipes "$@"
+  '';
 in
 {
   meta.maintainers = with maintainers; [ ambroisie ];
diff --git a/nixpkgs/nixos/modules/services/networking/go-camo.nix b/nixpkgs/nixos/modules/services/networking/go-camo.nix
new file mode 100644
index 000000000000..cb3b6eade464
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/networking/go-camo.nix
@@ -0,0 +1,73 @@
+{ lib, pkgs, config, ... }:
+
+let
+  cfg = config.services.go-camo;
+  inherit (lib) mkOption mkEnableOption mkIf mkMerge types optionalString;
+in
+{
+  options.services.go-camo = {
+    enable = mkEnableOption "go-camo service";
+    listen = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Address:Port to bind to for HTTP (default: 0.0.0.0:8080).";
+      apply = v: optionalString (v != null) "--listen=${v}";
+    };
+    sslListen = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Address:Port to bind to for HTTPS.";
+      apply = v: optionalString (v != null) "--ssl-listen=${v}";
+    };
+    sslKey = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "Path to TLS private key.";
+      apply = v: optionalString (v != null) "--ssl-key=${v}";
+    };
+    sslCert = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "Path to TLS certificate.";
+      apply = v: optionalString (v != null) "--ssl-cert=${v}";
+    };
+    keyFile = mkOption {
+      type = types.path;
+      default = null;
+      description = ''
+        A file containing the HMAC key to use for signing URLs.
+        The file can contain any string. Can be generated using "openssl rand -base64 18 > the_file".
+      '';
+    };
+    extraOptions = mkOption {
+      type = with types; listOf str;
+      default = [];
+      description = "Extra options passed to the go-camo command.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.go-camo = {
+      description = "go-camo service";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = {
+        GOCAMO_HMAC_FILE = "%d/hmac";
+      };
+      script = ''
+        export GOCAMO_HMAC=$(cat "$GOCAMO_HMAC_FILE")
+        exec ${lib.escapeShellArgs(lib.lists.remove "" ([ "${pkgs.go-camo}/bin/go-camo" cfg.listen cfg.sslListen cfg.sslKey cfg.sslCert ] ++ cfg.extraOptions))}
+      '';
+      serviceConfig = {
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        DynamicUser = true;
+        User = "gocamo";
+        Group = "gocamo";
+        LoadCredential = [
+          "hmac:${cfg.keyFile}"
+        ];
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/networking/hostapd.nix b/nixpkgs/nixos/modules/services/networking/hostapd.nix
index 00482e59acf3..40542155ed63 100644
--- a/nixpkgs/nixos/modules/services/networking/hostapd.nix
+++ b/nixpkgs/nixos/modules/services/networking/hostapd.nix
@@ -909,7 +909,7 @@ in {
                 in {
                   settings = {
                     ssid = bssCfg.ssid;
-                    utf8_ssid = bssCfg.ssid;
+                    utf8_ssid = bssCfg.utf8Ssid;
 
                     logger_syslog = mkDefault (-1);
                     logger_syslog_level = bssCfg.logLevel;
diff --git a/nixpkgs/nixos/modules/services/networking/knot.nix b/nixpkgs/nixos/modules/services/networking/knot.nix
index 94c32586736a..6488a159b3b7 100644
--- a/nixpkgs/nixos/modules/services/networking/knot.nix
+++ b/nixpkgs/nixos/modules/services/networking/knot.nix
@@ -1,8 +1,36 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, utils, ... }:
 
-with lib;
 
 let
+  inherit (lib)
+    attrNames
+    concatMapStrings
+    concatMapStringsSep
+    concatStrings
+    concatStringsSep
+    elem
+    filter
+    flip
+    hasAttr
+    hasPrefix
+    isAttrs
+    isBool
+    isDerivation
+    isList
+    mapAttrsToList
+    mkChangedOptionModule
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    optionals
+    types
+  ;
+
+  inherit (utils)
+    escapeSystemdExecArgs
+  ;
+
   cfg = config.services.knot;
 
   yamlConfig = let
@@ -113,8 +141,7 @@ let
   mkConfigFile = configString: pkgs.writeTextFile {
     name = "knot.conf";
     text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + configString;
-    # TODO: maybe we could do some checks even when private keys complicate this?
-    checkPhase = lib.optionalString (cfg.keyFiles == []) ''
+    checkPhase = lib.optionalString cfg.checkConfig ''
       ${cfg.package}/bin/knotc --config=$out conf-check
     '';
   };
@@ -142,12 +169,45 @@ let
 in {
   options = {
     services.knot = {
-      enable = mkEnableOption (lib.mdDoc "Knot authoritative-only DNS server");
+      enable = mkEnableOption "Knot authoritative-only DNS server";
+
+      enableXDP = mkOption {
+        type = types.bool;
+        default = lib.hasAttrByPath [ "xdp" "listen" ] cfg.settings;
+        defaultText = ''
+          Enabled when the `xdp.listen` setting is configured through `settings`.
+        '';
+        example = true;
+        description = ''
+          Extends the systemd unit with permissions to allow for the use of
+          the eXpress Data Path (XDP).
+
+          ::: {.note}
+            Make sure to read up on functional [limitations](https://www.knot-dns.cz/docs/latest/singlehtml/index.html#mode-xdp-limitations)
+            when running in XDP mode.
+          :::
+        '';
+      };
+
+      checkConfig = mkOption {
+        type = types.bool;
+        # TODO: maybe we could do some checks even when private keys complicate this?
+        # conf-check fails hard on missing IPs/devices with XDP
+        default = cfg.keyFiles == [] && !cfg.enableXDP;
+        defaultText = ''
+          Disabled when the config uses `keyFiles` or `enableXDP`.
+        '';
+        example = false;
+        description = ''
+          Toggles the configuration test at build time. It runs in a
+          sandbox, and therefore cannot be used in all scenarios.
+        '';
+      };
 
       extraArgs = mkOption {
         type = types.listOf types.str;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           List of additional command line parameters for knotd
         '';
       };
@@ -155,7 +215,7 @@ in {
       keyFiles = mkOption {
         type = types.listOf types.path;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           A list of files containing additional configuration
           to be included using the include directive. This option
           allows to include configuration like TSIG keys without
@@ -168,7 +228,7 @@ in {
       settings = mkOption {
         type = types.attrs;
         default = {};
-        description = lib.mdDoc ''
+        description = ''
           Extra configuration as nix values.
         '';
       };
@@ -176,7 +236,7 @@ in {
       settingsFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           As alternative to ``settings``, you can provide whole configuration
           directly in the almost-YAML format of Knot DNS.
           You might want to utilize ``pkgs.writeText "knot.conf" "longConfigString"`` for this.
@@ -210,19 +270,35 @@ in {
       wants = [ "network.target" ];
       after = ["network.target" ];
 
-      serviceConfig = {
+      serviceConfig = let
+        # https://www.knot-dns.cz/docs/3.3/singlehtml/index.html#pre-requisites
+        xdpCapabilities = lib.optionals (cfg.enableXDP) [
+          "CAP_NET_ADMIN"
+          "CAP_NET_RAW"
+          "CAP_SYS_ADMIN"
+          "CAP_IPC_LOCK"
+        ] ++ lib.optionals (lib.versionOlder config.boot.kernelPackages.kernel.version "5.11") [
+          "CAP_SYS_RESOURCE"
+        ];
+      in {
         Type = "notify";
-        ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}";
-        ExecReload = "${knot-cli-wrappers}/bin/knotc reload";
+        ExecStart = escapeSystemdExecArgs ([
+          (lib.getExe cfg.package)
+          "--config=${configFile}"
+          "--socket=${socketFile}"
+        ] ++ cfg.extraArgs);
+        ExecReload = escapeSystemdExecArgs [
+          "${knot-cli-wrappers}/bin/knotc" "reload"
+        ];
         User = "knot";
         Group = "knot";
 
         AmbientCapabilities = [
           "CAP_NET_BIND_SERVICE"
-        ];
+        ] ++ xdpCapabilities;
         CapabilityBoundingSet = [
           "CAP_NET_BIND_SERVICE"
-        ];
+        ] ++ xdpCapabilities;
         DeviceAllow = "";
         DevicePolicy = "closed";
         LockPersonality = true;
@@ -247,6 +323,9 @@ in {
           "AF_INET"
           "AF_INET6"
           "AF_UNIX"
+        ] ++ optionals (cfg.enableXDP) [
+          "AF_NETLINK"
+          "AF_XDP"
         ];
         RestrictNamespaces = true;
         RestrictRealtime =true;
@@ -258,6 +337,8 @@ in {
         SystemCallFilter = [
           "@system-service"
           "~@privileged"
+        ] ++ optionals (cfg.enableXDP) [
+          "bpf"
         ];
         UMask = "0077";
       };
diff --git a/nixpkgs/nixos/modules/services/networking/libreswan.nix b/nixpkgs/nixos/modules/services/networking/libreswan.nix
index db4d2f7f0ba0..a44cac93d5f6 100644
--- a/nixpkgs/nixos/modules/services/networking/libreswan.nix
+++ b/nixpkgs/nixos/modules/services/networking/libreswan.nix
@@ -133,9 +133,6 @@ in
       "ipsec.d/01-nixos.conf".source = configFile;
     } // policyFiles;
 
-    # Create NSS database directory
-    systemd.tmpfiles.rules = [ "d /var/lib/ipsec/nss 755 root root -" ];
-
     systemd.services.ipsec = {
       description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec";
       wantedBy = [ "multi-user.target" ];
@@ -153,6 +150,10 @@ in
         echo 0 | tee /proc/sys/net/ipv4/conf/*/send_redirects
         echo 0 | tee /proc/sys/net/ipv{4,6}/conf/*/accept_redirects
       '';
+      serviceConfig = {
+        StateDirectory = "ipsec/nss";
+        StateDirectoryMode = 0700;
+      };
     };
 
   };
diff --git a/nixpkgs/nixos/modules/services/security/opensnitch.nix b/nixpkgs/nixos/modules/services/security/opensnitch.nix
index 97ac3a72804c..42cf8159f3ea 100644
--- a/nixpkgs/nixos/modules/services/security/opensnitch.nix
+++ b/nixpkgs/nixos/modules/services/security/opensnitch.nix
@@ -36,7 +36,8 @@ in {
 
         description = mdDoc ''
           Declarative configuration of firewall rules.
-          All rules will be stored in `/var/lib/opensnitch/rules`.
+          All rules will be stored in `/var/lib/opensnitch/rules` by default.
+          Rules path can be configured with `settings.Rules.Path`.
           See [upstream documentation](https://github.com/evilsocket/opensnitch/wiki/Rules)
           for available options.
         '';
@@ -79,15 +80,6 @@ in {
               '';
             };
 
-            DefaultDuration = mkOption {
-              type = types.enum [
-                "once" "always" "until restart" "30s" "5m" "15m" "30m" "1h"
-              ];
-              description = mdDoc ''
-                Default duration of firewall rule.
-              '';
-            };
-
             InterceptUnknown = mkOption {
               type = types.bool;
               description = mdDoc ''
@@ -134,6 +126,30 @@ in {
               };
 
             };
+
+            Ebpf.ModulesPath = mkOption {
+              type = types.path;
+              default = if cfg.settings.ProcMonitorMethod == "ebpf" then "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd" else null;
+              defaultText = literalExpression ''
+                if cfg.settings.ProcMonitorMethod == "ebpf" then
+                  "\\$\\{config.boot.kernelPackages.opensnitch-ebpf\\}/etc/opensnitchd"
+                else null;
+              '';
+              description = mdDoc ''
+                Configure eBPF modules path. Used when
+                `settings.ProcMonitorMethod` is set to `ebpf`.
+              '';
+            };
+
+            Rules.Path = mkOption {
+              type = types.path;
+              default = "/var/lib/opensnitch/rules";
+              description = mdDoc ''
+                Path to the directory where firewall rules can be found and will
+                get stored by the NixOS module.
+              '';
+            };
+
           };
         };
         description = mdDoc ''
@@ -151,40 +167,42 @@ in {
 
     systemd = {
       packages = [ pkgs.opensnitch ];
-      services.opensnitchd.wantedBy = [ "multi-user.target" ];
+      services.opensnitchd = {
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          ExecStart = [
+            ""
+            "${pkgs.opensnitch}/bin/opensnitchd --config-file ${format.generate "default-config.json" cfg.settings}"
+          ];
+        };
+        preStart = mkIf (cfg.rules != {}) (let
+          rules = flip mapAttrsToList predefinedRules (file: content: {
+          inherit (content) file;
+          local = "${cfg.settings.Rules.Path}/${file}.json";
+        });
+        in ''
+          # Remove all firewall rules from rules path (configured with
+          # cfg.settings.Rules.Path) that are symlinks to a store-path, but aren't
+          # declared in `cfg.rules` (i.e. all networks that were "removed" from
+          # `cfg.rules`).
+          find ${cfg.settings.Rules.Path} -type l -lname '${builtins.storeDir}/*' ${optionalString (rules != {}) ''
+            -not \( ${concatMapStringsSep " -o " ({ local, ... }:
+              "-name '${baseNameOf local}*'")
+            rules} \) \
+          ''} -delete
+          ${concatMapStrings ({ file, local }: ''
+            ln -sf '${file}' "${local}"
+          '') rules}
+        '');
+      };
+      tmpfiles.rules = [
+        "d ${cfg.settings.Rules.Path} 0750 root root - -"
+        "L+ /etc/opensnitchd/system-fw.json - - - - ${pkgs.opensnitch}/etc/opensnitchd/system-fw.json"
+      ];
     };
 
-    systemd.services.opensnitchd.preStart = mkIf (cfg.rules != {}) (let
-      rules = flip mapAttrsToList predefinedRules (file: content: {
-        inherit (content) file;
-        local = "/var/lib/opensnitch/rules/${file}.json";
-      });
-    in ''
-      # Remove all firewall rules from `/var/lib/opensnitch/rules` that are symlinks to a store-path,
-      # but aren't declared in `cfg.rules` (i.e. all networks that were "removed" from
-      # `cfg.rules`).
-      find /var/lib/opensnitch/rules -type l -lname '${builtins.storeDir}/*' ${optionalString (rules != {}) ''
-        -not \( ${concatMapStringsSep " -o " ({ local, ... }:
-          "-name '${baseNameOf local}*'")
-        rules} \) \
-      ''} -delete
-      ${concatMapStrings ({ file, local }: ''
-        ln -sf '${file}' "${local}"
-      '') rules}
-
-      if [ ! -f /etc/opensnitchd/system-fw.json ]; then
-        cp "${pkgs.opensnitch}/etc/opensnitchd/system-fw.json" "/etc/opensnitchd/system-fw.json"
-      fi
-    '');
-
-    environment.etc = mkMerge [ ({
-      "opensnitchd/default-config.json".source = format.generate "default-config.json" cfg.settings;
-    }) (mkIf (cfg.settings.ProcMonitorMethod == "ebpf") {
-      "opensnitchd/opensnitch.o".source = "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd/opensnitch.o";
-      "opensnitchd/opensnitch-dns.o".source = "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd/opensnitch-dns.o";
-      "opensnitchd/opensnitch-procs.o".source = "${config.boot.kernelPackages.opensnitch-ebpf}/etc/opensnitchd/opensnitch-procs.o";
-    })];
-
   };
+
+  meta.maintainers = with lib.maintainers; [ onny ];
 }
 
diff --git a/nixpkgs/nixos/modules/services/system/automatic-timezoned.nix b/nixpkgs/nixos/modules/services/system/automatic-timezoned.nix
index 8934ed3a7ef2..7d3cd004a7ba 100644
--- a/nixpkgs/nixos/modules/services/system/automatic-timezoned.nix
+++ b/nixpkgs/nixos/modules/services/system/automatic-timezoned.nix
@@ -50,7 +50,7 @@ in
         serviceConfig = {
           Type = "exec";
           User = "automatic-timezoned";
-          ExecStart = "${cfg.package}/bin/automatic-timezoned --zoneinfo-path=${pkgs.tzdata}/share/zoneinfo/zone1970.tab";
+          ExecStart = "${cfg.package}/bin/automatic-timezoned";
         };
         wantedBy = [ "default.target" ];
       };
diff --git a/nixpkgs/nixos/modules/services/torrent/transmission.nix b/nixpkgs/nixos/modules/services/torrent/transmission.nix
index 5dd02eb33163..a9fb123b981e 100644
--- a/nixpkgs/nixos/modules/services/torrent/transmission.nix
+++ b/nixpkgs/nixos/modules/services/torrent/transmission.nix
@@ -74,7 +74,7 @@ in
             description = lib.mdDoc "";
           };
           options.message-level = mkOption {
-            type = types.ints.between 0 3;
+            type = types.ints.between 0 6;
             default = 2;
             description = lib.mdDoc "Set verbosity of transmission messages.";
           };
diff --git a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
index 8d09d1b97828..7fc710c6fcec 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mastodon.nix
@@ -4,7 +4,8 @@ let
   cfg = config.services.mastodon;
   opt = options.services.mastodon;
 
-  # We only want to create a database if we're actually going to connect to it.
+  # We only want to create a Redis and PostgreSQL databases if we're actually going to connect to it local.
+  redisActuallyCreateLocally = cfg.redis.createLocally && (cfg.redis.host == "127.0.0.1" || cfg.redis.enableUnixSocket);
   databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "/run/postgresql";
 
   env = {
@@ -33,6 +34,7 @@ let
 
     TRUSTED_PROXY_IP = cfg.trustedProxy;
   }
+  // lib.optionalAttrs (cfg.redis.createLocally && cfg.redis.enableUnixSocket) { REDIS_URL = "unix://${config.services.redis.servers.mastodon.unixSocket}"; }
   // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; }
   // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN  = cfg.smtp.user; }
   // lib.optionalAttrs (cfg.elasticsearch.host != null) { ES_HOST = cfg.elasticsearch.host; }
@@ -116,9 +118,11 @@ let
       threads = toString (if processCfg.threads == null then cfg.sidekiqThreads else processCfg.threads);
     in {
       after = [ "network.target" "mastodon-init-dirs.service" ]
+        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
         ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
         ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
       requires = [ "mastodon-init-dirs.service" ]
+        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
         ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
         ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
       description = "Mastodon sidekiq${jobClassLabel}";
@@ -146,9 +150,11 @@ let
         name = "mastodon-streaming-${toString i}";
         value = {
           after = [ "network.target" "mastodon-init-dirs.service" ]
+            ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
             ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
             ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
           requires = [ "mastodon-init-dirs.service" ]
+            ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
             ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
             ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
           wantedBy = [ "mastodon.target" "mastodon-streaming.target" ];
@@ -404,6 +410,19 @@ in {
           type = lib.types.port;
           default = 31637;
         };
+
+        passwordFile = lib.mkOption {
+          description = lib.mdDoc "A file containing the password for Redis database.";
+          type = lib.types.nullOr lib.types.path;
+          default = null;
+          example = "/run/keys/mastodon-redis-password";
+        };
+
+        enableUnixSocket = lib.mkOption {
+          description = lib.mdDoc "Use Unix socket";
+          type = lib.types.bool;
+          default = true;
+        };
       };
 
       database = {
@@ -613,6 +632,13 @@ in {
   config = lib.mkIf cfg.enable (lib.mkMerge [{
     assertions = [
       {
+        assertion = redisActuallyCreateLocally -> (!cfg.redis.enableUnixSocket || cfg.redis.passwordFile == null);
+        message = ''
+          <option>services.mastodon.redis.enableUnixSocket</option> needs to be disabled if
+            <option>services.mastodon.redis.passwordFile</option> is used.
+        '';
+      }
+      {
         assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user && cfg.database.user == cfg.database.name);
         message = ''
           For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer
@@ -689,6 +715,8 @@ in {
         OTP_SECRET="$(cat ${cfg.otpSecretFile})"
         VAPID_PRIVATE_KEY="$(cat ${cfg.vapidPrivateKeyFile})"
         VAPID_PUBLIC_KEY="$(cat ${cfg.vapidPublicKeyFile})"
+      '' + lib.optionalString (cfg.redis.passwordFile != null)''
+        REDIS_PASSWORD="$(cat ${cfg.redis.passwordFile})"
       '' + lib.optionalString (cfg.database.passwordFile != null) ''
         DB_PASS="$(cat ${cfg.database.passwordFile})"
       '' + lib.optionalString cfg.smtp.authenticate ''
@@ -751,9 +779,11 @@ in {
 
     systemd.services.mastodon-web = {
       after = [ "network.target" "mastodon-init-dirs.service" ]
+        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
         ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
         ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
       requires = [ "mastodon-init-dirs.service" ]
+        ++ lib.optional redisActuallyCreateLocally "redis-mastodon.service"
         ++ lib.optional databaseActuallyCreateLocally "postgresql.service"
         ++ lib.optional cfg.automaticMigrations "mastodon-init-db.service";
       wantedBy = [ "mastodon.target" ];
@@ -834,11 +864,14 @@ in {
       enable = true;
       hostname = lib.mkDefault "${cfg.localDomain}";
     };
-    services.redis.servers.mastodon = lib.mkIf (cfg.redis.createLocally && cfg.redis.host == "127.0.0.1") {
-      enable = true;
-      port = cfg.redis.port;
-      bind = "127.0.0.1";
-    };
+    services.redis.servers.mastodon = lib.mkIf redisActuallyCreateLocally (lib.mkMerge [
+      {
+        enable = true;
+      }
+      (lib.mkIf (!cfg.redis.enableUnixSocket) {
+        port = cfg.redis.port;
+      })
+    ]);
     services.postgresql = lib.mkIf databaseActuallyCreateLocally {
       enable = true;
       ensureUsers = [
@@ -859,6 +892,7 @@ in {
         };
       })
       (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package pkgs.imagemagick ])
+      (lib.mkIf (cfg.redis.createLocally && cfg.redis.enableUnixSocket) {${config.services.mastodon.user}.extraGroups = [ "redis-mastodon" ];})
     ];
 
     users.groups.${cfg.group}.members = lib.optional cfg.configureNginx config.services.nginx.user;
diff --git a/nixpkgs/nixos/modules/services/web-apps/photoprism.nix b/nixpkgs/nixos/modules/services/web-apps/photoprism.nix
index d3773cc9cf78..ccf995fccf3e 100644
--- a/nixpkgs/nixos/modules/services/web-apps/photoprism.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/photoprism.nix
@@ -12,17 +12,14 @@ let
     lib.mapAttrs (_: toString) cfg.settings
   );
 
-  manage =
-    let
-      setupEnv = lib.concatStringsSep "\n" (lib.mapAttrsToList (name: val: "export ${name}=${lib.escapeShellArg val}") env);
-    in
-    pkgs.writeShellScript "manage" ''
-      ${setupEnv}
-      eval "$(${config.systemd.package}/bin/systemctl show -pUID,MainPID photoprism.service | ${pkgs.gnused}/bin/sed "s/UID/ServiceUID/")"
-      exec ${pkgs.util-linux}/bin/nsenter \
-        -t $MainPID -m -S $ServiceUID -G $ServiceUID --wdns=${cfg.storagePath} \
-        ${cfg.package}/bin/photoprism "$@"
-    '';
+  manage = pkgs.writeShellScript "manage" ''
+    set -o allexport # Export the following env vars
+    ${lib.toShellVars env}
+    eval "$(${config.systemd.package}/bin/systemctl show -pUID,MainPID photoprism.service | ${pkgs.gnused}/bin/sed "s/UID/ServiceUID/")"
+    exec ${pkgs.util-linux}/bin/nsenter \
+      -t $MainPID -m -S $ServiceUID -G $ServiceUID --wdns=${cfg.storagePath} \
+      ${cfg.package}/bin/photoprism "$@"
+  '';
 in
 {
   meta.maintainers = with lib.maintainers; [ stunkymonkey ];
diff --git a/nixpkgs/nixos/modules/services/web-servers/garage.nix b/nixpkgs/nixos/modules/services/web-servers/garage.nix
index 47b4c6ab416e..48dd5b34757c 100644
--- a/nixpkgs/nixos/modules/services/web-servers/garage.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/garage.nix
@@ -30,7 +30,7 @@ in
     };
 
     logLevel = mkOption {
-      type = types.enum ([ "info" "debug" "trace" ]);
+      type = types.enum ([ "error" "warn" "info" "debug" "trace" ]);
       default = "info";
       example = "debug";
       description = lib.mdDoc "Garage log level, see <https://garagehq.deuxfleurs.fr/documentation/quick-start/#launching-the-garage-server> for examples.";
diff --git a/nixpkgs/nixos/modules/services/web-servers/ttyd.nix b/nixpkgs/nixos/modules/services/web-servers/ttyd.nix
index e545869ca432..14361df2bb66 100644
--- a/nixpkgs/nixos/modules/services/web-servers/ttyd.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/ttyd.nix
@@ -1,11 +1,17 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
 
   cfg = config.services.ttyd;
 
+  inherit (lib)
+    optionals
+    types
+    concatLists
+    mapAttrsToList
+    mkOption
+    ;
+
   # Command line arguments for the ttyd daemon
   args = [ "--port" (toString cfg.port) ]
          ++ optionals (cfg.socket != null) [ "--interface" cfg.socket ]
@@ -14,6 +20,7 @@ let
          ++ (concatLists (mapAttrsToList (_k: _v: [ "--client-option" "${_k}=${_v}" ]) cfg.clientOptions))
          ++ [ "--terminal-type" cfg.terminalType ]
          ++ optionals cfg.checkOrigin [ "--check-origin" ]
+         ++ optionals cfg.writeable [ "--writable" ] # the typo is correct
          ++ [ "--max-clients" (toString cfg.maxClients) ]
          ++ optionals (cfg.indexFile != null) [ "--index" cfg.indexFile ]
          ++ optionals cfg.enableIPv6 [ "--ipv6" ]
@@ -30,40 +37,40 @@ in
 
   options = {
     services.ttyd = {
-      enable = mkEnableOption (lib.mdDoc "ttyd daemon");
+      enable = lib.mkEnableOption ("ttyd daemon");
 
       port = mkOption {
         type = types.port;
         default = 7681;
-        description = lib.mdDoc "Port to listen on (use 0 for random port)";
+        description = "Port to listen on (use 0 for random port)";
       };
 
       socket = mkOption {
         type = types.nullOr types.path;
         default = null;
         example = "/var/run/ttyd.sock";
-        description = lib.mdDoc "UNIX domain socket path to bind.";
+        description = "UNIX domain socket path to bind.";
       };
 
       interface = mkOption {
         type = types.nullOr types.str;
         default = null;
         example = "eth0";
-        description = lib.mdDoc "Network interface to bind.";
+        description = "Network interface to bind.";
       };
 
       username = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = lib.mdDoc "Username for basic authentication.";
+        description = "Username for basic http authentication.";
       };
 
       passwordFile = mkOption {
         type = types.nullOr types.path;
         default = null;
         apply = value: if value == null then null else toString value;
-        description = lib.mdDoc ''
-          File containing the password to use for basic authentication.
+        description = ''
+          File containing the password to use for basic http authentication.
           For insecurely putting the password in the globally readable store use
           `pkgs.writeText "ttydpw" "MyPassword"`.
         '';
@@ -72,19 +79,46 @@ in
       signal = mkOption {
         type = types.ints.u8;
         default = 1;
-        description = lib.mdDoc "Signal to send to the command on session close.";
+        description = "Signal to send to the command on session close.";
+      };
+
+      entrypoint = mkOption {
+        type = types.listOf types.str;
+        default = [ "${pkgs.shadow}/bin/login" ];
+        defaultText = lib.literalExpression ''
+          [ "''${pkgs.shadow}/bin/login" ]
+        '';
+        example = lib.literalExpression ''
+          [ (lib.getExe pkgs.htop) ]
+        '';
+        description = "Which command ttyd runs.";
+        apply = lib.escapeShellArgs;
+      };
+
+      user = mkOption {
+        type = types.str;
+        # `login` needs to be run as root
+        default = "root";
+        description = "Which unix user ttyd should run as.";
+      };
+
+      writeable = mkOption {
+        type = types.nullOr types.bool;
+        default = null; # null causes an eval error, forcing the user to consider attack surface
+        example = true;
+        description = "Allow clients to write to the TTY.";
       };
 
       clientOptions = mkOption {
         type = types.attrsOf types.str;
         default = {};
-        example = literalExpression ''
+        example = lib.literalExpression ''
           {
             fontSize = "16";
             fontFamily = "Fira Code";
           }
         '';
-        description = lib.mdDoc ''
+        description = ''
           Attribute set of client options for xtermjs.
           <https://xtermjs.org/docs/api/terminal/interfaces/iterminaloptions/>
         '';
@@ -93,50 +127,50 @@ in
       terminalType = mkOption {
         type = types.str;
         default = "xterm-256color";
-        description = lib.mdDoc "Terminal type to report.";
+        description = "Terminal type to report.";
       };
 
       checkOrigin = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Whether to allow a websocket connection from a different origin.";
+        description = "Whether to allow a websocket connection from a different origin.";
       };
 
       maxClients = mkOption {
         type = types.int;
         default = 0;
-        description = lib.mdDoc "Maximum clients to support (0, no limit)";
+        description = "Maximum clients to support (0, no limit)";
       };
 
       indexFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = lib.mdDoc "Custom index.html path";
+        description = "Custom index.html path";
       };
 
       enableIPv6 = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Whether or not to enable IPv6 support.";
+        description = "Whether or not to enable IPv6 support.";
       };
 
       enableSSL = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc "Whether or not to enable SSL (https) support.";
+        description = "Whether or not to enable SSL (https) support.";
       };
 
       certFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = lib.mdDoc "SSL certificate file path.";
+        description = "SSL certificate file path.";
       };
 
       keyFile = mkOption {
         type = types.nullOr types.path;
         default = null;
         apply = value: if value == null then null else toString value;
-        description = lib.mdDoc ''
+        description = ''
           SSL key file path.
           For insecurely putting the keyFile in the globally readable store use
           `pkgs.writeText "ttydKeyFile" "SSLKEY"`.
@@ -146,25 +180,27 @@ in
       caFile = mkOption {
         type = types.nullOr types.path;
         default = null;
-        description = lib.mdDoc "SSL CA file path for client certificate verification.";
+        description = "SSL CA file path for client certificate verification.";
       };
 
       logLevel = mkOption {
         type = types.int;
         default = 7;
-        description = lib.mdDoc "Set log level.";
+        description = "Set log level.";
       };
     };
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
 
     assertions =
       [ { assertion = cfg.enableSSL
             -> cfg.certFile != null && cfg.keyFile != null && cfg.caFile != null;
           message = "SSL is enabled for ttyd, but no certFile, keyFile or caFile has been specified."; }
+        { assertion = cfg.writeable != null;
+          message = "services.ttyd.writeable must be set"; }
         { assertion = ! (cfg.interface != null && cfg.socket != null);
           message = "Cannot set both interface and socket for ttyd."; }
         { assertion = (cfg.username != null) == (cfg.passwordFile != null);
@@ -177,21 +213,19 @@ in
       wantedBy = [ "multi-user.target" ];
 
       serviceConfig = {
-        # Runs login which needs to be run as root
-        # login: Cannot possibly work without effective root
-        User = "root";
+        User = cfg.user;
         LoadCredential = lib.optionalString (cfg.passwordFile != null) "TTYD_PASSWORD_FILE:${cfg.passwordFile}";
       };
 
       script = if cfg.passwordFile != null then ''
         PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/TTYD_PASSWORD_FILE")
         ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
-          --credential ${escapeShellArg cfg.username}:"$PASSWORD" \
-          ${pkgs.shadow}/bin/login
+          --credential ${lib.escapeShellArg cfg.username}:"$PASSWORD" \
+          ${cfg.entrypoint}
       ''
       else ''
         ${pkgs.ttyd}/bin/ttyd ${lib.escapeShellArgs args} \
-          ${pkgs.shadow}/bin/login
+          ${cfg.entrypoint}
       '';
     };
   };
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix
index 463c45675cee..fe39097a22e8 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/budgie.nix
@@ -39,6 +39,10 @@ let
     '';
     destination = "/share/gnome-background-properties/nixos.xml";
   };
+
+  budgie-control-center = pkgs.budgie.budgie-control-center.override {
+    enableSshSocket = config.services.openssh.startWhenNeeded;
+  };
 in {
   options = {
     services.xserver.desktopManager.budgie = {
@@ -114,7 +118,7 @@ in {
       [
         # Budgie Desktop.
         budgie.budgie-backgrounds
-        budgie.budgie-control-center
+        budgie-control-center
         (budgie.budgie-desktop-with-plugins.override { plugins = cfg.extraPlugins; })
         budgie.budgie-desktop-view
         budgie.budgie-screensaver
@@ -233,8 +237,8 @@ in {
     services.gvfs.enable = mkDefault true;
 
     # Register packages for DBus.
-    services.dbus.packages = with pkgs; [
-      budgie.budgie-control-center
+    services.dbus.packages = [
+      budgie-control-center
     ];
 
     # Register packages for udev.
diff --git a/nixpkgs/nixos/modules/services/x11/window-managers/icewm.nix b/nixpkgs/nixos/modules/services/x11/window-managers/icewm.nix
index 48741aa41d85..e3cb5cc3be2b 100644
--- a/nixpkgs/nixos/modules/services/x11/window-managers/icewm.nix
+++ b/nixpkgs/nixos/modules/services/x11/window-managers/icewm.nix
@@ -17,7 +17,7 @@ in
       { name = "icewm";
         start =
           ''
-            ${pkgs.icewm}/bin/icewm &
+            ${pkgs.icewm}/bin/icewm-session &
             waitPID=$!
           '';
       };
diff --git a/nixpkgs/nixos/modules/system/boot/kernel.nix b/nixpkgs/nixos/modules/system/boot/kernel.nix
index a46331ccd431..b0ac857feb4b 100644
--- a/nixpkgs/nixos/modules/system/boot/kernel.nix
+++ b/nixpkgs/nixos/modules/system/boot/kernel.nix
@@ -81,6 +81,13 @@ in
             extraStructuredConfig.FOO = lib.kernel.yes;
             features.foo = true;
           }
+          {
+            name = "foo-ml-mbox";
+            patch = (fetchurl {
+              url = "https://lore.kernel.org/lkml/19700205182810.58382-1-email@domain/t.mbox.gz";
+              hash = "sha256-...";
+            });
+          }
         ]
       '';
       description = lib.mdDoc ''
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1.nix b/nixpkgs/nixos/modules/system/boot/stage-1.nix
index e990aeea7a14..8f3f3612805f 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-1.nix
@@ -621,6 +621,11 @@ in
             path the secret should have inside the initrd, the value
             is the path it should be copied from (or null for the same
             path inside and out).
+
+            Note that `nixos-rebuild switch` will generate the initrd
+            also for past generations, so if secrets are moved or deleted
+            you will also have to garbage collect the generations that
+            use those secrets.
           '';
         example = literalExpression
           ''
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix b/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix
index 000b18c01609..edc25784367a 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/oomd.nix
@@ -49,15 +49,15 @@ in {
 
     systemd.slices."-".sliceConfig = lib.mkIf cfg.enableRootSlice {
       ManagedOOMMemoryPressure = "kill";
-      ManagedOOMMemoryPressureLimit = "80%";
+      ManagedOOMMemoryPressureLimit = lib.mkDefault "80%";
     };
     systemd.slices."system".sliceConfig = lib.mkIf cfg.enableSystemSlice {
       ManagedOOMMemoryPressure = "kill";
-      ManagedOOMMemoryPressureLimit = "80%";
+      ManagedOOMMemoryPressureLimit = lib.mkDefault "80%";
     };
     systemd.slices."user-".sliceConfig = lib.mkIf cfg.enableUserSlices {
       ManagedOOMMemoryPressure = "kill";
-      ManagedOOMMemoryPressureLimit = "80%";
+      ManagedOOMMemoryPressureLimit = lib.mkDefault "80%";
     };
     systemd.user.units."slice" = lib.mkIf cfg.enableUserSlices {
       text = ''
diff --git a/nixpkgs/nixos/modules/system/etc/build-composefs-dump.py b/nixpkgs/nixos/modules/system/etc/build-composefs-dump.py
index bf4ec791ecf7..bba454dd888d 100644
--- a/nixpkgs/nixos/modules/system/etc/build-composefs-dump.py
+++ b/nixpkgs/nixos/modules/system/etc/build-composefs-dump.py
@@ -199,7 +199,8 @@ def main() -> None:
                         size=os.stat(source).st_size,
                         filetype=FileType.file,
                         mode=mode,
-                        payload=target,
+                        # payload needs to be relative path in this case
+                        payload=target.lstrip("/"),
                     )
             paths[target] = composefs_path
             add_leading_directories(target, attrs, paths)
diff --git a/nixpkgs/nixos/modules/virtualisation/docker.nix b/nixpkgs/nixos/modules/virtualisation/docker.nix
index b0d61ee06091..d4d34d13a94d 100644
--- a/nixpkgs/nixos/modules/virtualisation/docker.nix
+++ b/nixpkgs/nixos/modules/virtualisation/docker.nix
@@ -93,8 +93,18 @@ in
         default = null;
         description =
           lib.mdDoc ''
-            This option determines which Docker storage driver to use. By default
-            it let's docker automatically choose preferred storage driver.
+            This option determines which Docker
+            [storage driver](https://docs.docker.com/storage/storagedriver/select-storage-driver/)
+            to use.
+            By default it lets docker automatically choose the preferred storage
+            driver.
+            However, it is recommended to specify a storage driver explicitly, as
+            docker's default varies over versions.
+
+            ::: {.warning}
+            Changing the storage driver will cause any existing containers
+            and images to become inaccessible.
+            :::
           '';
       };
 
diff --git a/nixpkgs/nixos/modules/virtualisation/incus.nix b/nixpkgs/nixos/modules/virtualisation/incus.nix
index bbe5b48b95bb..3bbe0ba45851 100644
--- a/nixpkgs/nixos/modules/virtualisation/incus.nix
+++ b/nixpkgs/nixos/modules/virtualisation/incus.nix
@@ -97,6 +97,12 @@ in
           considered failed and systemd will attempt to restart it.
         '';
       };
+
+      ui = {
+        enable = lib.mkEnableOption (lib.mdDoc "(experimental) Incus UI");
+
+        package = lib.mkPackageOption pkgs [ "incus" "ui" ] { };
+      };
     };
   };
 
@@ -165,10 +171,12 @@ in
         "${config.boot.zfs.package}/lib/udev"
       ];
 
-      environment = {
+      environment = lib.mkMerge [ {
         # Override Path to the LXC template configuration directory
         INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
-      };
+      } (lib.mkIf (cfg.ui.enable) {
+        "INCUS_UI" = cfg.ui.package;
+      }) ];
 
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/incusd --group incus-admin";
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index 31af6ec64214..8193c3dfe840 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -799,7 +799,7 @@ in {
   solanum = handleTest ./solanum.nix {};
   sonarr = handleTest ./sonarr.nix {};
   sonic-server = handleTest ./sonic-server.nix {};
-  sourcehut = handleTest ./sourcehut.nix {};
+  sourcehut = handleTest ./sourcehut {};
   spacecookie = handleTest ./spacecookie.nix {};
   spark = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./spark {};
   sqlite3-to-mysql = handleTest ./sqlite3-to-mysql.nix {};
diff --git a/nixpkgs/nixos/tests/docker-tools.nix b/nixpkgs/nixos/tests/docker-tools.nix
index 90af817e75ed..f252eb9ff61e 100644
--- a/nixpkgs/nixos/tests/docker-tools.nix
+++ b/nixpkgs/nixos/tests/docker-tools.nix
@@ -46,6 +46,18 @@ let
         echo 'runAsRoot has run.'
       '';
     };
+
+  chownTestImage =
+    pkgs.dockerTools.streamLayeredImage {
+      name = "chown-test";
+      tag = "latest";
+      enableFakechroot = true;
+      fakeRootCommands = ''
+        touch /testfile
+        chown 12345:12345 /testfile
+      '';
+      config.Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "/testfile" ];
+    };
 in {
   name = "docker-tools";
   meta = with pkgs.lib.maintainers; {
@@ -71,14 +83,29 @@ in {
             docker.succeed("${examples.helloOnRoot} | docker load")
             docker.succeed("docker run --rm hello | grep -i hello")
             docker.succeed("docker image rm hello:latest")
+
         with subtest("includeStorePath = false; breaks example"):
             docker.succeed("${examples.helloOnRootNoStore} | docker load")
             docker.fail("docker run --rm hello | grep -i hello")
             docker.succeed("docker image rm hello:latest")
+        with subtest("includeStorePath = false; breaks example (fakechroot)"):
+            docker.succeed("${examples.helloOnRootNoStoreFakechroot} | docker load")
+            docker.fail("docker run --rm hello | grep -i hello")
+            docker.succeed("docker image rm hello:latest")
+
+        with subtest("Ensure ZERO paths are added to the store"):
+            docker.fail("${examples.helloOnRootNoStore} | ${pkgs.crane}/bin/crane export - - | tar t | grep 'nix/store/'")
+        with subtest("Ensure ZERO paths are added to the store (fakechroot)"):
+            docker.fail("${examples.helloOnRootNoStoreFakechroot} | ${pkgs.crane}/bin/crane export - - | tar t | grep 'nix/store/'")
+
         with subtest("includeStorePath = false; works with mounted store"):
             docker.succeed("${examples.helloOnRootNoStore} | docker load")
             docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello")
             docker.succeed("docker image rm hello:latest")
+        with subtest("includeStorePath = false; works with mounted store (fakechroot)"):
+            docker.succeed("${examples.helloOnRootNoStoreFakechroot} | docker load")
+            docker.succeed("docker run --rm --volume ${builtins.storeDir}:${builtins.storeDir}:ro hello | grep -i hello")
+            docker.succeed("docker image rm hello:latest")
 
     with subtest("Ensure Docker images use a stable date by default"):
         docker.succeed(
@@ -128,6 +155,15 @@ in {
         docker.succeed("docker images --format '{{.Tag}}' | grep -F '${examples.nixLayered.imageTag}'")
         docker.succeed("docker rmi ${examples.nixLayered.imageName}")
 
+    with subtest("Check that images with alternative compression schemas load"):
+        docker.succeed(
+            "docker load --input='${examples.bashZstdCompressed}'",
+            "docker rmi ${examples.bashZstdCompressed.imageName}",
+        )
+        docker.succeed(
+            "docker load --input='${examples.bashUncompressed}'",
+            "docker rmi ${examples.bashUncompressed.imageName}",
+        )
 
     with subtest(
         "Check if the nix store is correctly initialized by listing "
@@ -449,6 +485,18 @@ in {
             "docker run --rm ${examples.layeredImageWithFakeRootCommands.imageName} /hello/bin/layeredImageWithFakeRootCommands-hello"
         )
 
+    with subtest("mergeImage correctly deals with varying compression schemas in inputs"):
+        docker.succeed("docker load --input='${examples.mergeVaryingCompressor}'")
+
+        for sub_image, tag in [
+            ("${examples.redis.imageName}", "${examples.redis.imageTag}"),
+            ("${examples.bashUncompressed.imageName}", "${examples.bashUncompressed.imageTag}"),
+            ("${examples.bashZstdCompressed.imageName}", "${examples.bashZstdCompressed.imageTag}"),
+        ]:
+            docker.succeed(f"docker images --format '{{{{.Repository}}}}-{{{{.Tag}}}}' | grep -F '{sub_image}-{tag}'")
+            docker.succeed(f"docker rmi {sub_image}")
+
+
     with subtest("exportImage produces a valid tarball"):
         docker.succeed(
             "tar -tf ${examples.exportBash} | grep '\./bin/bash' > /dev/null"
@@ -550,5 +598,11 @@ in {
             "${examples.nix-shell-build-derivation} | docker load",
             "docker run --rm -it nix-shell-build-derivation"
         )
+
+    with subtest("streamLayeredImage: chown is persistent in fakeRootCommands"):
+        docker.succeed(
+            "${chownTestImage} | docker load",
+            "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)"
+        )
   '';
 })
diff --git a/nixpkgs/nixos/tests/go-camo.nix b/nixpkgs/nixos/tests/go-camo.nix
new file mode 100644
index 000000000000..513964c31c43
--- /dev/null
+++ b/nixpkgs/nixos/tests/go-camo.nix
@@ -0,0 +1,30 @@
+{ system ? builtins.currentSystem, config ? { }
+, pkgs ? import ../.. { inherit system config; } }:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+
+{
+  gocamo_file_key = let
+      key_val = "12345678";
+    in
+    makeTest {
+    name = "go-camo-file-key";
+    meta = {
+      maintainers = [ pkgs.lib.maintainers.viraptor ];
+    };
+
+    nodes.machine = { config, pkgs, ... }: {
+      services.go-camo = {
+        enable = true;
+        keyFile = pkgs.writeText "foo" key_val;
+      };
+    };
+
+    # go-camo responds to http requests
+    testScript = ''
+      machine.wait_for_unit("go-camo.service")
+      machine.wait_for_open_port(8080)
+      machine.succeed("curl http://localhost:8080")
+    '';
+  };
+}
diff --git a/nixpkgs/nixos/tests/incus/default.nix b/nixpkgs/nixos/tests/incus/default.nix
index 26e8a4ac4c77..c8e53774599b 100644
--- a/nixpkgs/nixos/tests/incus/default.nix
+++ b/nixpkgs/nixos/tests/incus/default.nix
@@ -9,5 +9,6 @@
   lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; };
   preseed = import ./preseed.nix { inherit system pkgs; };
   socket-activated = import ./socket-activated.nix { inherit system pkgs; };
+  ui = import ./ui.nix {inherit system pkgs;};
   virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { inherit system pkgs; };
 }
diff --git a/nixpkgs/nixos/tests/incus/ui.nix b/nixpkgs/nixos/tests/incus/ui.nix
new file mode 100644
index 000000000000..24ce1217d8df
--- /dev/null
+++ b/nixpkgs/nixos/tests/incus/ui.nix
@@ -0,0 +1,63 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "incus-ui";
+
+  meta = {
+    maintainers = lib.teams.lxc.members;
+  };
+
+  nodes.machine = { lib, ... }: {
+    virtualisation = {
+      incus.enable = true;
+      incus.ui.enable = true;
+    };
+
+    environment.systemPackages =
+      let
+        seleniumScript = pkgs.writers.writePython3Bin "selenium-script"
+          {
+            libraries = with pkgs.python3Packages; [ selenium ];
+          } ''
+          from selenium import webdriver
+          from selenium.webdriver.common.by import By
+          from selenium.webdriver.firefox.options import Options
+          from selenium.webdriver.support.ui import WebDriverWait
+
+          options = Options()
+          options.add_argument("--headless")
+          service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}")  # noqa: E501
+
+          driver = webdriver.Firefox(options=options, service=service)
+          driver.implicitly_wait(10)
+          driver.get("https://localhost:8443/ui")
+
+          wait = WebDriverWait(driver, 60)
+
+          assert len(driver.find_elements(By.CLASS_NAME, "l-application")) > 0
+          assert len(driver.find_elements(By.CLASS_NAME, "l-navigation__drawer")) > 0
+
+          driver.close()
+        '';
+      in
+      with pkgs; [ curl firefox-unwrapped geckodriver seleniumScript ];
+  };
+
+
+  testScript = ''
+    machine.wait_for_unit("sockets.target")
+    machine.wait_for_unit("incus.service")
+    machine.wait_for_file("/var/lib/incus/unix.socket")
+
+    # Configure incus listen address
+    machine.succeed("incus config set core.https_address :8443")
+    machine.succeed("systemctl restart incus")
+
+    # Check that the INCUS_UI environment variable is populated in the systemd unit
+    machine.succeed("cat /etc/systemd/system/incus.service | grep 'INCUS_UI'")
+
+    # Ensure the endpoint returns an HTML page with 'Incus UI' in the title
+    machine.succeed("curl -kLs https://localhost:8443/ui | grep '<title>Incus UI</title>'")
+
+    # Ensure the application is actually rendered by the Javascript
+    machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/keepalived.nix b/nixpkgs/nixos/tests/keepalived.nix
index ce291514591f..16564511d85d 100644
--- a/nixpkgs/nixos/tests/keepalived.nix
+++ b/nixpkgs/nixos/tests/keepalived.nix
@@ -1,6 +1,6 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }: {
   name = "keepalived";
-  maintainers = [ lib.maintainers.raitobezarius ];
+  meta.maintainers = [ lib.maintainers.raitobezarius ];
 
   nodes = {
     node1 = { pkgs, ... }: {
diff --git a/nixpkgs/nixos/tests/knot.nix b/nixpkgs/nixos/tests/knot.nix
index 44efd93b6fa9..c5af8bf1edcc 100644
--- a/nixpkgs/nixos/tests/knot.nix
+++ b/nixpkgs/nixos/tests/knot.nix
@@ -114,11 +114,14 @@ in {
       services.knot.extraArgs = [ "-v" ];
       services.knot.settings = {
         server = {
+          automatic-acl = true;
+        };
+
+        xdp = {
           listen = [
-            "0.0.0.0@53"
-            "::@53"
+            "eth1"
           ];
-          automatic-acl = true;
+          tcp = true;
         };
 
         remote.primary = {
@@ -140,7 +143,7 @@ in {
           "sub.example.com".file = "sub.example.com.zone";
         };
 
-        log.syslog.any = "info";
+        log.syslog.any = "debug";
       };
     };
     client = { lib, nodes, ... }: {
diff --git a/nixpkgs/nixos/tests/lemmy.nix b/nixpkgs/nixos/tests/lemmy.nix
index e8d747f89a9e..d93df3646837 100644
--- a/nixpkgs/nixos/tests/lemmy.nix
+++ b/nixpkgs/nixos/tests/lemmy.nix
@@ -51,7 +51,8 @@ in
 
     with subtest("the backend starts and responds"):
         server.wait_for_open_port(${toString backendPort})
-        server.succeed("curl --fail localhost:${toString backendPort}/api/v3/site")
+        # wait until succeeds, it just needs few seconds for migrations, but lets give it 10s max
+        server.wait_until_succeeds("curl --fail localhost:${toString backendPort}/api/v3/site", 10)
 
     with subtest("the UI starts and responds"):
         server.wait_for_unit("lemmy-ui.service")
diff --git a/nixpkgs/nixos/tests/opensnitch.nix b/nixpkgs/nixos/tests/opensnitch.nix
index d84e4e0a935b..a1af07647f71 100644
--- a/nixpkgs/nixos/tests/opensnitch.nix
+++ b/nixpkgs/nixos/tests/opensnitch.nix
@@ -31,7 +31,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           enable = true;
           settings.DefaultAction = "deny";
           rules = {
-            opensnitch = {
+            curl = {
               name = "curl";
               enabled = true;
               action = "allow";
diff --git a/nixpkgs/nixos/tests/prometheus-exporters.nix b/nixpkgs/nixos/tests/prometheus-exporters.nix
index 7e74f27174ec..632656ad5795 100644
--- a/nixpkgs/nixos/tests/prometheus-exporters.nix
+++ b/nixpkgs/nixos/tests/prometheus-exporters.nix
@@ -218,6 +218,9 @@ let
         services.dnsmasq.enable = true;
       };
       exporterTest = ''
+        wait_for_unit("dnsmasq.service")
+        wait_for_open_port(53)
+        wait_for_file("/var/lib/dnsmasq/dnsmasq.leases")
         wait_for_unit("prometheus-dnsmasq-exporter.service")
         wait_for_open_port(9153)
         succeed("curl -sSf http://localhost:9153/metrics | grep 'dnsmasq_leases 0'")
diff --git a/nixpkgs/nixos/tests/qownnotes.nix b/nixpkgs/nixos/tests/qownnotes.nix
index 93801cb98702..3390ba6d9025 100644
--- a/nixpkgs/nixos/tests/qownnotes.nix
+++ b/nixpkgs/nixos/tests/qownnotes.nix
@@ -21,6 +21,7 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
 
   enableOCR = true;
 
+  # https://nixos.org/manual/nixos/stable/#ssec-machine-objects
   testScript = { nodes, ... }: let
     aliceDo = cmd: ''machine.succeed("su - alice -c '${cmd}' >&2 &");'';
     in ''
@@ -52,8 +53,13 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
         machine.wait_for_text("App metric")
         machine.send_key("ret")
 
+        # Doesn't work for non-root
+        #machine.wait_for_window("QOwnNotes - ${pkgs.qownnotes.version}")
+
+        # OCR doesn't seem to be able any more to handle the main window
+        #machine.wait_for_text("QOwnNotes - ${pkgs.qownnotes.version}")
+
         # The main window should now show up
-        machine.wait_for_text("QOwnNotes - ${pkgs.qownnotes.version}")
         machine.wait_for_open_port(22222)
         machine.wait_for_console_text("QOwnNotes server listening on port 22222")
 
@@ -63,7 +69,13 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
         machine.send_key("ctrl-n")
         machine.sleep(1)
         machine.send_chars("This is a NixOS test!\n")
-        machine.wait_for_text("This is a NixOS test!")
+        machine.wait_until_succeeds("find /home/alice/Notes -type f | grep -qi 'Note 2'")
+
+        # OCR doesn't seem to be able any more to handle the main window
+        #machine.wait_for_text("This is a NixOS test!")
+
+        # Doesn't work for non-root
+        #machine.wait_for_window("- QOwnNotes - ${pkgs.qownnotes.version}")
 
         machine.screenshot("QOwnNotes-NewNote")
   '';
diff --git a/nixpkgs/nixos/tests/slurm.nix b/nixpkgs/nixos/tests/slurm.nix
index a6b02e970b0c..ad516b6e8d2b 100644
--- a/nixpkgs/nixos/tests/slurm.nix
+++ b/nixpkgs/nixos/tests/slurm.nix
@@ -45,7 +45,7 @@ let
       '';
     in pkgs.runCommand "mpitest" {} ''
       mkdir -p $out/bin
-      ${pkgs.openmpi}/bin/mpicc ${mpitestC} -o $out/bin/mpitest
+      ${lib.getDev pkgs.mpi}/bin/mpicc ${mpitestC} -o $out/bin/mpitest
     '';
 in {
   name = "slurm";
diff --git a/nixpkgs/nixos/tests/sourcehut.nix b/nixpkgs/nixos/tests/sourcehut.nix
deleted file mode 100644
index 0b258acc2af1..000000000000
--- a/nixpkgs/nixos/tests/sourcehut.nix
+++ /dev/null
@@ -1,252 +0,0 @@
-import ./make-test-python.nix ({ pkgs, lib, ... }:
-let
-  domain = "sourcehut.localdomain";
-
-  # Note that wildcard certificates just under the TLD (eg. *.com)
-  # would be rejected by clients like curl.
-  tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
-    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \
-      -subj '/CN=${domain}' -extensions v3_req \
-      -addext 'subjectAltName = DNS:*.${domain}'
-    install -D -t $out key.pem cert.pem
-  '';
-
-  images = {
-    nixos.unstable.x86_64 =
-      let
-        systemConfig = { pkgs, ... }: {
-          # passwordless ssh server
-          services.openssh = {
-            enable = true;
-            settings = {
-              PermitRootLogin = "yes";
-              PermitEmptyPasswords = true;
-            };
-          };
-
-          users = {
-            mutableUsers = false;
-            # build user
-            extraUsers."build" = {
-              isNormalUser = true;
-              uid = 1000;
-              extraGroups = [ "wheel" ];
-              password = "";
-            };
-            users.root.password = "";
-          };
-
-          security.sudo.wheelNeedsPassword = false;
-          nix.settings.trusted-users = [ "root" "build" ];
-          documentation.nixos.enable = false;
-
-          # builds.sr.ht-image-specific network settings
-          networking = {
-            hostName = "build";
-            dhcpcd.enable = false;
-            defaultGateway.address = "10.0.2.2";
-            usePredictableInterfaceNames = false;
-            interfaces."eth0".ipv4.addresses = [{
-              address = "10.0.2.15";
-              prefixLength = 25;
-            }];
-            enableIPv6 = false;
-            nameservers = [
-              # OpenNIC anycast
-              "185.121.177.177"
-              "169.239.202.202"
-              # Google
-              "8.8.8.8"
-            ];
-            firewall.allowedTCPPorts = [ 22 ];
-          };
-
-          environment.systemPackages = [
-            pkgs.gitMinimal
-            #pkgs.mercurial
-            pkgs.curl
-            pkgs.gnupg
-          ];
-        };
-        qemuConfig = { pkgs, ... }: {
-          imports = [ systemConfig ];
-          fileSystems."/".device = "/dev/disk/by-label/nixos";
-          boot.initrd.availableKernelModules = [
-            "ahci"
-            "ehci_pci"
-            "sd_mod"
-            "usb_storage"
-            "usbhid"
-            "virtio_balloon"
-            "virtio_blk"
-            "virtio_pci"
-            "virtio_ring"
-            "xhci_pci"
-          ];
-          boot.loader = {
-            grub = {
-              version = 2;
-              device = "/dev/vda";
-            };
-            timeout = 0;
-          };
-        };
-        config = (import (pkgs.path + "/nixos/lib/eval-config.nix") {
-          inherit pkgs; modules = [ qemuConfig ];
-          system = "x86_64-linux";
-        }).config;
-      in
-      import (pkgs.path + "/nixos/lib/make-disk-image.nix") {
-        inherit pkgs lib config;
-        diskSize = 16000;
-        format = "qcow2-compressed";
-        contents = [
-          { source = pkgs.writeText "gitconfig" ''
-              [user]
-                name = builds.sr.ht
-                email = build@sr.ht
-            '';
-            target = "/home/build/.gitconfig";
-            user = "build";
-            group = "users";
-            mode = "644";
-          }
-        ];
-      };
-  };
-
-in
-{
-  name = "sourcehut";
-
-  meta.maintainers = [ pkgs.lib.maintainers.tomberek ];
-
-  nodes.machine = { config, pkgs, nodes, ... }: {
-    # buildsrht needs space
-    virtualisation.diskSize = 4 * 1024;
-    virtualisation.memorySize = 2 * 1024;
-    networking.domain = domain;
-    networking.enableIPv6 = false;
-    networking.extraHosts = ''
-      ${config.networking.primaryIPAddress} builds.${domain}
-      ${config.networking.primaryIPAddress} git.${domain}
-      ${config.networking.primaryIPAddress} meta.${domain}
-    '';
-
-    services.sourcehut = {
-      enable = true;
-      nginx.enable = true;
-      nginx.virtualHost = {
-        forceSSL = true;
-        sslCertificate = "${tls-cert}/cert.pem";
-        sslCertificateKey = "${tls-cert}/key.pem";
-      };
-      postgresql.enable = true;
-      redis.enable = true;
-
-      meta.enable = true;
-      builds = {
-        enable = true;
-        # FIXME: see why it does not seem to activate fully.
-        #enableWorker = true;
-        inherit images;
-      };
-      git.enable = true;
-
-      settings."sr.ht" = {
-        global-domain = config.networking.domain;
-        service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
-        network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
-      };
-      settings."builds.sr.ht" = {
-        oauth-client-secret = pkgs.writeText "buildsrht-oauth-client-secret" "2260e9c4d9b8dcedcef642860e0504bc";
-        oauth-client-id = "299db9f9c2013170";
-      };
-      settings."git.sr.ht" = {
-        oauth-client-secret = pkgs.writeText "gitsrht-oauth-client-secret" "3597288dc2c716e567db5384f493b09d";
-        oauth-client-id = "d07cb713d920702e";
-      };
-      settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
-      settings.mail = {
-        smtp-from = "root+hut@${domain}";
-        # WARNING: take care to keep pgp-privkey outside the Nix store in production,
-        # or use LoadCredentialEncrypted=
-        pgp-privkey = toString (pkgs.writeText "sourcehut.pgp-privkey" ''
-          -----BEGIN PGP PRIVATE KEY BLOCK-----
-
-          lFgEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
-          Gk7hYQoAAP9X4oPmxxrHN8LewBpWITdBomNqlHoiP7mI0nz/BOPJHxEktDZuaXhv
-          cy90ZXN0cy9zb3VyY2VodXQgPHJvb3QraHV0QHNvdXJjZWh1dC5sb2NhbGRvbWFp
-          bj6IlwQTFgoAPxYhBPqjgjnL8RHN4JnADNicgXaYm0jJBQJioNE5AhsDBQkDwmcA
-          BgsJCAcDCgUVCgkICwUWAwIBAAIeBQIXgAAKCRDYnIF2mJtIySVCAP9e2nHsVHSi
-          2B1YGZpVG7Xf36vxljmMkbroQy+0gBPwRwEAq+jaiQqlbGhQ7R/HMFcAxBIVsq8h
-          Aw1rngsUd0o3dAicXQRioNE5EgorBgEEAZdVAQUBAQdAXZV2Sd5ZNBVTBbTGavMv
-          D6ORrUh8z7TI/3CsxCE7+yADAQgHAAD/c1RU9xH+V/uI1fE7HIn/zL0LUPpsuce2
-          cH++g4u3kBgTOYh+BBgWCgAmFiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg0TkC
-          GwwFCQPCZwAACgkQ2JyBdpibSMlKagD/cTre6p1m8QuJ7kwmCFRSz5tBzIuYMMgN
-          xtT7dmS91csA/35fWsOykSiFRojQ7ccCSUTHL7ApF2EbL968tP/D2hIG
-          =Hjoc
-          -----END PGP PRIVATE KEY BLOCK-----
-        '');
-        pgp-pubkey = pkgs.writeText "sourcehut.pgp-pubkey" ''
-          -----BEGIN PGP PUBLIC KEY BLOCK-----
-
-          mDMEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
-          Gk7hYQq0Nm5peG9zL3Rlc3RzL3NvdXJjZWh1dCA8cm9vdCtodXRAc291cmNlaHV0
-          LmxvY2FsZG9tYWluPoiXBBMWCgA/FiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg
-          0TkCGwMFCQPCZwAGCwkIBwMKBRUKCQgLBRYDAgEAAh4FAheAAAoJENicgXaYm0jJ
-          JUIA/17acexUdKLYHVgZmlUbtd/fq/GWOYyRuuhDL7SAE/BHAQCr6NqJCqVsaFDt
-          H8cwVwDEEhWyryEDDWueCxR3Sjd0CLg4BGKg0TkSCisGAQQBl1UBBQEBB0BdlXZJ
-          3lk0FVMFtMZq8y8Po5GtSHzPtMj/cKzEITv7IAMBCAeIfgQYFgoAJhYhBPqjgjnL
-          8RHN4JnADNicgXaYm0jJBQJioNE5AhsMBQkDwmcAAAoJENicgXaYm0jJSmoA/3E6
-          3uqdZvELie5MJghUUs+bQcyLmDDIDcbU+3ZkvdXLAP9+X1rDspEohUaI0O3HAklE
-          xy+wKRdhGy/evLT/w9oSBg==
-          =pJD7
-          -----END PGP PUBLIC KEY BLOCK-----
-        '';
-        pgp-key-id = "0xFAA38239CBF111CDE099C00CD89C8176989B48C9";
-      };
-    };
-
-    networking.firewall.allowedTCPPorts = [ 443 ];
-    security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
-    services.nginx = {
-      enable = true;
-      recommendedGzipSettings = true;
-      recommendedOptimisation = true;
-      recommendedTlsSettings = true;
-      recommendedProxySettings = true;
-    };
-
-    services.postgresql = {
-      enable = true;
-      enableTCPIP = false;
-      settings.unix_socket_permissions = "0770";
-    };
-  };
-
-  testScript = ''
-    start_all()
-    machine.wait_for_unit("multi-user.target")
-
-    # Testing metasrht
-    machine.wait_for_unit("metasrht-api.service")
-    machine.wait_for_unit("metasrht.service")
-    machine.wait_for_unit("metasrht-webhooks.service")
-    machine.wait_for_open_port(5000)
-    machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
-    machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
-
-    # Testing buildsrht
-    machine.wait_for_unit("buildsrht.service")
-    machine.wait_for_open_port(5002)
-    machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
-    #machine.wait_for_unit("buildsrht-worker.service")
-
-    # Testing gitsrht
-    machine.wait_for_unit("gitsrht-api.service")
-    machine.wait_for_unit("gitsrht.service")
-    machine.wait_for_unit("gitsrht-webhooks.service")
-    machine.succeed("curl -sL http://git.${domain} | grep git.${domain}")
-  '';
-})
diff --git a/nixpkgs/nixos/tests/sourcehut/builds.nix b/nixpkgs/nixos/tests/sourcehut/builds.nix
new file mode 100644
index 000000000000..f1f928ecc3d0
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/builds.nix
@@ -0,0 +1,54 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }:
+let
+  domain = "sourcehut.localdomain";
+in
+{
+  name = "sourcehut";
+
+  meta.maintainers = with pkgs.lib.maintainers; [ tomberek nessdoor ];
+
+  nodes.machine = { config, pkgs, nodes, ... }: {
+    imports = [
+      ./nodes/common.nix
+    ];
+
+    networking.domain = domain;
+    networking.extraHosts = ''
+      ${config.networking.primaryIPAddress} builds.${domain}
+      ${config.networking.primaryIPAddress} meta.${domain}
+    '';
+
+    services.sourcehut = {
+      builds = {
+        enable = true;
+        # FIXME: see why it does not seem to activate fully.
+        #enableWorker = true;
+        images = { };
+      };
+
+      settings."builds.sr.ht" = {
+        oauth-client-secret = pkgs.writeText "buildsrht-oauth-client-secret" "2260e9c4d9b8dcedcef642860e0504bc";
+        oauth-client-id = "299db9f9c2013170";
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+
+    with subtest("Check whether meta comes up"):
+         machine.wait_for_unit("metasrht-api.service")
+         machine.wait_for_unit("metasrht.service")
+         machine.wait_for_unit("metasrht-webhooks.service")
+         machine.wait_for_open_port(5000)
+         machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
+         machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
+
+    with subtest("Check whether builds comes up"):
+         machine.wait_for_unit("buildsrht.service")
+         machine.wait_for_open_port(5002)
+         machine.succeed("curl -sL http://localhost:5002 | grep builds.${domain}")
+         #machine.wait_for_unit("buildsrht-worker.service")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/sourcehut/default.nix b/nixpkgs/nixos/tests/sourcehut/default.nix
new file mode 100644
index 000000000000..04f1551d70d9
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/default.nix
@@ -0,0 +1,6 @@
+{ system, pkgs, ... }:
+
+{
+  git = import ./git.nix { inherit system pkgs; };
+  builds = import ./builds.nix { inherit system pkgs; };
+}
diff --git a/nixpkgs/nixos/tests/sourcehut/git.nix b/nixpkgs/nixos/tests/sourcehut/git.nix
new file mode 100644
index 000000000000..ed184d5d5518
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/git.nix
@@ -0,0 +1,96 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }:
+let
+  domain = "sourcehut.localdomain";
+in
+{
+  name = "sourcehut";
+
+  meta.maintainers = with pkgs.lib.maintainers; [ tomberek nessdoor ];
+
+  nodes.machine = { config, pkgs, nodes, ... }: {
+    imports = [
+      ./nodes/common.nix
+    ];
+
+    networking.domain = domain;
+    networking.extraHosts = ''
+      ${config.networking.primaryIPAddress} git.${domain}
+      ${config.networking.primaryIPAddress} meta.${domain}
+    '';
+
+    services.sourcehut = {
+      git.enable = true;
+      settings."git.sr.ht" = {
+        oauth-client-secret = pkgs.writeText "gitsrht-oauth-client-secret" "3597288dc2c716e567db5384f493b09d";
+        oauth-client-id = "d07cb713d920702e";
+      };
+    };
+
+    environment.systemPackages = with pkgs; [
+      git
+    ];
+  };
+
+  testScript =
+    let
+      userName = "nixos-test";
+      userPass = "AutoNixosTestPwd";
+      hutConfig = pkgs.writeText "hut-config" ''
+        instance "${domain}" {
+          # Will be replaced at runtime with the generated token
+          access-token "OAUTH-TOKEN"
+        }
+      '';
+      sshConfig = pkgs.writeText "ssh-config" ''
+        Host git.${domain}
+             IdentityFile = ~/.ssh/id_rsa
+      '';
+    in
+    ''
+      start_all()
+      machine.wait_for_unit("multi-user.target")
+      machine.wait_for_unit("sshd.service")
+
+      with subtest("Check whether meta comes up"):
+           machine.wait_for_unit("metasrht-api.service")
+           machine.wait_for_unit("metasrht.service")
+           machine.wait_for_unit("metasrht-webhooks.service")
+           machine.wait_for_open_port(5000)
+           machine.succeed("curl -sL http://localhost:5000 | grep meta.${domain}")
+           machine.succeed("curl -sL http://meta.${domain} | grep meta.${domain}")
+
+      with subtest("Create a new user account and OAuth access key"):
+           machine.succeed("echo ${userPass} | metasrht-manageuser -ps -e ${userName}@${domain}\
+                            -t active_paying ${userName}");
+           (_, token) = machine.execute("srht-gen-oauth-tok -i ${domain} -q ${userName} ${userPass}")
+           token = token.strip().replace("/", r"\\/") # Escape slashes in token before passing it to sed
+           machine.execute("mkdir -p ~/.config/hut/")
+           machine.execute("sed s/OAUTH-TOKEN/" + token + "/ ${hutConfig} > ~/.config/hut/config")
+
+      with subtest("Check whether git comes up"):
+           machine.wait_for_unit("gitsrht-api.service")
+           machine.wait_for_unit("gitsrht.service")
+           machine.wait_for_unit("gitsrht-webhooks.service")
+           machine.succeed("curl -sL http://git.${domain} | grep git.${domain}")
+
+      with subtest("Add an SSH key for Git access"):
+           machine.execute("ssh-keygen -q -N \"\" -t rsa -f ~/.ssh/id_rsa")
+           machine.execute("cat ${sshConfig} > ~/.ssh/config")
+           machine.succeed("hut meta ssh-key create ~/.ssh/id_rsa.pub")
+
+      with subtest("Create a new repo and push contents to it"):
+           machine.execute("git init test")
+           machine.execute("echo \"Hello world!\" > test/hello.txt")
+           machine.execute("cd test && git add .")
+           machine.execute("cd test && git commit -m \"Initial commit\"")
+           machine.execute("cd test && git tag v0.1")
+           machine.succeed("cd test && git remote add origin gitsrht@git.${domain}:~${userName}/test")
+           machine.execute("( echo -n 'git.${domain} '; cat /etc/ssh/ssh_host_ed25519_key.pub ) > ~/.ssh/known_hosts")
+           machine.succeed("hut git create test")
+           machine.succeed("cd test && git push --tags --set-upstream origin master")
+
+      with subtest("Verify that the repo is downloadable and its contents match the original"):
+           machine.succeed("curl https://git.${domain}/~${userName}/test/archive/v0.1.tar.gz | tar -xz")
+           machine.succeed("diff test-v0.1/hello.txt test/hello.txt")
+    '';
+})
diff --git a/nixpkgs/nixos/tests/sourcehut/nodes/common.nix b/nixpkgs/nixos/tests/sourcehut/nodes/common.nix
new file mode 100644
index 000000000000..f0a81358f972
--- /dev/null
+++ b/nixpkgs/nixos/tests/sourcehut/nodes/common.nix
@@ -0,0 +1,107 @@
+{ config, pkgs, nodes, ... }:
+let
+  domain = config.networking.domain;
+
+  # Note that wildcard certificates just under the TLD (eg. *.com)
+  # would be rejected by clients like curl.
+  tls-cert = pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -days 36500 \
+      -subj '/CN=${domain}' -extensions v3_req \
+      -addext 'subjectAltName = DNS:*.${domain}'
+    install -D -t $out key.pem cert.pem
+  '';
+in
+{
+  # buildsrht needs space
+  virtualisation.diskSize = 4 * 1024;
+  virtualisation.memorySize = 2 * 1024;
+  networking.enableIPv6 = false;
+
+  services.sourcehut = {
+    enable = true;
+    nginx.enable = true;
+    nginx.virtualHost = {
+      forceSSL = true;
+      sslCertificate = "${tls-cert}/cert.pem";
+      sslCertificateKey = "${tls-cert}/key.pem";
+    };
+    postgresql.enable = true;
+    redis.enable = true;
+
+    meta.enable = true;
+
+    settings."sr.ht" = {
+      environment = "production";
+      global-domain = config.networking.domain;
+      service-key = pkgs.writeText "service-key" "8b327279b77e32a3620e2fc9aabce491cc46e7d821fd6713b2a2e650ce114d01";
+      network-key = pkgs.writeText "network-key" "cEEmc30BRBGkgQZcHFksiG7hjc6_dK1XR2Oo5Jb9_nQ=";
+    };
+    settings.webhooks.private-key = pkgs.writeText "webhook-key" "Ra3IjxgFiwG9jxgp4WALQIZw/BMYt30xWiOsqD0J7EA=";
+    settings.mail = {
+      smtp-from = "root+hut@${domain}";
+      # WARNING: take care to keep pgp-privkey outside the Nix store in production,
+      # or use LoadCredentialEncrypted=
+      pgp-privkey = toString (pkgs.writeText "sourcehut.pgp-privkey" ''
+        -----BEGIN PGP PRIVATE KEY BLOCK-----
+
+        lFgEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
+        Gk7hYQoAAP9X4oPmxxrHN8LewBpWITdBomNqlHoiP7mI0nz/BOPJHxEktDZuaXhv
+        cy90ZXN0cy9zb3VyY2VodXQgPHJvb3QraHV0QHNvdXJjZWh1dC5sb2NhbGRvbWFp
+        bj6IlwQTFgoAPxYhBPqjgjnL8RHN4JnADNicgXaYm0jJBQJioNE5AhsDBQkDwmcA
+        BgsJCAcDCgUVCgkICwUWAwIBAAIeBQIXgAAKCRDYnIF2mJtIySVCAP9e2nHsVHSi
+        2B1YGZpVG7Xf36vxljmMkbroQy+0gBPwRwEAq+jaiQqlbGhQ7R/HMFcAxBIVsq8h
+        Aw1rngsUd0o3dAicXQRioNE5EgorBgEEAZdVAQUBAQdAXZV2Sd5ZNBVTBbTGavMv
+        D6ORrUh8z7TI/3CsxCE7+yADAQgHAAD/c1RU9xH+V/uI1fE7HIn/zL0LUPpsuce2
+        cH++g4u3kBgTOYh+BBgWCgAmFiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg0TkC
+        GwwFCQPCZwAACgkQ2JyBdpibSMlKagD/cTre6p1m8QuJ7kwmCFRSz5tBzIuYMMgN
+        xtT7dmS91csA/35fWsOykSiFRojQ7ccCSUTHL7ApF2EbL968tP/D2hIG
+        =Hjoc
+        -----END PGP PRIVATE KEY BLOCK-----
+      '');
+      pgp-pubkey = pkgs.writeText "sourcehut.pgp-pubkey" ''
+        -----BEGIN PGP PUBLIC KEY BLOCK-----
+
+        mDMEYqDRORYJKwYBBAHaRw8BAQdAehGoy36FUx2OesYm07be2rtLyvR5Pb/ltstd
+        Gk7hYQq0Nm5peG9zL3Rlc3RzL3NvdXJjZWh1dCA8cm9vdCtodXRAc291cmNlaHV0
+        LmxvY2FsZG9tYWluPoiXBBMWCgA/FiEE+qOCOcvxEc3gmcAM2JyBdpibSMkFAmKg
+        0TkCGwMFCQPCZwAGCwkIBwMKBRUKCQgLBRYDAgEAAh4FAheAAAoJENicgXaYm0jJ
+        JUIA/17acexUdKLYHVgZmlUbtd/fq/GWOYyRuuhDL7SAE/BHAQCr6NqJCqVsaFDt
+        H8cwVwDEEhWyryEDDWueCxR3Sjd0CLg4BGKg0TkSCisGAQQBl1UBBQEBB0BdlXZJ
+        3lk0FVMFtMZq8y8Po5GtSHzPtMj/cKzEITv7IAMBCAeIfgQYFgoAJhYhBPqjgjnL
+        8RHN4JnADNicgXaYm0jJBQJioNE5AhsMBQkDwmcAAAoJENicgXaYm0jJSmoA/3E6
+        3uqdZvELie5MJghUUs+bQcyLmDDIDcbU+3ZkvdXLAP9+X1rDspEohUaI0O3HAklE
+        xy+wKRdhGy/evLT/w9oSBg==
+        =pJD7
+        -----END PGP PUBLIC KEY BLOCK-----
+      '';
+      pgp-key-id = "0xFAA38239CBF111CDE099C00CD89C8176989B48C9";
+    };
+  };
+
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
+  services.nginx = {
+    enable = true;
+    recommendedGzipSettings = true;
+    recommendedOptimisation = true;
+    recommendedTlsSettings = true;
+    recommendedProxySettings = true;
+  };
+
+  services.postgresql = {
+    enable = true;
+    enableTCPIP = false;
+    settings.unix_socket_permissions = "0770";
+  };
+
+  services.openssh = {
+    enable = true;
+    settings.PasswordAuthentication = false;
+    settings.PermitRootLogin = "no";
+  };
+
+  environment.systemPackages = with pkgs; [
+    hut # For interacting with the Sourcehut APIs via CLI
+    srht-gen-oauth-tok # To automatically generate user OAuth tokens
+  ];
+}
diff --git a/nixpkgs/nixos/tests/stalwart-mail.nix b/nixpkgs/nixos/tests/stalwart-mail.nix
index b5589966a160..634c0e2e3926 100644
--- a/nixpkgs/nixos/tests/stalwart-mail.nix
+++ b/nixpkgs/nixos/tests/stalwart-mail.nix
@@ -42,20 +42,22 @@ in import ./make-test-python.nix ({ lib, ... }: {
 
         session.auth.mechanisms = [ "PLAIN" ];
         session.auth.directory = "in-memory";
-        jmap.directory = "in-memory";  # shared with imap
+        storage.directory = "in-memory";  # shared with imap
 
         session.rcpt.directory = "in-memory";
         queue.outbound.next-hop = [ "local" ];
 
         directory."in-memory" = {
           type = "memory";
-          users = [
+          principals = [
             {
+              type = "individual";
               name = "alice";
               secret = "foobar";
               email = [ "alice@${domain}" ];
             }
             {
+              type = "individual";
               name = "bob";
               secret = "foobar";
               email = [ "bob@${domain}" ];
@@ -90,8 +92,9 @@ in import ./make-test-python.nix ({ lib, ... }: {
 
         with IMAP4('localhost') as imap:
             imap.starttls()
-            imap.login('bob', 'foobar')
-            imap.select('"All Mail"')
+            status, [caps] = imap.login('bob', 'foobar')
+            assert status == 'OK'
+            imap.select()
             status, [ref] = imap.search(None, 'ALL')
             assert status == 'OK'
             [msgId] = ref.split()
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/default.nix b/nixpkgs/nixos/tests/web-apps/mastodon/default.nix
index 411ebfcd731b..178590d13b63 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/default.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/default.nix
@@ -5,5 +5,5 @@ let
 in
 {
   standard = handleTestOn supportedSystems ./standard.nix { inherit system; };
-  remote-postgresql = handleTestOn supportedSystems ./remote-postgresql.nix { inherit system; };
+  remote-databases = handleTestOn supportedSystems ./remote-databases.nix { inherit system; };
 }
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix b/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix
index 6548883db452..fa6430a99353 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/remote-postgresql.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/remote-databases.nix
@@ -16,7 +16,14 @@ in
   meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin ];
 
   nodes = {
-    database = { config, ... }: {
+    databases = { config, ... }: {
+      environment = {
+        etc = {
+          "redis/password-redis-db".text = ''
+            ogjhJL8ynrP7MazjYOF6
+          '';
+        };
+      };
       networking = {
         interfaces.eth1 = {
           ipv4.addresses = [
@@ -24,7 +31,17 @@ in
           ];
         };
         extraHosts = hosts;
-        firewall.allowedTCPPorts = [ config.services.postgresql.port ];
+        firewall.allowedTCPPorts = [
+          config.services.redis.servers.mastodon.port
+          config.services.postgresql.port
+        ];
+      };
+
+      services.redis.servers.mastodon = {
+        enable = true;
+        bind = "0.0.0.0";
+        port = 31637;
+        requirePassFile = "/etc/redis/password-redis-db";
       };
 
       services.postgresql = {
@@ -83,6 +100,9 @@ in
 
       environment = {
         etc = {
+          "mastodon/password-redis-db".text = ''
+            ogjhJL8ynrP7MazjYOF6
+          '';
           "mastodon/password-posgressql-db".text = ''
             SoDTZcISc3f1M1LJsRLT
           '';
@@ -108,6 +128,12 @@ in
         localDomain = "mastodon.local";
         enableUnixSocket = false;
         streamingProcesses = 2;
+        redis = {
+          createLocally = false;
+          host = "192.168.2.102";
+          port = 31637;
+          passwordFile = "/etc/mastodon/password-redis-db";
+        };
         database = {
           createLocally = false;
           host = "192.168.2.102";
@@ -151,12 +177,14 @@ in
     extraInit = ''
       nginx.wait_for_unit("nginx.service")
       nginx.wait_for_open_port(443)
-      database.wait_for_unit("postgresql.service")
-      database.wait_for_open_port(5432)
+      databases.wait_for_unit("redis-mastodon.service")
+      databases.wait_for_unit("postgresql.service")
+      databases.wait_for_open_port(31637)
+      databases.wait_for_open_port(5432)
     '';
     extraShutdown = ''
       nginx.shutdown()
-      database.shutdown()
+      databases.shutdown()
     '';
   };
 })
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/script.nix b/nixpkgs/nixos/tests/web-apps/mastodon/script.nix
index afb7c0e0a0eb..9184c63c8941 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/script.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/script.nix
@@ -8,7 +8,6 @@
 
   ${extraInit}
 
-  server.wait_for_unit("redis-mastodon.service")
   server.wait_for_unit("mastodon-sidekiq-all.service")
   server.wait_for_unit("mastodon-streaming.target")
   server.wait_for_unit("mastodon-web.service")
diff --git a/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix b/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix
index e5eb30fef597..ddc764e2168c 100644
--- a/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix
+++ b/nixpkgs/nixos/tests/web-apps/mastodon/standard.nix
@@ -34,12 +34,6 @@ in
         pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
       };
 
-      services.redis.servers.mastodon = {
-        enable = true;
-        bind = "127.0.0.1";
-        port = 31637;
-      };
-
       # TODO remove once https://github.com/NixOS/nixpkgs/pull/266270 is resolved.
       services.postgresql.package = pkgs.postgresql_14;
 
@@ -89,6 +83,7 @@ in
     extraInit = ''
       server.wait_for_unit("nginx.service")
       server.wait_for_open_port(443)
+      server.wait_for_unit("redis-mastodon.service")
       server.wait_for_unit("postgresql.service")
       server.wait_for_open_port(5432)
     '';
diff --git a/nixpkgs/nixos/tests/web-servers/ttyd.nix b/nixpkgs/nixos/tests/web-servers/ttyd.nix
index d161673684b3..b79a2032ec75 100644
--- a/nixpkgs/nixos/tests/web-servers/ttyd.nix
+++ b/nixpkgs/nixos/tests/web-servers/ttyd.nix
@@ -2,18 +2,28 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
   name = "ttyd";
   meta.maintainers = with lib.maintainers; [ stunkymonkey ];
 
-  nodes.machine = { pkgs, ... }: {
+  nodes.readonly = { pkgs, ... }: {
+    services.ttyd = {
+      enable = true;
+      entrypoint = [ (lib.getExe pkgs.htop) ];
+      writeable = false;
+    };
+  };
+
+  nodes.writeable = { pkgs, ... }: {
     services.ttyd = {
       enable = true;
       username = "foo";
       passwordFile = pkgs.writeText "password" "bar";
+      writeable = true;
     };
   };
 
   testScript = ''
-    machine.wait_for_unit("ttyd.service")
-    machine.wait_for_open_port(7681)
-    response = machine.succeed("curl -vvv -u foo:bar -s -H 'Host: ttyd' http://127.0.0.1:7681/")
-    assert '<title>ttyd - Terminal</title>' in response, "Page didn't load successfully"
+    for machine in [readonly, writeable]:
+      machine.wait_for_unit("ttyd.service")
+      machine.wait_for_open_port(7681)
+      response = machine.succeed("curl -vvv -u foo:bar -s -H 'Host: ttyd' http://127.0.0.1:7681/")
+      assert '<title>ttyd - Terminal</title>' in response, "Page didn't load successfully"
   '';
 })