about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/gpu-accel.chapter.md28
-rw-r--r--nixos/doc/manual/configuration/x-windows.chapter.md19
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md5
-rw-r--r--nixos/doc/manual/release-notes/rl-2411.section.md67
-rw-r--r--nixos/lib/make-disk-image.nix9
-rw-r--r--nixos/modules/config/swap.nix10
-rw-r--r--nixos/modules/hardware/graphics.nix126
-rw-r--r--nixos/modules/hardware/opengl.nix158
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix69
-rw-r--r--nixos/modules/hardware/video/nvidia.nix4
-rw-r--r--nixos/modules/hardware/video/virtualbox.nix7
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixos/modules/misc/mandoc.nix23
-rw-r--r--nixos/modules/module-list.nix10
-rw-r--r--nixos/modules/programs/dublin-traceroute.nix4
-rw-r--r--nixos/modules/programs/joycond-cemuhook.nix2
-rw-r--r--nixos/modules/programs/miriway.nix2
-rw-r--r--nixos/modules/programs/mouse-actions.nix2
-rw-r--r--nixos/modules/programs/nix-required-mounts.nix118
-rw-r--r--nixos/modules/programs/steam.nix7
-rw-r--r--nixos/modules/programs/turbovnc.nix4
-rw-r--r--nixos/modules/programs/wayland/hyprland.nix7
-rw-r--r--nixos/modules/programs/wayland/wayland-session.nix2
-rw-r--r--nixos/modules/programs/xonsh.nix13
-rw-r--r--nixos/modules/security/ca.nix6
-rw-r--r--nixos/modules/security/ipa.nix16
-rw-r--r--nixos/modules/security/pam.nix2
-rw-r--r--nixos/modules/security/polkit.nix14
-rw-r--r--nixos/modules/security/sudo-rs.nix2
-rw-r--r--nixos/modules/services/admin/docuum.nix30
-rw-r--r--nixos/modules/services/admin/meshcentral.nix2
-rw-r--r--nixos/modules/services/cluster/kubernetes/default.nix2
-rw-r--r--nixos/modules/services/cluster/kubernetes/kubelet.nix2
-rw-r--r--nixos/modules/services/continuous-integration/gitlab-runner.nix232
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix2
-rw-r--r--nixos/modules/services/databases/memcached.nix2
-rw-r--r--nixos/modules/services/databases/neo4j.nix2
-rw-r--r--nixos/modules/services/desktop-managers/lomiri.nix2
-rw-r--r--nixos/modules/services/desktop-managers/plasma6.nix9
-rw-r--r--nixos/modules/services/desktops/deepin/deepin-anything.nix38
-rw-r--r--nixos/modules/services/desktops/gnome/gnome-keyring.nix57
-rw-r--r--nixos/modules/services/display-managers/default.nix4
-rw-r--r--nixos/modules/services/hardware/amdgpu.nix43
-rw-r--r--nixos/modules/services/hardware/amdvlk.nix4
-rw-r--r--nixos/modules/services/hardware/openrgb.nix2
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix5
-rw-r--r--nixos/modules/services/mail/mailman.nix2
-rw-r--r--nixos/modules/services/mail/stalwart-mail.nix9
-rw-r--r--nixos/modules/services/matrix/mautrix-signal.nix2
-rw-r--r--nixos/modules/services/matrix/mautrix-whatsapp.nix2
-rw-r--r--nixos/modules/services/matrix/synapse.nix2
-rw-r--r--nixos/modules/services/misc/anki-sync-server.md2
-rw-r--r--nixos/modules/services/misc/graphical-desktop.nix2
-rw-r--r--nixos/modules/services/misc/mqtt2influxdb.nix2
-rw-r--r--nixos/modules/services/misc/ollama.nix35
-rw-r--r--nixos/modules/services/misc/paperless.nix2
-rw-r--r--nixos/modules/services/misc/portunus.nix2
-rw-r--r--nixos/modules/services/misc/private-gpt.nix2
-rw-r--r--nixos/modules/services/misc/snapper.nix298
-rw-r--r--nixos/modules/services/misc/sourcehut/service.nix9
-rw-r--r--nixos/modules/services/misc/spice-autorandr.nix2
-rw-r--r--nixos/modules/services/misc/tandoor-recipes.nix4
-rw-r--r--nixos/modules/services/monitoring/grafana-reporter.nix2
-rw-r--r--nixos/modules/services/monitoring/netdata.nix65
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix70
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix19
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/minio.nix69
-rw-r--r--nixos/modules/services/monitoring/rustdesk-server.nix2
-rw-r--r--nixos/modules/services/monitoring/thanos.nix2
-rw-r--r--nixos/modules/services/monitoring/ups.nix4
-rw-r--r--nixos/modules/services/network-filesystems/davfs2.nix9
-rw-r--r--nixos/modules/services/network-filesystems/openafs/server.nix2
-rw-r--r--nixos/modules/services/network-filesystems/samba-wsdd.nix2
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix6
-rw-r--r--nixos/modules/services/networking/antennas.nix5
-rw-r--r--nixos/modules/services/networking/gns3-server.nix6
-rw-r--r--nixos/modules/services/networking/haproxy.nix2
-rw-r--r--nixos/modules/services/networking/hylafax/options.nix4
-rw-r--r--nixos/modules/services/networking/netbird.nix1
-rw-r--r--nixos/modules/services/networking/netbird/dashboard.nix2
-rw-r--r--nixos/modules/services/networking/netbird/management.nix4
-rw-r--r--nixos/modules/services/networking/netbird/server.nix4
-rw-r--r--nixos/modules/services/networking/netbird/signal.nix2
-rw-r--r--nixos/modules/services/networking/networkd-dispatcher.nix2
-rw-r--r--nixos/modules/services/networking/nncp.nix4
-rw-r--r--nixos/modules/services/networking/scion/scion-control.nix2
-rw-r--r--nixos/modules/services/networking/scion/scion-daemon.nix2
-rw-r--r--nixos/modules/services/networking/scion/scion-dispatcher.nix2
-rw-r--r--nixos/modules/services/networking/scion/scion-router.nix2
-rw-r--r--nixos/modules/services/networking/scion/scion.nix5
-rw-r--r--nixos/modules/services/networking/wg-access-server.nix124
-rw-r--r--nixos/modules/services/networking/wstunnel.nix431
-rw-r--r--nixos/modules/services/networking/zerotierone.nix10
-rw-r--r--nixos/modules/services/search/qdrant.nix1
-rw-r--r--nixos/modules/services/search/quickwit.nix2
-rw-r--r--nixos/modules/services/security/fail2ban.nix2
-rw-r--r--nixos/modules/services/security/haveged.nix2
-rw-r--r--nixos/modules/services/security/sslmate-agent.nix2
-rw-r--r--nixos/modules/services/security/step-ca.nix2
-rw-r--r--nixos/modules/services/security/vaultwarden/backup.sh18
-rw-r--r--nixos/modules/services/system/localtimed.nix8
-rw-r--r--nixos/modules/services/torrent/flood.nix85
-rw-r--r--nixos/modules/services/torrent/rtorrent.nix28
-rw-r--r--nixos/modules/services/torrent/transmission.nix5
-rw-r--r--nixos/modules/services/ttys/kmscon.nix46
-rw-r--r--nixos/modules/services/wayland/cage.nix2
-rw-r--r--nixos/modules/services/web-apps/akkoma.nix2
-rw-r--r--nixos/modules/services/web-apps/audiobookshelf.nix2
-rw-r--r--nixos/modules/services/web-apps/firefly-iii.nix4
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.nix2
-rw-r--r--nixos/modules/services/web-apps/keycloak.md12
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix28
-rw-r--r--nixos/modules/services/web-apps/mealie.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix49
-rw-r--r--nixos/modules/services/web-apps/pretix.nix2
-rw-r--r--nixos/modules/services/web-apps/silverbullet.nix2
-rw-r--r--nixos/modules/services/web-apps/suwayomi-server.nix4
-rw-r--r--nixos/modules/services/web-apps/zitadel.nix2
-rw-r--r--nixos/modules/services/web-servers/bluemap.nix4
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix6
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix16
-rw-r--r--nixos/modules/services/x11/desktop-managers/cinnamon.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/deepin.nix15
-rw-r--r--nixos/modules/services/x11/desktop-managers/phosh.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix16
-rw-r--r--nixos/modules/services/x11/xserver.nix7
-rw-r--r--nixos/modules/system/boot/clevis.nix2
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix10
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix6
-rw-r--r--nixos/modules/system/etc/etc.nix14
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix3
-rw-r--r--nixos/modules/testing/test-instrumentation.nix4
-rw-r--r--nixos/modules/virtualisation/docker.nix4
-rw-r--r--nixos/modules/virtualisation/incus.nix2
-rw-r--r--nixos/modules/virtualisation/libvirtd.nix12
-rw-r--r--nixos/modules/virtualisation/multipass.nix4
-rw-r--r--nixos/modules/virtualisation/oci-image.nix2
-rw-r--r--nixos/modules/virtualisation/oci-options.nix6
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix2
-rw-r--r--nixos/modules/virtualisation/spice-usb-redirection.nix2
-rw-r--r--nixos/modules/virtualisation/vagrant-guest.nix3
-rw-r--r--nixos/tests/all-tests.nix17
-rw-r--r--nixos/tests/armagetronad.nix2
-rw-r--r--nixos/tests/cagebreak.nix6
-rw-r--r--nixos/tests/crabfit.nix2
-rw-r--r--nixos/tests/flood.nix27
-rw-r--r--nixos/tests/home-assistant.nix8
-rw-r--r--nixos/tests/initrd-secrets.nix2
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix2
-rw-r--r--nixos/tests/installer.nix16
-rw-r--r--nixos/tests/kafka.nix9
-rw-r--r--nixos/tests/keycloak.nix9
-rw-r--r--nixos/tests/libreswan-nat.nix238
-rw-r--r--nixos/tests/libreswan.nix4
-rw-r--r--nixos/tests/matomo.nix6
-rw-r--r--nixos/tests/monado.nix2
-rw-r--r--nixos/tests/netdata.nix5
-rw-r--r--nixos/tests/nextcloud/basic.nix1
-rw-r--r--nixos/tests/nextcloud/default.nix2
-rw-r--r--nixos/tests/nix-required-mounts/default.nix58
-rw-r--r--nixos/tests/nix-required-mounts/ensure-path-not-present.nix13
-rw-r--r--nixos/tests/nix-required-mounts/test-require-feature.nix26
-rw-r--r--nixos/tests/nix-required-mounts/test-structured-attrs-empty.nix8
-rw-r--r--nixos/tests/nix-required-mounts/test-structured-attrs.nix18
-rw-r--r--nixos/tests/odoo.nix12
-rw-r--r--nixos/tests/openarena.nix2
-rw-r--r--nixos/tests/private-gpt.nix2
-rw-r--r--nixos/tests/prometheus/alertmanager.nix148
-rw-r--r--nixos/tests/prometheus/config-reload.nix116
-rw-r--r--nixos/tests/prometheus/default.nix13
-rw-r--r--nixos/tests/prometheus/federation.nix213
-rw-r--r--nixos/tests/prometheus/prometheus-pair.nix87
-rw-r--r--nixos/tests/prometheus/pushgateway.nix94
-rw-r--r--nixos/tests/prometheus/remote-write.nix73
-rw-r--r--nixos/tests/quake3.nix2
-rw-r--r--nixos/tests/rtorrent.nix25
-rw-r--r--nixos/tests/searx.nix182
-rw-r--r--nixos/tests/seatd.nix2
-rw-r--r--nixos/tests/snapper.nix5
-rw-r--r--nixos/tests/tandoor-recipes-script-name.nix95
-rw-r--r--nixos/tests/thanos.nix (renamed from nixos/tests/prometheus.nix)58
-rw-r--r--nixos/tests/tigervnc.nix2
-rw-r--r--nixos/tests/timezone.nix2
-rw-r--r--nixos/tests/tinywl.nix2
-rw-r--r--nixos/tests/tomcat.nix9
-rw-r--r--nixos/tests/vaultwarden.nix4
-rw-r--r--nixos/tests/wg-access-server.nix28
-rw-r--r--nixos/tests/wstunnel.nix96
190 files changed, 3453 insertions, 1189 deletions
diff --git a/nixos/doc/manual/configuration/gpu-accel.chapter.md b/nixos/doc/manual/configuration/gpu-accel.chapter.md
index 3b98bdd97c681..8afa2807b7b60 100644
--- a/nixos/doc/manual/configuration/gpu-accel.chapter.md
+++ b/nixos/doc/manual/configuration/gpu-accel.chapter.md
@@ -30,7 +30,7 @@ $ export \
 ```
 
 The second mechanism is to add the OpenCL driver package to
-[](#opt-hardware.opengl.extraPackages).
+[](#opt-hardware.graphics.extraPackages).
 This links the ICD file under `/run/opengl-driver`, where it will be visible
 to the ICD loader.
 
@@ -51,12 +51,12 @@ Platform Vendor      Advanced Micro Devices, Inc.
 Modern AMD [Graphics Core
 Next](https://en.wikipedia.org/wiki/Graphics_Core_Next) (GCN) GPUs are
 supported through the rocmPackages.clr.icd package. Adding this package to
-[](#opt-hardware.opengl.extraPackages)
+[](#opt-hardware.graphics.extraPackages)
 enables OpenCL support:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     rocmPackages.clr.icd
   ];
 }
@@ -71,13 +71,13 @@ intel-compute-runtime package. The proprietary Intel OpenCL runtime, in
 the intel-ocl package, is an alternative for Gen7 GPUs.
 
 The intel-compute-runtime or intel-ocl package can be added to
-[](#opt-hardware.opengl.extraPackages)
+[](#opt-hardware.graphics.extraPackages)
 to enable OpenCL support. For example, for Gen8 and later GPUs, the following
 configuration can be used:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     intel-compute-runtime
   ];
 }
@@ -90,8 +90,8 @@ compute API for GPUs. It is used directly by games or indirectly though
 compatibility layers like
 [DXVK](https://github.com/doitsujin/dxvk/wiki).
 
-By default, if [](#opt-hardware.opengl.driSupport)
-is enabled, mesa is installed and provides Vulkan for supported hardware.
+By default, if [](#opt-hardware.graphics.enable)
+is enabled, Mesa is installed and provides Vulkan for supported hardware.
 
 Similar to OpenCL, Vulkan drivers are loaded through the *Installable
 Client Driver* (ICD) mechanism. ICD files for Vulkan are JSON files that
@@ -110,7 +110,7 @@ $ export \
 ```
 
 The second mechanism is to add the Vulkan driver package to
-[](#opt-hardware.opengl.extraPackages).
+[](#opt-hardware.graphics.extraPackages).
 This links the ICD file under `/run/opengl-driver`, where it will be
 visible to the ICD loader.
 
@@ -140,18 +140,18 @@ Modern AMD [Graphics Core
 Next](https://en.wikipedia.org/wiki/Graphics_Core_Next) (GCN) GPUs are
 supported through either radv, which is part of mesa, or the amdvlk
 package. Adding the amdvlk package to
-[](#opt-hardware.opengl.extraPackages)
+[](#opt-hardware.graphics.extraPackages)
 makes amdvlk the default driver and hides radv and lavapipe from the device list.
 A specific driver can be forced as follows:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     pkgs.amdvlk
   ];
 
   # To enable Vulkan support for 32-bit applications, also add:
-  hardware.opengl.extraPackages32 = [
+  hardware.graphics.extraPackages32 = [
     pkgs.driversi686Linux.amdvlk
   ];
 
@@ -171,7 +171,7 @@ graphics hardware acceleration capabilities for video processing.
 
 VA-API drivers are loaded by `libva`. The version in nixpkgs is built to search
 the opengl driver path, so drivers can be installed in
-[](#opt-hardware.opengl.extraPackages).
+[](#opt-hardware.graphics.extraPackages).
 
 VA-API can be tested using:
 
@@ -185,7 +185,7 @@ Modern Intel GPUs use the iHD driver, which can be installed with:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     intel-media-driver
   ];
 }
@@ -195,7 +195,7 @@ Older Intel GPUs use the i965 driver, which can be installed with:
 
 ```nix
 {
-  hardware.opengl.extraPackages = [
+  hardware.graphics.extraPackages = [
     intel-vaapi-driver
   ];
 }
diff --git a/nixos/doc/manual/configuration/x-windows.chapter.md b/nixos/doc/manual/configuration/x-windows.chapter.md
index 31752330dd9a8..362ae5d7e5df1 100644
--- a/nixos/doc/manual/configuration/x-windows.chapter.md
+++ b/nixos/doc/manual/configuration/x-windows.chapter.md
@@ -79,7 +79,7 @@ Wine, you should also set the following:
 
 ```nix
 {
-  hardware.opengl.driSupport32Bit = true;
+  hardware.graphics.enable32Bit = true;
 }
 ```
 
@@ -183,23 +183,6 @@ If you have an older card, you may have to use one of the legacy drivers:
 You may need to reboot after enabling this driver to prevent a clash
 with other kernel modules.
 
-## Proprietary AMD drivers {#sec-x11--graphics-cards-amd}
-
-AMD provides a proprietary driver for its graphics cards that is not
-enabled by default because it's not Free Software, is often broken in
-nixpkgs and as of this writing doesn't offer more features or
-performance. If you still want to use it anyway, you need to explicitly
-set:
-
-```nix
-{
-  services.xserver.videoDrivers = [ "amdgpu-pro" ];
-}
-```
-
-You will need to reboot after enabling this driver to prevent a clash
-with other kernel modules.
-
 ## Touchpads {#sec-x11-touchpads}
 
 Support for Synaptics touchpads (found in many laptops such as the Dell
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 3a2c70fb7a31b..dad45f12373e6 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -453,7 +453,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 
     The new names are as follows:
     - `bindAddress`: [`services.keycloak.settings.http-host`](#opt-services.keycloak.settings.http-host)
-    - `forceBackendUrlToFrontendUrl`: [`services.keycloak.settings.hostname-strict-backchannel`](#opt-services.keycloak.settings.hostname-strict-backchannel)
+    - `forceBackendUrlToFrontendUrl`: `services.keycloak.settings.hostname-strict-backchannel`
     - `httpPort`: [`services.keycloak.settings.http-port`](#opt-services.keycloak.settings.http-port)
     - `httpsPort`: [`services.keycloak.settings.https-port`](#opt-services.keycloak.settings.https-port)
 
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index 165e3e13a78ef..b1b18b35e9c28 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -62,7 +62,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 <!-- Please keep entries alphabetically sorted. -->
 
 - [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
-The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server software.
+The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the ankisyncd software.
 
 - [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable).
 
@@ -207,6 +207,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [xdg-terminal-exec](https://github.com/Vladimir-csp/xdg-terminal-exec), the proposed Default Terminal Execution Specification.
 
+- Convenience options for `amdgpu`, open source driver for Radeon cards, is now available under `hardware.amdgpu`.
+
 - [ydotool](https://github.com/ReimuNotMoe/ydotool), a generic command-line automation tool now has a module. Available as [programs.ydotool](#opt-programs.ydotool.enable).
 
 - [your_spotify](https://github.com/Yooooomi/your_spotify), a self hosted Spotify tracking dashboard. Available as [services.your_spotify](#opt-services.your_spotify.enable)
@@ -822,3 +824,4 @@ Module System:
 ### Internal {#sec-release-24.05-lib-internal}
 - `lib` now has [Readme for contributing](https://github.com/NixOS/nixpkgs/tree/master/lib#readme).
 - Some function's documentation is now written using the [accepted doc comment syntax](https://github.com/NixOS/rfcs/pull/145).
+- `odoo` has been updated from `16.0.20231024` to `17.0.20240507`.
diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md
index 889d399749323..d94df902dd3d6 100644
--- a/nixos/doc/manual/release-notes/rl-2411.section.md
+++ b/nixos/doc/manual/release-notes/rl-2411.section.md
@@ -4,6 +4,8 @@
 
 ## Highlights {#sec-release-24.11-highlights}
 
+- Convenience options for `amdgpu`, open source driver for Radeon cards, is now available under `hardware.amdgpu`.
+
 - [AMDVLK](https://github.com/GPUOpen-Drivers/AMDVLK), AMD's open source Vulkan driver, is now available to be configured as `hardware.amdgpu.amdvlk` option.
   This also allows configuring runtime settings of AMDVLK and enabling experimental features.
 
@@ -15,21 +17,49 @@
 
 - [Quickwit](https://quickwit.io), sub-second search & analytics engine on cloud storage. Available as [services.quickwit](options.html#opt-services.quickwit).
 
+- [Flood](https://flood.js.org/), a beautiful WebUI for various torrent clients. Available as [services.flood](options.html#opt-services.flood).
+
 - [Renovate](https://github.com/renovatebot/renovate), a dependency updating tool for various git forges and language ecosystems. Available as [services.renovate](#opt-services.renovate.enable).
 
+- [wg-access-server](https://github.com/freifunkMUC/wg-access-server/), an all-in-one WireGuard VPN solution with a web ui for connecting devices. Available at [services.wg-access-server](#opt-services.wg-access-server.enable).
+
 ## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
 
+- `transmission` package has been aliased with a `trace` warning to `transmission_3`. Since [Transmission 4 has been released last year](https://github.com/transmission/transmission/releases/tag/4.0.0), and Transmission 3 will eventually go away, it was decided perform this warning alias to make people aware of the new version. The `services.transmission.package` defaults to `transmission_3` as well because the upgrade can cause data loss in certain specific usage patterns (examples: [#5153](https://github.com/transmission/transmission/issues/5153), [#6796](https://github.com/transmission/transmission/issues/6796)). Please make sure to back up to your data directory per your usage:
+  - `transmission-gtk`: `~/.config/transmission`
+  - `transmission-daemon` using NixOS module: `${config.services.transmission.home}/.config/transmission-daemon` (defaults to `/var/lib/transmission/.config/transmission-daemon`)
+
 - `androidenv.androidPkgs_9_0` has been removed, and replaced with `androidenv.androidPkgs` for a more complete Android SDK including support for Android 9 and later.
 
+- `wstunnel` has had a major version upgrade that entailed rewriting the program in Rust.
+  The module was updated to accommodate for breaking changes.
+  Breaking changes to the module API were minimised as much as possible,
+  but some were nonetheless inevitable due to changes in the upstream CLI.
+  Certain options were moved from separate CLI arguments into the forward specifications,
+  and those options were also removed from the module's API,
+  please consult the wstunnel man page for more detail.
+  Also be aware that if you have set additional options in `services.wstunnel.{clients,servers}.<name>.extraArgs`,
+  that those might have been removed or modified upstream.
+
+- `clang-tools_<version>` packages have been moved into `llvmPackages_<version>` (i.e. `clang-tools_18` is now `llvmPackages_18.clang-tools`).
+  - For convenience, the top-level `clang-tools` attribute remains and is now bound to `llvmPackages.clang-tools`.
+  - Top-level `clang_tools_<version>` attributes are now aliases; these will be removed in a future release.
+
 - `nginx` package no longer includes `gd` and `geoip` dependencies. For enabling it, override `nginx` package with the optionals `withImageFilter` and `withGeoIP`.
 
 - `openssh` and `openssh_hpn` are now compiled without Kerberos 5 / GSSAPI support in an effort to reduce the attack surface of the components for the majority of users. Users needing this support can
   use the new `opensshWithKerberos` and `openssh_hpnWithKerberos` flavors (e.g. `programs.ssh.package = pkgs.openssh_gssapi`).
 
+- `security.ipa.ipaHostname` now defaults to the value of `networking.fqdn` if
+  it is set, instead of the previous hardcoded default of
+  `${networking.hostName}.${security.ipa.domain}`.
+
 - `nvimpager` was updated to version 0.13.0, which changes the order of user and
   nvimpager settings: user commands in `-c` and `--cmd` now override the
   respective default settings because they are executed later.
 
+- `pkgs.nextcloud27` has been removed since it's EOL.
+
 - `services.forgejo.mailerPasswordFile` has been deprecated by the drop-in replacement `services.forgejo.secrets.mailer.PASSWD`,
   which is part of the new free-form `services.forgejo.secrets` option.
   `services.forgejo.secrets` is a small wrapper over systemd's `LoadCredential=`. It has the same structure (sections/keys) as
@@ -49,6 +79,10 @@
   before changing the package to `pkgs.stalwart-mail` in
   [`services.stalwart-mail.package`](#opt-services.stalwart-mail.package).
 
+- `androidndkPkgs` has been updated to `androidndkPkgs_26`.
+
+- Android NDK version 26 and SDK version 33 are now the default versions used for cross compilation to android.
+
 - `haskell.lib.compose.justStaticExecutables` now disallows references to GHC in the
   output by default, to alert users to closure size issues caused by
   [#164630](https://github.com/NixOS/nixpkgs/issues/164630). See ["Packaging
@@ -57,10 +91,27 @@
   for information on working around `output '...' is not allowed to refer to
   the following paths` errors caused by this change.
 
+- The `stalwart-mail` service now runs under the `stalwart-mail` system user
+  instead of a dynamically created one via `DynamicUser`, to avoid automatic
+  ownership changes on its large file store each time the service was started.
+  This change requires to manually move the state directory from
+  `/var/lib/private/stalwart-mail` to `/var/lib/stalwart-mail` and to
+  change the ownership of the directory and its content to `stalwart-mail`.
+
 - The `stalwart-mail` module now uses RocksDB as the default storage backend
   for `stateVersion` ≥ 24.11. (It was previously using SQLite for structured
   data and the filesystem for blobs).
 
+- `libe57format` has been updated to `>= 3.0.0`, which contains some backward-incompatible API changes. See the [release note](https://github.com/asmaloney/libE57Format/releases/tag/v3.0.0) for more details.
+
+- `gitlab` deprecated support for *runner registration tokens* in GitLab 16.0, disabled their support in GitLab 17.0 and will
+  ultimately remove it in GitLab 18.0, as outlined in the
+  [documentation](https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#estimated-time-frame-for-planned-changes).
+  After upgrading to GitLab >= 17.0, it is possible to re-enable support for registration tokens in the UI until GitLab 18.0.
+  Refer to the manual on [using registration tokens after GitLab 17.0](https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#using-registration-tokens-after-gitlab-170).
+  GitLab administrators should migrate to the [new runner registration workflow](https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#using-registration-tokens-after-gitlab-170)
+  with *runner authentication tokens* until the release of GitLab 18.0.
+
 - `zx` was updated to v8, which introduces several breaking changes.
   See the [v8 changelog](https://github.com/google/zx/releases/tag/8.0.0) for more information.
 
@@ -73,10 +124,20 @@
   services.portunus.ldap.package = pkgs.openldap.override { libxcrypt = pkgs.libxcrypt-legacy; };
   ```
 
+- The default value of `services.kubernetes.kubelet.hostname` is now lowercased.
+  Explicitly set `kubelet.hostname` to `networking.fqdnOrHostName` to get back
+  the old default behavior.
+
+- `keycloak` was updated to version 25, which introduces new hostname related options.
+  See [Upgrading Guide](https://www.keycloak.org/docs/25.0.1/upgrading/#migrating-to-25-0-0) for instructions.
+
 - The `tracy` package no longer works on X11, since it's moved to Wayland
   support, which is the intended default behavior by Tracy maintainers.
   X11 users have to switch to the new package `tracy-x11`.
 
+- The `services.prometheus.exporters.minio` option has been removed, as it's upstream implementation was broken and unmaintained.
+  Minio now has built-in [Prometheus metrics exposure](https://min.io/docs/minio/linux/operations/monitoring/collect-minio-metrics-using-prometheus.html), which can be used instead.
+
 ## Other Notable Changes {#sec-release-24.11-notable-changes}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -88,3 +149,9 @@
 - To facilitate dependency injection, the `imgui` package now builds a static archive using vcpkg' CMake rules.
   The derivation now installs "impl" headers selectively instead of by a wildcard.
   Use `imgui.src` if you just want to access the unpacked sources.
+
+- Support for *runner registration tokens* has been [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872)
+  in `gitlab-runner` 15.6 and is expected to be removed in `gitlab-runner` 18.0. Configuration of existing runners
+  should be changed to using *runner authentication tokens* by configuring
+  {option}`services.gitlab-runner.services.<name>.authenticationTokenConfigFile` instead of the former
+  {option}`services.gitlab-runner.services.<name>.registrationConfigFile` option.
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix
index 9bdbf4e0713de..1220bbfd5ed7c 100644
--- a/nixos/lib/make-disk-image.nix
+++ b/nixos/lib/make-disk-image.nix
@@ -603,10 +603,11 @@ let format' = format; in let
       ${lib.optionalString installBootLoader ''
         # In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
         # Use this option to create a symlink from vda to any arbitrary device you want.
-        ${optionalString (config.boot.loader.grub.enable && config.boot.loader.grub.device != "/dev/vda") ''
-            mkdir -p $(dirname ${config.boot.loader.grub.device})
-            ln -s /dev/vda ${config.boot.loader.grub.device}
-        ''}
+        ${optionalString (config.boot.loader.grub.enable) (lib.concatMapStringsSep " " (device:
+          lib.optionalString (device != "/dev/vda") ''
+            mkdir -p "$(dirname ${device})"
+            ln -s /dev/vda ${device}
+          '') config.boot.loader.grub.devices)}
 
         # Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
 
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index a606ebd767598..53aea5d847129 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -275,7 +275,6 @@ in
                     chattr +C "$DEVICE" 2>/dev/null || true
 
                     dd if=/dev/zero of="$DEVICE" bs=1M count=${toString sw.size}
-                    chmod 0600 ${sw.device}
                     ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
                   fi
                 ''}
@@ -292,9 +291,12 @@ in
 
             unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
             unitConfig.DefaultDependencies = false; # needed to prevent a cycle
-            serviceConfig.Type = "oneshot";
-            serviceConfig.RemainAfterExit = sw.randomEncryption.enable;
-            serviceConfig.ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
+            serviceConfig = {
+              Type = "oneshot";
+              RemainAfterExit = sw.randomEncryption.enable;
+              UMask = "0177";
+              ExecStop = optionalString sw.randomEncryption.enable "${pkgs.cryptsetup}/bin/cryptsetup luksClose ${sw.deviceName}";
+            };
             restartIfChanged = false;
           };
 
diff --git a/nixos/modules/hardware/graphics.nix b/nixos/modules/hardware/graphics.nix
new file mode 100644
index 0000000000000..99c122f75c2a1
--- /dev/null
+++ b/nixos/modules/hardware/graphics.nix
@@ -0,0 +1,126 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.hardware.graphics;
+
+  driversEnv = pkgs.buildEnv {
+    name = "graphics-drivers";
+    paths = [ cfg.package ] ++ cfg.extraPackages;
+  };
+
+  driversEnv32 = pkgs.buildEnv {
+    name = "graphics-drivers-32bit";
+    paths = [ cfg.package32 ] ++ cfg.extraPackages32;
+  };
+in
+{
+  imports = [
+    (lib.mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "opengl" "extraPackages" ])
+    (lib.mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
+    (lib.mkRemovedOptionModule [ "hardware" "opengl" "driSupport"] "The setting can be removed.")
+
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "enable"] [ "hardware" "graphics" "enable" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "driSupport32Bit"] [ "hardware" "graphics" "enable32Bit" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "package"] [ "hardware" "graphics" "package" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "package32"] [ "hardware" "graphics" "package32" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages"] [ "hardware" "graphics" "extraPackages" ])
+    (lib.mkRenamedOptionModule [ "hardware" "opengl" "extraPackages32"] [ "hardware" "graphics" "extraPackages32" ])
+  ];
+
+  options.hardware.graphics = {
+    enable = lib.mkOption {
+      description = ''
+        Whether to enable hardware accelerated graphics drivers.
+
+        This is required to allow most graphical applications and
+        environments to use hardware rendering, video encode/decode
+        acceleration, etc.
+
+        This option should be enabled by default by the corresponding modules,
+        so you do not usually have to set it yourself.
+      '';
+      type = lib.types.bool;
+      default = false;
+    };
+
+    enable32Bit = lib.mkOption {
+      description = ''
+        On 64-bit systems, whether to also install 32-bit drivers for
+        32-bit applications (such as Wine).
+      '';
+      type = lib.types.bool;
+      default = false;
+    };
+
+    package = lib.mkOption {
+      description = ''
+        The package that provides the default driver set.
+      '';
+      type = lib.types.package;
+      internal = true;
+    };
+
+    package32 = lib.mkOption {
+      description = ''
+        The package that provides the 32-bit driver set. Used when {option}`enable32Bit` is enabled.
+        set.
+      '';
+      type = lib.types.package;
+      internal = true;
+    };
+
+    extraPackages = lib.mkOption {
+      description = ''
+        Additional packages to add to the default graphics driver lookup path.
+        This can be used to add OpenCL drivers, VA-API/VDPAU drivers, etc.
+
+        ::: {.note}
+        intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
+        :::
+      '';
+      type = lib.types.listOf lib.types.package;
+      default = [];
+      example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
+    };
+
+    extraPackages32 = lib.mkOption {
+      description = ''
+        Additional packages to add to 32-bit graphics driver lookup path on 64-bit systems.
+        Used when {option}`enable32Bit` is set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers, etc.
+
+        ::: {.note}
+        intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
+        :::
+      '';
+      type = lib.types.listOf lib.types.package;
+      default = [];
+      example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.enable32Bit -> pkgs.stdenv.isx86_64;
+        message = "`hardware.graphics.enable32Bit` only makes sense on a 64-bit system.";
+      }
+      {
+        assertion = cfg.enable32Bit -> (config.boot.kernelPackages.kernel.features.ia32Emulation or false);
+        message = "`hardware.graphics.enable32Bit` requires a kernel that supports 32-bit emulation";
+      }
+    ];
+
+    systemd.tmpfiles.settings.graphics-driver = {
+      "/run/opengl-driver"."L+".argument = toString driversEnv;
+      "/run/opengl-driver-32" =
+        if pkgs.stdenv.isi686 then
+          { "L+".argument = "opengl-driver"; }
+        else if cfg.enable32Bit then
+          { "L+".argument = toString driversEnv32; }
+        else
+          { "r" = {}; };
+    };
+
+    hardware.graphics.package = lib.mkDefault pkgs.mesa.drivers;
+    hardware.graphics.package32 = lib.mkDefault pkgs.pkgsi686Linux.mesa.drivers;
+  };
+}
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
deleted file mode 100644
index 3ca9deec8961d..0000000000000
--- a/nixos/modules/hardware/opengl.nix
+++ /dev/null
@@ -1,158 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-
-  cfg = config.hardware.opengl;
-
-  kernelPackages = config.boot.kernelPackages;
-
-  videoDrivers = config.services.xserver.videoDrivers;
-
-  package = pkgs.buildEnv {
-    name = "opengl-drivers";
-    paths = [ cfg.package ] ++ cfg.extraPackages;
-  };
-
-  package32 = pkgs.buildEnv {
-    name = "opengl-drivers-32bit";
-    paths = [ cfg.package32 ] ++ cfg.extraPackages32;
-  };
-
-in
-
-{
-
-  imports = [
-    (lib.mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "opengl" "extraPackages" ])
-    (lib.mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] "S3TC support is now always enabled in Mesa.")
-  ];
-
-  options = {
-
-    hardware.opengl = {
-      enable = lib.mkOption {
-        description = ''
-          Whether to enable OpenGL drivers. This is needed to enable
-          OpenGL support in X11 systems, as well as for Wayland compositors
-          like sway and Weston. It is enabled by default
-          by the corresponding modules, so you do not usually have to
-          set it yourself, only if there is no module for your wayland
-          compositor of choice. See services.xserver.enable and
-          programs.sway.enable.
-        '';
-        type = lib.types.bool;
-        default = false;
-      };
-
-      driSupport = lib.mkOption {
-        type = lib.types.bool;
-        default = true;
-        description = ''
-          Whether to enable accelerated OpenGL rendering through the
-          Direct Rendering Interface (DRI).
-        '';
-      };
-
-      driSupport32Bit = lib.mkOption {
-        type = lib.types.bool;
-        default = false;
-        description = ''
-          On 64-bit systems, whether to support Direct Rendering for
-          32-bit applications (such as Wine).  This is currently only
-          supported for the `nvidia` as well as
-          `Mesa`.
-        '';
-      };
-
-      package = lib.mkOption {
-        type = lib.types.package;
-        internal = true;
-        description = ''
-          The package that provides the OpenGL implementation.
-        '';
-      };
-
-      package32 = lib.mkOption {
-        type = lib.types.package;
-        internal = true;
-        description = ''
-          The package that provides the 32-bit OpenGL implementation on
-          64-bit systems. Used when {option}`driSupport32Bit` is
-          set.
-        '';
-      };
-
-      extraPackages = lib.mkOption {
-        type = lib.types.listOf lib.types.package;
-        default = [];
-        example = lib.literalExpression "with pkgs; [ intel-media-driver intel-ocl intel-vaapi-driver ]";
-        description = ''
-          Additional packages to add to OpenGL drivers.
-          This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
-
-          ::: {.note}
-          intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
-          :::
-        '';
-      };
-
-      extraPackages32 =lib. mkOption {
-        type = lib.types.listOf lib.types.package;
-        default = [];
-        example = lib.literalExpression "with pkgs.pkgsi686Linux; [ intel-media-driver intel-vaapi-driver ]";
-        description = ''
-          Additional packages to add to 32-bit OpenGL drivers on 64-bit systems.
-          Used when {option}`driSupport32Bit` is set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
-
-          ::: {.note}
-          intel-media-driver supports hardware Broadwell (2014) or newer. Older hardware should use the mostly unmaintained intel-vaapi-driver driver.
-          :::
-        '';
-      };
-
-      setLdLibraryPath = lib.mkOption {
-        type = lib.types.bool;
-        internal = true;
-        default = false;
-        description = ''
-          Whether the `LD_LIBRARY_PATH` environment variable
-          should be set to the locations of driver libraries. Drivers which
-          rely on overriding libraries should set this to true. Drivers which
-          support `libglvnd` and other dispatch libraries
-          instead of overriding libraries should not set this.
-        '';
-      };
-    };
-
-  };
-
-  config = lib.mkIf cfg.enable {
-    assertions = [
-      { assertion = cfg.driSupport32Bit -> pkgs.stdenv.isx86_64;
-        message = "Option driSupport32Bit only makes sense on a 64-bit system.";
-      }
-      { assertion = cfg.driSupport32Bit -> (config.boot.kernelPackages.kernel.features.ia32Emulation or false);
-        message = "Option driSupport32Bit requires a kernel that supports 32bit emulation";
-      }
-    ];
-
-    systemd.tmpfiles.settings.opengl = {
-      "/run/opengl-driver"."L+".argument = toString package;
-      "/run/opengl-driver-32" =
-        if pkgs.stdenv.isi686 then
-          { "L+".argument = "opengl-driver"; }
-        else if cfg.driSupport32Bit then
-          { "L+".argument = toString package32; }
-        else
-          { "r" = {}; };
-    };
-
-    environment.sessionVariables.LD_LIBRARY_PATH = lib.mkIf cfg.setLdLibraryPath
-      ([ "/run/opengl-driver/lib" ] ++ lib.optional cfg.driSupport32Bit "/run/opengl-driver-32/lib");
-
-    hardware.opengl.package = lib.mkDefault pkgs.mesa.drivers;
-    hardware.opengl.package32 = lib.mkDefault pkgs.pkgsi686Linux.mesa.drivers;
-
-    boot.extraModulePackages = lib.optional (lib.elem "virtualbox" videoDrivers) kernelPackages.virtualboxGuestAdditions;
-  };
-}
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
deleted file mode 100644
index 2a86280eec8cb..0000000000000
--- a/nixos/modules/hardware/video/amdgpu-pro.nix
+++ /dev/null
@@ -1,69 +0,0 @@
-# This module provides the proprietary AMDGPU-PRO drivers.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  drivers = config.services.xserver.videoDrivers;
-
-  enabled = elem "amdgpu-pro" drivers;
-
-  package = config.boot.kernelPackages.amdgpu-pro;
-  package32 = pkgs.pkgsi686Linux.linuxPackages.amdgpu-pro.override { kernel = null; };
-
-  opengl = config.hardware.opengl;
-
-in
-
-{
-
-  config = mkIf enabled {
-    services.xserver.drivers = singleton
-      { name = "amdgpu"; modules = [ package ]; display = true; };
-
-    hardware.opengl.package = package;
-    hardware.opengl.package32 = package32;
-    hardware.opengl.setLdLibraryPath = true;
-
-    boot.extraModulePackages = [ package.kmod ];
-
-    boot.kernelPackages = pkgs.linuxKernel.packagesFor
-      (pkgs.linuxKernel.kernels.linux_5_10.override {
-        structuredExtraConfig = {
-          DEVICE_PRIVATE = kernel.yes;
-          KALLSYMS_ALL = kernel.yes;
-        };
-      });
-
-    hardware.firmware = [ package.fw ];
-
-    systemd.tmpfiles.settings.amdgpu-pro = {
-      "/run/amdgpu"."L+".argument = "${package}/opt/amdgpu";
-      "/run/amdgpu-pro"."L+".argument = "${package}/opt/amdgpu-pro";
-    };
-
-    system.requiredKernelConfig = with config.lib.kernelConfig; [
-      (isYes "DEVICE_PRIVATE")
-      (isYes "KALLSYMS_ALL")
-    ];
-
-    boot.initrd.extraUdevRulesCommands = mkIf (!config.boot.initrd.systemd.enable) ''
-      cp -v ${package}/etc/udev/rules.d/*.rules $out/
-    '';
-    boot.initrd.services.udev.packages = [ package ];
-
-    environment.systemPackages =
-      [ package.vulkan ] ++
-      # this isn't really DRI, but we'll reuse this option for now
-      optional config.hardware.opengl.driSupport32Bit package32.vulkan;
-
-    environment.etc = {
-      "modprobe.d/blacklist-radeon.conf".source = package + "/etc/modprobe.d/blacklist-radeon.conf";
-      amd.source = package + "/etc/amd";
-    };
-
-  };
-
-}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index bb32f55313afc..e38050e637b1c 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -293,7 +293,7 @@ in
             KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
             KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 1'"
           '';
-          hardware.opengl = {
+          hardware.graphics = {
             extraPackages = [ nvidia_x11.out ];
             extraPackages32 = [ nvidia_x11.lib32 ];
           };
@@ -463,7 +463,7 @@ in
             "egl/egl_external_platform.d".source = "/run/opengl-driver/share/egl/egl_external_platform.d/";
           };
 
-          hardware.opengl = {
+          hardware.graphics = {
             extraPackages = [ pkgs.nvidia-vaapi-driver ];
             extraPackages32 = [ pkgs.pkgsi686Linux.nvidia-vaapi-driver ];
           };
diff --git a/nixos/modules/hardware/video/virtualbox.nix b/nixos/modules/hardware/video/virtualbox.nix
new file mode 100644
index 0000000000000..31ed92b7d148e
--- /dev/null
+++ b/nixos/modules/hardware/video/virtualbox.nix
@@ -0,0 +1,7 @@
+{ lib, config, ... }:
+let
+  inherit (config.boot) kernelPackages;
+  inherit (config.services.xserver) videoDrivers;
+in {
+  boot.extraModulePackages = lib.mkIf (lib.elem "virtualbox" videoDrivers) [ kernelPackages.virtualboxGuestAdditions ];
+}
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index e4241e9654036..54d3a107d6276 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/azvn85cras6xv4z5j85fiy406f24r1q0-nix-2.18.1";
-  i686-linux = "/nix/store/9bnwy7f9h0kzdzmcnjjsjg0aak5waj40-nix-2.18.1";
-  aarch64-linux = "/nix/store/hh65xwqm9s040s3cgn9vzcmrxj0sf5ij-nix-2.18.1";
-  x86_64-darwin = "/nix/store/6zi5fqzn9n17wrk8r41rhdw4j7jqqsi3-nix-2.18.1";
-  aarch64-darwin = "/nix/store/0pbq6wzr2f1jgpn5212knyxpwmkjgjah-nix-2.18.1";
+  x86_64-linux = "/nix/store/1w4b47zhp33md29wjhgg549pc281vv02-nix-2.18.4";
+  i686-linux = "/nix/store/hz02kn0ffn3wdi2xs7lndpr88v4v4fp2-nix-2.18.4";
+  aarch64-linux = "/nix/store/90zwqa9z2fgldc7ki1p5gfvglchjh9r6-nix-2.18.4";
+  x86_64-darwin = "/nix/store/bd1ix5mj9lj2yh7bqnmdjc24zlg5jivk-nix-2.18.4";
+  aarch64-darwin = "/nix/store/5hvsmklhqiay5i4q5vdkg60p8qpc69rz-nix-2.18.4";
 }
diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix
index 706e2ac2c2836..166693930b5c7 100644
--- a/nixos/modules/misc/mandoc.nix
+++ b/nixos/modules/misc/mandoc.nix
@@ -96,12 +96,17 @@ in
                 {option}`documentation.man.mandoc.manPath` to an empty list (`[]`).
               '';
             };
-            output.fragment = lib.mkEnableOption ''
-              Omit the <!DOCTYPE> declaration and the <html>, <head>, and <body>
-              elements and only emit the subtree below the <body> element in HTML
-              output of {manpage}`mandoc(1)`. The style argument will be ignored.
-              This is useful when embedding manual content within existing documents.
-            '';
+            output.fragment = lib.mkOption {
+              type = lib.types.bool;
+              default = false;
+              example = true;
+              description = ''
+                Whether to omit the <!DOCTYPE> declaration and the <html>, <head>, and <body>
+                elements and only emit the subtree below the <body> element in HTML
+                output of {manpage}`mandoc(1)`. The style argument will be ignored.
+                This is useful when embedding manual content within existing documents.
+              '';
+            };
             output.includes = lib.mkOption {
               type = with lib.types; nullOr str;
               default = null;
@@ -160,9 +165,9 @@ in
               '';
             };
             output.toc = lib.mkEnableOption ''
-              In HTML output of {manpage}`mandoc(1)`, If an input file contains
-              at least two non-standard sections, print a table of contents near
-              the beginning of the output.
+              printing a table of contents near the beginning of the HTML output
+              of {manpage}`mandoc(1)` if an input file contains at least two
+              non-standard sections
             '';
             output.width = lib.mkOption {
               type = with lib.types; nullOr int;
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index b20e98a9f229b..3cfdd943b35b0 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -67,6 +67,7 @@
   ./hardware/gkraken.nix
   ./hardware/glasgow.nix
   ./hardware/gpgsmartcards.nix
+  ./hardware/graphics.nix
   ./hardware/hackrf.nix
   ./hardware/i2c.nix
   ./hardware/infiniband.nix
@@ -84,7 +85,6 @@
   ./hardware/new-lg4ff.nix
   ./hardware/nitrokey.nix
   ./hardware/onlykey/default.nix
-  ./hardware/opengl.nix
   ./hardware/openrazer.nix
   ./hardware/opentabletdriver.nix
   ./hardware/pcmcia.nix
@@ -103,7 +103,6 @@
   ./hardware/uni-sync.nix
   ./hardware/usb-modeswitch.nix
   ./hardware/usb-storage.nix
-  ./hardware/video/amdgpu-pro.nix
   ./hardware/video/bumblebee.nix
   ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/displaylink.nix
@@ -111,6 +110,7 @@
   ./hardware/video/nvidia.nix
   ./hardware/video/switcheroo-control.nix
   ./hardware/video/uvcvideo/default.nix
+  ./hardware/video/virtualbox.nix
   ./hardware/video/webcam/facetimehd.nix
   ./hardware/video/webcam/ipu6.nix
   ./hardware/wooting.nix
@@ -243,6 +243,7 @@
   ./programs/nh.nix
   ./programs/nix-index.nix
   ./programs/nix-ld.nix
+  ./programs/nix-required-mounts.nix
   ./programs/nm-applet.nix
   ./programs/nncp.nix
   ./programs/noisetorch.nix
@@ -477,6 +478,7 @@
   ./services/desktops/bamf.nix
   ./services/desktops/blueman.nix
   ./services/desktops/cpupower-gui.nix
+  ./services/desktops/deepin/deepin-anything.nix
   ./services/desktops/deepin/dde-api.nix
   ./services/desktops/deepin/app-services.nix
   ./services/desktops/deepin/dde-daemon.nix
@@ -549,6 +551,7 @@
   ./services/games/xonotic.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
+  ./services/hardware/amdgpu.nix
   ./services/hardware/amdvlk.nix
   ./services/hardware/argonone.nix
   ./services/hardware/asusd.nix
@@ -879,6 +882,7 @@
   ./services/monitoring/osquery.nix
   ./services/monitoring/parsedmarc.nix
   ./services/monitoring/prometheus/alertmanager-irc-relay.nix
+  ./services/monitoring/prometheus/alertmanager-webhook-logger.nix
   ./services/monitoring/prometheus/alertmanager.nix
   ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/exporters.nix
@@ -1222,6 +1226,7 @@
   ./services/networking/vsftpd.nix
   ./services/networking/wasabibackend.nix
   ./services/networking/websockify.nix
+  ./services/networking/wg-access-server.nix
   ./services/networking/wg-netmanager.nix
   ./services/networking/webhook.nix
   ./services/networking/wg-quick.nix
@@ -1316,6 +1321,7 @@
   ./services/system/zram-generator.nix
   ./services/torrent/deluge.nix
   ./services/torrent/flexget.nix
+  ./services/torrent/flood.nix
   ./services/torrent/magnetico.nix
   ./services/torrent/opentracker.nix
   ./services/torrent/peerflix.nix
diff --git a/nixos/modules/programs/dublin-traceroute.nix b/nixos/modules/programs/dublin-traceroute.nix
index de9446ad7377c..c764352843e78 100644
--- a/nixos/modules/programs/dublin-traceroute.nix
+++ b/nixos/modules/programs/dublin-traceroute.nix
@@ -8,9 +8,7 @@ in {
 
   options = {
     programs.dublin-traceroute = {
-      enable = lib.mkEnableOption ''
-      dublin-traceroute, add it to the global environment and configure a setcap wrapper for it.
-      '';
+      enable = lib.mkEnableOption "dublin-traceroute (including setcap wrapper)";
 
       package = lib.mkPackageOption pkgs "dublin-traceroute" { };
     };
diff --git a/nixos/modules/programs/joycond-cemuhook.nix b/nixos/modules/programs/joycond-cemuhook.nix
index 6cdd198a7df23..c01a00478113a 100644
--- a/nixos/modules/programs/joycond-cemuhook.nix
+++ b/nixos/modules/programs/joycond-cemuhook.nix
@@ -1,7 +1,7 @@
 { lib, pkgs, config, ... }:
 {
   options.programs.joycond-cemuhook = {
-    enable = lib.mkEnableOption "joycond-cemuhook, a program to enable support for cemuhook's UDP protocol for joycond devices.";
+    enable = lib.mkEnableOption "joycond-cemuhook, a program to enable support for cemuhook's UDP protocol for joycond devices";
   };
 
   config = lib.mkIf config.programs.joycond-cemuhook.enable {
diff --git a/nixos/modules/programs/miriway.nix b/nixos/modules/programs/miriway.nix
index 00c1356ab0836..418bb3dc4f2dd 100644
--- a/nixos/modules/programs/miriway.nix
+++ b/nixos/modules/programs/miriway.nix
@@ -65,7 +65,7 @@ in {
       };
     };
 
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
     fonts.enableDefaultPackages = lib.mkDefault true;
     programs.dconf.enable = lib.mkDefault true;
     programs.xwayland.enable = lib.mkDefault true;
diff --git a/nixos/modules/programs/mouse-actions.nix b/nixos/modules/programs/mouse-actions.nix
index fdf39d56d3838..73dc783e3100b 100644
--- a/nixos/modules/programs/mouse-actions.nix
+++ b/nixos/modules/programs/mouse-actions.nix
@@ -6,7 +6,7 @@ in
   {
     options.programs.mouse-actions = {
       enable = lib.mkEnableOption ''
-        mouse-actions udev rules. This is a prerequisite for using mouse-actions without being root.
+        mouse-actions udev rules. This is a prerequisite for using mouse-actions without being root
       '';
     };
     config = lib.mkIf cfg.enable {
diff --git a/nixos/modules/programs/nix-required-mounts.nix b/nixos/modules/programs/nix-required-mounts.nix
new file mode 100644
index 0000000000000..5d25958a7698d
--- /dev/null
+++ b/nixos/modules/programs/nix-required-mounts.nix
@@ -0,0 +1,118 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.programs.nix-required-mounts;
+  package = pkgs.nix-required-mounts;
+
+  Mount =
+    with lib;
+    types.submodule {
+      options.host = mkOption {
+        type = types.str;
+        description = "Host path to mount";
+      };
+      options.guest = mkOption {
+        type = types.str;
+        description = "Location in the sandbox to mount the host path at";
+      };
+    };
+  Pattern =
+    with lib.types;
+    types.submodule (
+      { config, name, ... }:
+      {
+        options.onFeatures = lib.mkOption {
+          type = listOf types.str;
+          description = "Which requiredSystemFeatures should trigger relaxation of the sandbox";
+          default = [ name ];
+        };
+        options.paths = lib.mkOption {
+          type = listOf (oneOf [
+            path
+            Mount
+          ]);
+          description = "A list of glob patterns, indicating which paths to expose to the sandbox";
+        };
+        options.unsafeFollowSymlinks = lib.mkEnableOption ''
+          Instructs the hook to mount the symlink targets as well, when any of
+          the `paths` contain symlinks. This may not work correctly with glob
+          patterns.
+        '';
+      }
+    );
+
+  driverPaths = [
+    pkgs.addOpenGLRunpath.driverLink
+
+    # mesa:
+    config.hardware.opengl.package
+
+    # nvidia_x11, etc:
+  ] ++ config.hardware.opengl.extraPackages; # nvidia_x11
+
+  defaults = {
+    nvidia-gpu.onFeatures = package.allowedPatterns.nvidia-gpu.onFeatures;
+    nvidia-gpu.paths = package.allowedPatterns.nvidia-gpu.paths ++ driverPaths;
+    nvidia-gpu.unsafeFollowSymlinks = false;
+  };
+in
+{
+  meta.maintainers = with lib.maintainers; [ SomeoneSerge ];
+  options.programs.nix-required-mounts = {
+    enable = lib.mkEnableOption "Expose extra paths to the sandbox depending on derivations' requiredSystemFeatures";
+    presets.nvidia-gpu.enable = lib.mkEnableOption ''
+      Declare the support for derivations that require an Nvidia GPU to be
+      available, e.g. derivations with `requiredSystemFeatures = [ "cuda" ]`.
+      This mounts the corresponding userspace drivers and device nodes in the
+      sandbox, but only for derivations that request these special features.
+
+      You may extend or override the exposed paths via the
+      `programs.nix-required-mounts.allowedPatterns.nvidia-gpu.paths` option.
+    '';
+    allowedPatterns =
+      with lib.types;
+      lib.mkOption rec {
+        type = attrsOf Pattern;
+        description = "The hook config, describing which paths to mount for which system features";
+        default = { };
+        defaultText = lib.literalExpression ''
+          {
+            opengl.paths = config.hardware.opengl.extraPackages ++ [
+              config.hardware.opengl.package
+              pkgs.addOpenGLRunpath.driverLink
+              "/dev/dri"
+            ];
+          }
+        '';
+        example.require-ipfs.paths = [ "/ipfs" ];
+        example.require-ipfs.onFeatures = [ "ifps" ];
+      };
+    extraWrapperArgs = lib.mkOption {
+      type = with lib.types; listOf str;
+      default = [ ];
+      description = "List of extra arguments (such as `--add-flags -v`) to pass to the hook's wrapper";
+    };
+    package = lib.mkOption {
+      type = lib.types.package;
+      default = package.override { inherit (cfg) allowedPatterns extraWrapperArgs; };
+      description = "The final package with the final config applied";
+      internal = true;
+    };
+  };
+  config = lib.mkIf cfg.enable (
+    lib.mkMerge [
+      { nix.settings.pre-build-hook = lib.getExe cfg.package; }
+      (lib.mkIf cfg.presets.nvidia-gpu.enable {
+        nix.settings.system-features = cfg.allowedPatterns.nvidia-gpu.onFeatures;
+        programs.nix-required-mounts.allowedPatterns = {
+          inherit (defaults) nvidia-gpu;
+        };
+      })
+    ]
+  );
+}
diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix
index 5138588dbd3e5..2ee464dc22d37 100644
--- a/nixos/modules/programs/steam.nix
+++ b/nixos/modules/programs/steam.nix
@@ -50,7 +50,7 @@ in {
         }) // (prev.extraEnv or {});
         extraLibraries = pkgs: let
           prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
-          additionalLibs = with config.hardware.opengl;
+          additionalLibs = with config.hardware.graphics;
             if pkgs.stdenv.hostPlatform.is64bit
             then [ package ] ++ extraPackages
             else [ package32 ] ++ extraPackages32;
@@ -176,10 +176,9 @@ in {
   };
 
   config = lib.mkIf cfg.enable {
-    hardware.opengl = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
+    hardware.graphics = { # this fixes the "glXChooseVisual failed" bug, context: https://github.com/NixOS/nixpkgs/issues/47932
       enable = true;
-      driSupport = true;
-      driSupport32Bit = true;
+      enable32Bit = true;
     };
 
     security.wrappers = lib.mkIf (cfg.gamescopeSession.enable && gamescopeCfg.capSysNice) {
diff --git a/nixos/modules/programs/turbovnc.nix b/nixos/modules/programs/turbovnc.nix
index c28b7f7d79910..c7ab18a2e2886 100644
--- a/nixos/modules/programs/turbovnc.nix
+++ b/nixos/modules/programs/turbovnc.nix
@@ -17,7 +17,7 @@ in
           Whether to set up NixOS such that TurboVNC's built-in software OpenGL
           implementation works.
 
-          This will enable {option}`hardware.opengl.enable` so that OpenGL
+          This will enable {option}`hardware.graphics.enable` so that OpenGL
           programs can find Mesa's llvmpipe drivers.
 
           Setting this option to `false` does not mean that software
@@ -46,7 +46,7 @@ in
     # can find the llvmpipe `swrast.so` software rendering DRI lib via `libglvnd`.
     # This comment exists to explain why `hardware.` is involved,
     # even though 100% software rendering is used.
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
 
   };
 }
diff --git a/nixos/modules/programs/wayland/hyprland.nix b/nixos/modules/programs/wayland/hyprland.nix
index 5a21bd153b632..6e69c1730e57b 100644
--- a/nixos/modules/programs/wayland/hyprland.nix
+++ b/nixos/modules/programs/wayland/hyprland.nix
@@ -38,12 +38,13 @@ in
     xwayland.enable = lib.mkEnableOption "XWayland" // { default = true; };
 
     systemd.setPath.enable = lib.mkEnableOption null // {
-      default = true;
+      default = lib.versionOlder cfg.package.version "0.41.2";
+      defaultText = lib.literalExpression ''lib.versionOlder cfg.package.version "0.41.2"'';
       example = false;
       description = ''
         Set environment path of systemd to include the current system's bin directory.
         This is needed in Hyprland setups, where opening links in applications do not work.
-        Enabled by default.
+        Enabled by default for Hyprland versions older than 0.41.2.
       '';
     };
   };
@@ -63,7 +64,7 @@ in
 
       systemd = lib.mkIf cfg.systemd.setPath.enable {
         user.extraConfig = ''
-          DefaultEnvironment="PATH=$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/%u/bin:/run/wrappers/bin"
+          DefaultEnvironment="PATH=/run/wrappers/bin:/etc/profiles/per-user/%u/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:$PATH"
         '';
       };
     }
diff --git a/nixos/modules/programs/wayland/wayland-session.nix b/nixos/modules/programs/wayland/wayland-session.nix
index 877b106684700..09fb2a5f14b2e 100644
--- a/nixos/modules/programs/wayland/wayland-session.nix
+++ b/nixos/modules/programs/wayland/wayland-session.nix
@@ -11,7 +11,7 @@
     pam.services.swaylock = {};
   };
 
-  hardware.opengl.enable = lib.mkDefault true;
+  hardware.graphics.enable = lib.mkDefault true;
   fonts.enableDefaultPackages = lib.mkDefault true;
 
   programs = {
diff --git a/nixos/modules/programs/xonsh.nix b/nixos/modules/programs/xonsh.nix
index eed5152ba69a5..6bf18d4ebd89c 100644
--- a/nixos/modules/programs/xonsh.nix
+++ b/nixos/modules/programs/xonsh.nix
@@ -23,7 +23,7 @@ in
       };
 
       package = lib.mkPackageOption pkgs "xonsh" {
-        example = "xonsh.override { extraPackages = ps: [ ps.requests ]; }";
+        example = "xonsh.wrapper.override { extraPackages = ps: [ ps.requests ]; }";
       };
 
       config = lib.mkOption {
@@ -61,17 +61,14 @@ in
               aliases['ls'] = _ls_alias
           del _ls_alias
 
-
       ${cfg.config}
     '';
 
     environment.systemPackages = [ cfg.package ];
 
-    environment.shells =
-      [ "/run/current-system/sw/bin/xonsh"
-        "${cfg.package}/bin/xonsh"
-      ];
-
+    environment.shells = [
+      "/run/current-system/sw/bin/xonsh"
+      "${lib.getExe cfg.package}"
+    ];
   };
-
 }
diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
index af5d91b35f2eb..8aae6eb3f29b0 100644
--- a/nixos/modules/security/ca.nix
+++ b/nixos/modules/security/ca.nix
@@ -26,13 +26,13 @@ in
 
     security.pki.useCompatibleBundle = mkEnableOption ''usage of a compatibility bundle.
 
-      Such a bundle consist exclusively of `BEGIN CERTIFICATE` and no `BEGIN TRUSTED CERTIFICATE`,
-      which is a OpenSSL specific PEM format.
+      Such a bundle consists exclusively of `BEGIN CERTIFICATE` and no `BEGIN TRUSTED CERTIFICATE`,
+      which is an OpenSSL specific PEM format.
 
       It is known to be incompatible with certain software stacks.
 
       Nevertheless, enabling this will strip all additional trust rules provided by the
-      certificates themselves, this can have security consequences depending on your usecases.
+      certificates themselves. This can have security consequences depending on your usecases
     '';
 
     security.pki.certificateFiles = mkOption {
diff --git a/nixos/modules/security/ipa.nix b/nixos/modules/security/ipa.nix
index 543b1abfa672c..e746ca75724a1 100644
--- a/nixos/modules/security/ipa.nix
+++ b/nixos/modules/security/ipa.nix
@@ -85,6 +85,18 @@ in {
         description = "Whether to cache credentials.";
       };
 
+      ipaHostname = mkOption {
+        type = types.str;
+        example = "myworkstation.example.com";
+        default = if config.networking.domain != null then config.networking.fqdn
+                  else "${config.networking.hostName}.${cfg.domain}";
+        defaultText = literalExpression ''
+          if config.networking.domain != null then config.networking.fqdn
+          else "''${networking.hostName}.''${security.ipa.domain}"
+        '';
+        description = "Fully-qualified hostname used to identify this host in the IPA domain.";
+      };
+
       ifpAllowedUids = mkOption {
         type = types.listOf types.str;
         default = ["root"];
@@ -218,7 +230,7 @@ in {
 
       ipa_domain = ${cfg.domain}
       ipa_server = _srv_, ${cfg.server}
-      ipa_hostname = ${config.networking.hostName}.${cfg.domain}
+      ipa_hostname = ${cfg.ipaHostname}
 
       cache_credentials = ${pyBool cfg.cacheCredentials}
       krb5_store_password_if_offline = ${pyBool cfg.offlinePasswords}
@@ -232,7 +244,6 @@ in {
       ldap_user_extra_attrs = mail:mail, sn:sn, givenname:givenname, telephoneNumber:telephoneNumber, lock:nsaccountlock
 
       [sssd]
-      debug_level = 65510
       services = nss, sudo, pam, ssh, ifp
       domains = ${cfg.domain}
 
@@ -244,7 +255,6 @@ in {
       pam_verbosity = 3
 
       [sudo]
-      debug_level = 65510
 
       [autofs]
 
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 5d3bed2fb02c8..f77e819d0c83a 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -1055,7 +1055,7 @@ in
           the dp9ik pam module provided by tlsclient.
 
           If set, users can be authenticated against the 9front
-          authentication server given in {option}`security.pam.dp9ik.authserver`.
+          authentication server given in {option}`security.pam.dp9ik.authserver`
         '';
       control = mkOption {
         default = "sufficient";
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
index f7ee4f0068dde..76f623096fb76 100644
--- a/nixos/modules/security/polkit.nix
+++ b/nixos/modules/security/polkit.nix
@@ -14,6 +14,8 @@ in
 
     security.polkit.enable = mkEnableOption "polkit";
 
+    security.polkit.package = mkPackageOption pkgs "polkit" { };
+
     security.polkit.debug = mkEnableOption "debug logs from polkit. This is required in order to see log messages from rule definitions";
 
     security.polkit.extraConfig = mkOption {
@@ -57,13 +59,13 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.polkit.bin pkgs.polkit.out ];
+    environment.systemPackages = [ cfg.package.bin cfg.package.out ];
 
-    systemd.packages = [ pkgs.polkit.out ];
+    systemd.packages = [ cfg.package.out ];
 
     systemd.services.polkit.serviceConfig.ExecStart = [
       ""
-      "${pkgs.polkit.out}/lib/polkit-1/polkitd ${optionalString (!cfg.debug) "--no-debug"}"
+      "${cfg.package.out}/lib/polkit-1/polkitd ${optionalString (!cfg.debug) "--no-debug"}"
     ];
 
     systemd.services.polkit.restartTriggers = [ config.system.path ];
@@ -82,7 +84,7 @@ in
         ${cfg.extraConfig}
       ''; #TODO: validation on compilation (at least against typos)
 
-    services.dbus.packages = [ pkgs.polkit.out ];
+    services.dbus.packages = [ cfg.package.out ];
 
     security.pam.services.polkit-1 = {};
 
@@ -91,13 +93,13 @@ in
         { setuid = true;
           owner = "root";
           group = "root";
-          source = "${pkgs.polkit.bin}/bin/pkexec";
+          source = "${cfg.package.bin}/bin/pkexec";
         };
       polkit-agent-helper-1 =
         { setuid = true;
           owner = "root";
           group = "root";
-          source = "${pkgs.polkit.out}/lib/polkit-1/polkit-agent-helper-1";
+          source = "${cfg.package.out}/lib/polkit-1/polkit-agent-helper-1";
         };
     };
 
diff --git a/nixos/modules/security/sudo-rs.nix b/nixos/modules/security/sudo-rs.nix
index 6ccf42ed7f087..e63a64d4691c0 100644
--- a/nixos/modules/security/sudo-rs.nix
+++ b/nixos/modules/security/sudo-rs.nix
@@ -41,7 +41,7 @@ in
 
     enable = mkEnableOption ''
       a memory-safe implementation of the {command}`sudo` command,
-      which allows non-root users to execute commands as root.
+      which allows non-root users to execute commands as root
     '';
 
     package = mkPackageOption pkgs "sudo-rs" { };
diff --git a/nixos/modules/services/admin/docuum.nix b/nixos/modules/services/admin/docuum.nix
index 6f6cd4e027337..51a21740b276a 100644
--- a/nixos/modules/services/admin/docuum.nix
+++ b/nixos/modules/services/admin/docuum.nix
@@ -2,7 +2,7 @@
 
 let
   cfg = config.services.docuum;
-  inherit (lib) mkIf mkEnableOption mkOption getExe types;
+  inherit (lib) mkIf mkEnableOption mkOption getExe types optionals concatMap;
 in
 {
   options.services.docuum = {
@@ -14,6 +14,27 @@ in
       default = "10 GB";
       example = "50%";
     };
+
+    minAge = mkOption {
+      description = "Sets the minimum age of images to be considered for deletion.";
+      type = types.nullOr types.str;
+      default = null;
+      example = "1d";
+    };
+
+    keep = mkOption {
+      description = "Prevents deletion of images for which repository:tag matches the specified regex.";
+      type = types.listOf types.str;
+      default = [];
+      example = [ "^my-image" ];
+    };
+
+    deletionChunkSize = mkOption {
+      description = "Removes specified quantity of images at a time.";
+      type = types.int;
+      default = 1;
+      example = 10;
+    };
   };
 
   config = mkIf cfg.enable {
@@ -35,10 +56,13 @@ in
         DynamicUser = true;
         StateDirectory = "docuum";
         SupplementaryGroups = [ "docker" ];
-        ExecStart = utils.escapeSystemdExecArgs [
+        ExecStart = utils.escapeSystemdExecArgs ([
           (getExe pkgs.docuum)
           "--threshold" cfg.threshold
-        ];
+          "--deletion-chunk-size" cfg.deletionChunkSize
+        ] ++ (concatMap (keep: [ "--keep" keep ]) cfg.keep)
+          ++ (optionals (cfg.minAge != null) [ "--min-age" cfg.minAge ])
+        );
       };
     };
   };
diff --git a/nixos/modules/services/admin/meshcentral.nix b/nixos/modules/services/admin/meshcentral.nix
index 25779e01123ec..6e0801e1c0894 100644
--- a/nixos/modules/services/admin/meshcentral.nix
+++ b/nixos/modules/services/admin/meshcentral.nix
@@ -42,5 +42,5 @@ in with lib; {
       };
     };
   };
-  meta.maintainers = [ maintainers.lheckemann ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/services/cluster/kubernetes/default.nix b/nixos/modules/services/cluster/kubernetes/default.nix
index 89bbedf4d0401..01760ffbc72da 100644
--- a/nixos/modules/services/cluster/kubernetes/default.nix
+++ b/nixos/modules/services/cluster/kubernetes/default.nix
@@ -261,7 +261,7 @@ in {
           name = "service-account";
           CN = "system:service-account-signer";
           action = ''
-            systemctl reload \
+            systemctl restart \
               kube-apiserver.service \
               kube-controller-manager.service
           '';
diff --git a/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixos/modules/services/cluster/kubernetes/kubelet.nix
index c841f4e5f1862..f36edeaf64ceb 100644
--- a/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -356,7 +356,7 @@ in
       boot.kernelModules = ["br_netfilter" "overlay"];
 
       services.kubernetes.kubelet.hostname =
-        mkDefault config.networking.fqdnOrHostName;
+        mkDefault (lib.toLower config.networking.fqdnOrHostName);
 
       services.kubernetes.pki.certs = with top.lib; {
         kubelet = mkCert {
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 62c62c5d2360e..1771ca0b980b9 100644
--- a/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -1,7 +1,43 @@
 { config, lib, pkgs, ... }:
-with builtins;
-with lib;
+
 let
+  inherit (builtins)
+    hashString
+    map
+    substring
+    toJSON
+    toString
+    unsafeDiscardStringContext
+    ;
+
+  inherit (lib)
+    any
+    assertMsg
+    attrValues
+    concatStringsSep
+    escapeShellArg
+    filterAttrs
+    hasPrefix
+    isStorePath
+    literalExpression
+    mapAttrs'
+    mapAttrsToList
+    mkDefault
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    mkRemovedOptionModule
+    mkRenamedOptionModule
+    nameValuePair
+    optional
+    optionalAttrs
+    optionals
+    teams
+    toShellVar
+    types
+    ;
+
   cfg = config.services.gitlab-runner;
   hasDocker = config.virtualisation.docker.enable;
 
@@ -20,17 +56,16 @@ let
   configPath = ''"$HOME"/.gitlab-runner/config.toml'';
   configureScript = pkgs.writeShellApplication {
     name = "gitlab-runner-configure";
-    runtimeInputs = with pkgs; [
+    runtimeInputs = [ cfg.package ] ++ (with pkgs; [
         bash
         gawk
         jq
         moreutils
         remarshal
         util-linux
-        cfg.package
         perl
         python3
-    ];
+    ]);
     text = if (cfg.configFile != null) then ''
       cp ${cfg.configFile} ${configPath}
       # make config file readable by service
@@ -84,15 +119,20 @@ let
         # TODO so here we should mention NEW_SERVICES
         if [ -v 'NEW_SERVICES["${name}"]' ] ; then
           bash -c ${escapeShellArg (concatStringsSep " \\\n " ([
-            "set -a && source ${service.registrationConfigFile} &&"
+            "set -a && source ${
+              if service.registrationConfigFile != null
+              then service.registrationConfigFile
+              else service.authenticationTokenConfigFile} &&"
             "gitlab-runner register"
             "--non-interactive"
             "--name '${name}'"
             "--executor ${service.executor}"
             "--limit ${toString service.limit}"
             "--request-concurrency ${toString service.requestConcurrency}"
+          ]
+            ++ optional (service.authenticationTokenConfigFile == null)
             "--maximum-timeout ${toString service.maximumTimeout}"
-          ] ++ service.registrationFlags
+            ++ service.registrationFlags
             ++ optional (service.buildsDir != null)
             "--builds-dir ${service.buildsDir}"
             ++ optional (service.cloneUrl != null)
@@ -103,11 +143,11 @@ let
             "--pre-build-script ${service.preBuildScript}"
             ++ optional (service.postBuildScript != null)
             "--post-build-script ${service.postBuildScript}"
-            ++ optional (service.tagList != [ ])
+            ++ optional (service.authenticationTokenConfigFile == null && service.tagList != [ ])
             "--tag-list ${concatStringsSep "," service.tagList}"
-            ++ optional service.runUntagged
+            ++ optional (service.authenticationTokenConfigFile == null && service.runUntagged)
             "--run-untagged"
-            ++ optional service.protected
+            ++ optional (service.authenticationTokenConfigFile == null && service.protected)
             "--access-level ref_protected"
             ++ optional service.debugTraceDisabled
             "--debug-trace-disabled"
@@ -214,9 +254,14 @@ in {
           # nix store will be readable in runner, might be insecure
           nix = {
             # File should contain at least these two variables:
-            # `CI_SERVER_URL`
-            # `REGISTRATION_TOKEN`
+            # - `CI_SERVER_URL`
+            # - `REGISTRATION_TOKEN`
+            #
+            # NOTE: Support for runner registration tokens will be removed in GitLab 18.0.
+            # Please migrate to runner authentication tokens soon. For reference, the example
+            # runners below this one are configured with authentication tokens instead.
             registrationConfigFile = "/run/secrets/gitlab-runner-registration";
+
             dockerImage = "alpine";
             dockerVolumes = [
               "/nix/store:/nix/store:ro"
@@ -255,8 +300,9 @@ in {
           docker-images = {
             # File should contain at least these two variables:
             # `CI_SERVER_URL`
-            # `REGISTRATION_TOKEN`
-            registrationConfigFile = "/run/secrets/gitlab-runner-registration";
+            # `CI_SERVER_TOKEN`
+            authenticationTokenConfigFile = "/run/secrets/gitlab-runner-docker-images-token-env";
+
             dockerImage = "docker:stable";
             dockerVolumes = [
               "/var/run/docker.sock:/var/run/docker.sock"
@@ -269,8 +315,9 @@ in {
           shell = {
             # File should contain at least these two variables:
             # `CI_SERVER_URL`
-            # `REGISTRATION_TOKEN`
-            registrationConfigFile = "/run/secrets/gitlab-runner-registration";
+            # `CI_SERVER_TOKEN`
+            authenticationTokenConfigFile = "/run/secrets/gitlab-runner-shell-token-env";
+
             executor = "shell";
             tagList = [ "shell" ];
           };
@@ -278,30 +325,67 @@ in {
           default = {
             # File should contain at least these two variables:
             # `CI_SERVER_URL`
-            # `REGISTRATION_TOKEN`
-            registrationConfigFile = "/run/secrets/gitlab-runner-registration";
+            # `CI_SERVER_TOKEN`
+            authenticationTokenConfigFile = "/run/secrets/gitlab-runner-default-token-env";
             dockerImage = "debian:stable";
           };
         }
       '';
       type = types.attrsOf (types.submodule {
         options = {
+          authenticationTokenConfigFile = mkOption {
+            type = with types; nullOr path;
+            default = null;
+            description = ''
+              Absolute path to a file containing environment variables used for
+              gitlab-runner registrations with *runner authentication tokens*.
+              They replace the deprecated *runner registration tokens*, as
+              outlined in the [GitLab documentation].
+
+              A list of all supported environment variables can be found with
+              `gitlab-runner register --help`.
+
+              The ones you probably want to set are:
+              - `CI_SERVER_URL=<CI server URL>`
+              - `CI_SERVER_TOKEN=<runner authentication token secret>`
+
+              ::: {.warning}
+              Make sure to use a quoted absolute path,
+              or it is going to be copied to Nix Store.
+              :::
+
+              [GitLab documentation]: https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#estimated-time-frame-for-planned-changes
+            '';
+          };
           registrationConfigFile = mkOption {
-            type = types.path;
+            type = with types; nullOr path;
+            default = null;
             description = ''
               Absolute path to a file with environment variables
-              used for gitlab-runner registration.
+              used for gitlab-runner registration with *runner registration
+              tokens*.
+
               A list of all supported environment variables can be found in
               `gitlab-runner register --help`.
 
-              Ones that you probably want to set is
+              The ones you probably want to set are:
+              - `CI_SERVER_URL=<CI server URL>`
+              - `REGISTRATION_TOKEN=<registration secret>`
 
-              `CI_SERVER_URL=<CI server URL>`
+              Support for *runner registration tokens* is deprecated since
+              GitLab 16.0, has been disabled by default in GitLab 17.0 and
+              will be removed in GitLab 18.0, as outlined in the
+              [GitLab documentation]. Please consider migrating to
+              [runner authentication tokens] and check the documentation on
+              {option}`services.gitlab-runner.services.<name>.authenticationTokenConfigFile`.
 
-              `REGISTRATION_TOKEN=<registration secret>`
-
-              WARNING: make sure to use quoted absolute path,
+              ::: {.warning}
+              Make sure to use a quoted absolute path,
               or it is going to be copied to Nix Store.
+              :::
+
+              [GitLab documentation]: https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#estimated-time-frame-for-planned-changes
+              [runner authentication tokens]: https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html#the-new-runner-registration-workflow
             '';
           };
           registrationFlags = mkOption {
@@ -439,6 +523,9 @@ in {
             default = [ ];
             description = ''
               Tag list.
+
+              This option has no effect for runners registered with an runner
+              authentication tokens and will be ignored.
             '';
           };
           runUntagged = mkOption {
@@ -447,6 +534,9 @@ in {
             description = ''
               Register to run untagged builds; defaults to
               `true` when {option}`tagList` is empty.
+
+              This option has no effect for runners registered with an runner
+              authentication tokens and will be ignored.
             '';
           };
           limit = mkOption {
@@ -470,6 +560,9 @@ in {
             description = ''
               What is the maximum timeout (in seconds) that will be set for
               job when using this Runner. 0 (default) simply means don't limit.
+
+              This option has no effect for runners registered with an runner
+              authentication tokens and will be ignored.
             '';
           };
           protected = mkOption {
@@ -478,6 +571,9 @@ in {
             description = ''
               When set to true Runner will only run on pipelines
               triggered on protected branches.
+
+              This option has no effect for runners registered with an runner
+              authentication tokens and will be ignored.
             '';
           };
           debugTraceDisabled = mkOption {
@@ -530,9 +626,67 @@ in {
     };
   };
   config = mkIf cfg.enable {
-    warnings = mapAttrsToList
-      (n: v: "services.gitlab-runner.services.${n}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.")
-      (filterAttrs (n: v: isStorePath v.registrationConfigFile) cfg.services);
+    assertions =
+      mapAttrsToList (name: serviceConfig: {
+        assertion = serviceConfig.registrationConfigFile == null || serviceConfig.authenticationTokenConfigFile == null;
+        message = "`services.gitlab-runner.${name}.registrationConfigFile` and `services.gitlab-runner.services.${name}.authenticationTokenConfigFile` are mutually exclusive.";
+      }) cfg.services;
+
+    warnings =
+      mapAttrsToList
+        (name: serviceConfig: "services.gitlab-runner.services.${name}.`registrationConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.")
+        (filterAttrs (name: serviceConfig: isStorePath serviceConfig.registrationConfigFile) cfg.services)
+      ++ mapAttrsToList
+        (name: serviceConfig: "services.gitlab-runner.services.${name}.`authenticationTokenConfigFile` points to a file in Nix Store. You should use quoted absolute path to prevent this.")
+        (filterAttrs (name: serviceConfig: isStorePath serviceConfig.authenticationTokenConfigFile) cfg.services)
+      ++ mapAttrsToList
+        (name: serviceConfig: ''
+          Runner registration tokens have been deprecated and disabled by default in GitLab >= 17.0.
+          Consider migrating to runner authentication tokens by setting `services.gitlab-runner.services.${name}.authenticationTokenConfigFile`.
+          https://docs.gitlab.com/17.0/ee/ci/runners/new_creation_workflow.html''
+        )
+        (
+          filterAttrs (name: serviceConfig:
+            serviceConfig.authenticationTokenConfigFile == null
+          ) cfg.services
+        )
+      ++ mapAttrsToList
+        (name: serviceConfig: ''
+          `services.gitlab-runner.services.${name}.protected` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
+        )
+        (
+          filterAttrs (name: serviceConfig:
+            serviceConfig.authenticationTokenConfigFile != null && serviceConfig.protected == true
+          ) cfg.services
+        )
+      ++ mapAttrsToList
+        (name: serviceConfig: ''
+          `services.gitlab-runner.services.${name}.runUntagged` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
+        )
+        (
+          filterAttrs (name: serviceConfig:
+            serviceConfig.authenticationTokenConfigFile != null && serviceConfig.runUntagged == true
+          ) cfg.services
+        )
+      ++ mapAttrsToList
+        (name: v: ''
+          `services.gitlab-runner.services.${name}.maximumTimeout` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
+        )
+        (
+          filterAttrs (name: serviceConfig:
+            serviceConfig.authenticationTokenConfigFile != null && serviceConfig.maximumTimeout != 0
+          ) cfg.services
+        )
+      ++ mapAttrsToList
+        (name: v: ''
+          `services.gitlab-runner.services.${name}.tagList` with runner authentication tokens has no effect and will be ignored. Please remove it from your configuration.''
+        )
+        (
+          filterAttrs (serviceName: serviceConfig:
+            serviceConfig.authenticationTokenConfigFile != null && serviceConfig.tagList != [ ]
+          ) cfg.services
+        )
+      ;
 
     environment.systemPackages = [ cfg.package ];
     systemd.services.gitlab-runner = {
@@ -545,15 +699,19 @@ in {
       environment = config.networking.proxy.envVars // {
         HOME = "/var/lib/gitlab-runner";
       };
-      path = with pkgs; [
-        bash
-        gawk
-        jq
-        moreutils
-        remarshal
-        util-linux
-        cfg.package
-      ] ++ cfg.extraPackages;
+
+      path =
+        (with pkgs; [
+          bash
+          gawk
+          jq
+          moreutils
+          remarshal
+          util-linux
+        ])
+        ++ [ cfg.package ]
+        ++ cfg.extraPackages;
+
       reloadIfChanged = true;
       serviceConfig = {
         # Set `DynamicUser` under `systemd.services.gitlab-runner.serviceConfig`
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index 7b671ba9ed9de..a23120739b7fa 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -237,6 +237,8 @@ in {
       serviceConfig = {
         User = cfg.user;
         StateDirectory = mkIf (hasPrefix "/var/lib/jenkins" cfg.home) "jenkins";
+        # For (possible) socket use
+        RuntimeDirectory = "jenkins";
       };
     };
   };
diff --git a/nixos/modules/services/databases/memcached.nix b/nixos/modules/services/databases/memcached.nix
index e38931b6b7ea8..7a3afc5efafcf 100644
--- a/nixos/modules/services/databases/memcached.nix
+++ b/nixos/modules/services/databases/memcached.nix
@@ -37,7 +37,7 @@ in
         description = "The port to bind to.";
       };
 
-      enableUnixSocket = mkEnableOption "Unix Domain Socket at /run/memcached/memcached.sock instead of listening on an IP address and port. The `listen` and `port` options are ignored.";
+      enableUnixSocket = mkEnableOption "Unix Domain Socket at /run/memcached/memcached.sock instead of listening on an IP address and port. The `listen` and `port` options are ignored";
 
       maxMemory = mkOption {
         type = types.ints.unsigned;
diff --git a/nixos/modules/services/databases/neo4j.nix b/nixos/modules/services/databases/neo4j.nix
index 4369ec2007dcf..c3f1872e0822a 100644
--- a/nixos/modules/services/databases/neo4j.nix
+++ b/nixos/modules/services/databases/neo4j.nix
@@ -620,6 +620,6 @@ in {
     };
 
   meta = {
-    maintainers = with lib.maintainers; [ patternspandemic jonringer ];
+    maintainers = with lib.maintainers; [ patternspandemic ];
   };
 }
diff --git a/nixos/modules/services/desktop-managers/lomiri.nix b/nixos/modules/services/desktop-managers/lomiri.nix
index eec33597dc711..0b871aa38183e 100644
--- a/nixos/modules/services/desktop-managers/lomiri.nix
+++ b/nixos/modules/services/desktop-managers/lomiri.nix
@@ -63,7 +63,7 @@ in {
     ];
 
     # Copy-pasted basic stuff
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
     fonts.enableDefaultPackages = lib.mkDefault true;
     programs.dconf.enable = lib.mkDefault true;
 
diff --git a/nixos/modules/services/desktop-managers/plasma6.nix b/nixos/modules/services/desktop-managers/plasma6.nix
index d4f961254f028..796e24286f9e4 100644
--- a/nixos/modules/services/desktop-managers/plasma6.nix
+++ b/nixos/modules/services/desktop-managers/plasma6.nix
@@ -264,9 +264,12 @@ in {
         enable = true;
         package = kdePackages.kwallet-pam;
       };
-      kde.kwallet = {
-        enable = true;
-        package = kdePackages.kwallet-pam;
+      kde = {
+        allowNullPassword = true;
+        kwallet = {
+          enable = true;
+          package = kdePackages.kwallet-pam;
+        };
       };
       kde-fingerprint = lib.mkIf config.services.fprintd.enable { fprintAuth = true; };
       kde-smartcard = lib.mkIf config.security.pam.p11.enable { p11Auth = true; };
diff --git a/nixos/modules/services/desktops/deepin/deepin-anything.nix b/nixos/modules/services/desktops/deepin/deepin-anything.nix
new file mode 100644
index 0000000000000..4e88a789551b9
--- /dev/null
+++ b/nixos/modules/services/desktops/deepin/deepin-anything.nix
@@ -0,0 +1,38 @@
+{ config, pkgs, lib, ... }:
+
+{
+
+  meta = {
+    maintainers = lib.teams.deepin.members;
+  };
+
+  options = {
+
+    services.deepin.deepin-anything = {
+
+      enable = lib.mkEnableOption "deepin anything file search tool";
+
+    };
+
+  };
+
+  config = lib.mkIf config.services.deepin.dde-api.enable {
+    environment.systemPackages = [ pkgs.deepin.deepin-anything ];
+
+    services.dbus.packages = [ pkgs.deepin.deepin-anything ];
+
+    users.groups.deepin-anything = { };
+
+    users.users.deepin-anything = {
+      description = "Deepin Anything Server";
+      home = "/var/lib/deepin-anything";
+      createHome = true;
+      group = "deepin-anything";
+      isSystemUser = true;
+    };
+
+    boot.extraModulePackages = [ config.boot.kernelPackages.deepin-anything-module ];
+    boot.kernelModules = [ "vfs_monitor" ];
+  };
+
+}
diff --git a/nixos/modules/services/desktops/gnome/gnome-keyring.nix b/nixos/modules/services/desktops/gnome/gnome-keyring.nix
index 79bce0ade2fc5..02b198fd81cb9 100644
--- a/nixos/modules/services/desktops/gnome/gnome-keyring.nix
+++ b/nixos/modules/services/desktops/gnome/gnome-keyring.nix
@@ -1,45 +1,52 @@
 # GNOME Keyring daemon.
 
-{ config, pkgs, lib, ... }:
-
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  cfg = config.services.gnome.gnome-keyring;
+in
 {
 
   meta = {
     maintainers = lib.teams.gnome.members;
   };
 
-  ###### interface
-
   options = {
-
     services.gnome.gnome-keyring = {
-
-      enable = lib.mkOption {
-        type = lib.types.bool;
-        default = false;
-        description = ''
-          Whether to enable GNOME Keyring daemon, a service designed to
-          take care of the user's security credentials,
-          such as user names and passwords.
-        '';
-      };
-
+      enable = lib.mkEnableOption ''
+        GNOME Keyring daemon, a service designed to
+        take care of the user's security credentials,
+        such as user names and passwords
+      '';
     };
-
   };
 
-
-  ###### implementation
-
-  config = lib.mkIf config.services.gnome.gnome-keyring.enable {
-
+  config = lib.mkIf cfg.enable {
     environment.systemPackages = [ pkgs.gnome.gnome-keyring ];
 
-    services.dbus.packages = [ pkgs.gnome.gnome-keyring pkgs.gcr ];
+    services.dbus.packages = [
+      pkgs.gnome.gnome-keyring
+      pkgs.gcr
+    ];
 
     xdg.portal.extraPortals = [ pkgs.gnome.gnome-keyring ];
 
-    security.pam.services.login.enableGnomeKeyring = true;
+    security.pam.services = lib.mkMerge [
+      {
+        login.enableGnomeKeyring = true;
+      }
+      (lib.mkIf config.services.xserver.displayManager.gdm.enable {
+        gdm-password.enableGnomeKeyring = true;
+        gdm-autologin.enableGnomeKeyring = true;
+      })
+      (lib.mkIf (config.services.xserver.displayManager.gdm.enable && config.services.fprintd.enable) {
+        gdm-fingerprint.enableGnomeKeyring = true;
+      })
+    ];
 
     security.wrappers.gnome-keyring-daemon = {
       owner = "root";
@@ -47,7 +54,5 @@
       capabilities = "cap_ipc_lock=ep";
       source = "${pkgs.gnome.gnome-keyring}/bin/gnome-keyring-daemon";
     };
-
   };
-
 }
diff --git a/nixos/modules/services/display-managers/default.nix b/nixos/modules/services/display-managers/default.nix
index feba4b163ccd2..9a7bd6c84b15b 100644
--- a/nixos/modules/services/display-managers/default.nix
+++ b/nixos/modules/services/display-managers/default.nix
@@ -212,9 +212,7 @@ in
       after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
       restartIfChanged = false;
 
-      environment = lib.optionalAttrs config.hardware.opengl.setLdLibraryPath {
-        LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ];
-      } // cfg.environment;
+      environment = cfg.environment;
 
       preStart = cfg.preStart;
       script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd;
diff --git a/nixos/modules/services/hardware/amdgpu.nix b/nixos/modules/services/hardware/amdgpu.nix
new file mode 100644
index 0000000000000..1952be08a17cf
--- /dev/null
+++ b/nixos/modules/services/hardware/amdgpu.nix
@@ -0,0 +1,43 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.amdgpu;
+in {
+  options.hardware.amdgpu = {
+    legacySupport.enable = lib.mkEnableOption ''
+      using `amdgpu` kernel driver instead of `radeon` for Southern Islands
+      (Radeon HD 7000) series and Sea Islands (Radeon HD 8000)
+      series cards. Note: this removes support for analog video outputs,
+      which is only available in the `radeon` driver
+    '';
+    initrd.enable = lib.mkEnableOption ''
+      loading `amdgpu` kernelModule in stage 1.
+      Can fix lower resolution in boot screen during initramfs phase
+    '';
+    opencl.enable = lib.mkEnableOption ''OpenCL support using ROCM runtime library'';
+    # cfg.amdvlk option is defined in ./amdvlk.nix module
+  };
+
+  config = {
+    boot.kernelParams = lib.optionals cfg.legacySupport.enable [
+      "amdgpu.si_support=1"
+      "amdgpu.cik_support=1"
+      "radeon.si_support=0"
+      "radeon.cik_support=0"
+    ];
+
+    boot.initrd.kernelModules = lib.optionals cfg.initrd.enable [ "amdgpu" ];
+
+    hardware.graphics = lib.mkIf cfg.opencl.enable {
+      enable = lib.mkDefault true;
+      extraPackages = [
+        pkgs.rocmPackages.clr
+        pkgs.rocmPackages.clr.icd
+      ];
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ johnrtitor ];
+  };
+}
diff --git a/nixos/modules/services/hardware/amdvlk.nix b/nixos/modules/services/hardware/amdvlk.nix
index 20879f2f21b43..32d6fb3be21dc 100644
--- a/nixos/modules/services/hardware/amdvlk.nix
+++ b/nixos/modules/services/hardware/amdvlk.nix
@@ -31,11 +31,9 @@ in {
   };
 
   config = lib.mkIf cfg.enable {
-    hardware.opengl = {
+    hardware.graphics = {
       enable = true;
-      driSupport = true;
       extraPackages = [ cfg.package ];
-      driSupport32Bit = cfg.support32Bit.enable;
       extraPackages32 = [ cfg.support32Bit.package ];
     };
 
diff --git a/nixos/modules/services/hardware/openrgb.nix b/nixos/modules/services/hardware/openrgb.nix
index 6ae5b4e587c6c..1f7e4ffb9265a 100644
--- a/nixos/modules/services/hardware/openrgb.nix
+++ b/nixos/modules/services/hardware/openrgb.nix
@@ -51,5 +51,5 @@ in {
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ jonringer ];
+  meta.maintainers = with lib.maintainers; [ ];
 }
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index d94adfb4aa1c7..c58a31539ed81 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -518,8 +518,9 @@ in {
           # recreate symlinks for desired components
           declare -a components=(${escapeShellArgs cfg.customComponents})
           for component in "''${components[@]}"; do
-            path="$(dirname $(find "$component" -name "manifest.json"))"
-            ln -fns "$path" "${cfg.configDir}/custom_components/"
+            readarray -t manifests < <(find "$component" -name manifest.json)
+            readarray -t paths < <(dirname "''${manifests[@]}")
+            ln -fns "''${paths[@]}" "${cfg.configDir}/custom_components/"
           done
         '';
       in
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 180c9800d7345..ab10206fea42e 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -646,7 +646,7 @@ in {
   };
 
   meta = {
-    maintainers = with lib.maintainers; [ lheckemann qyliss ];
+    maintainers = with lib.maintainers; [ qyliss ];
     doc = ./mailman.md;
   };
 
diff --git a/nixos/modules/services/mail/stalwart-mail.nix b/nixos/modules/services/mail/stalwart-mail.nix
index ed3c5389354ce..776243a68af53 100644
--- a/nixos/modules/services/mail/stalwart-mail.nix
+++ b/nixos/modules/services/mail/stalwart-mail.nix
@@ -37,8 +37,6 @@ in {
         ansi = mkDefault false;  # no colour markers to journald
         enable = mkDefault true;
       };
-      queue.path = mkDefault "${dataDir}/queue";
-      report.path = mkDefault "${dataDir}/reports";
       store = if useLegacyStorage then {
         # structured data in SQLite, blobs on filesystem
         db.type = mkDefault "sqlite";
@@ -62,6 +60,9 @@ in {
       resolver.public-suffix = lib.mkDefault [
         "file://${pkgs.publicsuffix-list}/share/publicsuffix/public_suffix_list.dat"
       ];
+      config.resource = {
+        spam-filter = lib.mkDefault "file://${cfg.package}/etc/stalwart/spamfilter.toml";
+      };
     };
 
     # This service stores a potentially large amount of data.
@@ -83,9 +84,9 @@ in {
         after = [ "local-fs.target" "network.target" ];
 
         preStart = if useLegacyStorage then ''
-          mkdir -p ${dataDir}/{queue,reports,data/blobs}
+          mkdir -p ${dataDir}/data/blobs
         '' else ''
-          mkdir -p ${dataDir}/{queue,reports,db}
+          mkdir -p ${dataDir}/db
         '';
 
         serviceConfig = {
diff --git a/nixos/modules/services/matrix/mautrix-signal.nix b/nixos/modules/services/matrix/mautrix-signal.nix
index faca10551abb6..0da95b9c8a7b4 100644
--- a/nixos/modules/services/matrix/mautrix-signal.nix
+++ b/nixos/modules/services/matrix/mautrix-signal.nix
@@ -52,7 +52,7 @@ let
 in
 {
   options.services.mautrix-signal = {
-    enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge.";
+    enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge";
 
     settings = lib.mkOption {
       apply = lib.recursiveUpdate defaultConfig;
diff --git a/nixos/modules/services/matrix/mautrix-whatsapp.nix b/nixos/modules/services/matrix/mautrix-whatsapp.nix
index 31f64c16d7913..d124edc216dd0 100644
--- a/nixos/modules/services/matrix/mautrix-whatsapp.nix
+++ b/nixos/modules/services/matrix/mautrix-whatsapp.nix
@@ -47,7 +47,7 @@
 
 in {
   options.services.mautrix-whatsapp = {
-    enable = lib.mkEnableOption "mautrix-whatsapp, a puppeting/relaybot bridge between Matrix and WhatsApp.";
+    enable = lib.mkEnableOption "mautrix-whatsapp, a puppeting/relaybot bridge between Matrix and WhatsApp";
 
     settings = lib.mkOption {
       type = settingsFormat.type;
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index bc88fb53012b7..6d2e6201d66d3 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -1121,7 +1121,7 @@ in {
           The client listener on matrix-synapse is configured to use UNIX domain sockets.
           This configuration is incompatible with the `register_new_matrix_user` script.
 
-          Disable  `services.mastrix-synapse.enableRegistrationScript` to continue.
+          Disable  `services.matrix-synapse.enableRegistrationScript` to continue.
         '';
       }
     ]
diff --git a/nixos/modules/services/misc/anki-sync-server.md b/nixos/modules/services/misc/anki-sync-server.md
index f58d3d8ad0dab..5482a4aa0e5ff 100644
--- a/nixos/modules/services/misc/anki-sync-server.md
+++ b/nixos/modules/services/misc/anki-sync-server.md
@@ -52,7 +52,7 @@ following options:
 
 ```nix
 {
-  services.anki-sync-server.host = "0.0.0.0";
+  services.anki-sync-server.address = "0.0.0.0";
   services.anki-sync-server.openFirewall = true;
 }
 ```
diff --git a/nixos/modules/services/misc/graphical-desktop.nix b/nixos/modules/services/misc/graphical-desktop.nix
index a88c02e610bf4..c8fe0d921c6ad 100644
--- a/nixos/modules/services/misc/graphical-desktop.nix
+++ b/nixos/modules/services/misc/graphical-desktop.nix
@@ -38,7 +38,7 @@ in
 
     fonts.enableDefaultPackages = lib.mkDefault true;
 
-    hardware.opengl.enable = lib.mkDefault true;
+    hardware.graphics.enable = lib.mkDefault true;
 
     programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3;
 
diff --git a/nixos/modules/services/misc/mqtt2influxdb.nix b/nixos/modules/services/misc/mqtt2influxdb.nix
index 925139b449b8e..d07ce1e66ba31 100644
--- a/nixos/modules/services/misc/mqtt2influxdb.nix
+++ b/nixos/modules/services/misc/mqtt2influxdb.nix
@@ -124,7 +124,7 @@ let
 in {
   options = {
     services.mqtt2influxdb = {
-      enable = mkEnableOption "BigClown MQTT to InfluxDB bridge.";
+      enable = mkEnableOption "BigClown MQTT to InfluxDB bridge";
       package = mkPackageOption pkgs ["python3Packages" "mqtt2influxdb"] {};
       environmentFiles = mkOption {
         type = types.listOf types.path;
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index 886eaa180b9ef..c460514783efc 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -1,6 +1,6 @@
 { config, lib, pkgs, ... }:
 let
-  inherit (lib) types;
+  inherit (lib) types mkBefore;
 
   cfg = config.services.ollama;
   ollamaPackage = cfg.package.override {
@@ -98,9 +98,25 @@ in
             - otherwise defaults to `false`
           - `false`: disable GPU, only use CPU
           - `"rocm"`: supported by most modern AMD GPUs
+            - may require overriding gpu type with `services.ollama.rocmOverrideGfx`
+              if rocm doesn't detect your AMD gpu
           - `"cuda"`: supported by most modern NVIDIA GPUs
         '';
       };
+      rocmOverrideGfx = lib.mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "10.3.0";
+        description = ''
+          Override what rocm will detect your gpu model as.
+          For example, make rocm treat your RX 5700 XT (or any other model)
+          as an RX 6900 XT using a value of `"10.3.0"` (gfx 1030).
+
+          This sets the value of `HSA_OVERRIDE_GFX_VERSION`. See [ollama's docs](
+          https://github.com/ollama/ollama/blob/main/docs/gpu.md#amd-radeon
+          ) for details.
+        '';
+      };
       environmentVariables = lib.mkOption {
         type = types.attrsOf types.str;
         default = { };
@@ -116,6 +132,14 @@ in
           Since `ollama run` is mostly a shell around the ollama server, this is usually sufficient.
         '';
       };
+      loadModels = lib.mkOption {
+        type = types.listOf types.str;
+        default = [ ];
+        description = ''
+          The models to download as soon as the service starts.
+          Search for models of your choice from: https://ollama.com/library
+        '';
+      };
       openFirewall = lib.mkOption {
         type = types.bool;
         default = false;
@@ -136,6 +160,7 @@ in
         HOME = cfg.home;
         OLLAMA_MODELS = cfg.models;
         OLLAMA_HOST = "${cfg.host}:${toString cfg.port}";
+        HSA_OVERRIDE_GFX_VERSION = lib.mkIf (cfg.rocmOverrideGfx != null) cfg.rocmOverrideGfx;
       };
       serviceConfig = {
         ExecStart = "${lib.getExe ollamaPackage} serve";
@@ -144,6 +169,14 @@ in
         DynamicUser = cfg.sandbox;
         ReadWritePaths = cfg.writablePaths;
       };
+      postStart = mkBefore ''
+        set -x
+        export OLLAMA_HOST=${lib.escapeShellArg cfg.host}:${builtins.toString cfg.port}
+        for model in ${lib.escapeShellArgs cfg.loadModels}
+        do
+          ${lib.escapeShellArg (lib.getExe ollamaPackage)} pull "$model"
+        done
+      '';
     };
 
     networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix
index e564fe3b8317b..6d6a49c10bddc 100644
--- a/nixos/modules/services/misc/paperless.nix
+++ b/nixos/modules/services/misc/paperless.nix
@@ -225,7 +225,7 @@ in
       effectively never complete due to running into timeouts.
 
       This sets `OMP_NUM_THREADS` to `1` in order to mitigate the issue. See
-      https://github.com/NixOS/nixpkgs/issues/240591 for more information.
+      https://github.com/NixOS/nixpkgs/issues/240591 for more information
     '' // mkOption { default = true; };
   };
 
diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix
index c7abb2cfa2a3e..a9a069b0c0555 100644
--- a/nixos/modules/services/misc/portunus.nix
+++ b/nixos/modules/services/misc/portunus.nix
@@ -70,7 +70,7 @@ in
 
         To activate dex, first a search user must be created in the Portunus web ui
         and then the password must to be set as the `DEX_SEARCH_USER_PASSWORD` environment variable
-        in the [](#opt-services.dex.environmentFile) setting.
+        in the [](#opt-services.dex.environmentFile) setting
       '';
 
       oidcClients = mkOption {
diff --git a/nixos/modules/services/misc/private-gpt.nix b/nixos/modules/services/misc/private-gpt.nix
index 9a3e5317cdb14..ad9b6f5ffa80f 100644
--- a/nixos/modules/services/misc/private-gpt.nix
+++ b/nixos/modules/services/misc/private-gpt.nix
@@ -117,5 +117,5 @@ in
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ drupol ];
+  meta.maintainers = with lib.maintainers; [ ];
 }
diff --git a/nixos/modules/services/misc/snapper.nix b/nixos/modules/services/misc/snapper.nix
index a42fca5b60289..1b16ef7958ad2 100644
--- a/nixos/modules/services/misc/snapper.nix
+++ b/nixos/modules/services/misc/snapper.nix
@@ -1,16 +1,32 @@
-{ config, pkgs, lib, ... }:
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
 
 with lib;
 
 let
   cfg = config.services.snapper;
 
-  mkValue = v:
-    if isList v then "\"${concatMapStringsSep " " (escape [ "\\" " " ]) v}\""
-    else if v == true then "yes"
-    else if v == false then "no"
-    else if isString v then "\"${v}\""
-    else builtins.toJSON v;
+  mkValue =
+    v:
+    if isList v then
+      "\"${
+        concatMapStringsSep " " (escape [
+          "\\"
+          " "
+        ]) v
+      }\""
+    else if v == true then
+      "yes"
+    else if v == false then
+      "no"
+    else if isString v then
+      "\"${v}\""
+    else
+      builtins.toJSON v;
 
   mkKeyValue = k: v: "${k}=${mkValue v}";
 
@@ -43,7 +59,7 @@ let
 
     ALLOW_GROUPS = mkOption {
       type = types.listOf safeStr;
-      default = [];
+      default = [ ];
       description = ''
         List of groups allowed to operate with the config.
 
@@ -53,7 +69,7 @@ let
 
     ALLOW_USERS = mkOption {
       type = types.listOf safeStr;
-      default = [];
+      default = [ ];
       example = [ "alice" ];
       description = ''
         List of users allowed to operate with the config. "root" is always
@@ -78,6 +94,54 @@ let
         Defines whether hourly snapshots should be created.
       '';
     };
+
+    TIMELINE_LIMIT_HOURLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_DAILY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_WEEKLY = mkOption {
+      type = types.str;
+      default = "0";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_MONTHLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_QUARTERLY = mkOption {
+      type = types.str;
+      default = "0";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
+
+    TIMELINE_LIMIT_YEARLY = mkOption {
+      type = types.str;
+      default = "10";
+      description = ''
+        Limits for timeline cleanup.
+      '';
+    };
   };
 in
 
@@ -152,112 +216,129 @@ in
         is valid here, even if NixOS doesn't document it.
       '';
 
-      type = types.attrsOf (types.submodule {
-        freeformType = types.attrsOf (types.oneOf [ (types.listOf safeStr) types.bool safeStr types.number ]);
-
-        options = configOptions;
-      });
+      type = types.attrsOf (
+        types.submodule {
+          freeformType = types.attrsOf (
+            types.oneOf [
+              (types.listOf safeStr)
+              types.bool
+              safeStr
+              types.number
+            ]
+          );
+
+          options = configOptions;
+        }
+      );
     };
   };
 
-  config = mkIf (cfg.configs != {}) (let
-    documentation = [ "man:snapper(8)" "man:snapper-configs(5)" ];
-  in {
-
-    environment = {
-
-      systemPackages = [ pkgs.snapper ];
-
-      # Note: snapper/config-templates/default is only needed for create-config
-      #       which is not the NixOS way to configure.
-      etc = {
-
-        "sysconfig/snapper".text = ''
-          SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
-        '';
-
-      }
-      // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({
-        text = lib.generators.toKeyValue { inherit mkKeyValue; } (filterAttrs (k: v: v != defaultOf k) subvolume);
-      })) cfg.configs)
-      // (lib.optionalAttrs (cfg.filters != null) {
-        "snapper/filters/default.txt".text = cfg.filters;
-      });
-
-    };
+  config = mkIf (cfg.configs != { }) (
+    let
+      documentation = [
+        "man:snapper(8)"
+        "man:snapper-configs(5)"
+      ];
+    in
+    {
+      environment = {
+
+        systemPackages = [ pkgs.snapper ];
+
+        # Note: snapper/config-templates/default is only needed for create-config
+        #       which is not the NixOS way to configure.
+        etc =
+          {
+
+            "sysconfig/snapper".text = ''
+              SNAPPER_CONFIGS="${lib.concatStringsSep " " (builtins.attrNames cfg.configs)}"
+            '';
+          }
+          // (mapAttrs' (
+            name: subvolume:
+            nameValuePair "snapper/configs/${name}" ({
+              text = lib.generators.toKeyValue { inherit mkKeyValue; } (
+                filterAttrs (k: v: v != defaultOf k) subvolume
+              );
+            })
+          ) cfg.configs)
+          // (lib.optionalAttrs (cfg.filters != null) { "snapper/filters/default.txt".text = cfg.filters; });
+      };
 
-    services.dbus.packages = [ pkgs.snapper ];
-
-    systemd.services.snapperd = {
-      description = "DBus interface for snapper";
-      inherit documentation;
-      serviceConfig = {
-        Type = "dbus";
-        BusName = "org.opensuse.Snapper";
-        ExecStart = "${pkgs.snapper}/bin/snapperd";
-        CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
-        LockPersonality = true;
-        NoNewPrivileges = false;
-        PrivateNetwork = true;
-        ProtectHostname = true;
-        RestrictAddressFamilies = "AF_UNIX";
-        RestrictRealtime = true;
+      services.dbus.packages = [ pkgs.snapper ];
+
+      systemd.services.snapperd = {
+        description = "DBus interface for snapper";
+        inherit documentation;
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "org.opensuse.Snapper";
+          ExecStart = "${pkgs.snapper}/bin/snapperd";
+          CapabilityBoundingSet = "CAP_DAC_OVERRIDE CAP_FOWNER CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_SYS_ADMIN CAP_SYS_MODULE CAP_IPC_LOCK CAP_SYS_NICE";
+          LockPersonality = true;
+          NoNewPrivileges = false;
+          PrivateNetwork = true;
+          ProtectHostname = true;
+          RestrictAddressFamilies = "AF_UNIX";
+          RestrictRealtime = true;
+        };
       };
-    };
 
-    systemd.services.snapper-timeline = {
-      description = "Timeline of Snapper Snapshots";
-      inherit documentation;
-      requires = [ "local-fs.target" ];
-      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
-    };
+      systemd.services.snapper-timeline = {
+        description = "Timeline of Snapper Snapshots";
+        inherit documentation;
+        requires = [ "local-fs.target" ];
+        serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --timeline";
+      };
 
-    systemd.timers.snapper-timeline = {
-      wantedBy = [ "timers.target" ];
-      timerConfig = {
-        Persistent = cfg.persistentTimer;
-        OnCalendar = cfg.snapshotInterval;
+      systemd.timers.snapper-timeline = {
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          Persistent = cfg.persistentTimer;
+          OnCalendar = cfg.snapshotInterval;
+        };
       };
-    };
 
-    systemd.services.snapper-cleanup = {
-      description = "Cleanup of Snapper Snapshots";
-      inherit documentation;
-      serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
-    };
+      systemd.services.snapper-cleanup = {
+        description = "Cleanup of Snapper Snapshots";
+        inherit documentation;
+        serviceConfig.ExecStart = "${pkgs.snapper}/lib/snapper/systemd-helper --cleanup";
+      };
 
-    systemd.timers.snapper-cleanup = {
-      description = "Cleanup of Snapper Snapshots";
-      inherit documentation;
-      wantedBy = [ "timers.target" ];
-      requires = [ "local-fs.target" ];
-      timerConfig.OnBootSec = "10m";
-      timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
-    };
+      systemd.timers.snapper-cleanup = {
+        description = "Cleanup of Snapper Snapshots";
+        inherit documentation;
+        wantedBy = [ "timers.target" ];
+        requires = [ "local-fs.target" ];
+        timerConfig.OnBootSec = "10m";
+        timerConfig.OnUnitActiveSec = cfg.cleanupInterval;
+      };
 
-    systemd.services.snapper-boot = lib.optionalAttrs cfg.snapshotRootOnBoot {
-      description = "Take snapper snapshot of root on boot";
-      inherit documentation;
-      serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
-      serviceConfig.Type = "oneshot";
-      requires = [ "local-fs.target" ];
-      wantedBy = [ "multi-user.target" ];
-      unitConfig.ConditionPathExists = "/etc/snapper/configs/root";
-    };
+      systemd.services.snapper-boot = lib.mkIf cfg.snapshotRootOnBoot {
+        description = "Take snapper snapshot of root on boot";
+        inherit documentation;
+        serviceConfig.ExecStart = "${pkgs.snapper}/bin/snapper --config root create --cleanup-algorithm number --description boot";
+        serviceConfig.Type = "oneshot";
+        requires = [ "local-fs.target" ];
+        wantedBy = [ "multi-user.target" ];
+        unitConfig.ConditionPathExists = "/etc/snapper/configs/root";
+      };
 
-    assertions =
-      concatMap
-        (name:
-          let
-            sub = cfg.configs.${name};
-          in
-          [ { assertion = !(sub ? extraConfig);
-              message = ''
-                The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it.
-                The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'.
-              '';
-            }
-          ] ++
+      assertions = concatMap (
+        name:
+        let
+          sub = cfg.configs.${name};
+        in
+        [
+          {
+            assertion = !(sub ? extraConfig);
+            message = ''
+              The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it.
+              The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'.
+            '';
+          }
+        ]
+        ++
           map
             (attr: {
               assertion = !(hasAttr attr sub);
@@ -265,8 +346,11 @@ in
                 The option definition `services.snapper.configs.${name}.${attr}' has been renamed to `services.snapper.configs.${name}.${toUpper attr}'.
               '';
             })
-            [ "fstype" "subvolume" ]
-        )
-        (attrNames cfg.configs);
-  });
+            [
+              "fstype"
+              "subvolume"
+            ]
+      ) (attrNames cfg.configs);
+    }
+  );
 }
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index ce5a0e78627c1..3507a49ea13a8 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -324,7 +324,8 @@ in
               };
               preStart =
                 let
-                  version = pkgs.sourcehut.${srvsrht}.version;
+                  package = pkgs.sourcehut.${srvsrht};
+                  version = package.version;
                   stateDir = "/var/lib/sourcehut/${srvsrht}";
                 in
                 mkBefore ''
@@ -336,14 +337,14 @@ in
                   if test ! -e ${stateDir}/db; then
                     # Setup the initial database.
                     # Note that it stamps the alembic head afterward
-                    ${cfg.python}/bin/${srvsrht}-initdb
+                    ${package}/bin/${srvsrht}-initdb
                     echo ${version} >${stateDir}/db
                   fi
 
                   ${optionalString cfg.settings.${iniKey}.migrate-on-upgrade ''
                     if [ "$(cat ${stateDir}/db)" != "${version}" ]; then
                       # Manage schema migrations using alembic
-                      ${cfg.python}/bin/${srvsrht}-migrate -a upgrade head
+                      ${package}/bin/${srvsrht}-migrate -a upgrade head
                       echo ${version} >${stateDir}/db
                     fi
                   ''}
@@ -389,7 +390,7 @@ in
               after = [ "network.target" "${srvsrht}.service" ];
               serviceConfig = {
                 Type = "oneshot";
-                ExecStart = "${cfg.python}/bin/${timerName}";
+                ExecStart = "${pkgs.sourcehut.${srvsrht}}/bin/${timerName}";
               };
             }
             (timer.service or { })
diff --git a/nixos/modules/services/misc/spice-autorandr.nix b/nixos/modules/services/misc/spice-autorandr.nix
index 0d58d28657172..92b8a15e93c5d 100644
--- a/nixos/modules/services/misc/spice-autorandr.nix
+++ b/nixos/modules/services/misc/spice-autorandr.nix
@@ -6,7 +6,7 @@ in
 {
   options = {
     services.spice-autorandr = {
-      enable = lib.mkEnableOption "spice-autorandr service that will automatically resize display to match SPICE client window size.";
+      enable = lib.mkEnableOption "spice-autorandr service that will automatically resize display to match SPICE client window size";
       package = lib.mkPackageOption pkgs "spice-autorandr" { };
     };
   };
diff --git a/nixos/modules/services/misc/tandoor-recipes.nix b/nixos/modules/services/misc/tandoor-recipes.nix
index a2210f3d7db5a..1c903d280378e 100644
--- a/nixos/modules/services/misc/tandoor-recipes.nix
+++ b/nixos/modules/services/misc/tandoor-recipes.nix
@@ -22,7 +22,7 @@ let
     ${lib.toShellVars env}
     eval "$(${config.systemd.package}/bin/systemctl show -pUID,GID,MainPID tandoor-recipes.service)"
     exec ${pkgs.util-linux}/bin/nsenter \
-      -t $MainPID -m -S $UID -G $GID \
+      -t $MainPID -m -S $UID -G $GID --wdns=${env.MEDIA_ROOT} \
       ${pkg}/bin/tandoor-recipes "$@"
   '';
 in
@@ -88,7 +88,7 @@ in
         Group = "tandoor_recipes";
         DynamicUser = true;
         StateDirectory = "tandoor-recipes";
-        WorkingDirectory = "/var/lib/tandoor-recipes";
+        WorkingDirectory = env.MEDIA_ROOT;
         RuntimeDirectory = "tandoor-recipes";
 
         BindReadOnlyPaths = [
diff --git a/nixos/modules/services/monitoring/grafana-reporter.nix b/nixos/modules/services/monitoring/grafana-reporter.nix
index 340ab7abd19b7..528041cab37af 100644
--- a/nixos/modules/services/monitoring/grafana-reporter.nix
+++ b/nixos/modules/services/monitoring/grafana-reporter.nix
@@ -60,7 +60,7 @@ in {
           "-templates ${cfg.templateDir}"
         ];
       in {
-        ExecStart = "${pkgs.grafana_reporter}/bin/grafana-reporter ${args}";
+        ExecStart = "${pkgs.grafana-reporter}/bin/grafana-reporter ${args}";
       };
     };
   };
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index 90e00e91deed2..8f89408bdea59 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -13,6 +13,9 @@ let
     ln -s /run/wrappers/bin/slabinfo.plugin $out/libexec/netdata/plugins.d/slabinfo.plugin
     ln -s /run/wrappers/bin/freeipmi.plugin $out/libexec/netdata/plugins.d/freeipmi.plugin
     ln -s /run/wrappers/bin/systemd-journal.plugin $out/libexec/netdata/plugins.d/systemd-journal.plugin
+    ln -s /run/wrappers/bin/logs-management.plugin $out/libexec/netdata/plugins.d/logs-management.plugin
+    ln -s /run/wrappers/bin/network-viewer.plugin $out/libexec/netdata/plugins.d/network-viewer.plugin
+    ln -s /run/wrappers/bin/debugfs.plugin $out/libexec/netdata/plugins.d/debugfs.plugin
   '';
 
   plugins = [
@@ -47,6 +50,7 @@ let
 
   defaultUser = "netdata";
 
+  isThereAnyWireGuardTunnels = config.networking.wireguard.enable || lib.any (c: lib.hasAttrByPath [ "netdevConfig" "Kind" ] c && c.netdevConfig.Kind == "wireguard") (builtins.attrValues config.systemd.network.netdevs);
 in {
   options = {
     services.netdata = {
@@ -86,6 +90,14 @@ in {
             Whether to enable python-based plugins
           '';
         };
+        recommendedPythonPackages = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Whether to enable a set of recommended Python plugins
+            by installing extra Python packages.
+          '';
+        };
         extraPackages = mkOption {
           type = types.functionTo (types.listOf types.package);
           default = ps: [];
@@ -198,13 +210,26 @@ in {
         }
       ];
 
+    # Includes a set of recommended Python plugins in exchange of imperfect disk consumption.
+    services.netdata.python.extraPackages = lib.mkIf cfg.python.recommendedPythonPackages (ps: [
+      ps.requests
+      ps.pandas
+      ps.numpy
+      ps.psycopg2
+      ps.python-ldap
+      ps.netdata-pandas
+      ps.changefinder
+    ]);
+
     services.netdata.configDir.".opt-out-from-anonymous-statistics" = mkIf (!cfg.enableAnalyticsReporting) (pkgs.writeText ".opt-out-from-anonymous-statistics" "");
     environment.etc."netdata/netdata.conf".source = configFile;
     environment.etc."netdata/conf.d".source = configDirectory;
 
     systemd.services.netdata = {
       description = "Real time performance monitoring";
-      after = [ "network.target" ];
+      after = [ "network.target" "suid-sgid-wrappers.service" ];
+      # No wrapper means no "useful" netdata.
+      requires = [ "suid-sgid-wrappers.service" ];
       wantedBy = [ "multi-user.target" ];
       path = (with pkgs; [
           curl
@@ -213,10 +238,16 @@ in {
           which
           procps
           bash
+          nvme-cli # for go.d
+          iw # for charts.d
+          apcupsd # for charts.d
+          # TODO: firehol # for FireQoS -- this requires more NixOS module support.
           util-linux # provides logger command; required for syslog health alarms
       ])
         ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages)
-        ++ lib.optional config.virtualisation.libvirtd.enable (config.virtualisation.libvirtd.package);
+        ++ lib.optional config.virtualisation.libvirtd.enable config.virtualisation.libvirtd.package
+        ++ lib.optional config.virtualisation.docker.enable config.virtualisation.docker.package
+        ++ lib.optionals config.virtualisation.podman.enable [ pkgs.jq config.virtualisation.podman.package ];
       environment = {
         PYTHONPATH = "${cfg.package}/libexec/netdata/python.d/python_modules";
         NETDATA_PIPENAME = "/run/netdata/ipc";
@@ -256,6 +287,8 @@ in {
         # Configuration directory and mode
         ConfigurationDirectory = "netdata";
         ConfigurationDirectoryMode = "0755";
+        # AmbientCapabilities
+        AmbientCapabilities = lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN";
         # Capabilities
         CapabilityBoundingSet = [
           "CAP_DAC_OVERRIDE"      # is required for freeipmi and slabinfo plugins
@@ -269,7 +302,7 @@ in {
           "CAP_SYS_CHROOT"        # is required for cgroups plugin
           "CAP_SETUID"            # is required for cgroups and cgroups-network plugins
           "CAP_SYSLOG"            # is required for systemd-journal plugin
-        ];
+        ] ++ lib.optional isThereAnyWireGuardTunnels "CAP_NET_ADMIN";
         # Sandboxing
         ProtectSystem = "full";
         ProtectHome = "read-only";
@@ -308,6 +341,14 @@ in {
         permissions = "u+rx,g+x,o-rwx";
       };
 
+      "debugfs.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/debugfs.plugin.org";
+        capabilities = "cap_dac_read_search+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
+
       "cgroup-network" = {
         source = "${cfg.package}/libexec/netdata/plugins.d/cgroup-network.org";
         capabilities = "cap_setuid+ep";
@@ -332,6 +373,14 @@ in {
         permissions = "u+rx,g+x,o-rwx";
       };
 
+      "logs-management.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/logs-management.plugin.org";
+        capabilities = "cap_dac_read_search,cap_syslog+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
+
       "slabinfo.plugin" = {
         source = "${cfg.package}/libexec/netdata/plugins.d/slabinfo.plugin.org";
         capabilities = "cap_dac_override+ep";
@@ -348,6 +397,14 @@ in {
         group = cfg.group;
         permissions = "u+rx,g+x,o-rwx";
       };
+    } // optionalAttrs (cfg.package.withNetworkViewer) {
+      "network-viewer.plugin" = {
+        source = "${cfg.package}/libexec/netdata/plugins.d/network-viewer.plugin.org";
+        capabilities = "cap_sys_admin,cap_dac_read_search,cap_sys_ptrace+ep";
+        owner = cfg.user;
+        group = cfg.group;
+        permissions = "u+rx,g+x,o-rwx";
+      };
     };
 
     security.pam.loginLimits = [
@@ -359,6 +416,8 @@ in {
       ${defaultUser} = {
         group = defaultUser;
         isSystemUser = true;
+        extraGroups = lib.optional config.virtualisation.docker.enable "docker"
+          ++ lib.optional config.virtualisation.podman.enable "podman";
       };
     };
 
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix b/nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix
new file mode 100644
index 0000000000000..b4307a76e1b02
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager-webhook-logger.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.alertmanagerWebhookLogger;
+in
+{
+  options.services.prometheus.alertmanagerWebhookLogger = {
+    enable = mkEnableOption "Alertmanager Webhook Logger";
+
+    package = mkPackageOption pkgs "alertmanager-webhook-logger" { };
+
+    extraFlags = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      description = "Extra command line options to pass to alertmanager-webhook-logger.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.alertmanager-webhook-logger = {
+      description = "Alertmanager Webhook Logger";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+
+      serviceConfig = {
+        ExecStart = ''
+          ${cfg.package}/bin/alertmanager-webhook-logger \
+          ${escapeShellArgs cfg.extraFlags}
+        '';
+
+        DynamicUser = true;
+        NoNewPrivileges = true;
+
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        ProtectHome = "tmpfs";
+
+        PrivateTmp = true;
+        PrivateDevices = true;
+        PrivateIPC = true;
+
+        ProtectHostname = true;
+        ProtectClock = true;
+        ProtectKernelTunables = true;
+        ProtectKernelModules = true;
+        ProtectKernelLogs = true;
+        ProtectControlGroups = true;
+
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+
+        SystemCallFilter = [
+          "@system-service"
+          "~@cpu-emulation"
+          "~@privileged"
+          "~@reboot"
+          "~@setuid"
+          "~@swap"
+        ];
+      };
+    };
+  };
+
+  meta.maintainers = [ maintainers.jpds ];
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 2dc12a221bf06..dc357f6cc5fb3 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -52,7 +52,6 @@ let
     "lnd"
     "mail"
     "mikrotik"
-    "minio"
     "modemmanager"
     "mongodb"
     "mysqld"
@@ -279,20 +278,16 @@ let
 in
 {
 
-  imports = (lib.forEach [ "blackboxExporter" "collectdExporter" "fritzboxExporter"
-                   "jsonExporter" "minioExporter" "nginxExporter" "nodeExporter"
-                   "snmpExporter" "unifiExporter" "varnishExporter" ]
-       (opt: lib.mkRemovedOptionModule [ "services" "prometheus" "${opt}" ] ''
-         The prometheus exporters are now configured using `services.prometheus.exporters'.
-         See the 18.03 release notes for more information.
-       '' ));
-
   options.services.prometheus.exporters = mkOption {
     type = types.submodule {
       options = (mkSubModules);
       imports = [
         ../../../misc/assertions.nix
         (lib.mkRenamedOptionModule [ "unifi-poller" ] [ "unpoller" ])
+        (lib.mkRemovedOptionModule [ "minio" ] ''
+          The Minio exporter has been removed, as it was broken and unmaintained.
+          See the 24.11 release notes for more information.
+        '')
       ];
     };
     description = "Prometheus exporter configuration";
@@ -438,11 +433,7 @@ in
         ''
       )
     ] ++ config.services.prometheus.exporters.warnings;
-  }] ++ [(mkIf config.services.minio.enable {
-    services.prometheus.exporters.minio.minioAddress  = mkDefault "http://localhost:9000";
-    services.prometheus.exporters.minio.minioAccessKey = mkDefault config.services.minio.accessKey;
-    services.prometheus.exporters.minio.minioAccessSecret = mkDefault config.services.minio.secretKey;
-  })] ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable {
+  }]  ++ [(mkIf config.services.prometheus.exporters.rtl_433.enable {
     hardware.rtl-sdr.enable = mkDefault true;
   })] ++ [(mkIf config.services.postfix.enable {
     services.prometheus.exporters.postfix.group = mkDefault config.services.postfix.setgidGroup;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
deleted file mode 100644
index 8faff5908b8a9..0000000000000
--- a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
+++ /dev/null
@@ -1,69 +0,0 @@
-{ config, lib, pkgs, options, ... }:
-
-let
-  cfg = config.services.prometheus.exporters.minio;
-  inherit (lib)
-    mkOption
-    types
-    optionalString
-    concatStringsSep
-    escapeShellArg
-    ;
-in
-{
-  port = 9290;
-  extraOpts = {
-    minioAddress = mkOption {
-      type = types.str;
-      example = "https://10.0.0.1:9000";
-      description = ''
-        The URL of the minio server.
-        Use HTTPS if Minio accepts secure connections only.
-        By default this connects to the local minio server if enabled.
-      '';
-    };
-
-    minioAccessKey = mkOption {
-      type = types.str;
-      example = "yourMinioAccessKey";
-      description = ''
-        The value of the Minio access key.
-        It is required in order to connect to the server.
-        By default this uses the one from the local minio server if enabled
-        and `config.services.minio.accessKey`.
-      '';
-    };
-
-    minioAccessSecret = mkOption {
-      type = types.str;
-      description = ''
-        The value of the Minio access secret.
-        It is required in order to connect to the server.
-        By default this uses the one from the local minio server if enabled
-        and `config.services.minio.secretKey`.
-      '';
-    };
-
-    minioBucketStats = mkOption {
-      type = types.bool;
-      default = false;
-      description = ''
-        Collect statistics about the buckets and files in buckets.
-        It requires more computation, use it carefully in case of large buckets..
-      '';
-    };
-  };
-  serviceOpts = {
-    serviceConfig = {
-      ExecStart = ''
-        ${pkgs.prometheus-minio-exporter}/bin/minio-exporter \
-          -web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
-          -minio.server ${cfg.minioAddress} \
-          -minio.access-key ${escapeShellArg cfg.minioAccessKey} \
-          -minio.access-secret ${escapeShellArg cfg.minioAccessSecret} \
-          ${optionalString cfg.minioBucketStats "-minio.bucket-stats"} \
-          ${concatStringsSep " \\\n  " cfg.extraFlags}
-      '';
-    };
-  };
-}
diff --git a/nixos/modules/services/monitoring/rustdesk-server.nix b/nixos/modules/services/monitoring/rustdesk-server.nix
index 21e6128c7226a..ea4dd43cbb35b 100644
--- a/nixos/modules/services/monitoring/rustdesk-server.nix
+++ b/nixos/modules/services/monitoring/rustdesk-server.nix
@@ -4,7 +4,7 @@ let
   UDPPorts = [21116];
 in {
   options.services.rustdesk-server = with lib; with types; {
-    enable = mkEnableOption "RustDesk, a remote access and remote control software, allowing maintenance of computers and other devices.";
+    enable = mkEnableOption "RustDesk, a remote access and remote control software, allowing maintenance of computers and other devices";
 
     package = mkPackageOption pkgs "rustdesk-server" {};
 
diff --git a/nixos/modules/services/monitoring/thanos.nix b/nixos/modules/services/monitoring/thanos.nix
index f4cec0a545cb7..10f4d08f8874e 100644
--- a/nixos/modules/services/monitoring/thanos.nix
+++ b/nixos/modules/services/monitoring/thanos.nix
@@ -696,7 +696,7 @@ in {
     };
 
     store = paramsToOptions params.store // {
-      enable = mkEnableOption "the Thanos store node giving access to blocks in a bucket provider.";
+      enable = mkEnableOption "the Thanos store node giving access to blocks in a bucket provider";
       arguments = mkArgumentsOption "store";
     };
 
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index 0a0d5eadccd30..35a2d61da1de4 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -385,8 +385,8 @@ in
 
     power.ups = {
       enable = mkEnableOption ''
-        Enables support for Power Devices, such as Uninterruptible Power
-        Supplies, Power Distribution Units and Solar Controllers.
+        support for Power Devices, such as Uninterruptible Power
+        Supplies, Power Distribution Units and Solar Controllers
       '';
 
       mode = mkOption {
diff --git a/nixos/modules/services/network-filesystems/davfs2.nix b/nixos/modules/services/network-filesystems/davfs2.nix
index 9a7d0daa6421f..49a363476c975 100644
--- a/nixos/modules/services/network-filesystems/davfs2.nix
+++ b/nixos/modules/services/network-filesystems/davfs2.nix
@@ -4,7 +4,7 @@ let
   inherit (lib.attrsets) optionalAttrs;
   inherit (lib.generators) toINIWithGlobalSection;
   inherit (lib.lists) optional;
-  inherit (lib.modules) mkIf;
+  inherit (lib.modules) mkIf mkRemovedOptionModule;
   inherit (lib.options) literalExpression mkEnableOption mkOption;
   inherit (lib.strings) escape;
   inherit (lib.types) attrsOf bool int lines oneOf str submodule;
@@ -27,6 +27,13 @@ let
 in
 {
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "davfs2" "extraConfig" ] ''
+      The option extraConfig got removed, please migrate to
+      services.davfs2.settings instead.
+    '')
+  ];
+
   options.services.davfs2 = {
     enable = mkEnableOption "davfs2";
 
diff --git a/nixos/modules/services/network-filesystems/openafs/server.nix b/nixos/modules/services/network-filesystems/openafs/server.nix
index a399aa6c23bca..8186277b47775 100644
--- a/nixos/modules/services/network-filesystems/openafs/server.nix
+++ b/nixos/modules/services/network-filesystems/openafs/server.nix
@@ -183,7 +183,7 @@ in {
 
           enableFabs = mkEnableOption ''
             FABS, the flexible AFS backup system. It stores volumes as dump files, relying on other
-            pre-existing backup solutions for handling them.
+            pre-existing backup solutions for handling them
           '';
 
           buserverArgs = mkOption {
diff --git a/nixos/modules/services/network-filesystems/samba-wsdd.nix b/nixos/modules/services/network-filesystems/samba-wsdd.nix
index 608b48cf0305c..f46bf802511ae 100644
--- a/nixos/modules/services/network-filesystems/samba-wsdd.nix
+++ b/nixos/modules/services/network-filesystems/samba-wsdd.nix
@@ -10,7 +10,7 @@ in {
     services.samba-wsdd = {
       enable = mkEnableOption ''
         Web Services Dynamic Discovery host daemon. This enables (Samba) hosts, like your local NAS device,
-        to be found by Web Service Discovery Clients like Windows.
+        to be found by Web Service Discovery Clients like Windows
       '';
       interface = mkOption {
         type = types.nullOr types.str;
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 66ef3f14ed700..c70d0cf7beac3 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -201,14 +201,10 @@ in
               message   = "If samba.nsswins is enabled, then samba.enableWinbindd must also be enabled";
             }
           ];
-        # Always provide a smb.conf to shut up programs like smbclient and smbspool.
-        environment.etc."samba/smb.conf".source = mkOptionDefault (
-          if cfg.enable then configFile
-          else pkgs.writeText "smb-dummy.conf" "# Samba is disabled."
-        );
       }
 
       (mkIf cfg.enable {
+        environment.etc."samba/smb.conf".source = configFile;
 
         system.nssModules = optional cfg.nsswins samba;
         system.nssDatabases.hosts = optional cfg.nsswins "wins";
diff --git a/nixos/modules/services/networking/antennas.nix b/nixos/modules/services/networking/antennas.nix
index ef98af22f20f2..a37df953fc923 100644
--- a/nixos/modules/services/networking/antennas.nix
+++ b/nixos/modules/services/networking/antennas.nix
@@ -50,10 +50,7 @@ in
       };
 
       serviceConfig = {
-         ExecStart = "${pkgs.antennas}/bin/antennas";
-
-        # Antennas expects all resources like html and config to be relative to it's working directory
-        WorkingDirectory = "${pkgs.antennas}/libexec/antennas/deps/antennas/";
+        ExecStart = "${pkgs.antennas}/bin/antennas";
 
         # Hardening
         CapabilityBoundingSet = [ "" ];
diff --git a/nixos/modules/services/networking/gns3-server.nix b/nixos/modules/services/networking/gns3-server.nix
index ba0d6be30f499..b2f25b158bbbc 100644
--- a/nixos/modules/services/networking/gns3-server.nix
+++ b/nixos/modules/services/networking/gns3-server.nix
@@ -87,17 +87,17 @@ in {
       };
 
       dynamips = {
-        enable = lib.mkEnableOption ''Whether to enable Dynamips support.'';
+        enable = lib.mkEnableOption ''Dynamips support'';
         package = lib.mkPackageOptionMD pkgs "dynamips" { };
       };
 
       ubridge = {
-        enable = lib.mkEnableOption ''Whether to enable uBridge support.'';
+        enable = lib.mkEnableOption ''uBridge support'';
         package = lib.mkPackageOptionMD pkgs "ubridge" { };
       };
 
       vpcs = {
-        enable = lib.mkEnableOption ''Whether to enable VPCS support.'';
+        enable = lib.mkEnableOption ''VPCS support'';
         package = lib.mkPackageOptionMD pkgs "vpcs" { };
       };
     };
diff --git a/nixos/modules/services/networking/haproxy.nix b/nixos/modules/services/networking/haproxy.nix
index c764b447b0cb9..19b096bf49069 100644
--- a/nixos/modules/services/networking/haproxy.nix
+++ b/nixos/modules/services/networking/haproxy.nix
@@ -17,7 +17,7 @@ with lib;
   options = {
     services.haproxy = {
 
-      enable = mkEnableOption "HAProxy, the reliable, high performance TCP/HTTP load balancer.";
+      enable = mkEnableOption "HAProxy, the reliable, high performance TCP/HTTP load balancer";
 
       package = mkPackageOption pkgs "haproxy" { };
 
diff --git a/nixos/modules/services/networking/hylafax/options.nix b/nixos/modules/services/networking/hylafax/options.nix
index 1880aebe7a6be..973dfa054afcb 100644
--- a/nixos/modules/services/networking/hylafax/options.nix
+++ b/nixos/modules/services/networking/hylafax/options.nix
@@ -312,9 +312,9 @@ in
     };
 
     faxqclean.enable.spoolInit = mkEnableOption ''
-      Purge old files from the spooling area with
+      purging old files from the spooling area with
       {file}`faxqclean`
-      each time the spooling area is initialized.
+      each time the spooling area is initialized
     '';
     faxqclean.enable.frequency = mkOption {
       type = nullOr nonEmptyStr;
diff --git a/nixos/modules/services/networking/netbird.nix b/nixos/modules/services/networking/netbird.nix
index 7add377896cab..e68c39946fe3b 100644
--- a/nixos/modules/services/networking/netbird.nix
+++ b/nixos/modules/services/networking/netbird.nix
@@ -37,7 +37,6 @@ in
 {
   meta.maintainers = with maintainers; [
     misuzu
-    thubrecht
   ];
   meta.doc = ./netbird.md;
 
diff --git a/nixos/modules/services/networking/netbird/dashboard.nix b/nixos/modules/services/networking/netbird/dashboard.nix
index 6fc3086155900..788b724231be3 100644
--- a/nixos/modules/services/networking/netbird/dashboard.nix
+++ b/nixos/modules/services/networking/netbird/dashboard.nix
@@ -39,7 +39,7 @@ in
 
     package = mkPackageOption pkgs "netbird-dashboard" { };
 
-    enableNginx = mkEnableOption "Nginx reverse-proxy to serve the dashboard.";
+    enableNginx = mkEnableOption "Nginx reverse-proxy to serve the dashboard";
 
     domain = mkOption {
       type = str;
diff --git a/nixos/modules/services/networking/netbird/management.nix b/nixos/modules/services/networking/netbird/management.nix
index 52f033959143c..f4b5bbf643239 100644
--- a/nixos/modules/services/networking/netbird/management.nix
+++ b/nixos/modules/services/networking/netbird/management.nix
@@ -137,7 +137,7 @@ in
 
 {
   options.services.netbird.server.management = {
-    enable = mkEnableOption "Netbird Management Service.";
+    enable = mkEnableOption "Netbird Management Service";
 
     package = mkPackageOption pkgs "netbird" { };
 
@@ -335,7 +335,7 @@ in
       description = "Log level of the netbird services.";
     };
 
-    enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird management service.";
+    enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird management service";
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/services/networking/netbird/server.nix b/nixos/modules/services/networking/netbird/server.nix
index 2b6ad696646e9..1725374d03c6b 100644
--- a/nixos/modules/services/networking/netbird/server.nix
+++ b/nixos/modules/services/networking/netbird/server.nix
@@ -16,7 +16,7 @@ in
 
 {
   meta = {
-    maintainers = with lib.maintainers; [thubrecht patrickdag];
+    maintainers = with lib.maintainers; [patrickdag];
     doc = ./server.md;
   };
 
@@ -31,7 +31,7 @@ in
   options.services.netbird.server = {
     enable = mkEnableOption "Netbird Server stack, comprising the dashboard, management API and signal service";
 
-    enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird server services.";
+    enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird server services";
 
     domain = mkOption {
       type = str;
diff --git a/nixos/modules/services/networking/netbird/signal.nix b/nixos/modules/services/networking/netbird/signal.nix
index 8408d20e874b5..b53e9d40c2eed 100644
--- a/nixos/modules/services/networking/netbird/signal.nix
+++ b/nixos/modules/services/networking/netbird/signal.nix
@@ -28,7 +28,7 @@ in
 
     package = mkPackageOption pkgs "netbird" { };
 
-    enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird signal service.";
+    enableNginx = mkEnableOption "Nginx reverse-proxy for the netbird signal service";
 
     domain = mkOption {
       type = str;
diff --git a/nixos/modules/services/networking/networkd-dispatcher.nix b/nixos/modules/services/networking/networkd-dispatcher.nix
index 039888e3c0646..427835870e59f 100644
--- a/nixos/modules/services/networking/networkd-dispatcher.nix
+++ b/nixos/modules/services/networking/networkd-dispatcher.nix
@@ -14,7 +14,7 @@ in {
       enable = mkEnableOption ''
         Networkd-dispatcher service for systemd-networkd connection status
         change. See [https://gitlab.com/craftyguy/networkd-dispatcher](upstream instructions)
-        for usage.
+        for usage
       '';
 
       rules = mkOption {
diff --git a/nixos/modules/services/networking/nncp.nix b/nixos/modules/services/networking/nncp.nix
index f4ed7ecc7d4a6..8c5b5a61a181d 100644
--- a/nixos/modules/services/networking/nncp.nix
+++ b/nixos/modules/services/networking/nncp.nix
@@ -34,9 +34,7 @@ in {
           [](#opt-programs.nncp.settings)
         '';
         socketActivation = {
-          enable = mkEnableOption ''
-            Whether to run nncp-daemon persistently or socket-activated.
-          '';
+          enable = mkEnableOption "socket activation for nncp-daemon";
           listenStreams = mkOption {
             type = with types; listOf str;
             description = ''
diff --git a/nixos/modules/services/networking/scion/scion-control.nix b/nixos/modules/services/networking/scion/scion-control.nix
index c3a22039aa524..b86f1ef26b358 100644
--- a/nixos/modules/services/networking/scion/scion-control.nix
+++ b/nixos/modules/services/networking/scion/scion-control.nix
@@ -24,7 +24,7 @@ let
       level = "info";
     };
   };
-  configFile = toml.generate "scion-control.toml" (defaultConfig // cfg.settings);
+  configFile = toml.generate "scion-control.toml" (recursiveUpdate defaultConfig cfg.settings);
 in
 {
   options.services.scion.scion-control = {
diff --git a/nixos/modules/services/networking/scion/scion-daemon.nix b/nixos/modules/services/networking/scion/scion-daemon.nix
index 53b56841c3929..a9c0c80f101f8 100644
--- a/nixos/modules/services/networking/scion/scion-daemon.nix
+++ b/nixos/modules/services/networking/scion/scion-daemon.nix
@@ -21,7 +21,7 @@ let
       level = "info";
     };
   };
-  configFile = toml.generate "scion-daemon.toml" (defaultConfig // cfg.settings);
+  configFile = toml.generate "scion-daemon.toml" (recursiveUpdate defaultConfig cfg.settings);
 in
 {
   options.services.scion.scion-daemon = {
diff --git a/nixos/modules/services/networking/scion/scion-dispatcher.nix b/nixos/modules/services/networking/scion/scion-dispatcher.nix
index 05d1fd0782af5..9118ebefa18f3 100644
--- a/nixos/modules/services/networking/scion/scion-dispatcher.nix
+++ b/nixos/modules/services/networking/scion/scion-dispatcher.nix
@@ -15,7 +15,7 @@ let
       level = "info";
     };
   };
-  configFile = toml.generate "scion-dispatcher.toml" (defaultConfig // cfg.settings);
+  configFile = toml.generate "scion-dispatcher.toml" (recursiveUpdate defaultConfig cfg.settings);
 in
 {
   options.services.scion.scion-dispatcher = {
diff --git a/nixos/modules/services/networking/scion/scion-router.nix b/nixos/modules/services/networking/scion/scion-router.nix
index 488dfd12b3a57..3579005a429ed 100644
--- a/nixos/modules/services/networking/scion/scion-router.nix
+++ b/nixos/modules/services/networking/scion/scion-router.nix
@@ -11,7 +11,7 @@ let
       config_dir = "/etc/scion";
     };
   };
-  configFile = toml.generate "scion-router.toml" (defaultConfig // cfg.settings);
+  configFile = toml.generate "scion-router.toml" (recursiveUpdate defaultConfig cfg.settings);
 in
 {
   options.services.scion.scion-router = {
diff --git a/nixos/modules/services/networking/scion/scion.nix b/nixos/modules/services/networking/scion/scion.nix
index 5e3445edbb89a..b8bfef8b93b58 100644
--- a/nixos/modules/services/networking/scion/scion.nix
+++ b/nixos/modules/services/networking/scion/scion.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 
@@ -17,6 +17,9 @@ in
     };
   };
   config = mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.scion
+    ];
     services.scion = {
       scion-dispatcher.enable = true;
       scion-daemon.enable = true;
diff --git a/nixos/modules/services/networking/wg-access-server.nix b/nixos/modules/services/networking/wg-access-server.nix
new file mode 100644
index 0000000000000..5876699924b22
--- /dev/null
+++ b/nixos/modules/services/networking/wg-access-server.nix
@@ -0,0 +1,124 @@
+{ config, pkgs, lib, ... }:
+let
+  inherit (lib) mkEnableOption mkPackageOption mkOption types;
+
+  cfg = config.services.wg-access-server;
+
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "config.yaml" cfg.settings;
+in
+{
+
+  options.services.wg-access-server = {
+    enable = mkEnableOption "wg-access-server";
+
+    package = mkPackageOption pkgs "wg-access-server" { };
+
+    settings = mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+        options = {
+          dns.enable = mkOption {
+            type = types.bool;
+            default = true;
+            description = ''
+              Enable/disable the embedded DNS proxy server.
+              This is enabled by default and allows VPN clients to avoid DNS leaks by sending all DNS requests to wg-access-server itself.
+            '';
+          };
+          storage = mkOption {
+            type = types.str;
+            default = "sqlite3://db.sqlite";
+            description = "A storage backend connection string. See [storage docs](https://www.freie-netze.org/wg-access-server/3-storage/)";
+          };
+        };
+      };
+      description = "See https://www.freie-netze.org/wg-access-server/2-configuration/ for possible options";
+    };
+
+    secretsFile = mkOption {
+      type = types.path;
+      description = ''
+        yaml file containing all secrets. this needs to be in the same structure as the configuration.
+
+        This must to contain the admin password and wireguard private key.
+        As well as the secrets for your auth backend.
+
+        Example:
+        ```yaml
+        adminPassword: <admin password>
+        wireguard:
+          privateKey: <wireguard private key>
+        auth:
+          oidc:
+            clientSecret: <client secret>
+        ```
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions =
+      map
+        (attrPath:
+          {
+            assertion = !lib.hasAttrByPath attrPath config.services.wg-access-server.settings;
+            message = ''
+              {option}`services.wg-access-server.settings.${lib.concatStringsSep "." attrPath}` must definded
+              in {option}`services.wg-access-server.secretsFile`.
+            '';
+          })
+        [
+          [ "adminPassword" ]
+          [ "wireguard" "privateKey" ]
+          [ "auth" "sessionStore" ]
+          [ "auth" "oidc" "clientSecret" ]
+          [ "auth" "gitlab" "clientSecret" ]
+        ];
+
+    boot.kernel.sysctl = {
+      "net.ipv4.conf.all.forwarding" = "1";
+      "net.ipv6.conf.all.forwarding" = "1";
+    };
+
+    systemd.services.wg-access-server = {
+      description = "WG access server";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      script = ''
+        # merge secrets into main config
+        yq eval-all "select(fileIndex == 0) * select(fileIndex == 1)" ${configFile} $CREDENTIALS_DIRECTORY/SECRETS_FILE \
+          > "$STATE_DIRECTORY/config.yml"
+
+        ${lib.getExe cfg.package} serve --config "$STATE_DIRECTORY/config.yml"
+      '';
+
+      path = with pkgs; [
+        iptables
+        # needed by startup script
+        yq-go
+      ];
+
+      serviceConfig =
+        let
+          capabilities = [
+            "CAP_NET_ADMIN"
+          ] ++ lib.optional cfg.settings.dns.enabled "CAP_NET_BIND_SERVICE";
+        in
+        {
+          WorkingDirectory = "/var/lib/wg-access-server";
+          StateDirectory = "wg-access-server";
+
+          LoadCredential = [
+            "SECRETS_FILE:${cfg.secretsFile}"
+          ];
+
+          # Hardening
+          DynamicUser = true;
+          AmbientCapabilities = capabilities;
+          CapabilityBoundingSet = capabilities;
+        };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/wstunnel.nix b/nixos/modules/services/networking/wstunnel.nix
index 1b169567624c0..bd7536351955a 100644
--- a/nixos/modules/services/networking/wstunnel.nix
+++ b/nixos/modules/services/networking/wstunnel.nix
@@ -1,83 +1,94 @@
-{ config, lib, options, pkgs, utils, ... }:
-with lib;
+{ config
+, lib
+, pkgs
+, ...
+}:
+
 let
   cfg = config.services.wstunnel;
-  attrsToArgs = attrs: utils.escapeSystemdExecArgs (
-    mapAttrsToList
-    (name: value: if value == true then "--${name}" else "--${name}=${value}")
-    attrs
-  );
 
-  hostPortToString = { host, port }: "${host}:${builtins.toString port}";
+  hostPortToString = { host, port }: "${host}:${toString port}";
 
   hostPortSubmodule = {
     options = {
-      host = mkOption {
+      host = lib.mkOption {
         description = "The hostname.";
-        type = types.str;
+        type = lib.types.str;
       };
-      port = mkOption {
+      port = lib.mkOption {
         description = "The port.";
-        type = types.port;
+        type = lib.types.port;
       };
     };
   };
 
   commonOptions = {
-    enable = mkOption {
-      description = "Whether to enable this `wstunnel` instance.";
-      type = types.bool;
+    enable = lib.mkEnableOption "this `wstunnel` instance." // {
       default = true;
     };
 
-    package = mkPackageOption pkgs "wstunnel" {};
+    package = lib.mkPackageOption pkgs "wstunnel" { };
 
-    autoStart = mkOption {
-      description = "Whether this tunnel server should be started automatically.";
-      type = types.bool;
-      default = true;
-    };
+    autoStart =
+      lib.mkEnableOption "starting this wstunnel instance automatically." // {
+        default = true;
+      };
 
-    extraArgs = mkOption {
-      description = "Extra command line arguments to pass to `wstunnel`. Attributes of the form `argName = true;` will be translated to `--argName`, and `argName = \"value\"` to `--argName=value`.";
-      type = with types; attrsOf (either str bool);
-      default = {};
+    extraArgs = lib.mkOption {
+      description = ''
+        Extra command line arguments to pass to `wstunnel`.
+        Attributes of the form `argName = true;` will be translated to `--argName`,
+        and `argName = \"value\"` to `--argName value`.
+      '';
+      type = with lib.types; attrsOf (either str bool);
+      default = { };
       example = {
         "someNewOption" = true;
         "someNewOptionWithValue" = "someValue";
       };
     };
 
-    loggingLevel = mkOption {
+    loggingLevel = lib.mkOption {
       description = ''
         Passed to --log-lvl
 
         Control the log verbosity. i.e: TRACE, DEBUG, INFO, WARN, ERROR, OFF
         For more details, checkout [EnvFilter](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#example-syntax)
       '';
-      type = types.nullOr types.str;
+      type = lib.types.nullOr lib.types.str;
       example = "INFO";
       default = null;
     };
 
-    environmentFile = mkOption {
-      description = "Environment file to be passed to the systemd service. Useful for passing secrets to the service to prevent them from being world-readable in the Nix store. Note however that the secrets are passed to `wstunnel` through the command line, which makes them locally readable for all users of the system at runtime.";
-      type = types.nullOr types.path;
+    environmentFile = lib.mkOption {
+      description = ''
+        Environment file to be passed to the systemd service.
+        Useful for passing secrets to the service to prevent them from being
+        world-readable in the Nix store.
+        Note however that the secrets are passed to `wstunnel` through
+        the command line, which makes them locally readable for all users of
+        the system at runtime.
+      '';
+      type = lib.types.nullOr lib.types.path;
       default = null;
       example = "/var/lib/secrets/wstunnelSecrets";
     };
   };
 
-  serverSubmodule = { config, ...}: {
+  serverSubmodule = { config, ... }: {
     options = commonOptions // {
-      listen = mkOption {
-        description = "Address and port to listen on. Setting the port to a value below 1024 will also give the process the required `CAP_NET_BIND_SERVICE` capability.";
-        type = types.submodule hostPortSubmodule;
+      listen = lib.mkOption {
+        description = ''
+          Address and port to listen on.
+          Setting the port to a value below 1024 will also give the process
+          the required `CAP_NET_BIND_SERVICE` capability.
+        '';
+        type = lib.types.submodule hostPortSubmodule;
         default = {
           host = "0.0.0.0";
           port = if config.enableHTTPS then 443 else 80;
         };
-        defaultText = literalExpression ''
+        defaultText = lib.literalExpression ''
           {
             host = "0.0.0.0";
             port = if enableHTTPS then 443 else 80;
@@ -85,39 +96,50 @@ let
         '';
       };
 
-      restrictTo = mkOption {
-        description = "Accepted traffic will be forwarded only to this service. Set to `null` to allow forwarding to arbitrary addresses.";
-        type = types.listOf (types.submodule hostPortSubmodule);
-        default = [];
+      restrictTo = lib.mkOption {
+        description = ''
+          Accepted traffic will be forwarded only to this service.
+        '';
+        type = lib.types.listOf (lib.types.submodule hostPortSubmodule);
+        default = [ ];
         example = [{
           host = "127.0.0.1";
           port = 51820;
         }];
       };
 
-      enableHTTPS = mkOption {
+      enableHTTPS = lib.mkOption {
         description = "Use HTTPS for the tunnel server.";
-        type = types.bool;
+        type = lib.types.bool;
         default = true;
       };
 
-      tlsCertificate = mkOption {
-        description = "TLS certificate to use instead of the hardcoded one in case of HTTPS connections. Use together with `tlsKey`.";
-        type = types.nullOr types.path;
+      tlsCertificate = lib.mkOption {
+        description = ''
+          TLS certificate to use instead of the hardcoded one in case of HTTPS connections.
+          Use together with `tlsKey`.
+        '';
+        type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/var/lib/secrets/cert.pem";
       };
 
-      tlsKey = mkOption {
-        description = "TLS key to use instead of the hardcoded on in case of HTTPS connections. Use together with `tlsCertificate`.";
-        type = types.nullOr types.path;
+      tlsKey = lib.mkOption {
+        description = ''
+          TLS key to use instead of the hardcoded on in case of HTTPS connections.
+          Use together with `tlsCertificate`.
+        '';
+        type = lib.types.nullOr lib.types.path;
         default = null;
         example = "/var/lib/secrets/key.pem";
       };
 
-      useACMEHost = mkOption {
-        description = "Use a certificate generated by the NixOS ACME module for the given host. Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`.";
-        type = types.nullOr types.str;
+      useACMEHost = lib.mkOption {
+        description = ''
+          Use a certificate generated by the NixOS ACME module for the given host.
+          Note that this will not generate a new certificate - you will need to do so with `security.acme.certs`.
+        '';
+        type = lib.types.nullOr lib.types.str;
         default = null;
         example = "example.com";
       };
@@ -126,95 +148,113 @@ let
 
   clientSubmodule = { config, ... }: {
     options = commonOptions // {
-      connectTo = mkOption {
+      connectTo = lib.mkOption {
         description = "Server address and port to connect to.";
-        type = types.str;
+        type = lib.types.str;
         example = "https://wstunnel.server.com:8443";
       };
 
-      localToRemote = mkOption {
+      localToRemote = lib.mkOption {
         description = ''Listen on local and forwards traffic from remote.'';
-        type = types.listOf (types.str);
-        default = [];
+        type = lib.types.listOf (lib.types.str);
+        default = [ ];
         example = [
           "tcp://1212:google.com:443"
           "unix:///tmp/wstunnel.sock:g.com:443"
         ];
       };
 
-      remoteToLocal = mkOption {
+      remoteToLocal = lib.mkOption {
         description = "Listen on remote and forwards traffic from local. Only tcp is supported";
-        type = types.listOf (types.str);
-        default = [];
+        type = lib.types.listOf lib.types.str;
+        default = [ ];
         example = [
           "tcp://1212:google.com:443"
           "unix://wstunnel.sock:g.com:443"
         ];
       };
 
-      addNetBind = mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024";
+      addNetBind = lib.mkEnableOption "Whether add CAP_NET_BIND_SERVICE to the tunnel service, this should be enabled if you want to bind port < 1024";
 
-      httpProxy = mkOption {
+      httpProxy = lib.mkOption {
         description = ''
           Proxy to use to connect to the wstunnel server (`USER:PASS@HOST:PORT`).
 
           ::: {.warning}
-          Passwords specified here will be world-readable in the Nix store! To pass a password to the service, point the `environmentFile` option to a file containing `PROXY_PASSWORD=<your-password-here>` and set this option to `<user>:$PROXY_PASSWORD@<host>:<port>`. Note however that this will also locally leak the passwords at runtime via e.g. /proc/<pid>/cmdline.
-
+          Passwords specified here will be world-readable in the Nix store!
+          To pass a password to the service, point the `environmentFile` option
+          to a file containing `PROXY_PASSWORD=<your-password-here>` and set
+          this option to `<user>:$PROXY_PASSWORD@<host>:<port>`.
+          Note however that this will also locally leak the passwords at
+          runtime via e.g. /proc/<pid>/cmdline.
           :::
         '';
-        type = types.nullOr types.str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      soMark = mkOption {
-        description = "Mark network packets with the SO_MARK sockoption with the specified value. Setting this option will also enable the required `CAP_NET_ADMIN` capability for the systemd service.";
-        type = types.nullOr types.int;
+      soMark = lib.mkOption {
+        description = ''
+          Mark network packets with the SO_MARK sockoption with the specified value.
+          Setting this option will also enable the required `CAP_NET_ADMIN` capability
+          for the systemd service.
+        '';
+        type = lib.types.nullOr lib.types.ints.unsigned;
         default = null;
       };
 
-      upgradePathPrefix = mkOption {
-        description = "Use a specific HTTP path prefix that will show up in the upgrade request to the `wstunnel` server. Useful when running `wstunnel` behind a reverse proxy.";
-        type = types.nullOr types.str;
+      upgradePathPrefix = lib.mkOption {
+        description = ''
+          Use a specific HTTP path prefix that will show up in the upgrade
+          request to the `wstunnel` server.
+          Useful when running `wstunnel` behind a reverse proxy.
+        '';
+        type = lib.types.nullOr lib.types.str;
         default = null;
         example = "wstunnel";
       };
 
-      tlsSNI = mkOption {
+      tlsSNI = lib.mkOption {
         description = "Use this as the SNI while connecting via TLS. Useful for circumventing hostname-based firewalls.";
-        type = types.nullOr types.str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      tlsVerifyCertificate = mkOption {
+      tlsVerifyCertificate = lib.mkOption {
         description = "Whether to verify the TLS certificate of the server. It might be useful to set this to `false` when working with the `tlsSNI` option.";
-        type = types.bool;
+        type = lib.types.bool;
         default = true;
       };
 
       # The original argument name `websocketPingFrequency` is a misnomer, as the frequency is the inverse of the interval.
-      websocketPingInterval = mkOption {
+      websocketPingInterval = lib.mkOption {
         description = "Frequency at which the client will send websocket ping to the server.";
-        type = types.nullOr types.ints.unsigned;
+        type = lib.types.nullOr lib.types.ints.unsigned;
         default = null;
       };
 
-      upgradeCredentials = mkOption {
+      upgradeCredentials = lib.mkOption {
         description = ''
-          Use these credentials to authenticate during the HTTP upgrade request (Basic authorization type, `USER:[PASS]`).
+          Use these credentials to authenticate during the HTTP upgrade request
+          (Basic authorization type, `USER:[PASS]`).
 
           ::: {.warning}
-          Passwords specified here will be world-readable in the Nix store! To pass a password to the service, point the `environmentFile` option to a file containing `HTTP_PASSWORD=<your-password-here>` and set this option to `<user>:$HTTP_PASSWORD`. Note however that this will also locally leak the passwords at runtime via e.g. /proc/<pid>/cmdline.
+          Passwords specified here will be world-readable in the Nix store!
+          To pass a password to the service, point the `environmentFile` option
+          to a file containing `HTTP_PASSWORD=<your-password-here>` and set this
+          option to `<user>:$HTTP_PASSWORD`.
+          Note however that this will also locally leak the passwords at runtime
+          via e.g. /proc/<pid>/cmdline.
           :::
         '';
-        type = types.nullOr types.str;
+        type = lib.types.nullOr lib.types.str;
         default = null;
       };
 
-      customHeaders = mkOption {
+      customHeaders = lib.mkOption {
         description = "Custom HTTP headers to send during the upgrade request.";
-        type = types.attrsOf types.str;
-        default = {};
+        type = lib.types.attrsOf lib.types.str;
+        default = { };
         example = {
           "X-Some-Header" = "some-value";
         };
@@ -224,49 +264,63 @@ let
 
   generateServerUnit = name: serverCfg: {
     name = "wstunnel-server-${name}";
-    value = {
-      description = "wstunnel server - ${name}";
-      requires = [ "network.target" "network-online.target" ];
-      after = [ "network.target" "network-online.target" ];
-      wantedBy = optional serverCfg.autoStart "multi-user.target";
-
-      serviceConfig = let
-        certConfig = config.security.acme.certs."${serverCfg.useACMEHost}";
-      in {
-        Type = "simple";
-        ExecStart = with serverCfg; let
-          resolvedTlsCertificate = if useACMEHost != null
-            then "${certConfig.directory}/fullchain.pem"
-            else tlsCertificate;
-          resolvedTlsKey = if useACMEHost != null
-            then "${certConfig.directory}/key.pem"
-            else tlsKey;
-        in ''
-          ${package}/bin/wstunnel \
+    value =
+      let
+        certConfig = config.security.acme.certs.${serverCfg.useACMEHost};
+      in
+      {
+        description = "wstunnel server - ${name}";
+        requires = [ "network.target" "network-online.target" ];
+        after = [ "network.target" "network-online.target" ];
+        wantedBy = lib.optional serverCfg.autoStart "multi-user.target";
+
+        environment.RUST_LOG = serverCfg.loggingLevel;
+
+        serviceConfig = {
+          Type = "exec";
+          EnvironmentFile =
+            lib.optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
+          DynamicUser = true;
+          SupplementaryGroups =
+            lib.optional (serverCfg.useACMEHost != null) certConfig.group;
+          PrivateTmp = true;
+          AmbientCapabilities =
+            lib.optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+          NoNewPrivileges = true;
+          RestrictNamespaces = "uts ipc pid user cgroup";
+          ProtectSystem = "strict";
+          ProtectHome = true;
+          ProtectKernelTunables = true;
+          ProtectKernelModules = true;
+          ProtectControlGroups = true;
+          PrivateDevices = true;
+          RestrictSUIDSGID = true;
+
+          Restart = "on-failure";
+          RestartSec = 2;
+          RestartSteps = 20;
+          RestartMaxDelaySec = "5min";
+        };
+
+        script = with serverCfg; ''
+          ${lib.getExe package} \
             server \
-            ${concatStringsSep " " (builtins.map (hostPair:   "--restrict-to ${utils.escapeSystemdExecArg (hostPortToString hostPair)}") restrictTo)} \
-            ${optionalString (resolvedTlsCertificate != null) "--tls-certificate ${utils.escapeSystemdExecArg resolvedTlsCertificate}"} \
-            ${optionalString (resolvedTlsKey != null)         "--tls-private-key ${utils.escapeSystemdExecArg resolvedTlsKey}"} \
-            ${optionalString (loggingLevel != null) "--log-lvl ${loggingLevel}"} \
-            ${attrsToArgs extraArgs} \
-            ${utils.escapeSystemdExecArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"}
+            ${lib.cli.toGNUCommandLineShell { } (
+              lib.recursiveUpdate
+              {
+                restrict-to = map hostPortToString restrictTo;
+                tls-certificate = if useACMEHost != null
+                                  then "${certConfig.directory}/fullchain.pem"
+                                  else "${tlsCertificate}";
+                tls-private-key = if useACMEHost != null
+                                  then "${certConfig.directory}/key.pem"
+                                  else "${tlsKey}";
+              }
+              extraArgs
+            )} \
+            ${lib.escapeShellArg "${if enableHTTPS then "wss" else "ws"}://${hostPortToString listen}"}
         '';
-        EnvironmentFile = optional (serverCfg.environmentFile != null) serverCfg.environmentFile;
-        DynamicUser = true;
-        SupplementaryGroups = optional (serverCfg.useACMEHost != null) certConfig.group;
-        PrivateTmp = true;
-        AmbientCapabilities = optionals (serverCfg.listen.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
-        NoNewPrivileges = true;
-        RestrictNamespaces = "uts ipc pid user cgroup";
-        ProtectSystem = "strict";
-        ProtectHome = true;
-        ProtectKernelTunables = true;
-        ProtectKernelModules = true;
-        ProtectControlGroups = true;
-        PrivateDevices = true;
-        RestrictSUIDSGID = true;
       };
-    };
   };
 
   generateClientUnit = name: clientCfg: {
@@ -275,30 +329,19 @@ let
       description = "wstunnel client - ${name}";
       requires = [ "network.target" "network-online.target" ];
       after = [ "network.target" "network-online.target" ];
-      wantedBy = optional clientCfg.autoStart "multi-user.target";
+      wantedBy = lib.optional clientCfg.autoStart "multi-user.target";
+
+      environment.RUST_LOG = clientCfg.loggingLevel;
 
       serviceConfig = {
-        Type = "simple";
-        ExecStart = with clientCfg; ''
-          ${package}/bin/wstunnel client \
-            ${concatStringsSep " " (builtins.map (x:          "--local-to-remote ${x}") localToRemote)} \
-            ${concatStringsSep " " (builtins.map (x:          "--remote-to-local ${x}") remoteToLocal)} \
-            ${concatStringsSep " " (mapAttrsToList (n: v:     "--http-headers \"${n}: ${v}\"") customHeaders)} \
-            ${optionalString (httpProxy != null)              "--http-proxy ${httpProxy}"} \
-            ${optionalString (soMark != null)                 "--socket-so-mark=${toString soMark}"} \
-            ${optionalString (upgradePathPrefix != null)      "--http-upgrade-path-prefix ${upgradePathPrefix}"} \
-            ${optionalString (tlsSNI != null)                 "--tls-sni-override ${tlsSNI}"} \
-            ${optionalString tlsVerifyCertificate             "--tls-verify-certificate"} \
-            ${optionalString (websocketPingInterval != null)  "--websocket-ping-frequency-sec ${toString websocketPingInterval}"} \
-            ${optionalString (upgradeCredentials != null)     "--http-upgrade-credentials ${upgradeCredentials}"} \
-            ${optionalString (loggingLevel != null) "--log-lvl ${loggingLevel}"} \
-            ${attrsToArgs extraArgs} \
-            ${utils.escapeSystemdExecArg connectTo}
-        '';
-        EnvironmentFile = optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
+        Type = "exec";
+        EnvironmentFile =
+          lib.optional (clientCfg.environmentFile != null) clientCfg.environmentFile;
         DynamicUser = true;
         PrivateTmp = true;
-        AmbientCapabilities = (optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]) ++ (optionals (clientCfg.addNetBind) [ "CAP_NET_BIND_SERVICE" ]);
+        AmbientCapabilities =
+          (lib.optionals clientCfg.addNetBind [ "CAP_NET_BIND_SERVICE" ]) ++
+          (lib.optionals (clientCfg.soMark != null) [ "CAP_NET_ADMIN" ]);
         NoNewPrivileges = true;
         RestrictNamespaces = "uts ipc pid user cgroup";
         ProtectSystem = "strict";
@@ -308,17 +351,45 @@ let
         ProtectControlGroups = true;
         PrivateDevices = true;
         RestrictSUIDSGID = true;
+
+        Restart = "on-failure";
+        RestartSec = 2;
+        RestartSteps = 20;
+        RestartMaxDelaySec = "5min";
       };
+
+      script = with clientCfg; ''
+        ${lib.getExe package} \
+          client \
+          ${lib.cli.toGNUCommandLineShell { } (
+            lib.recursiveUpdate
+            {
+              local-to-remote = localToRemote;
+              remote-to-local = remoteToLocal;
+              http-headers = lib.mapAttrsToList (n: v: "${n}:${v}") customHeaders;
+              http-proxy = httpProxy;
+              socket-so-mark = soMark;
+              http-upgrade-path-prefix = upgradePathPrefix;
+              tls-sni-override = tlsSNI;
+              tls-verify-certificate = tlsVerifyCertificate;
+              websocket-ping-frequency-sec = websocketPingInterval;
+              http-upgrade-credentials = upgradeCredentials;
+            }
+            extraArgs
+          )} \
+          ${lib.escapeShellArg connectTo}
+      '';
     };
   };
-in {
+in
+{
   options.services.wstunnel = {
-    enable = mkEnableOption "wstunnel";
+    enable = lib.mkEnableOption "wstunnel";
 
-    servers = mkOption {
+    servers = lib.mkOption {
       description = "`wstunnel` servers to set up.";
-      type = types.attrsOf (types.submodule serverSubmodule);
-      default = {};
+      type = lib.types.attrsOf (lib.types.submodule serverSubmodule);
+      default = { };
       example = {
         "wg-tunnel" = {
           listen = {
@@ -336,13 +407,13 @@ in {
       };
     };
 
-    clients = mkOption {
+    clients = lib.mkOption {
       description = "`wstunnel` clients to set up.";
-      type = types.attrsOf (types.submodule clientSubmodule);
-      default = {};
+      type = lib.types.attrsOf (lib.types.submodule clientSubmodule);
+      default = { };
       example = {
         "wg-tunnel" = {
-          connectTo = "https://wstunnel.server.com:8443";
+          connectTo = "wss://wstunnel.server.com:8443";
           localToRemote = [
             "tcp://1212:google.com:443"
             "tcp://2:n.lan:4?proxy_protocol"
@@ -356,28 +427,42 @@ in {
     };
   };
 
-  config = mkIf cfg.enable {
-    systemd.services = (mapAttrs' generateServerUnit (filterAttrs (n: v: v.enable) cfg.servers)) // (mapAttrs' generateClientUnit (filterAttrs (n: v: v.enable) cfg.clients));
-
-    assertions = (mapAttrsToList (name: serverCfg: {
-      assertion = !(serverCfg.useACMEHost != null && (serverCfg.tlsCertificate != null || serverCfg.tlsKey != null));
-      message = ''
-        Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive.
-      '';
-    }) cfg.servers) ++
-    (mapAttrsToList (name: serverCfg: {
-      assertion = !((serverCfg.tlsCertificate != null || serverCfg.tlsKey != null) && !(serverCfg.tlsCertificate != null && serverCfg.tlsKey != null));
-      message = ''
-        services.wstunnel.servers."${name}".tlsCertificate and services.wstunnel.servers."${name}".tlsKey need to be set together.
-      '';
-    }) cfg.servers) ++
-    (mapAttrsToList (name: clientCfg: {
-      assertion = !(clientCfg.localToRemote == [] && clientCfg.remoteToLocal == []);
-      message = ''
-        Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set.
-      '';
-    }) cfg.clients);
+  config = lib.mkIf cfg.enable {
+    systemd.services =
+      (lib.mapAttrs' generateServerUnit (lib.filterAttrs (n: v: v.enable) cfg.servers)) //
+      (lib.mapAttrs' generateClientUnit (lib.filterAttrs (n: v: v.enable) cfg.clients));
+
+    assertions =
+      (lib.mapAttrsToList
+        (name: serverCfg: {
+          assertion =
+            !(serverCfg.useACMEHost != null && serverCfg.tlsCertificate != null);
+          message = ''
+            Options services.wstunnel.servers."${name}".useACMEHost and services.wstunnel.servers."${name}".{tlsCertificate, tlsKey} are mutually exclusive.
+          '';
+        })
+        cfg.servers) ++
+
+      (lib.mapAttrsToList
+        (name: serverCfg: {
+          assertion =
+            (serverCfg.tlsCertificate == null && serverCfg.tlsKey == null) ||
+            (serverCfg.tlsCertificate != null && serverCfg.tlsKey != null);
+          message = ''
+            services.wstunnel.servers."${name}".tlsCertificate and services.wstunnel.servers."${name}".tlsKey need to be set together.
+          '';
+        })
+        cfg.servers) ++
+
+      (lib.mapAttrsToList
+        (name: clientCfg: {
+          assertion = !(clientCfg.localToRemote == [ ] && clientCfg.remoteToLocal == [ ]);
+          message = ''
+            Either one of services.wstunnel.clients."${name}".localToRemote or services.wstunnel.clients."${name}".remoteToLocal must be set.
+          '';
+        })
+        cfg.clients);
   };
 
-  meta.maintainers = with maintainers; [ alyaeanyx neverbehave ];
+  meta.maintainers = with lib.maintainers; [ alyaeanyx rvdp neverbehave ];
 }
diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix
index 86c1efc629a98..2485327ed44e6 100644
--- a/nixos/modules/services/networking/zerotierone.nix
+++ b/nixos/modules/services/networking/zerotierone.nix
@@ -4,7 +4,9 @@ with lib;
 
 let
   cfg = config.services.zerotierone;
-  localConfFile = pkgs.writeText "zt-local.conf" (builtins.toJSON cfg.localConf);
+
+  settingsFormat = pkgs.formats.json {};
+  localConfFile = settingsFormat.generate "zt-local.conf" cfg.localConf;
   localConfFilePath = "/var/lib/zerotier-one/local.conf";
 in
 {
@@ -32,7 +34,7 @@ in
   options.services.zerotierone.package = mkPackageOption pkgs "zerotierone" { };
 
   options.services.zerotierone.localConf = mkOption {
-    default = null;
+    default = {};
     description = ''
       Optional configuration to be written to the Zerotier JSON-based local.conf.
       If set, the configuration will be symlinked to `/var/lib/zerotier-one/local.conf` at build time.
@@ -41,7 +43,7 @@ in
     example = {
       settings.allowTcpFallbackRelay = false;
     };
-    type = types.nullOr types.attrs;
+    type = settingsFormat.type;
   };
 
   config = mkIf cfg.enable {
@@ -60,7 +62,7 @@ in
         chown -R root:root /var/lib/zerotier-one
       '' + (concatMapStrings (netId: ''
         touch "/var/lib/zerotier-one/networks.d/${netId}.conf"
-      '') cfg.joinNetworks) + optionalString (cfg.localConf != null) ''
+      '') cfg.joinNetworks) + optionalString (cfg.localConf != {}) ''
         if [ -L "${localConfFilePath}" ]
         then
           rm ${localConfFilePath}
diff --git a/nixos/modules/services/search/qdrant.nix b/nixos/modules/services/search/qdrant.nix
index f28178a5f1751..41a4e9b41f6d9 100644
--- a/nixos/modules/services/search/qdrant.nix
+++ b/nixos/modules/services/search/qdrant.nix
@@ -60,6 +60,7 @@ in {
 
   config = mkIf cfg.enable {
     services.qdrant.settings = {
+      service.static_content_dir = mkDefault pkgs.qdrant-web-ui;
       storage.storage_path = mkDefault "/var/lib/qdrant/storage";
       storage.snapshots_path = mkDefault "/var/lib/qdrant/snapshots";
       # The following default values are the same as in the default config,
diff --git a/nixos/modules/services/search/quickwit.nix b/nixos/modules/services/search/quickwit.nix
index 6b2db935cf0bf..c4cc0c2427dff 100644
--- a/nixos/modules/services/search/quickwit.nix
+++ b/nixos/modules/services/search/quickwit.nix
@@ -160,7 +160,7 @@ in
         ProtectProc = "invisible";
         ProtectSystem = "strict";
         ReadWritePaths = [
-          "/var/lib/quickwit"
+          cfg.dataDir
         ];
         RestrictAddressFamilies = [
           "AF_NETLINK"
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
index c4031b64ba6aa..b6ce42d7318c8 100644
--- a/nixos/modules/services/security/fail2ban.nix
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -263,7 +263,7 @@ in
         '';
         type = with types; attrsOf (either lines (submodule ({ name, ... }: {
           options = {
-            enabled = mkEnableOption "this jail." // {
+            enabled = mkEnableOption "this jail" // {
               default = true;
               readOnly = name == "DEFAULT";
             };
diff --git a/nixos/modules/services/security/haveged.nix b/nixos/modules/services/security/haveged.nix
index 57cef7e44d503..4c686d74268af 100644
--- a/nixos/modules/services/security/haveged.nix
+++ b/nixos/modules/services/security/haveged.nix
@@ -17,7 +17,7 @@ in
 
       enable = mkEnableOption ''
         haveged entropy daemon, which refills /dev/random when low.
-        NOTE: does nothing on kernels newer than 5.6.
+        NOTE: does nothing on kernels newer than 5.6
       '';
       # source for the note https://github.com/jirka-h/haveged/issues/57
 
diff --git a/nixos/modules/services/security/sslmate-agent.nix b/nixos/modules/services/security/sslmate-agent.nix
index c850eb22a0311..57cb955a39dd9 100644
--- a/nixos/modules/services/security/sslmate-agent.nix
+++ b/nixos/modules/services/security/sslmate-agent.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.sslmate-agent;
 
 in {
-  meta.maintainers = with maintainers; [ wolfangaukang ];
+  meta.maintainers = [ ];
 
   options = {
     services.sslmate-agent = {
diff --git a/nixos/modules/services/security/step-ca.nix b/nixos/modules/services/security/step-ca.nix
index e9195fbd51608..43bc402e7818b 100644
--- a/nixos/modules/services/security/step-ca.nix
+++ b/nixos/modules/services/security/step-ca.nix
@@ -4,7 +4,7 @@ let
   settingsFormat = (pkgs.formats.json { });
 in
 {
-  meta.maintainers = with lib.maintainers; [ mohe2015 ];
+  meta.maintainers = with lib.maintainers; [ ];
 
   options = {
     services.step-ca = {
diff --git a/nixos/modules/services/security/vaultwarden/backup.sh b/nixos/modules/services/security/vaultwarden/backup.sh
index 7668da5bc88f3..0c1cd3aa544f6 100644
--- a/nixos/modules/services/security/vaultwarden/backup.sh
+++ b/nixos/modules/services/security/vaultwarden/backup.sh
@@ -1,17 +1,21 @@
 #!/usr/bin/env bash
 
+# Allow use of !() when copying to not copy certain files
+shopt -s extglob
+
 # Based on: https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault
 if [ ! -d "$BACKUP_FOLDER" ]; then
   echo "Backup folder '$BACKUP_FOLDER' does not exist" >&2
   exit 1
 fi
 
-if [[ ! -f "$DATA_FOLDER"/db.sqlite3 ]]; then
-  echo "Could not find SQLite database file '$DATA_FOLDER/db.sqlite3'" >&2
-  exit 1
+if [[ -f "$DATA_FOLDER"/db.sqlite3 ]]; then
+  sqlite3 "$DATA_FOLDER"/db.sqlite3 ".backup '$BACKUP_FOLDER/db.sqlite3'"
+fi
+
+if [ ! -d "$DATA_FOLDER" ]; then
+  echo "No data folder (yet). This will happen on first launch if backup is triggered before vaultwarden has started."
+  exit 0
 fi
 
-sqlite3 "$DATA_FOLDER"/db.sqlite3 ".backup '$BACKUP_FOLDER/db.sqlite3'"
-cp "$DATA_FOLDER"/rsa_key.{der,pem,pub.der} "$BACKUP_FOLDER"
-cp -r "$DATA_FOLDER"/attachments "$BACKUP_FOLDER"
-cp -r "$DATA_FOLDER"/icon_cache "$BACKUP_FOLDER"
+cp -r "$DATA_FOLDER"/!(db.*) "$BACKUP_FOLDER"/
diff --git a/nixos/modules/services/system/localtimed.nix b/nixos/modules/services/system/localtimed.nix
index 8af22892a117c..bd83d227aa35c 100644
--- a/nixos/modules/services/system/localtimed.nix
+++ b/nixos/modules/services/system/localtimed.nix
@@ -18,6 +18,8 @@ in {
           geoclue2 to determine the current location.
         '';
       };
+      package = mkPackageOption pkgs "localtime" { };
+      geoclue2Package = mkPackageOption pkgs "geoclue2-with-demo-agent" { };
     };
   };
 
@@ -29,14 +31,14 @@ in {
     };
 
     # Install the polkit rules.
-    environment.systemPackages = [ pkgs.localtime ];
+    environment.systemPackages = [ cfg.package ];
 
     systemd.services.localtimed = {
       wantedBy = [ "multi-user.target" ];
       partOf = [ "localtimed-geoclue-agent.service" ];
       after = [ "localtimed-geoclue-agent.service" ];
       serviceConfig = {
-        ExecStart = "${pkgs.localtime}/bin/localtimed";
+        ExecStart = "${cfg.package}/bin/localtimed";
         Restart = "on-failure";
         Type = "exec";
         User = "localtimed";
@@ -48,7 +50,7 @@ in {
       partOf = [ "geoclue.service" ];
       after = [ "geoclue.service" ];
       serviceConfig = {
-        ExecStart = "${pkgs.geoclue2-with-demo-agent}/libexec/geoclue-2.0/demos/agent";
+        ExecStart = "${cfg.geoclue2Package}/libexec/geoclue-2.0/demos/agent";
         Restart = "on-failure";
         Type = "exec";
         User = "localtimed";
diff --git a/nixos/modules/services/torrent/flood.nix b/nixos/modules/services/torrent/flood.nix
new file mode 100644
index 0000000000000..213f4ef046483
--- /dev/null
+++ b/nixos/modules/services/torrent/flood.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, utils, ... }:
+
+let
+  cfg = config.services.flood;
+in
+{
+  meta.maintainers = with lib.maintainers; [ thiagokokada ];
+
+  options.services.flood = {
+    enable = lib.mkEnableOption "flood";
+    package = lib.mkPackageOption pkgs "flood" { };
+    openFirewall = lib.mkEnableOption "" // {
+      description = "Whether to open the firewall for the port in {option}`services.flood.port`.";
+    };
+    port = lib.mkOption {
+      type = lib.types.int;
+      description = "Port to bind webserver.";
+      default = 3000;
+      example = 3001;
+    };
+    host = lib.mkOption {
+      type = lib.types.str;
+      description = "Host to bind webserver.";
+      default = "localhost";
+      example = "::";
+    };
+    extraArgs = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "Extra arguments passed to `flood`.";
+      default = [ ];
+      example = [ "--baseuri=/" ];
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.flood = {
+      description = "A modern web UI for various torrent clients.";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      unitConfig = {
+        Documentation = "https://github.com/jesec/flood/wiki";
+      };
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = "3s";
+        ExecStart = utils.escapeSystemdExecArgs ([
+          (lib.getExe cfg.package)
+          "--host"
+          cfg.host
+          "--port"
+          (toString cfg.port)
+          "--rundir=/var/lib/flood"
+        ] ++ cfg.extraArgs);
+
+        CapabilityBoundingSet = [ "" ];
+        DynamicUser = true;
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        StateDirectory = "flood";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "@pkey" "~@privileged" ];
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
+      cfg.port
+    ];
+  };
+}
+
diff --git a/nixos/modules/services/torrent/rtorrent.nix b/nixos/modules/services/torrent/rtorrent.nix
index 009c2ffe0a5b0..e0ce33d13462e 100644
--- a/nixos/modules/services/torrent/rtorrent.nix
+++ b/nixos/modules/services/torrent/rtorrent.nix
@@ -8,6 +8,8 @@ let
   opt = options.services.rtorrent;
 
 in {
+  meta.maintainers = with lib.maintainers; [ thiagokokada ];
+
   options.services.rtorrent = {
     enable = mkEnableOption "rtorrent";
 
@@ -202,7 +204,31 @@ in {
             ExecStartPre=''${pkgs.bash}/bin/bash -c "if test -e ${cfg.dataDir}/session/rtorrent.lock && test -z $(${pkgs.procps}/bin/pidof rtorrent); then rm -f ${cfg.dataDir}/session/rtorrent.lock; fi"'';
             ExecStart="${cfg.package}/bin/rtorrent -n -o system.daemon.set=true -o import=${rtorrentConfigFile}";
             RuntimeDirectory = "rtorrent";
-            RuntimeDirectoryMode = 755;
+            RuntimeDirectoryMode = 750;
+
+            CapabilityBoundingSet = [ "" ];
+            LockPersonality = true;
+            NoNewPrivileges = true;
+            PrivateDevices = true;
+            PrivateTmp = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            # If the default user is changed, there is a good chance that they
+            # want to store data in e.g.: $HOME directory
+            # Relax hardening in this case
+            ProtectHome = lib.mkIf (cfg.user == "rtorrent") true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            ProtectProc = "invisible";
+            ProtectSystem = "full";
+            RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+            RestrictNamespaces = true;
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            SystemCallArchitectures = "native";
+            SystemCallFilter = [ "@system-service" "~@privileged" ];
           };
         };
       };
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 52b472631dcfe..ceef0db78b094 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -174,7 +174,10 @@ in
         };
       };
 
-      package = mkPackageOption pkgs "transmission" {};
+      package = mkPackageOption pkgs "transmission" {
+        default = "transmission_3";
+        example = "pkgs.transmission_4";
+      };
 
       downloadDirPermissions = mkOption {
         type = with types; nullOr str;
diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix
index 74314e1e76e40..4803d577b94b1 100644
--- a/nixos/modules/services/ttys/kmscon.nix
+++ b/nixos/modules/services/ttys/kmscon.nix
@@ -67,36 +67,24 @@ in {
   };
 
   config = mkIf cfg.enable {
-    # Largely copied from unit provided with kmscon source
-    systemd.units."kmsconvt@.service".text = ''
-      [Unit]
-      Description=KMS System Console on %I
-      Documentation=man:kmscon(1)
-      After=systemd-user-sessions.service
-      After=plymouth-quit-wait.service
-      After=systemd-logind.service
-      After=systemd-vconsole-setup.service
-      Requires=systemd-logind.service
-      Before=getty.target
-      Conflicts=getty@%i.service
-      OnFailure=getty@%i.service
-      IgnoreOnIsolate=yes
-      ConditionPathExists=/dev/tty0
-
-      [Service]
-      ExecStart=
-      ExecStart=${pkgs.kmscon}/bin/kmscon "--vt=%I" ${cfg.extraOptions} --seats=seat0 --no-switchvt --configdir ${configDir} --login -- ${pkgs.shadow}/bin/login -p ${autologinArg}
-      UtmpIdentifier=%I
-      TTYPath=/dev/%I
-      TTYReset=yes
-      TTYVHangup=yes
-      TTYVTDisallocate=yes
-
-      X-RestartIfChanged=false
-    '';
+    systemd.packages = [ pkgs.kmscon ];
+
+    systemd.services."kmsconvt@" = {
+      after = [ "systemd-logind.service" "systemd-vconsole-setup.service" ];
+      requires = [ "systemd-logind.service" ];
+
+      serviceConfig.ExecStart = [
+        ""
+        ''
+          ${pkgs.kmscon}/bin/kmscon "--vt=%I" ${cfg.extraOptions} --seats=seat0 --no-switchvt --configdir ${configDir} --login -- ${pkgs.shadow}/bin/login -p ${autologinArg}
+        ''
+      ];
+
+      restartIfChanged = false;
+      aliases = [ "autovt@.service" ];
+    };
 
     systemd.suppressedSystemUnits = [ "autovt@.service" ];
-    systemd.units."kmsconvt@.service".aliases = [ "autovt@.service" ];
 
     systemd.services.systemd-vconsole-setup.enable = false;
     systemd.services.reload-systemd-vconsole-setup.enable = false;
@@ -107,7 +95,7 @@ in {
         fonts = optional (cfg.fonts != null) "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}";
       in lib.concatStringsSep "\n" (render ++ fonts);
 
-    hardware.opengl.enable = mkIf cfg.hwRender true;
+    hardware.graphics.enable = mkIf cfg.hwRender true;
 
     fonts = mkIf (cfg.fonts != null) {
       fontconfig.enable = true;
diff --git a/nixos/modules/services/wayland/cage.nix b/nixos/modules/services/wayland/cage.nix
index 91949f197cfed..870ae58f8646e 100644
--- a/nixos/modules/services/wayland/cage.nix
+++ b/nixos/modules/services/wayland/cage.nix
@@ -101,7 +101,7 @@ in {
       session required ${config.systemd.package}/lib/security/pam_systemd.so
     '';
 
-    hardware.opengl.enable = mkDefault true;
+    hardware.graphics.enable = mkDefault true;
 
     systemd.targets.graphical.wants = [ "cage-tty1.service" ];
 
diff --git a/nixos/modules/services/web-apps/akkoma.nix b/nixos/modules/services/web-apps/akkoma.nix
index 338e7c7383380..8ba3c7eaa1e6a 100644
--- a/nixos/modules/services/web-apps/akkoma.nix
+++ b/nixos/modules/services/web-apps/akkoma.nix
@@ -1136,6 +1136,6 @@ in {
     };
   };
 
-  meta.maintainers = with maintainers; [ mvs tcmal ];
+  meta.maintainers = with maintainers; [ mvs ];
   meta.doc = ./akkoma.md;
 }
diff --git a/nixos/modules/services/web-apps/audiobookshelf.nix b/nixos/modules/services/web-apps/audiobookshelf.nix
index 84dffc5f9d3c5..2f00c852ac8fe 100644
--- a/nixos/modules/services/web-apps/audiobookshelf.nix
+++ b/nixos/modules/services/web-apps/audiobookshelf.nix
@@ -8,7 +8,7 @@ in
 {
   options = {
     services.audiobookshelf = {
-      enable = mkEnableOption "Audiobookshelf, self-hosted audiobook and podcast server.";
+      enable = mkEnableOption "Audiobookshelf, self-hosted audiobook and podcast server";
 
       package = mkPackageOption pkgs "audiobookshelf" { };
 
diff --git a/nixos/modules/services/web-apps/firefly-iii.nix b/nixos/modules/services/web-apps/firefly-iii.nix
index 3e51bd226b02e..338f049093202 100644
--- a/nixos/modules/services/web-apps/firefly-iii.nix
+++ b/nixos/modules/services/web-apps/firefly-iii.nix
@@ -33,10 +33,10 @@ let
 
     ${optionalString (cfg.settings.DB_CONNECTION == "sqlite")
       "touch ${cfg.dataDir}/storage/database/database.sqlite"}
+    ${artisan} cache:clear
     ${artisan} package:discover
     ${artisan} firefly-iii:upgrade-database
     ${artisan} firefly-iii:laravel-passport-keys
-    ${artisan} cache:clear
     ${artisan} view:cache
     ${artisan} route:cache
     ${artisan} config:cache
@@ -283,8 +283,6 @@ in {
       before = [ "phpfpm-firefly-iii.service" ];
       serviceConfig = {
         ExecStart = firefly-iii-maintenance;
-        RuntimeDirectory = "phpfpm";
-        RuntimeDirectoryPreserve = true;
         RemainAfterExit = true;
       } // commonServiceConfig;
       unitConfig.JoinsNamespaceOf = "phpfpm-firefly-iii.service";
diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix
index 76753b89ec9ea..247b65c786636 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -170,7 +170,7 @@ in
       '';
     };
 
-    caddy.enable = mkEnableOption "Whether to enable caddy reverse proxy to expose jitsi-meet";
+    caddy.enable = mkEnableOption "caddy reverse proxy to expose jitsi-meet";
 
     prosody.enable = mkOption {
       type = bool;
diff --git a/nixos/modules/services/web-apps/keycloak.md b/nixos/modules/services/web-apps/keycloak.md
index 020bee4003489..4036885ce151c 100644
--- a/nixos/modules/services/web-apps/keycloak.md
+++ b/nixos/modules/services/web-apps/keycloak.md
@@ -68,13 +68,11 @@ to `/auth`. See the option description
 for more details.
 :::
 
-[](#opt-services.keycloak.settings.hostname-strict-backchannel)
-determines whether Keycloak should force all requests to go
-through the frontend URL. By default,
-Keycloak allows backend requests to
-instead use its local hostname or IP address and may also
-advertise it to clients through its OpenID Connect Discovery
-endpoint.
+[](#opt-services.keycloak.settings.hostname-backchannel-dynamic)
+Keycloak has the capability to offer a separate URL for backchannel requests,
+enabling internal communication while maintaining the use of a public URL
+for frontchannel requests. Moreover, the backchannel is dynamically
+resolved based on incoming headers endpoint.
 
 For more information on hostname configuration, see the [Hostname
 section of the Keycloak Server Installation and Configuration
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index 6d472cf48cd01..36bae2575974e 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -328,8 +328,7 @@ in
             };
 
             hostname = mkOption {
-              type = nullOr str;
-              default = null;
+              type = str;
               example = "keycloak.example.com";
               description = ''
                 The hostname part of the public URL used as base for
@@ -340,16 +339,13 @@ in
               '';
             };
 
-            hostname-strict-backchannel = mkOption {
+            hostname-backchannel-dynamic = mkOption {
               type = bool;
               default = false;
               example = true;
               description = ''
-                Whether Keycloak should force all requests to go
-                through the frontend URL. By default, Keycloak allows
-                backend requests to instead use its local hostname or
-                IP address and may also advertise it to clients
-                through its OpenID Connect Discovery endpoint.
+                Enables dynamic resolving of backchannel URLs,
+                including hostname, scheme, port and context path.
 
                 See <https://www.keycloak.org/server/hostname>
                 for more information about hostname configuration.
@@ -482,12 +478,20 @@ in
             message = "Setting up a local PostgreSQL db for Keycloak requires `standard_conforming_strings` turned on to work reliably";
           }
           {
-            assertion = cfg.settings.hostname != null || cfg.settings.hostname-url or null != null;
-            message = "Setting the Keycloak hostname is required, see `services.keycloak.settings.hostname`";
+            assertion = cfg.settings.hostname-url or null == null;
+            message = ''
+              The option `services.keycloak.settings.hostname-url' has been removed.
+              Set `services.keycloak.settings.hostname' instead.
+              See [New Hostname options](https://www.keycloak.org/docs/25.0.0/upgrading/#new-hostname-options) for details.
+            '';
           }
           {
-            assertion = !(cfg.settings.hostname != null && cfg.settings.hostname-url or null != null);
-            message = "`services.keycloak.settings.hostname` and `services.keycloak.settings.hostname-url` are mutually exclusive";
+            assertion = cfg.settings.hostname-strict-backchannel or null == null;
+            message = ''
+              The option `services.keycloak.settings.hostname-strict-backchannel' has been removed.
+              Set `services.keycloak.settings.hostname-backchannel-dynamic' instead.
+              See [New Hostname options](https://www.keycloak.org/docs/25.0.0/upgrading/#new-hostname-options) for details.
+            '';
           }
         ];
 
diff --git a/nixos/modules/services/web-apps/mealie.nix b/nixos/modules/services/web-apps/mealie.nix
index 0d41cffd3d9dd..2484b2489c0d0 100644
--- a/nixos/modules/services/web-apps/mealie.nix
+++ b/nixos/modules/services/web-apps/mealie.nix
@@ -59,7 +59,7 @@ in
         PRODUCTION = "true";
         ALEMBIC_CONFIG_FILE="${pkg}/config/alembic.ini";
         API_PORT = toString cfg.port;
-        BASE_URL = "http://localhost:${cfg.port}";
+        BASE_URL = "http://localhost:${toString cfg.port}";
         DATA_DIR = "/var/lib/mealie";
         CRF_MODEL_PATH = "/var/lib/mealie/model.crfmodel";
       } // (builtins.mapAttrs (_: val: toString val) cfg.settings);
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index d7eb2c6cb734f..bfb3e73e65102 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -80,6 +80,12 @@ let
     mkKeyValue = generators.mkKeyValueDefault {} " = ";
   };
 
+  phpCli = concatStringsSep " " ([
+    "${getExe phpPackage}"
+  ] ++ optionals (cfg.cli.memoryLimit != null) [
+    "-dmemory_limit=${cfg.cli.memoryLimit}"
+  ]);
+
   occ = pkgs.writeScriptBin "nextcloud-occ" ''
     #! ${pkgs.runtimeShell}
     cd ${webroot}
@@ -89,7 +95,7 @@ let
     fi
     export NEXTCLOUD_CONFIG_DIR="${datadir}/config"
     $sudo \
-      ${phpPackage}/bin/php \
+      ${phpCli} \
       occ "$@"
   '';
 
@@ -196,6 +202,9 @@ let
 in {
 
   imports = [
+    (mkRenamedOptionModule
+      [ "services" "nextcloud" "cron" "memoryLimit" ]
+      [ "services" "nextcloud" "cli" "memoryLimit" ])
     (mkRemovedOptionModule [ "services" "nextcloud" "enableBrokenCiphersForSSE" ] ''
       This option has no effect since there's no supported Nextcloud version packaged here
       using OpenSSL for RC4 SSE.
@@ -291,7 +300,7 @@ in {
     package = mkOption {
       type = types.package;
       description = "Which package to use for the Nextcloud instance.";
-      relatedPackages = [ "nextcloud26" "nextcloud27" "nextcloud28" ];
+      relatedPackages = [ "nextcloud28" "nextcloud29" ];
     };
     phpPackage = mkPackageOption pkgs "php" {
       example = "php82";
@@ -446,7 +455,13 @@ in {
       dbtableprefix = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = "Table prefix in Nextcloud's database.";
+        description = ''
+          Table prefix in Nextcloud's database.
+
+          __Note:__ since Nextcloud 20 it's not an option anymore to create a database
+          schema with a custom table prefix. This option only exists for backwards compatibility
+          with installations that were originally provisioned with Nextcloud <20.
+        '';
       };
       adminuser = mkOption {
         type = types.str;
@@ -474,7 +489,7 @@ in {
             implementation into the virtual filesystem.
 
             Further details about this feature can be found in the
-            [upstream documentation](https://docs.nextcloud.com/server/22/admin_manual/configuration_files/primary_storage.html).
+            [upstream documentation](https://docs.nextcloud.com/server/22/admin_manual/configuration_files/primary_storage.html)
           '';
           bucket = mkOption {
             type = types.str;
@@ -576,7 +591,7 @@ in {
         This is used by the theming app and for generating previews of certain images (e.g. SVG and HEIF).
         You may want to disable it for increased security. In that case, previews will still be available
         for some images (e.g. JPEG and PNG).
-        See <https://github.com/nextcloud/server/issues/13099>.
+        See <https://github.com/nextcloud/server/issues/13099>
     '' // {
       default = true;
     };
@@ -642,7 +657,6 @@ in {
       type = types.package;
       default = occ;
       defaultText = literalMD "generated script";
-      internal = true;
       description = ''
         The nextcloud-occ program preconfigured to target this Nextcloud instance.
       '';
@@ -794,7 +808,7 @@ in {
       };
     };
 
-    cron.memoryLimit = mkOption {
+    cli.memoryLimit = mkOption {
       type = types.nullOr types.str;
       default = null;
       example = "1G";
@@ -826,6 +840,13 @@ in {
           Using config.services.nextcloud.poolConfig is deprecated and will become unsupported in a future release.
           Please migrate your configuration to config.services.nextcloud.poolSettings.
         '')
+        ++ (optional (cfg.config.dbtableprefix != null) ''
+          Using `services.nextcloud.config.dbtableprefix` is deprecated. Fresh installations with this
+          option set are not allowed anymore since v20.
+
+          If you have an existing installation with a custom table prefix, make sure it is
+          set correctly in `config.php` and remove the option from your NixOS config.
+        '')
         ++ (optional (versionOlder cfg.package.version "25") (upgradeWarning 24 "22.11"))
         ++ (optional (versionOlder cfg.package.version "26") (upgradeWarning 25 "23.05"))
         ++ (optional (versionOlder cfg.package.version "27") (upgradeWarning 26 "23.11"))
@@ -840,8 +861,6 @@ in {
               nextcloud defined in an overlay, please set `services.nextcloud.package` to
               `pkgs.nextcloud`.
             ''
-          else if versionOlder stateVersion "23.05" then nextcloud25
-          else if versionOlder stateVersion "23.11" then nextcloud26
           else if versionOlder stateVersion "24.05" then nextcloud27
           else nextcloud29
         );
@@ -1010,14 +1029,8 @@ in {
           serviceConfig = {
             Type = "exec";
             User = "nextcloud";
-            ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e";
-            ExecStart = lib.concatStringsSep " " ([
-              (lib.getExe phpPackage)
-            ] ++ optional (cfg.cron.memoryLimit != null) "-dmemory_limit=${cfg.cron.memoryLimit}"
-              ++ [
-              "-f"
-              "${webroot}/cron.php"
-            ]);
+            ExecCondition = "${phpCli} -f ${webroot}/occ status -e";
+            ExecStart = "${phpCli} -f ${webroot}/cron.php";
             KillMode = "process";
           };
         };
@@ -1041,7 +1054,7 @@ in {
           serviceConfig = {
             Type = "exec";
             User = "nextcloud";
-            ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e";
+            ExecCondition = "${phpCli} -f ${webroot}/occ status -e";
           };
         };
       };
diff --git a/nixos/modules/services/web-apps/pretix.nix b/nixos/modules/services/web-apps/pretix.nix
index 9786b61160260..0fb635964fe65 100644
--- a/nixos/modules/services/web-apps/pretix.nix
+++ b/nixos/modules/services/web-apps/pretix.nix
@@ -63,7 +63,7 @@ in
   };
 
   options.services.pretix = {
-    enable = mkEnableOption "Pretix, a ticket shop application for conferences, festivals, concerts, etc.";
+    enable = mkEnableOption "Pretix, a ticket shop application for conferences, festivals, concerts, etc";
 
     package = mkPackageOption pkgs "pretix" { };
 
diff --git a/nixos/modules/services/web-apps/silverbullet.nix b/nixos/modules/services/web-apps/silverbullet.nix
index c316d074cbaab..5d5f950a9a661 100644
--- a/nixos/modules/services/web-apps/silverbullet.nix
+++ b/nixos/modules/services/web-apps/silverbullet.nix
@@ -12,7 +12,7 @@ in
 {
   options = {
     services.silverbullet = {
-      enable = lib.mkEnableOption "Silverbullet, an open-source, self-hosted, offline-capable Personal Knowledge Management (PKM) web application.";
+      enable = lib.mkEnableOption "Silverbullet, an open-source, self-hosted, offline-capable Personal Knowledge Management (PKM) web application";
 
       package = lib.mkPackageOptionMD pkgs "silverbullet" { };
 
diff --git a/nixos/modules/services/web-apps/suwayomi-server.nix b/nixos/modules/services/web-apps/suwayomi-server.nix
index 5b61852a534dc..ba2352d0e693f 100644
--- a/nixos/modules/services/web-apps/suwayomi-server.nix
+++ b/nixos/modules/services/web-apps/suwayomi-server.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.suwayomi-server = {
-      enable = mkEnableOption "Suwayomi, a free and open source manga reader server that runs extensions built for Tachiyomi.";
+      enable = mkEnableOption "Suwayomi, a free and open source manga reader server that runs extensions built for Tachiyomi";
 
       package = lib.mkPackageOptionMD pkgs "suwayomi-server" { };
 
@@ -72,7 +72,7 @@ in
               };
 
               basicAuthEnabled = mkEnableOption ''
-                Add basic access authentication to Suwayomi-Server.
+                basic access authentication for Suwayomi-Server.
                 Enabling this option is useful when hosting on a public network/the Internet
               '';
 
diff --git a/nixos/modules/services/web-apps/zitadel.nix b/nixos/modules/services/web-apps/zitadel.nix
index 99b0a0bc56f67..ed7fae8d9dda0 100644
--- a/nixos/modules/services/web-apps/zitadel.nix
+++ b/nixos/modules/services/web-apps/zitadel.nix
@@ -219,5 +219,5 @@ in
     users.groups.zitadel = lib.mkIf (cfg.group == "zitadel") { };
   };
 
-  meta.maintainers = with lib.maintainers; [ Sorixelle ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/services/web-servers/bluemap.nix b/nixos/modules/services/web-servers/bluemap.nix
index 28eaad3db313e..731468fd9a0ec 100644
--- a/nixos/modules/services/web-servers/bluemap.nix
+++ b/nixos/modules/services/web-servers/bluemap.nix
@@ -71,9 +71,7 @@ in {
 
     host = mkOption {
       type = lib.types.str;
-      default = "bluemap.${config.networking.domain}";
-      defaultText = lib.literalExpression "bluemap.\${config.networking.domain}";
-      description = "Domain to configure nginx for";
+      description = "Domain on which nginx will serve the bluemap webapp";
     };
 
     onCalendar = mkOption {
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index f9720c3629353..b5ff630a4d484 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -1086,9 +1086,9 @@ in
         '';
         description = "Declarative vhost config";
       };
-      validateConfigFile = lib.mkEnableOption ''
-        Validate configuration with pkgs.writeNginxConfig.
-      '' // { default = true; };
+      validateConfigFile = lib.mkEnableOption "validating configuration with pkgs.writeNginxConfig" // {
+        default = true;
+      };
     };
   };
 
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index e243778cc747c..1c5a9af5c9245 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -21,6 +21,14 @@ in
         example = "tomcat10";
       };
 
+      port = lib.mkOption {
+        type = lib.types.port;
+        default = 8080;
+        description = ''
+          The TCP port Tomcat should listen on.
+        '';
+      };
+
       purifyOnStart = lib.mkOption {
         type = lib.types.bool;
         default = false;
@@ -244,8 +252,12 @@ in
             hostElementsString = lib.concatMapStringsSep "\n" hostElementForVirtualHost cfg.virtualHosts;
             hostElementsSedString = lib.replaceStrings ["\n"] ["\\\n"] hostElementsString;
           in ''
-            # Create a modified server.xml which also includes all virtual hosts
-            sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\\"${lib.escapeShellArg hostElementsSedString} \
+            # Create a modified server.xml which listens on the given port,
+            # and also includes all virtual hosts.
+            # The host modification must be last here,
+            # else if hostElementsSedString is empty sed gets confused as to what to append
+            sed -e 's/<Connector port="8080"/<Connector port="${toString cfg.port}"/' \
+                -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\\"${lib.escapeShellArg hostElementsSedString} \
                   ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
           ''
         }
diff --git a/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index 2e0eef67c0b3e..fa67441e7ac49 100644
--- a/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -229,7 +229,6 @@ in
     })
 
     (mkIf serviceCfg.apps.enable {
-      programs.geary.enable = mkDefault (notExcluded pkgs.gnome.geary);
       programs.gnome-disks.enable = mkDefault (notExcluded pkgs.gnome.gnome-disk-utility);
       programs.gnome-terminal.enable = mkDefault (notExcluded pkgs.gnome.gnome-terminal);
       programs.file-roller.enable = mkDefault (notExcluded pkgs.gnome.file-roller);
@@ -247,7 +246,6 @@ in
         pix
 
         # external apps shipped with linux-mint
-        hexchat
         gnome-calculator
         gnome-calendar
         gnome-screenshot
diff --git a/nixos/modules/services/x11/desktop-managers/deepin.nix b/nixos/modules/services/x11/desktop-managers/deepin.nix
index 30bd14adb4192..1151178a8824e 100644
--- a/nixos/modules/services/x11/desktop-managers/deepin.nix
+++ b/nixos/modules/services/x11/desktop-managers/deepin.nix
@@ -53,6 +53,7 @@ in
       services.deepin.dde-daemon.enable = mkForce true;
       services.deepin.dde-api.enable = mkForce true;
       services.deepin.app-services.enable = mkForce true;
+      services.deepin.deepin-anything.enable = mkDefault true;
 
       services.colord.enable = mkDefault true;
       services.accounts-daemon.enable = mkDefault true;
@@ -98,11 +99,12 @@ in
         "/share/dsg"
         "/share/deepin-themes"
         "/share/deepin"
+        "/share/dde-shell"
       ];
 
       environment.etc = {
         "deepin-installer.conf".text = ''
-          system_info_vendor_name="Copyright (c) 2003-2023 NixOS contributors"
+          system_info_vendor_name="Copyright (c) 2003-2024 NixOS contributors"
         '';
       };
 
@@ -140,8 +142,10 @@ in
             dtkwidget
             dtkdeclarative
             qt5platform-plugins
+            qt6platform-plugins
+            qt5integration
+            qt6integration
             deepin-pw-check
-            deepin-turbo
 
             dde-account-faces
             deepin-icon-theme
@@ -152,7 +156,9 @@ in
             deepin-desktop-base
 
             startdde
+            # TODO: should remove dde-dock, but dde-shell still need it's dconfig
             dde-dock
+            dde-shell
             dde-launchpad
             dde-session-ui
             dde-session-shell
@@ -171,6 +177,7 @@ in
             dde-appearance
             dde-application-manager
             deepin-service-manager
+            dde-grand-search
           ];
           optionalPackages = [
             onboard # dde-dock plugin
@@ -194,7 +201,7 @@ in
         ++ utils.removePackagesByName optionalPackages config.environment.deepin.excludePackages;
 
       services.dbus.packages = with pkgs.deepin; [
-        dde-dock
+        dde-shell
         dde-launchpad
         dde-session-ui
         dde-session-shell
@@ -209,9 +216,11 @@ in
         dde-appearance
         dde-application-manager
         deepin-service-manager
+        dde-grand-search
       ];
 
       systemd.packages = with pkgs.deepin; [
+        dde-shell
         dde-launchpad
         dde-file-manager
         dde-calendar
diff --git a/nixos/modules/services/x11/desktop-managers/phosh.nix b/nixos/modules/services/x11/desktop-managers/phosh.nix
index e8494b2c017c9..12b39f927c012 100644
--- a/nixos/modules/services/x11/desktop-managers/phosh.nix
+++ b/nixos/modules/services/x11/desktop-managers/phosh.nix
@@ -216,7 +216,7 @@ in
 
     security.pam.services.phosh = {};
 
-    hardware.opengl.enable = mkDefault true;
+    hardware.graphics.enable = mkDefault true;
 
     services.gnome.core-shell.enable = true;
     services.gnome.core-os-services.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 727802f3a63e2..69a83ecb72065 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -116,7 +116,7 @@ in
     ] # TODO: NetworkManager doesn't belong here
       ++ optional config.networking.networkmanager.enable networkmanagerapplet
       ++ optional config.powerManagement.enable xfce4-power-manager
-      ++ optionals config.hardware.pulseaudio.enable [
+      ++ optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [
         pavucontrol
         # volume up/down keys support:
         # xfce4-pulseaudio-plugin includes all the functionalities of xfce4-volumed-pulse
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 107a2f1647925..51ab08e74f864 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -321,6 +321,22 @@ in
         session   include       login
       '';
 
+      login.fprintAuth = mkIf config.services.fprintd.enable false;
+      gdm-fingerprint.text = mkIf config.services.fprintd.enable ''
+        auth       required                    pam_shells.so
+        auth       requisite                   pam_nologin.so
+        auth       requisite                   pam_faillock.so      preauth
+        auth       required                    ${pkgs.fprintd}/lib/security/pam_fprintd.so
+        auth       optional                    pam_permit.so
+        auth       required                    pam_env.so
+        auth       [success=ok default=1]      ${pkgs.gnome.gdm}/lib/security/pam_gdm.so
+
+        account    include                     login
+
+        password   required                    pam_deny.so
+
+        session    include                     login
+      '';
     };
 
   };
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 5a86d055c2719..57e83399eded6 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -302,7 +302,7 @@ in
         default = [ "modesetting" "fbdev" ];
         example = [
           "nvidia"
-          "amdgpu-pro"
+          "amdgpu"
         ];
         # TODO(@oxij): think how to easily add the rest, like those nvidia things
         relatedPackages = concatLists
@@ -716,10 +716,7 @@ in
 
         restartIfChanged = false;
 
-        environment =
-          optionalAttrs config.hardware.opengl.setLdLibraryPath
-            { LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ]; }
-          // config.services.displayManager.environment;
+        environment = config.services.displayManager.environment;
 
         preStart =
           ''
diff --git a/nixos/modules/system/boot/clevis.nix b/nixos/modules/system/boot/clevis.nix
index d9390f5bc15f8..ac881e9535767 100644
--- a/nixos/modules/system/boot/clevis.nix
+++ b/nixos/modules/system/boot/clevis.nix
@@ -48,7 +48,7 @@ in
 
     assertions = (attrValues (mapAttrs
       (device: _: {
-        assertion = (any (fs: fs.device == device && (elem fs.fsType supportedFs)) config.system.build.fileSystems) || (hasAttr device config.boot.initrd.luks.devices);
+        assertion = (any (fs: fs.device == device && (elem fs.fsType supportedFs) || (fs.fsType == "zfs" && hasPrefix "${device}/" fs.device)) config.system.build.fileSystems) || (hasAttr device config.boot.initrd.luks.devices);
         message = ''
           No filesystem or LUKS device with the name ${device} is declared in your configuration.'';
       })
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index d1cd601c2d9b1..cbeec4588f593 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -150,9 +150,13 @@ in
         HostKey ${initrdKeyPath path}
       '')}
 
-      KexAlgorithms ${concatStringsSep "," sshdCfg.settings.KexAlgorithms}
-      Ciphers ${concatStringsSep "," sshdCfg.settings.Ciphers}
-      MACs ${concatStringsSep "," sshdCfg.settings.Macs}
+      '' + lib.optionalString (sshdCfg.settings.KexAlgorithms != null) ''
+        KexAlgorithms ${concatStringsSep "," sshdCfg.settings.KexAlgorithms}
+      '' + lib.optionalString (sshdCfg.settings.Ciphers != null) ''
+        Ciphers ${concatStringsSep "," sshdCfg.settings.Ciphers}
+      '' + lib.optionalString (sshdCfg.settings.Macs != null) ''
+        MACs ${concatStringsSep "," sshdCfg.settings.Macs}
+      '' + ''
 
       LogLevel ${sshdCfg.settings.LogLevel}
 
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index cee8663f0040e..e73048dc2ecbe 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -323,15 +323,15 @@ in {
     assertions = [
       {
         assertion = (hasPrefix "/" efi.efiSysMountPoint);
-        message = "The ESP mount point '${efi.efiSysMountPoint}' must be an absolute path";
+        message = "The ESP mount point '${toString efi.efiSysMountPoint}' must be an absolute path";
       }
       {
         assertion = cfg.xbootldrMountPoint == null || (hasPrefix "/" cfg.xbootldrMountPoint);
-        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' must be an absolute path";
+        message = "The XBOOTLDR mount point '${toString cfg.xbootldrMountPoint}' must be an absolute path";
       }
       {
         assertion = cfg.xbootldrMountPoint != efi.efiSysMountPoint;
-        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${efi.efiSysMountPoint}'";
+        message = "The XBOOTLDR mount point '${toString cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${toString efi.efiSysMountPoint}'";
       }
       {
         assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 80ca69e495e9d..87932075f3679 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -64,14 +64,6 @@ let
 
   etcHardlinks = filter (f: f.mode != "symlink" && f.mode != "direct-symlink") etc';
 
-  build-composefs-dump = pkgs.runCommand "build-composefs-dump.py"
-    {
-      buildInputs = [ pkgs.python3 ];
-    } ''
-    install ${./build-composefs-dump.py} $out
-    patchShebangs --host $out
-  '';
-
 in
 
 {
@@ -295,10 +287,12 @@ in
     system.build.etcMetadataImage =
       let
         etcJson = pkgs.writeText "etc-json" (builtins.toJSON etc');
-        etcDump = pkgs.runCommand "etc-dump" { } "${build-composefs-dump} ${etcJson} > $out";
+        etcDump = pkgs.runCommand "etc-dump" { } ''
+          ${lib.getExe pkgs.buildPackages.python3} ${./build-composefs-dump.py} ${etcJson} > $out
+        '';
       in
       pkgs.runCommand "etc-metadata.erofs" {
-        nativeBuildInputs = [ pkgs.composefs pkgs.erofs-utils ];
+        nativeBuildInputs = with pkgs.buildPackages; [ composefs erofs-utils ];
       } ''
         mkcomposefs --from-file ${etcDump} $out
         fsck.erofs $out
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index b75817a011cbd..7861a4b946b4b 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -17,8 +17,7 @@ let
   cfgZED = config.services.zfs.zed;
 
   selectModulePackage = package: config.boot.kernelPackages.${package.kernelModuleAttribute};
-  clevisDatasets = map (e: e.device) (filter (e: e.device != null && (hasAttr e.device config.boot.initrd.clevis.devices) && e.fsType == "zfs" && (fsNeededForBoot e)) config.system.build.fileSystems);
-
+  clevisDatasets = attrNames (filterAttrs (device: _: any (e: e.fsType == "zfs" && (fsNeededForBoot e) && (e.device == device || hasPrefix "${device}/" e.device)) config.system.build.fileSystems) config.boot.initrd.clevis.devices);
 
   inInitrd = config.boot.initrd.supportedFilesystems.zfs or false;
   inSystem = config.boot.supportedFilesystems.zfs or false;
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 2b365bc555855..00b6b28eb6537 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -57,12 +57,12 @@ in
   options.testing = {
 
     initrdBackdoor = lib.mkEnableOption ''
-      enable backdoor.service in initrd. Requires
+      backdoor.service in initrd. Requires
       boot.initrd.systemd.enable to be enabled. Boot will pause in
       stage 1 at initrd.target, and will listen for commands from the
       Machine python interface, just like stage 2 normally does. This
       enables commands to be sent to test and debug stage 1. Use
-      machine.switch_root() to leave stage 1 and proceed to stage 2.
+      machine.switch_root() to leave stage 1 and proceed to stage 2
     '';
 
   };
diff --git a/nixos/modules/virtualisation/docker.nix b/nixos/modules/virtualisation/docker.nix
index bcc649dcbec0a..8a0894ed85c3d 100644
--- a/nixos/modules/virtualisation/docker.nix
+++ b/nixos/modules/virtualisation/docker.nix
@@ -244,8 +244,8 @@ in
       };
 
       assertions = [
-        { assertion = cfg.enableNvidia && pkgs.stdenv.isx86_64 -> config.hardware.opengl.driSupport32Bit or false;
-          message = "Option enableNvidia on x86_64 requires 32bit support libraries";
+        { assertion = cfg.enableNvidia && pkgs.stdenv.isx86_64 -> config.hardware.graphics.enable32Bit or false;
+          message = "Option enableNvidia on x86_64 requires 32-bit support libraries";
         }];
 
       virtualisation.docker.daemon.settings = {
diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix
index 87568390bd3b8..2b69a7a076585 100644
--- a/nixos/modules/virtualisation/incus.nix
+++ b/nixos/modules/virtualisation/incus.nix
@@ -149,7 +149,7 @@ in
 
         Users in the "incus-admin" group can interact with
         the daemon (e.g. to start or stop containers) using the
-        {command}`incus` command line tool, among others.
+        {command}`incus` command line tool, among others
       '';
 
       package = lib.mkPackageOption pkgs "incus-lts" { };
diff --git a/nixos/modules/virtualisation/libvirtd.nix b/nixos/modules/virtualisation/libvirtd.nix
index 226ece8176708..9fbb126738a93 100644
--- a/nixos/modules/virtualisation/libvirtd.nix
+++ b/nixos/modules/virtualisation/libvirtd.nix
@@ -332,6 +332,14 @@ in
         libvirt NSS module options.
       '';
     };
+
+    sshProxy = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Weither to configure OpenSSH to use the [SSH Proxy](https://libvirt.org/ssh-proxy.html).
+      '';
+    };
   };
 
 
@@ -382,6 +390,10 @@ in
       source = "${cfg.qemu.package}/libexec/qemu-bridge-helper";
     };
 
+    programs.ssh.extraConfig = mkIf cfg.sshProxy ''
+      Include ${cfg.package}/etc/ssh/ssh_config.d/30-libvirt-ssh-proxy.conf
+    '';
+
     systemd.packages = [ cfg.package ];
 
     systemd.services.libvirtd-config = {
diff --git a/nixos/modules/virtualisation/multipass.nix b/nixos/modules/virtualisation/multipass.nix
index 7918a716a870b..8a55282c88d8c 100644
--- a/nixos/modules/virtualisation/multipass.nix
+++ b/nixos/modules/virtualisation/multipass.nix
@@ -10,9 +10,7 @@ in
 {
   options = {
     virtualisation.multipass = {
-      enable = lib.mkEnableOption ''
-        Multipass, a simple manager for virtualised Ubuntu instances.
-      '';
+      enable = lib.mkEnableOption "Multipass, a simple manager for virtualised Ubuntu instances";
 
       logLevel = lib.mkOption {
         type = lib.types.enum [ "error" "warning" "info" "debug" "trace" ];
diff --git a/nixos/modules/virtualisation/oci-image.nix b/nixos/modules/virtualisation/oci-image.nix
index d4af5016dd71c..1e2b90bfd46e2 100644
--- a/nixos/modules/virtualisation/oci-image.nix
+++ b/nixos/modules/virtualisation/oci-image.nix
@@ -9,10 +9,10 @@ in
   config = {
     system.build.OCIImage = import ../../lib/make-disk-image.nix {
       inherit config lib pkgs;
+      inherit (cfg) diskSize;
       name = "oci-image";
       configFile = ./oci-config-user.nix;
       format = "qcow2";
-      diskSize = 8192;
       partitionTableType = if cfg.efi then "efi" else "legacy";
     };
 
diff --git a/nixos/modules/virtualisation/oci-options.nix b/nixos/modules/virtualisation/oci-options.nix
index 0dfedc6a530c8..76f3475a42817 100644
--- a/nixos/modules/virtualisation/oci-options.nix
+++ b/nixos/modules/virtualisation/oci-options.nix
@@ -9,6 +9,12 @@
           Whether the OCI instance is using EFI.
         '';
       };
+      diskSize = lib.mkOption {
+        type = lib.types.int;
+        default = 8192;
+        description = "Size of the disk image created in MB.";
+        example = "diskSize = 12 * 1024; # 12GiB";
+      };
     };
   };
 }
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 3b81cfaf2b8ca..d1dc6404d4f51 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -900,7 +900,7 @@ in
     };
 
     virtualisation.tpm = {
-      enable = mkEnableOption "a TPM device in the virtual machine with a driver, using swtpm.";
+      enable = mkEnableOption "a TPM device in the virtual machine with a driver, using swtpm";
 
       package = mkPackageOption cfg.host.pkgs "swtpm" { };
 
diff --git a/nixos/modules/virtualisation/spice-usb-redirection.nix b/nixos/modules/virtualisation/spice-usb-redirection.nix
index 255327f2622c9..1631a91ccf863 100644
--- a/nixos/modules/virtualisation/spice-usb-redirection.nix
+++ b/nixos/modules/virtualisation/spice-usb-redirection.nix
@@ -22,5 +22,5 @@
     };
   };
 
-  meta.maintainers = [ lib.maintainers.lheckemann ];
+  meta.maintainers = [ ];
 }
diff --git a/nixos/modules/virtualisation/vagrant-guest.nix b/nixos/modules/virtualisation/vagrant-guest.nix
index 2fad376086e34..120a2a2324d21 100644
--- a/nixos/modules/virtualisation/vagrant-guest.nix
+++ b/nixos/modules/virtualisation/vagrant-guest.nix
@@ -11,8 +11,7 @@ let
     #!${pkgs.runtimeShell}
     if [ ! -e ~/.ssh/authorized_keys ]; then
       mkdir -m 0700 -p ~/.ssh
-      echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" >> ~/.ssh/authorized_keys
-      chmod 0600 ~/.ssh/authorized_keys
+      install -m 0600 <(echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key") ~/.ssh/authorized_keys
     fi
   '';
 in
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 746b29fd27258..d16b747bfa95e 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -328,6 +328,7 @@ in {
   firewall-nftables = handleTest ./firewall.nix { nftables = true; };
   fish = handleTest ./fish.nix {};
   flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {};
+  flood = handleTest ./flood.nix {};
   floorp = handleTest ./firefox.nix { firefoxPackage = pkgs.floorp; };
   fluentd = handleTest ./fluentd.nix {};
   fluidd = handleTest ./fluidd.nix {};
@@ -413,6 +414,7 @@ in {
   pyload = handleTest ./pyload.nix {};
   oci-containers = handleTestOn ["aarch64-linux" "x86_64-linux"] ./oci-containers.nix {};
   odoo = handleTest ./odoo.nix {};
+  odoo16 = handleTest ./odoo.nix { package = pkgs.odoo16; };
   odoo15 = handleTest ./odoo.nix { package = pkgs.odoo15; };
   # 9pnet_virtio used to mount /nix partition doesn't support
   # hibernation. This test happens to work on x86_64-linux but
@@ -498,7 +500,8 @@ in {
   libreddit = handleTest ./libreddit.nix {};
   librenms = handleTest ./librenms.nix {};
   libresprite = handleTest ./libresprite.nix {};
-  libreswan = handleTest ./libreswan.nix {};
+  libreswan = runTest ./libreswan.nix;
+  libreswan-nat = runTest ./libreswan-nat.nix;
   librewolf = handleTest ./firefox.nix { firefoxPackage = pkgs.librewolf; };
   libuiohook = handleTest ./libuiohook.nix {};
   libvirtd = handleTest ./libvirtd.nix {};
@@ -648,6 +651,7 @@ in {
   nix-config = handleTest ./nix-config.nix {};
   nix-ld = handleTest ./nix-ld.nix {};
   nix-misc = handleTest ./nix/misc.nix {};
+  nix-required-mounts = runTest ./nix-required-mounts;
   nix-serve = handleTest ./nix-serve.nix {};
   nix-serve-ssh = handleTest ./nix-serve-ssh.nix {};
   nixops = handleTest ./nixops/default.nix {};
@@ -772,7 +776,7 @@ in {
   printing-service = handleTest ./printing.nix { socket = false; };
   private-gpt = handleTest ./private-gpt.nix {};
   privoxy = handleTest ./privoxy.nix {};
-  prometheus = handleTest ./prometheus.nix {};
+  prometheus = handleTest ./prometheus {};
   prometheus-exporters = handleTest ./prometheus-exporters.nix {};
   prosody = handleTest ./xmpp/prosody.nix {};
   prosody-mysql = handleTest ./xmpp/prosody-mysql.nix {};
@@ -821,6 +825,7 @@ in {
   rstudio-server = handleTest ./rstudio-server.nix {};
   rsyncd = handleTest ./rsyncd.nix {};
   rsyslogd = handleTest ./rsyslogd.nix {};
+  rtorrent = handleTest ./rtorrent.nix {};
   rxe = handleTest ./rxe.nix {};
   sabnzbd = handleTest ./sabnzbd.nix {};
   samba = handleTest ./samba.nix {};
@@ -833,7 +838,7 @@ in {
   scrutiny = handleTest ./scrutiny.nix {};
   sddm = handleTest ./sddm.nix {};
   seafile = handleTest ./seafile.nix {};
-  searx = handleTest ./searx.nix {};
+  searx = runTest ./searx.nix;
   seatd = handleTest ./seatd.nix {};
   service-runner = handleTest ./service-runner.nix {};
   sftpgo = runTest ./sftpgo.nix;
@@ -955,6 +960,7 @@ in {
   systemd-homed = handleTest ./systemd-homed.nix {};
   systemtap = handleTest ./systemtap.nix {};
   tandoor-recipes = handleTest ./tandoor-recipes.nix {};
+  tandoor-recipes-script-name = handleTest ./tandoor-recipes-script-name.nix {};
   tang = handleTest ./tang.nix {};
   taskserver = handleTest ./taskserver.nix {};
   tayga = handleTest ./tayga.nix {};
@@ -964,6 +970,7 @@ in {
   teleport = handleTest ./teleport.nix {};
   thelounge = handleTest ./thelounge.nix {};
   terminal-emulators = handleTest ./terminal-emulators.nix {};
+  thanos = handleTest ./thanos.nix {};
   tiddlywiki = handleTest ./tiddlywiki.nix {};
   tigervnc = handleTest ./tigervnc.nix {};
   timescaledb = handleTest ./timescaledb.nix {};
@@ -978,7 +985,7 @@ in {
   traefik = handleTestOn ["aarch64-linux" "x86_64-linux"] ./traefik.nix {};
   trafficserver = handleTest ./trafficserver.nix {};
   transfer-sh = handleTest ./transfer-sh.nix {};
-  transmission = handleTest ./transmission.nix { transmission = pkgs.transmission; };
+  transmission_3 = handleTest ./transmission.nix { transmission = pkgs.transmission_3; };
   transmission_4 = handleTest ./transmission.nix { transmission = pkgs.transmission_4; };
   # tracee requires bpf
   tracee = handleTestOn ["x86_64-linux"] ./tracee.nix {};
@@ -1036,6 +1043,7 @@ in {
   wiki-js = handleTest ./wiki-js.nix {};
   wine = handleTest ./wine.nix {};
   wireguard = handleTest ./wireguard {};
+  wg-access-server = handleTest ./wg-access-server.nix {};
   without-nix = handleTest ./without-nix.nix {};
   wmderland = handleTest ./wmderland.nix {};
   workout-tracker = handleTest ./workout-tracker.nix {};
@@ -1043,6 +1051,7 @@ in {
   wordpress = handleTest ./wordpress.nix {};
   wrappers = handleTest ./wrappers.nix {};
   writefreely = handleTest ./web-apps/writefreely.nix {};
+  wstunnel = runTest ./wstunnel.nix;
   xandikos = handleTest ./xandikos.nix {};
   xautolock = handleTest ./xautolock.nix {};
   xfce = handleTest ./xfce.nix {};
diff --git a/nixos/tests/armagetronad.nix b/nixos/tests/armagetronad.nix
index d59827354b771..ca93ce8fb6c5d 100644
--- a/nixos/tests/armagetronad.nix
+++ b/nixos/tests/armagetronad.nix
@@ -12,7 +12,7 @@ let
     { pkgs, ... }:
 
     { imports = [ ./common/user-account.nix ./common/x11.nix ];
-      hardware.opengl.driSupport = true;
+      hardware.graphics.enable = true;
       virtualisation.memorySize = 256;
       environment = {
         systemPackages = [ pkgs.armagetronad ];
diff --git a/nixos/tests/cagebreak.nix b/nixos/tests/cagebreak.nix
index 1fef7cb57cfc5..4d7664c1505f3 100644
--- a/nixos/tests/cagebreak.nix
+++ b/nixos/tests/cagebreak.nix
@@ -14,9 +14,7 @@ in
   };
 
   nodes.machine = { config, ... }:
-  let
-    alice = config.users.users.alice;
-  in {
+  {
     # Automatically login on tty1 as a normal user:
     imports = [ ./common/user-account.nix ];
     services.getty.autologinUser = "alice";
@@ -31,7 +29,7 @@ in
       fi
     '';
 
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
     programs.xwayland.enable = true;
     security.polkit.enable = true;
     environment.systemPackages = [ pkgs.cagebreak pkgs.wayland-utils ];
diff --git a/nixos/tests/crabfit.nix b/nixos/tests/crabfit.nix
index 0cd0741f6fa4b..0daf47d52f25d 100644
--- a/nixos/tests/crabfit.nix
+++ b/nixos/tests/crabfit.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix (
   {
     name = "crabfit";
 
-    meta.maintainers = with lib.maintainers; [ thubrecht ];
+    meta.maintainers = with lib.maintainers; [ ];
 
     nodes = {
       machine =
diff --git a/nixos/tests/flood.nix b/nixos/tests/flood.nix
new file mode 100644
index 0000000000000..075d37e62835f
--- /dev/null
+++ b/nixos/tests/flood.nix
@@ -0,0 +1,27 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+let
+  port = 3001;
+in
+{
+  name = "flood";
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ thiagokokada ];
+  };
+
+  nodes.machine = { pkgs, ... }: {
+    services.flood = {
+      inherit port;
+      enable = true;
+      openFirewall = true;
+      extraArgs = [ "--baseuri=/" ];
+    };
+  };
+
+  testScript = /* python */ ''
+    machine.start()
+    machine.wait_for_unit("flood.service")
+    machine.wait_for_open_port(${toString port})
+
+    machine.succeed("curl --fail http://localhost:${toString port}")
+  '';
+})
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index 05fb2fa1e06aa..47902fa4e1340 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -44,6 +44,8 @@ in {
       # test loading custom components
       customComponents = with pkgs.home-assistant-custom-components; [
         prometheus_sensor
+        # tests loading multiple components from a single package
+        spook
       ];
 
       # test loading lovelace modules
@@ -179,7 +181,8 @@ in {
 
     with subtest("Check that custom components get installed"):
         hass.succeed("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json")
-        hass.wait_until_succeeds("journalctl -u home-assistant.service | grep -q 'We found a custom integration prometheus_sensor which has not been tested by Home Assistant'")
+        for integration in ("prometheus_sensor", "spook", "spook_inverse"):
+            hass.wait_until_succeeds(f"journalctl -u home-assistant.service | grep -q 'We found a custom integration {integration} which has not been tested by Home Assistant'")
 
     with subtest("Check that lovelace modules are referenced and fetchable"):
         hass.succeed("grep -q 'mini-graph-card-bundle.js' '${configDir}/configuration.yaml'")
@@ -228,7 +231,8 @@ in {
         cursor = get_journal_cursor()
         hass.succeed("${system}/specialisation/removeCustomThings/bin/switch-to-configuration test")
         hass.fail("grep -q 'mini-graph-card-bundle.js' '${configDir}/ui-lovelace.yaml'")
-        hass.fail("test -f ${configDir}/custom_components/prometheus_sensor/manifest.json")
+        for integration in ("prometheus_sensor", "spook", "spook_inverse"):
+            hass.fail(f"test -f ${configDir}/custom_components/{integration}/manifest.json")
         wait_for_homeassistant(cursor)
 
     with subtest("Check that no errors were logged"):
diff --git a/nixos/tests/initrd-secrets.nix b/nixos/tests/initrd-secrets.nix
index 0f3f83b0904e3..dbbdd83588491 100644
--- a/nixos/tests/initrd-secrets.nix
+++ b/nixos/tests/initrd-secrets.nix
@@ -9,7 +9,7 @@ let
   testWithCompressor = compressor: testing.makeTest {
     name = "initrd-secrets-${compressor}";
 
-    meta.maintainers = [ lib.maintainers.lheckemann ];
+    meta.maintainers = [ ];
 
     nodes.machine = { ... }: {
       virtualisation.useBootLoader = true;
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index 00205f9417718..3b5e0ed8e7bba 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -37,6 +37,8 @@
     clevisLuksFallback
     clevisZfs
     clevisZfsFallback
+    clevisZfsParentDataset
+    clevisZfsParentDatasetFallback
     gptAutoRoot
     clevisBcachefs
     clevisBcachefsFallback
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 3f57a64333dda..bb6ad79615fa3 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -714,7 +714,7 @@ let
     '';
   };
 
-  mkClevisZfsTest = { fallback ? false }: makeInstallerTest "clevis-zfs${optionalString fallback "-fallback"}" {
+  mkClevisZfsTest = { fallback ? false, parentDataset ? false }: makeInstallerTest "clevis-zfs${optionalString parentDataset "-parent-dataset"}${optionalString fallback "-fallback"}" {
     clevisTest = true;
     clevisFallbackTest = fallback;
     enableOCR = fallback;
@@ -731,17 +731,27 @@ let
         "udevadm settle",
         "mkswap /dev/vda2 -L swap",
         "swapon -L swap",
+    '' + optionalString (!parentDataset) ''
         "zpool create -O mountpoint=legacy rpool /dev/vda3",
         "echo -n password | zfs create"
         + " -o encryption=aes-256-gcm -o keyformat=passphrase rpool/root",
+    '' + optionalString (parentDataset) ''
+        "echo -n password | zpool create -O mountpoint=none -O encryption=on -O keyformat=passphrase rpool /dev/vda3",
+        "zfs create -o mountpoint=legacy rpool/root",
+    '' +
+    ''
         "mount -t zfs rpool/root /mnt",
         "mkfs.ext3 -L boot /dev/vda1",
         "mkdir -p /mnt/boot",
         "mount LABEL=boot /mnt/boot",
         "udevadm settle")
     '';
-    extraConfig = ''
+    extraConfig = optionalString (!parentDataset) ''
       boot.initrd.clevis.devices."rpool/root".secretFile = "/etc/nixos/clevis-secret.jwe";
+    '' + optionalString (parentDataset) ''
+      boot.initrd.clevis.devices."rpool".secretFile = "/etc/nixos/clevis-secret.jwe";
+    '' +
+    ''
       boot.zfs.requestEncryptionCredentials = true;
 
 
@@ -1359,6 +1369,8 @@ in {
   clevisLuksFallback = mkClevisLuksTest { fallback = true; };
   clevisZfs = mkClevisZfsTest { };
   clevisZfsFallback = mkClevisZfsTest { fallback = true; };
+  clevisZfsParentDataset = mkClevisZfsTest { parentDataset = true; };
+  clevisZfsParentDatasetFallback = mkClevisZfsTest { parentDataset = true; fallback = true; };
 } // optionalAttrs systemdStage1 {
   stratisRoot = makeInstallerTest "stratisRoot" {
     createPartitions = ''
diff --git a/nixos/tests/kafka.nix b/nixos/tests/kafka.nix
index f4f9827ab7b5f..8f843e1fc37da 100644
--- a/nixos/tests/kafka.nix
+++ b/nixos/tests/kafka.nix
@@ -103,13 +103,8 @@ let
   }) { inherit system; });
 
 in with pkgs; {
-  kafka_2_8 = makeKafkaTest "kafka_2_8" { kafkaPackage = apacheKafka_2_8; };
-  kafka_3_0 = makeKafkaTest "kafka_3_0" { kafkaPackage = apacheKafka_3_0; };
-  kafka_3_1 = makeKafkaTest "kafka_3_1" { kafkaPackage = apacheKafka_3_1; };
-  kafka_3_2 = makeKafkaTest "kafka_3_2" { kafkaPackage = apacheKafka_3_2; };
-  kafka_3_3 = makeKafkaTest "kafka_3_3" { kafkaPackage = apacheKafka_3_3; };
-  kafka_3_4 = makeKafkaTest "kafka_3_4" { kafkaPackage = apacheKafka_3_4; };
-  kafka_3_5 = makeKafkaTest "kafka_3_5" { kafkaPackage = apacheKafka_3_5; };
+  kafka_3_6 = makeKafkaTest "kafka_3_6" { kafkaPackage = apacheKafka_3_6; };
+  kafka_3_7 = makeKafkaTest "kafka_3_7" { kafkaPackage = apacheKafka_3_7; };
   kafka = makeKafkaTest "kafka" { kafkaPackage = apacheKafka; };
   kafka_kraft = makeKafkaTest "kafka_kraft" { kafkaPackage = apacheKafka; mode = "kraft"; };
 }
diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix
index 67b412c80961d..259f1340a22d7 100644
--- a/nixos/tests/keycloak.nix
+++ b/nixos/tests/keycloak.nix
@@ -44,7 +44,6 @@ let
             };
             plugins = with config.services.keycloak.package.plugins; [
               keycloak-discord
-              keycloak-metrics-spi
             ];
           };
           environment.systemPackages = with pkgs; [
@@ -122,14 +121,6 @@ let
                    | jq -r '"Authorization: bearer " + .access_token' >admin_auth_header
           """)
 
-          # Register the metrics SPI
-          keycloak.succeed(
-              """${pkgs.jre}/bin/keytool -import -alias snakeoil -file ${certs.ca.cert} -storepass aaaaaa -keystore cacert.jks -noprompt""",
-              """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh config credentials --server '${frontendUrl}' --realm master --user admin --password "$(<${adminPasswordFile})" """,
-              """KC_OPTS='-Djavax.net.ssl.trustStore=cacert.jks -Djavax.net.ssl.trustStorePassword=aaaaaa' kcadm.sh update events/config -s 'eventsEnabled=true' -s 'adminEventsEnabled=true' -s 'eventsListeners+=metrics-listener'""",
-              """curl -sSf '${frontendUrl}/realms/master/metrics' | grep '^keycloak_admin_event_UPDATE'"""
-          )
-
           # Publish the realm, including a test OIDC client and user
           keycloak.succeed(
               "curl -sSf -H @admin_auth_header -X POST -H 'Content-Type: application/json' -d @${realmDataJson} '${frontendUrl}/admin/realms/'"
diff --git a/nixos/tests/libreswan-nat.nix b/nixos/tests/libreswan-nat.nix
new file mode 100644
index 0000000000000..973e304f9e3a3
--- /dev/null
+++ b/nixos/tests/libreswan-nat.nix
@@ -0,0 +1,238 @@
+# This test sets up an IPsec VPN server that allows a client behind an IPv4 NAT
+# router to access the IPv6 internet. We check that the client initially can't
+# ping an IPv6 hosts and its connection to the server can be eavesdropped by
+# the router, but once the IPsec tunnel is enstablished it can talk to an
+# IPv6-only host and the connection is secure.
+#
+# Notes:
+#   - the VPN is implemented using policy-based routing.
+#   - the client is assigned an IPv6 address from the same /64 subnet
+#     of the server, without DHCPv6 or SLAAC.
+#   - the server acts as NDP proxy for the client, so that the latter
+#     becomes reachable at its assigned IPv6 via the server.
+#   - the client falls back to TCP if UDP is blocked
+
+{ lib, pkgs, ... }:
+
+let
+
+  # Common network setup
+  baseNetwork = {
+    # shared hosts file
+    networking.extraHosts = lib.mkVMOverride ''
+      203.0.113.1 router
+      203.0.113.2 server
+      2001:db8::2 inner
+      192.168.1.1 client
+    '';
+    # open a port for testing
+    networking.firewall.allowedUDPPorts = [ 1234 ];
+  };
+
+  # Common IPsec configuration
+  baseTunnel = {
+    services.libreswan.enable = true;
+    environment.etc."ipsec.d/tunnel.secrets" =
+      { text = ''@server %any : PSK "j1JbIi9WY07rxwcNQ6nbyThKCf9DGxWOyokXIQcAQUnafsNTUJxfsxwk9WYK8fHj"'';
+        mode = "600";
+      };
+  };
+
+  # Helpers to add a static IP address on an interface
+  setAddress4 = iface: addr: {
+    networking.interfaces.${iface}.ipv4.addresses =
+      lib.mkVMOverride [ { address = addr; prefixLength = 24; } ];
+  };
+  setAddress6 = iface: addr: {
+    networking.interfaces.${iface}.ipv6.addresses =
+      lib.mkVMOverride [ { address = addr; prefixLength = 64; } ];
+  };
+
+in
+
+{
+  name = "libreswan-nat";
+  meta = with lib.maintainers; {
+    maintainers = [ rnhmjoj ];
+  };
+
+  nodes.router = { pkgs, ... }: lib.mkMerge [
+    baseNetwork
+    (setAddress4 "eth1" "203.0.113.1")
+    (setAddress4 "eth2" "192.168.1.1")
+    {
+      virtualisation.vlans = [ 1 2 ];
+      environment.systemPackages = [ pkgs.tcpdump ];
+      networking.nat = {
+        enable = true;
+        externalInterface = "eth1";
+        internalInterfaces = [ "eth2" ];
+      };
+      networking.firewall.trustedInterfaces = [ "eth2" ];
+    }
+  ];
+
+  nodes.inner = lib.mkMerge [
+    baseNetwork
+    (setAddress6 "eth1" "2001:db8::2")
+    { virtualisation.vlans = [ 3 ]; }
+  ];
+
+  nodes.server = lib.mkMerge [
+    baseNetwork
+    baseTunnel
+    (setAddress4 "eth1" "203.0.113.2")
+    (setAddress6 "eth2" "2001:db8::1")
+    {
+      virtualisation.vlans = [ 1 3 ];
+      networking.firewall.allowedUDPPorts = [ 500 4500 ];
+      networking.firewall.allowedTCPPorts = [ 993 ];
+
+      # see https://github.com/NixOS/nixpkgs/pull/310857
+      networking.firewall.checkReversePath = false;
+
+      boot.kernel.sysctl = {
+        # enable forwarding packets
+        "net.ipv6.conf.all.forwarding" = 1;
+        "net.ipv4.conf.all.forwarding" = 1;
+        # enable NDP proxy for VPN clients
+        "net.ipv6.conf.all.proxy_ndp" = 1;
+      };
+
+      services.libreswan.configSetup = "listen-tcp=yes";
+      services.libreswan.connections.tunnel = ''
+        # server
+        left=203.0.113.2
+        leftid=@server
+        leftsubnet=::/0
+        leftupdown=${pkgs.writeScript "updown" ''
+          # act as NDP proxy for VPN clients
+          if test "$PLUTO_VERB" = up-client-v6; then
+            ip neigh add proxy "$PLUTO_PEER_CLIENT_NET" dev eth2
+          fi
+          if test "$PLUTO_VERB" = down-client-v6; then
+            ip neigh del proxy "$PLUTO_PEER_CLIENT_NET" dev eth2
+          fi
+        ''}
+
+        # clients
+        right=%any
+        rightaddresspool=2001:db8:0:0:c::/97
+        modecfgdns=2001:db8::1
+
+        # clean up vanished clients
+        dpddelay=30
+
+        auto=add
+        keyexchange=ikev2
+        rekey=no
+        narrowing=yes
+        fragmentation=yes
+        authby=secret
+
+        leftikeport=993
+        retransmit-timeout=10s
+      '';
+    }
+  ];
+
+  nodes.client = lib.mkMerge [
+    baseNetwork
+    baseTunnel
+    (setAddress4 "eth1" "192.168.1.2")
+    {
+      virtualisation.vlans = [ 2 ];
+      networking.defaultGateway = {
+        address = "192.168.1.1";
+        interface = "eth1";
+      };
+      services.libreswan.connections.tunnel = ''
+        # client
+        left=%defaultroute
+        leftid=@client
+        leftmodecfgclient=yes
+        leftsubnet=::/0
+
+        # server
+        right=203.0.113.2
+        rightid=@server
+        rightsubnet=::/0
+
+        auto=add
+        narrowing=yes
+        rekey=yes
+        fragmentation=yes
+        authby=secret
+
+        # fallback when UDP is blocked
+        enable-tcp=fallback
+        tcp-remoteport=993
+        retransmit-timeout=5s
+      '';
+    }
+  ];
+
+  testScript =
+    ''
+      def client_to_host(machine, msg: str):
+          """
+          Sends a message from client to server
+          """
+          machine.execute("nc -lu :: 1234 >/tmp/msg &")
+          client.sleep(1)
+          client.succeed(f"echo '{msg}' | nc -uw 0 {machine.name} 1234")
+          client.sleep(1)
+          machine.succeed(f"grep '{msg}' /tmp/msg")
+
+
+      def eavesdrop():
+          """
+          Starts eavesdropping on the router
+          """
+          match = "udp port 1234"
+          router.execute(f"tcpdump -i eth1 -c 1 -Avv {match} >/tmp/log &")
+
+
+      start_all()
+
+      with subtest("Network is up"):
+          client.wait_until_succeeds("ping -c1 server")
+          client.succeed("systemctl restart ipsec")
+          server.succeed("systemctl restart ipsec")
+
+      with subtest("Router can eavesdrop cleartext traffic"):
+          eavesdrop()
+          client_to_host(server, "I secretly love turnip")
+          router.sleep(1)
+          router.succeed("grep turnip /tmp/log")
+
+      with subtest("Libreswan is ready"):
+          client.wait_for_unit("ipsec")
+          server.wait_for_unit("ipsec")
+          client.succeed("ipsec checkconfig")
+          server.succeed("ipsec checkconfig")
+
+      with subtest("Client can't ping VPN host"):
+          client.fail("ping -c1 inner")
+
+      with subtest("Client can start the tunnel"):
+          client.succeed("ipsec start tunnel")
+          client.succeed("ip -6 addr show lo | grep -q 2001:db8:0:0:c")
+
+      with subtest("Client can ping VPN host"):
+          client.wait_until_succeeds("ping -c1 2001:db8::1")
+          client.succeed("ping -c1 inner")
+
+      with subtest("Eve no longer can eavesdrop"):
+          eavesdrop()
+          client_to_host(inner, "Just kidding, I actually like rhubarb")
+          router.sleep(1)
+          router.fail("grep rhubarb /tmp/log")
+
+      with subtest("TCP fallback is available"):
+          server.succeed("iptables -I nixos-fw -p udp -j DROP")
+          client.succeed("ipsec restart")
+          client.execute("ipsec start tunnel")
+          client.wait_until_succeeds("ping -c1 inner")
+    '';
+}
diff --git a/nixos/tests/libreswan.nix b/nixos/tests/libreswan.nix
index c798a04645bc0..6dd9a845d19af 100644
--- a/nixos/tests/libreswan.nix
+++ b/nixos/tests/libreswan.nix
@@ -3,7 +3,7 @@
 # Eve can eavesdrop the plaintext traffic between Alice and Bob, but once they
 # enable the secure tunnel Eve's spying becomes ineffective.
 
-import ./make-test-python.nix ({ lib, pkgs, ... }:
+{ lib, pkgs, ... }:
 
 let
 
@@ -133,4 +133,4 @@ in
           eve.sleep(1)
           eve.fail("grep rhubarb /tmp/log")
     '';
-})
+}
diff --git a/nixos/tests/matomo.nix b/nixos/tests/matomo.nix
index 130f3dd8485a3..cf54f71b738fc 100644
--- a/nixos/tests/matomo.nix
+++ b/nixos/tests/matomo.nix
@@ -41,14 +41,14 @@ let
 in {
   matomo = matomoTest pkgs.matomo // {
     name = "matomo";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ];
+    meta.maintainers = with maintainers; [ florianjacob mmilata twey boozedog ];
   };
   matomo-beta = matomoTest pkgs.matomo-beta // {
     name = "matomo-beta";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ];
+    meta.maintainers = with maintainers; [ florianjacob mmilata twey boozedog ];
   };
   matomo_5 = matomoTest pkgs.matomo_5 // {
     name = "matomo-5";
-    meta.maintainers = with maintainers; [ florianjacob kiwi mmilata twey boozedog ] ++ lib.teams.flyingcircus.members;
+    meta.maintainers = with maintainers; [ florianjacob mmilata twey boozedog ] ++ lib.teams.flyingcircus.members;
   };
 }
diff --git a/nixos/tests/monado.nix b/nixos/tests/monado.nix
index 8368950951e73..6f0d27ee42454 100644
--- a/nixos/tests/monado.nix
+++ b/nixos/tests/monado.nix
@@ -5,7 +5,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     { pkgs, ... }:
 
     {
-      hardware.opengl.enable = true;
+      hardware.graphics.enable = true;
       users.users.alice = {
         isNormalUser = true;
         uid = 1000;
diff --git a/nixos/tests/netdata.nix b/nixos/tests/netdata.nix
index e3438f63404e7..df4d342905c68 100644
--- a/nixos/tests/netdata.nix
+++ b/nixos/tests/netdata.nix
@@ -11,7 +11,10 @@ import ./make-test-python.nix ({ pkgs, ...} : {
       { pkgs, ... }:
         {
           environment.systemPackages = with pkgs; [ curl jq netdata ];
-          services.netdata.enable = true;
+          services.netdata = {
+            enable = true;
+            python.recommendedPythonPackages = true;
+          };
         };
     };
 
diff --git a/nixos/tests/nextcloud/basic.nix b/nixos/tests/nextcloud/basic.nix
index 2a32f2b4d1992..bea08e3231104 100644
--- a/nixos/tests/nextcloud/basic.nix
+++ b/nixos/tests/nextcloud/basic.nix
@@ -38,7 +38,6 @@ runTest ({ config, ... }: {
       services.nextcloud = {
         enable = true;
         datadir = "/var/lib/nextcloud-data";
-        config.dbtableprefix = "nixos_";
         autoUpdateApps = {
           enable = true;
           startAt = "20:00";
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
index 33aa227d2b032..9f8b06561b074 100644
--- a/nixos/tests/nextcloud/default.nix
+++ b/nixos/tests/nextcloud/default.nix
@@ -109,4 +109,4 @@ let
       ./with-objectstore.nix
     ];
 in
-listToAttrs (concatMap genTests [ 27 28 29 ])
+listToAttrs (concatMap genTests [ 28 29 ])
diff --git a/nixos/tests/nix-required-mounts/default.nix b/nixos/tests/nix-required-mounts/default.nix
new file mode 100644
index 0000000000000..60f894ce0bcc6
--- /dev/null
+++ b/nixos/tests/nix-required-mounts/default.nix
@@ -0,0 +1,58 @@
+{ pkgs, ... }:
+
+let
+  inherit (pkgs) lib;
+in
+
+{
+  name = "nix-required-mounts";
+  meta.maintainers = with lib.maintainers; [ SomeoneSerge ];
+  nodes.machine =
+    { config, pkgs, ... }:
+    {
+      virtualisation.writableStore = true;
+      system.extraDependencies = [ (pkgs.runCommand "deps" { } "mkdir $out").inputDerivation ];
+      nix.nixPath = [ "nixpkgs=${../../..}" ];
+      nix.settings.substituters = lib.mkForce [ ];
+      nix.settings.system-features = [ "supported-feature" ];
+      nix.settings.experimental-features = [ "nix-command" ];
+      programs.nix-required-mounts.enable = true;
+      programs.nix-required-mounts.allowedPatterns.supported-feature = {
+        onFeatures = [ "supported-feature" ];
+        paths = [
+          "/supported-feature-files"
+          {
+            host = "/usr/lib/imaginary-fhs-drivers";
+            guest = "/run/opengl-driver/lib";
+          }
+        ];
+        unsafeFollowSymlinks = true;
+      };
+      users.users.person.isNormalUser = true;
+      systemd.tmpfiles.rules = [
+        "d /supported-feature-files 0755 person users -"
+        "f /usr/lib/libcuda.so 0444 root root - fakeContent"
+        "L /usr/lib/imaginary-fhs-drivers/libcuda.so 0444 root root - /usr/lib/libcuda.so"
+      ];
+    };
+  testScript = ''
+    import shlex
+
+    def person_do(cmd, succeed=True):
+        cmd = shlex.quote(cmd)
+        cmd = f"su person -l -c {cmd} &>/dev/console"
+
+        if succeed:
+            return machine.succeed(cmd)
+        else:
+            return machine.fail(cmd)
+
+    start_all()
+
+    person_do("nix-build ${./ensure-path-not-present.nix} --argstr feature supported-feature")
+    person_do("nix-build ${./test-require-feature.nix} --argstr feature supported-feature")
+    person_do("nix-build ${./test-require-feature.nix} --argstr feature unsupported-feature", succeed=False)
+    person_do("nix-build ${./test-structured-attrs.nix} --argstr feature supported-feature")
+    person_do("nix-build ${./test-structured-attrs-empty.nix}")
+  '';
+}
diff --git a/nixos/tests/nix-required-mounts/ensure-path-not-present.nix b/nixos/tests/nix-required-mounts/ensure-path-not-present.nix
new file mode 100644
index 0000000000000..270c268fcbd9e
--- /dev/null
+++ b/nixos/tests/nix-required-mounts/ensure-path-not-present.nix
@@ -0,0 +1,13 @@
+{
+  pkgs ? import <nixpkgs> { },
+  feature,
+}:
+
+pkgs.runCommandNoCC "${feature}-not-present" { } ''
+  if [[ -e /${feature}-files ]]; then
+    echo "No ${feature} in requiredSystemFeatures, but /${feature}-files was mounted anyway"
+    exit 1
+  else
+    touch $out
+  fi
+''
diff --git a/nixos/tests/nix-required-mounts/test-require-feature.nix b/nixos/tests/nix-required-mounts/test-require-feature.nix
new file mode 100644
index 0000000000000..447fd49a300aa
--- /dev/null
+++ b/nixos/tests/nix-required-mounts/test-require-feature.nix
@@ -0,0 +1,26 @@
+{
+  pkgs ? import <nixpkgs> { },
+  feature,
+}:
+
+pkgs.runCommandNoCC "${feature}-present" { requiredSystemFeatures = [ feature ]; } ''
+  if [[ ! -e /${feature}-files ]]; then
+    echo "The host declares ${feature} support, but doesn't expose /${feature}-files" >&2
+    exit 1
+  fi
+  libcudaLocation=/run/opengl-driver/lib/libcuda.so
+  if [[ -e "$libcudaLocation" || -h "$libcudaLocation" ]] ; then
+    true # we're good
+  else
+    echo "The host declares ${feature} support, but it the hook fails to handle the hostPath != guestPath cases" >&2
+    exit 1
+  fi
+  if cat "$libcudaLocation" | xargs test fakeContent = ; then
+    true # we're good
+  else
+    echo "The host declares ${feature} support, but it seems to fail to follow symlinks" >&2
+    echo "The content of /run/opengl-driver/lib/libcuda.so is: $(cat /run/opengl-driver/lib/libcuda.so)" >&2
+    exit 1
+  fi
+  touch $out
+''
diff --git a/nixos/tests/nix-required-mounts/test-structured-attrs-empty.nix b/nixos/tests/nix-required-mounts/test-structured-attrs-empty.nix
new file mode 100644
index 0000000000000..86f2753309368
--- /dev/null
+++ b/nixos/tests/nix-required-mounts/test-structured-attrs-empty.nix
@@ -0,0 +1,8 @@
+{
+  pkgs ? import <nixpkgs> { },
+}:
+
+pkgs.runCommandNoCC "nix-required-mounts-structured-attrs-no-features" { __structuredAttrs = true; }
+  ''
+    touch $out
+  ''
diff --git a/nixos/tests/nix-required-mounts/test-structured-attrs.nix b/nixos/tests/nix-required-mounts/test-structured-attrs.nix
new file mode 100644
index 0000000000000..874910eee7bb3
--- /dev/null
+++ b/nixos/tests/nix-required-mounts/test-structured-attrs.nix
@@ -0,0 +1,18 @@
+{
+  pkgs ? import <nixpkgs> { },
+  feature,
+}:
+
+pkgs.runCommandNoCC "${feature}-present-structured"
+  {
+    __structuredAttrs = true;
+    requiredSystemFeatures = [ feature ];
+  }
+  ''
+    if [[ -e /${feature}-files ]]; then
+      touch $out
+    else
+      echo "The host declares ${feature} support, but doesn't expose /${feature}-files" >&2
+      echo "Do we fail to parse __structuredAttrs=true derivations?" >&2
+    fi
+  ''
diff --git a/nixos/tests/odoo.nix b/nixos/tests/odoo.nix
index 00ae4a2137d10..45ec7b7d7a6b7 100644
--- a/nixos/tests/odoo.nix
+++ b/nixos/tests/odoo.nix
@@ -14,6 +14,18 @@ import ./make-test-python.nix ({ pkgs, lib, package ? pkgs.odoo, ...} : {
         package = package;
         domain = "localhost";
       };
+
+      # odoo does not automatically initialize its database,
+      # even if passing what _should_ be the equivalent of these options:
+      #  settings = {
+      #    options = {
+      #      database = "odoo";
+      #      init = "base";
+      #    };
+      #  };
+      systemd.services.odoo.preStart = ''
+        HOME=$STATE_DIRECTORY ${package}/bin/odoo -d odoo -i base --stop-after-init --without-demo all
+      '';
     };
   };
 
diff --git a/nixos/tests/openarena.nix b/nixos/tests/openarena.nix
index 63dc1b9a68570..4dfe71a9a1e95 100644
--- a/nixos/tests/openarena.nix
+++ b/nixos/tests/openarena.nix
@@ -5,7 +5,7 @@ let
     { pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      hardware.opengl.driSupport = true;
+      hardware.graphics.enable = true;
       environment.systemPackages = [ pkgs.openarena ];
     };
 
diff --git a/nixos/tests/private-gpt.nix b/nixos/tests/private-gpt.nix
index d19e167cc303c..1c90101d29575 100644
--- a/nixos/tests/private-gpt.nix
+++ b/nixos/tests/private-gpt.nix
@@ -5,7 +5,7 @@ in
 {
   name = "private-gpt";
   meta = with lib.maintainers; {
-    maintainers = [ drupol ];
+    maintainers = [ ];
   };
 
   nodes = {
diff --git a/nixos/tests/prometheus/alertmanager.nix b/nixos/tests/prometheus/alertmanager.nix
new file mode 100644
index 0000000000000..feda8d8fc2bcc
--- /dev/null
+++ b/nixos/tests/prometheus/alertmanager.nix
@@ -0,0 +1,148 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-alertmanager";
+
+  nodes = {
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        alertmanagers = [
+          {
+            scheme = "http";
+            static_configs = [
+              {
+                targets = [
+                  "alertmanager:${toString config.services.prometheus.alertmanager.port}"
+                ];
+              }
+            ];
+          }
+        ];
+
+        rules = [
+          ''
+            groups:
+              - name: test
+                rules:
+                  - alert: InstanceDown
+                    expr: up == 0
+                    for: 5s
+                    labels:
+                      severity: page
+                    annotations:
+                      summary: "Instance {{ $labels.instance }} down"
+          ''
+        ];
+
+        scrapeConfigs = [
+          {
+            job_name = "alertmanager";
+            static_configs = [
+              {
+                targets = [
+                  "alertmanager:${toString config.services.prometheus.alertmanager.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    alertmanager = { config, pkgs, ... }: {
+      services.prometheus.alertmanager = {
+        enable = true;
+        openFirewall = true;
+
+        configuration = {
+          global = {
+            resolve_timeout = "1m";
+          };
+
+          route = {
+            # Root route node
+            receiver = "test";
+            group_by = ["..."];
+            continue = false;
+            group_wait = "1s";
+            group_interval = "15s";
+            repeat_interval = "24h";
+          };
+
+          receivers = [
+            {
+              name = "test";
+              webhook_configs = [
+                {
+                  url = "http://logger:6725";
+                  send_resolved = true;
+                  max_alerts = 0;
+                }
+              ];
+            }
+          ];
+        };
+      };
+    };
+
+    logger = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 6725 ];
+
+      services.prometheus.alertmanagerWebhookLogger.enable = true;
+    };
+  };
+
+  testScript = ''
+    alertmanager.wait_for_unit("alertmanager")
+    alertmanager.wait_for_open_port(9093)
+    alertmanager.wait_until_succeeds("curl -s http://127.0.0.1:9093/-/ready")
+    #alertmanager.wait_until_succeeds("journalctl -o cat -u alertmanager.service | grep 'version=${pkgs.prometheus-alertmanager.version}'")
+
+    logger.wait_for_unit("alertmanager-webhook-logger")
+    logger.wait_for_open_port(6725)
+
+    prometheus.wait_for_unit("prometheus")
+    prometheus.wait_for_open_port(9090)
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"alertmanager\"\}==1)' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(alertmanager_build_info)%20by%20(version)' | "
+      + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-alertmanager.version}\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\}!=1)' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=alertmanager_notifications_total\{integration=\"webhook\"\}' | "
+      + "jq '.data.result[0].value[1]' | grep -v '\"0\"'"
+    )
+
+    logger.wait_until_succeeds(
+      "journalctl -o cat -u alertmanager-webhook-logger.service | grep '\"alertname\":\"InstanceDown\"'"
+    )
+  '';
+})
diff --git a/nixos/tests/prometheus/config-reload.nix b/nixos/tests/prometheus/config-reload.nix
new file mode 100644
index 0000000000000..786668c624ea9
--- /dev/null
+++ b/nixos/tests/prometheus/config-reload.nix
@@ -0,0 +1,116 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-config-reload";
+
+  nodes = {
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        enableReload = true;
+        globalConfig.scrape_interval = "2s";
+        scrapeConfigs = [
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+
+      specialisation = {
+        "prometheus-config-change" = {
+          configuration = {
+            environment.systemPackages = [ pkgs.yq ];
+
+            # This configuration just adds a new prometheus job
+            # to scrape the node_exporter metrics of the s3 machine.
+            services.prometheus = {
+              scrapeConfigs = [
+                {
+                  job_name = "node";
+                  static_configs = [
+                    {
+                      targets = [ "node:${toString config.services.prometheus.exporters.node.port}" ];
+                    }
+                  ];
+                }
+              ];
+            };
+          };
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    prometheus.wait_for_unit("prometheus")
+    prometheus.wait_for_open_port(9090)
+
+    # Check if switching to a NixOS configuration that changes the prometheus
+    # configuration reloads (instead of restarts) prometheus before the switch
+    # finishes successfully:
+    with subtest("config change reloads prometheus"):
+      import json
+      # We check if prometheus has finished reloading by looking for the message
+      # "Completed loading of configuration file" in the journal between the start
+      # and finish of switching to the new NixOS configuration.
+      #
+      # To mark the start we record the journal cursor before starting the switch:
+      cursor_before_switching = json.loads(
+          prometheus.succeed("journalctl -n1 -o json --output-fields=__CURSOR")
+      )["__CURSOR"]
+
+      # Now we switch:
+      prometheus_config_change = prometheus.succeed(
+          "readlink /run/current-system/specialisation/prometheus-config-change"
+      ).strip()
+      prometheus.succeed(prometheus_config_change + "/bin/switch-to-configuration test")
+
+      # Next we retrieve all logs since the start of switching:
+      logs_after_starting_switching = prometheus.succeed(
+          """
+            journalctl --after-cursor='{cursor_before_switching}' -o json --output-fields=MESSAGE
+          """.format(
+              cursor_before_switching=cursor_before_switching
+          )
+      )
+
+      # Finally we check if the message "Completed loading of configuration file"
+      # occurs before the "finished switching to system configuration" message:
+      finished_switching_msg = (
+          "finished switching to system configuration " + prometheus_config_change
+      )
+      reloaded_before_switching_finished = False
+      finished_switching = False
+      for log_line in logs_after_starting_switching.split("\n"):
+          msg = json.loads(log_line)["MESSAGE"]
+          if "Completed loading of configuration file" in msg:
+              reloaded_before_switching_finished = True
+          if msg == finished_switching_msg:
+              finished_switching = True
+              break
+
+      assert reloaded_before_switching_finished
+      assert finished_switching
+
+      # Check if the reloaded config includes the new node job:
+      prometheus.succeed(
+        """
+          curl -sf http://127.0.0.1:9090/api/v1/status/config \
+            | jq -r .data.yaml \
+            | yq '.scrape_configs | any(.job_name == "node")' \
+            | grep true
+        """
+      )
+  '';
+})
diff --git a/nixos/tests/prometheus/default.nix b/nixos/tests/prometheus/default.nix
new file mode 100644
index 0000000000000..133922a453c05
--- /dev/null
+++ b/nixos/tests/prometheus/default.nix
@@ -0,0 +1,13 @@
+{ system ? builtins.currentSystem
+, config ? { }
+, pkgs ? import ../../.. { inherit system config; }
+}:
+
+{
+  alertmanager = import ./alertmanager.nix { inherit system pkgs; };
+  config-reload = import ./config-reload.nix { inherit system pkgs; };
+  federation = import ./federation.nix { inherit system pkgs; };
+  prometheus-pair = import ./prometheus-pair.nix { inherit system pkgs; };
+  pushgateway = import ./pushgateway.nix { inherit system pkgs; };
+  remote-write = import ./remote-write.nix { inherit system pkgs; };
+}
diff --git a/nixos/tests/prometheus/federation.nix b/nixos/tests/prometheus/federation.nix
new file mode 100644
index 0000000000000..0f05166c8f5da
--- /dev/null
+++ b/nixos/tests/prometheus/federation.nix
@@ -0,0 +1,213 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-federation";
+
+  nodes = {
+    global1 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "federate";
+            honor_labels = true;
+            metrics_path = "/federate";
+
+            params = {
+              "match[]" = [
+                "{job=\"node\"}"
+                "{job=\"prometheus\"}"
+              ];
+            };
+
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "global1:${toString config.services.prometheus.port}"
+                  "global2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    global2 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "federate";
+            honor_labels = true;
+            metrics_path = "/federate";
+
+            params = {
+              "match[]" = [
+                "{job=\"node\"}"
+                "{job=\"prometheus\"}"
+              ];
+            };
+
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "global1:${toString config.services.prometheus.port}"
+                  "global2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    prometheus1 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node1:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    prometheus2 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node2:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    node1 = { config, pkgs, ... }: {
+      services.prometheus.exporters.node = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+
+    node2 = { config, pkgs, ... }: {
+      services.prometheus.exporters.node = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+  };
+
+  testScript = ''
+    for machine in node1, node2:
+      machine.wait_for_unit("prometheus-node-exporter")
+      machine.wait_for_open_port(9100)
+
+    for machine in prometheus1, prometheus2, global1, global2:
+      machine.wait_for_unit("prometheus")
+      machine.wait_for_open_port(9090)
+
+    # Verify both servers got the same data from the exporter
+    for machine in prometheus1, prometheus2:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+
+    for machine in global1, global2:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"node\"\})' | "
+        + "jq '.data.result[0].value[1]' | grep '\"2\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(prometheus_build_info)' | "
+        + "jq '.data.result[0].value[1]' | grep '\"4\"'"
+      )
+  '';
+})
diff --git a/nixos/tests/prometheus/prometheus-pair.nix b/nixos/tests/prometheus/prometheus-pair.nix
new file mode 100644
index 0000000000000..3ac70ca0403ec
--- /dev/null
+++ b/nixos/tests/prometheus/prometheus-pair.nix
@@ -0,0 +1,87 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-pair";
+
+  nodes = {
+    prometheus1 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+        scrapeConfigs = [
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    prometheus2 = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+        scrapeConfigs = [
+          {
+            job_name = "prometheus";
+            static_configs = [
+              {
+                targets = [
+                  "prometheus1:${toString config.services.prometheus.port}"
+                  "prometheus2:${toString config.services.prometheus.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+  };
+
+  testScript = ''
+    for machine in prometheus1, prometheus2:
+      machine.wait_for_unit("prometheus")
+      machine.wait_for_open_port(9090)
+      machine.wait_until_succeeds("journalctl -o cat -u prometheus.service | grep 'version=${pkgs.prometheus.version}'")
+      machine.wait_until_succeeds("curl -sSf http://localhost:9090/-/healthy")
+
+    # Prometheii ready - run some queries
+    for machine in prometheus1, prometheus2:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\",version=\"${pkgs.prometheus.version}\"\}' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=prometheus_build_info\{instance=\"prometheus1:9090\"\}' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
+        + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus.version}\"'"
+      )
+
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(prometheus_build_info)%20by%20(version)' | "
+        + "jq '.data.result[0].value[1]' | grep '\"2\"'"
+      )
+
+    prometheus1.log(prometheus1.succeed("systemd-analyze security prometheus.service | grep -v '✓'"))
+  '';
+})
diff --git a/nixos/tests/prometheus/pushgateway.nix b/nixos/tests/prometheus/pushgateway.nix
new file mode 100644
index 0000000000000..7904c8bf45b04
--- /dev/null
+++ b/nixos/tests/prometheus/pushgateway.nix
@@ -0,0 +1,94 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-pushgateway";
+
+  nodes = {
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        scrapeConfigs = [
+          {
+            job_name = "pushgateway";
+            static_configs = [
+              {
+                targets = [
+                  "pushgateway:9091"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    pushgateway = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 9091 ];
+
+      services.prometheus.pushgateway = {
+        enable = true;
+      };
+    };
+
+    client = { config, pkgs, ... }: {
+    };
+  };
+
+  testScript = ''
+    pushgateway.wait_for_unit("pushgateway")
+    pushgateway.wait_for_open_port(9091)
+    pushgateway.wait_until_succeeds("curl -s http://127.0.0.1:9091/-/ready")
+    pushgateway.wait_until_succeeds("journalctl -o cat -u pushgateway.service | grep 'version=${pkgs.prometheus-pushgateway.version}'")
+
+    prometheus.wait_for_unit("prometheus")
+    prometheus.wait_for_open_port(9090)
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=count(up\{job=\"pushgateway\"\})' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=sum(pushgateway_build_info)%20by%20(version)' | "
+      + "jq '.data.result[0].metric.version' | grep '\"${pkgs.prometheus-pushgateway.version}\"'"
+    )
+
+    client.wait_for_unit("network-online.target")
+
+    # Add a metric and check in Prometheus
+    client.wait_until_succeeds(
+      "echo 'some_metric 3.14' | curl --data-binary @- http://pushgateway:9091/metrics/job/some_job"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
+      + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
+      + "jq '.data.result[0].value[1]' | grep 'null'"
+    )
+
+    # Delete the metric, check not in Prometheus
+    client.wait_until_succeeds(
+      "curl -X DELETE http://pushgateway:9091/metrics/job/some_job"
+    )
+
+    prometheus.wait_until_fails(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=some_metric' | "
+      + "jq '.data.result[0].value[1]' | grep '\"3.14\"'"
+    )
+
+    prometheus.wait_until_succeeds(
+      "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=absent(some_metric)' | "
+      + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+    )
+  '';
+})
diff --git a/nixos/tests/prometheus/remote-write.nix b/nixos/tests/prometheus/remote-write.nix
new file mode 100644
index 0000000000000..24092b9fb88da
--- /dev/null
+++ b/nixos/tests/prometheus/remote-write.nix
@@ -0,0 +1,73 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }:
+
+{
+  name = "prometheus-remote-write";
+
+  nodes = {
+    receiver = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        extraFlags = [ "--web.enable-remote-write-receiver" ];
+      };
+    };
+
+    prometheus = { config, pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+
+      networking.firewall.allowedTCPPorts = [ config.services.prometheus.port ];
+
+      services.prometheus = {
+        enable = true;
+        globalConfig.scrape_interval = "2s";
+
+        remoteWrite = [
+          {
+            url = "http://receiver:9090/api/v1/write";
+          }
+        ];
+
+        scrapeConfigs = [
+          {
+            job_name = "node";
+            static_configs = [
+              {
+                targets = [
+                  "node:${toString config.services.prometheus.exporters.node.port}"
+                ];
+              }
+            ];
+          }
+        ];
+      };
+    };
+
+    node = { config, pkgs, ... }: {
+      services.prometheus.exporters.node = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+  };
+
+  testScript = ''
+    node.wait_for_unit("prometheus-node-exporter")
+    node.wait_for_open_port(9100)
+
+    for machine in prometheus, receiver:
+      machine.wait_for_unit("prometheus")
+      machine.wait_for_open_port(9090)
+
+    # Verify both servers got the same data from the exporter
+    for machine in prometheus, receiver:
+      machine.wait_until_succeeds(
+        "curl -sf 'http://127.0.0.1:9090/api/v1/query?query=node_exporter_build_info\{instance=\"node:9100\"\}' | "
+        + "jq '.data.result[0].value[1]' | grep '\"1\"'"
+      )
+  '';
+})
diff --git a/nixos/tests/quake3.nix b/nixos/tests/quake3.nix
index 4b7ca03b365b5..ff4025e56f4c4 100644
--- a/nixos/tests/quake3.nix
+++ b/nixos/tests/quake3.nix
@@ -21,7 +21,7 @@ let
     { pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      hardware.opengl.driSupport = true;
+      hardware.graphics.enable = true;
       environment.systemPackages = [ pkgs.quake3demo ];
       nixpkgs.config.packageOverrides = overrides;
       nixpkgs.config.allowUnfreePredicate = unfreePredicate;
diff --git a/nixos/tests/rtorrent.nix b/nixos/tests/rtorrent.nix
new file mode 100644
index 0000000000000..77e78b549a96f
--- /dev/null
+++ b/nixos/tests/rtorrent.nix
@@ -0,0 +1,25 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+let
+  port = 50001;
+in
+{
+  name = "rtorrent";
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ thiagokokada ];
+  };
+
+  nodes.machine = { pkgs, ... }: {
+    services.rtorrent = {
+      inherit port;
+      enable = true;
+    };
+  };
+
+  testScript = /* python */ ''
+    machine.start()
+    machine.wait_for_unit("rtorrent.service")
+    machine.wait_for_open_port(${toString port})
+
+    machine.succeed("nc -z localhost ${toString port}")
+  '';
+})
diff --git a/nixos/tests/searx.nix b/nixos/tests/searx.nix
index 02a88f690db78..0008424f068b2 100644
--- a/nixos/tests/searx.nix
+++ b/nixos/tests/searx.nix
@@ -1,4 +1,4 @@
-import ./make-test-python.nix ({ pkgs, ...} :
+{ pkgs, ... }:
 
 {
   name = "searx";
@@ -7,108 +7,108 @@ import ./make-test-python.nix ({ pkgs, ...} :
   };
 
   # basic setup: searx running the built-in webserver
-  nodes.base = { ... }: {
-    imports = [ ../modules/profiles/minimal.nix ];
-
-    services.searx = {
-      enable = true;
-      environmentFile = pkgs.writeText "secrets" ''
-        WOLFRAM_API_KEY  = sometoken
-        SEARX_SECRET_KEY = somesecret
-      '';
+  nodes.base =
+    { ... }:
+    {
+      services.searx = {
+        enable = true;
+        environmentFile = pkgs.writeText "secrets" ''
+          WOLFRAM_API_KEY  = sometoken
+          SEARX_SECRET_KEY = somesecret
+        '';
 
-      settings.server =
-        { port = "8080";
+        settings.server = {
+          port = "8080";
           bind_address = "0.0.0.0";
           secret_key = "@SEARX_SECRET_KEY@";
         };
-      settings.engines = [
-        { name = "wolframalpha";
-          api_key = "@WOLFRAM_API_KEY@";
-          engine = "wolframalpha_api";
-        }
-        { name = "startpage";
-          shortcut = "start";
-        }
-      ];
-    };
+        settings.engines = [
+          {
+            name = "wolframalpha";
+            api_key = "@WOLFRAM_API_KEY@";
+            engine = "wolframalpha_api";
+          }
+          {
+            name = "startpage";
+            shortcut = "start";
+          }
+        ];
+      };
 
-  };
+    };
 
   # fancy setup: run in uWSGI and use nginx as proxy
-  nodes.fancy = { config, ... }: {
-    imports = [ ../modules/profiles/minimal.nix ];
-
-    services.searx = {
-      enable = true;
-      # searx refuses to run if unchanged
-      settings.server.secret_key = "somesecret";
-
-      runInUwsgi = true;
-      uwsgiConfig = {
-        # serve using the uwsgi protocol
-        socket = "/run/searx/uwsgi.sock";
-        chmod-socket = "660";
-
-        # use /searx as url "mountpoint"
-        mount = "/searx=searx.webapp:application";
-        module = "";
-        manage-script-name = true;
+  nodes.fancy =
+    { config, ... }:
+    {
+      services.searx = {
+        enable = true;
+        # searx refuses to run if unchanged
+        settings.server.secret_key = "somesecret";
+
+        runInUwsgi = true;
+        uwsgiConfig = {
+          # serve using the uwsgi protocol
+          socket = "/run/searx/uwsgi.sock";
+          chmod-socket = "660";
+
+          # use /searx as url "mountpoint"
+          mount = "/searx=searx.webapp:application";
+          module = "";
+          manage-script-name = true;
+        };
       };
-    };
 
-    # use nginx as reverse proxy
-    services.nginx.enable = true;
-    services.nginx.virtualHosts.localhost = {
-      locations."/searx".extraConfig =
-        ''
+      # use nginx as reverse proxy
+      services.nginx.enable = true;
+      services.nginx.virtualHosts.localhost = {
+        locations."/searx".extraConfig = ''
           include ${pkgs.nginx}/conf/uwsgi_params;
           uwsgi_pass unix:/run/searx/uwsgi.sock;
         '';
-      locations."/searx/static/".alias = "${config.services.searx.package}/share/static/";
-    };
-
-    # allow nginx access to the searx socket
-    users.users.nginx.extraGroups = [ "searx" ];
-
-  };
-
-  testScript =
-    ''
-      base.start()
-
-      with subtest("Settings have been merged"):
-          base.wait_for_unit("searx-init")
-          base.wait_for_file("/run/searx/settings.yml")
-          output = base.succeed(
-              "${pkgs.yq-go}/bin/yq eval"
-              " '.engines[] | select(.name==\"startpage\") | .shortcut'"
-              " /run/searx/settings.yml"
-          ).strip()
-          assert output == "start", "Settings not merged"
+        locations."/searx/static/".alias = "${config.services.searx.package}/share/static/";
+      };
 
-      with subtest("Environment variables have been substituted"):
-          base.succeed("grep -q somesecret /run/searx/settings.yml")
-          base.succeed("grep -q sometoken /run/searx/settings.yml")
-          base.copy_from_vm("/run/searx/settings.yml")
+      # allow nginx access to the searx socket
+      users.users.nginx.extraGroups = [ "searx" ];
 
-      with subtest("Basic setup is working"):
-          base.wait_for_open_port(8080)
-          base.wait_for_unit("searx")
-          base.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost:8080"
-          )
-          base.shutdown()
+    };
 
-      with subtest("Nginx+uWSGI setup is working"):
-          fancy.start()
-          fancy.wait_for_open_port(80)
-          fancy.wait_for_unit("uwsgi")
-          fancy.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2"
-          )
-          fancy.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2"
-          )
-    '';
-})
+  testScript = ''
+    base.start()
+
+    with subtest("Settings have been merged"):
+        base.wait_for_unit("searx-init")
+        base.wait_for_file("/run/searx/settings.yml")
+        output = base.succeed(
+            "${pkgs.yq-go}/bin/yq eval"
+            " '.engines[] | select(.name==\"startpage\") | .shortcut'"
+            " /run/searx/settings.yml"
+        ).strip()
+        assert output == "start", "Settings not merged"
+
+    with subtest("Environment variables have been substituted"):
+        base.succeed("grep -q somesecret /run/searx/settings.yml")
+        base.succeed("grep -q sometoken /run/searx/settings.yml")
+        base.copy_from_vm("/run/searx/settings.yml")
+
+    with subtest("Basic setup is working"):
+        base.wait_for_open_port(8080)
+        base.wait_for_unit("searx")
+        base.succeed(
+            "${pkgs.curl}/bin/curl --fail http://localhost:8080"
+        )
+        base.shutdown()
+
+    with subtest("Nginx+uWSGI setup is working"):
+        fancy.start()
+        fancy.wait_for_open_port(80)
+        fancy.wait_for_unit("uwsgi")
+        fancy.succeed(
+            "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2"
+        )
+        fancy.succeed(
+            "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2"
+        )
+  '';
+}
diff --git a/nixos/tests/seatd.nix b/nixos/tests/seatd.nix
index 138a6cb1cf44c..9178492fdb0ef 100644
--- a/nixos/tests/seatd.nix
+++ b/nixos/tests/seatd.nix
@@ -39,7 +39,7 @@ in
           dwl -s 'foot touch /tmp/foot_started'
     '';
 
-    hardware.opengl.enable = true;
+    hardware.graphics.enable = true;
     virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
     services.seatd.enable = true;
   };
diff --git a/nixos/tests/snapper.nix b/nixos/tests/snapper.nix
index 674523584fdaa..0369419930f15 100644
--- a/nixos/tests/snapper.nix
+++ b/nixos/tests/snapper.nix
@@ -19,7 +19,9 @@ import ./make-test-python.nix ({ ... }:
     services.snapper.filters = "/nix";
   };
 
-  testScript = ''
+  testScript = { nodes, ... }: let
+    inherit (nodes.machine.services.snapper) snapshotRootOnBoot;
+  in ''
     machine.succeed("btrfs subvolume create /home/.snapshots")
     machine.succeed("snapper -c home list")
     machine.succeed("snapper -c home create --description empty")
@@ -31,5 +33,6 @@ import ./make-test-python.nix ({ ... }:
     machine.succeed("snapper -c home delete 2")
     machine.succeed("systemctl --wait start snapper-timeline.service")
     machine.succeed("systemctl --wait start snapper-cleanup.service")
+    machine.${if snapshotRootOnBoot then "succeed" else "fail"}("systemctl cat snapper-boot.service")
   '';
 })
diff --git a/nixos/tests/tandoor-recipes-script-name.nix b/nixos/tests/tandoor-recipes-script-name.nix
new file mode 100644
index 0000000000000..9010ffa3ccfa1
--- /dev/null
+++ b/nixos/tests/tandoor-recipes-script-name.nix
@@ -0,0 +1,95 @@
+import ./make-test-python.nix (
+  { pkgs, lib, ... }:
+  {
+    name = "tandoor-recipes-script-name";
+
+    nodes.machine =
+      { pkgs, nodes, ... }:
+      {
+        services.tandoor-recipes = {
+          enable = true;
+          extraConfig = {
+            SCRIPT_NAME = "/any/path";
+            STATIC_URL = "${nodes.machine.services.tandoor-recipes.extraConfig.SCRIPT_NAME}/static/";
+          };
+        };
+      };
+
+    testScript =
+      { nodes, ... }:
+      let
+        inherit (nodes.machine.services.tandoor-recipes) address port;
+        inherit (nodes.machine.services.tandoor-recipes.extraConfig) SCRIPT_NAME;
+      in
+      ''
+        from html.parser import HTMLParser
+
+        origin_url = "http://${address}:${toString port}"
+        base_url = f"{origin_url}${SCRIPT_NAME}"
+        login_path = "/admin/login/"
+        login_url = f"{base_url}{login_path}"
+
+        cookie_jar_path = "/tmp/cookies.txt"
+        curl = f"curl --cookie {cookie_jar_path} --cookie-jar {cookie_jar_path} --fail --header 'Origin: {origin_url}' --show-error --silent"
+
+        print("Wait for the service to respond")
+        machine.wait_for_unit("tandoor-recipes.service")
+        machine.wait_until_succeeds(f"{curl} {login_url}")
+
+        username = "username"
+        password = "password"
+
+        print("Create admin user")
+        machine.succeed(
+            f"DJANGO_SUPERUSER_PASSWORD='{password}' /var/lib/tandoor-recipes/tandoor-recipes-manage createsuperuser --no-input --username='{username}' --email=nobody@example.com"
+        )
+
+        print("Get CSRF token for later requests")
+        csrf_token = machine.succeed(f"grep csrftoken {cookie_jar_path} | cut --fields=7").rstrip()
+
+        print("Log in as admin user")
+        machine.succeed(
+            f"{curl} --data 'csrfmiddlewaretoken={csrf_token}' --data 'username={username}' --data 'password={password}' {login_url}"
+        )
+
+        print("Get the contents of the logged in main page")
+        logged_in_page = machine.succeed(f"{curl} --location {base_url}")
+
+        class UrlParser(HTMLParser):
+            def __init__(self):
+                super().__init__()
+
+                self.urls: list[str] = []
+
+            def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
+                if tag == "form":
+                    for name, value in attrs:
+                        if name == "action" and value is not None:
+                            assert not value.endswith(login_path)
+                            break
+
+                if tag != "a":
+                    return
+
+                for name, value in attrs:
+                    if name == "href" and value is not None:
+                        if value.startswith(base_url):
+                            self.urls.append(value)
+                        elif value.startswith("/"):
+                            self.urls.append(f"{origin_url}{value}")
+                        else:
+                            print("Ignoring external URL: {value}")
+
+                        break
+
+        parser = UrlParser()
+        parser.feed(logged_in_page)
+
+        for url in parser.urls:
+            with subtest(f"Verify that {url} can be reached"):
+                machine.succeed(f"{curl} {url}")
+      '';
+
+    meta.maintainers = with lib.maintainers; [ l0b0 ];
+  }
+)
diff --git a/nixos/tests/prometheus.nix b/nixos/tests/thanos.nix
index 0111273893775..5bdfab7b3573f 100644
--- a/nixos/tests/prometheus.nix
+++ b/nixos/tests/thanos.nix
@@ -212,8 +212,6 @@ in import ./make-test-python.nix {
   };
 
   testScript = { nodes, ... } : ''
-    import json
-
     # Before starting the other machines we first make sure that our S3 service is online
     # and has a bucket added for thanos:
     s3.start()
@@ -289,61 +287,5 @@ in import ./make-test-python.nix {
         + "jq .thanos.labels.some_label | "
         + "grep 'required by thanos'"
     )
-
-    # Check if switching to a NixOS configuration that changes the prometheus
-    # configuration reloads (instead of restarts) prometheus before the switch
-    # finishes successfully:
-    with subtest("config change reloads prometheus"):
-        # We check if prometheus has finished reloading by looking for the message
-        # "Completed loading of configuration file" in the journal between the start
-        # and finish of switching to the new NixOS configuration.
-        #
-        # To mark the start we record the journal cursor before starting the switch:
-        cursor_before_switching = json.loads(
-            prometheus.succeed("journalctl -n1 -o json --output-fields=__CURSOR")
-        )["__CURSOR"]
-
-        # Now we switch:
-        prometheus_config_change = prometheus.succeed(
-            "readlink /run/current-system/specialisation/prometheus-config-change"
-        ).strip()
-        prometheus.succeed(prometheus_config_change + "/bin/switch-to-configuration test")
-
-        # Next we retrieve all logs since the start of switching:
-        logs_after_starting_switching = prometheus.succeed(
-            """
-              journalctl --after-cursor='{cursor_before_switching}' -o json --output-fields=MESSAGE
-            """.format(
-                cursor_before_switching=cursor_before_switching
-            )
-        )
-
-        # Finally we check if the message "Completed loading of configuration file"
-        # occurs before the "finished switching to system configuration" message:
-        finished_switching_msg = (
-            "finished switching to system configuration " + prometheus_config_change
-        )
-        reloaded_before_switching_finished = False
-        finished_switching = False
-        for log_line in logs_after_starting_switching.split("\n"):
-            msg = json.loads(log_line)["MESSAGE"]
-            if "Completed loading of configuration file" in msg:
-                reloaded_before_switching_finished = True
-            if msg == finished_switching_msg:
-                finished_switching = True
-                break
-
-        assert reloaded_before_switching_finished
-        assert finished_switching
-
-        # Check if the reloaded config includes the new s3-node_exporter job:
-        prometheus.succeed(
-          """
-            curl -sf http://127.0.0.1:${toString queryPort}/api/v1/status/config \
-              | jq -r .data.yaml \
-              | yq '.scrape_configs | any(.job_name == "s3-node_exporter")' \
-              | grep true
-          """
-        )
   '';
 }
diff --git a/nixos/tests/tigervnc.nix b/nixos/tests/tigervnc.nix
index b80cb49519c45..79c4f19178d5e 100644
--- a/nixos/tests/tigervnc.nix
+++ b/nixos/tests/tigervnc.nix
@@ -7,7 +7,7 @@ with import ../lib/testing-python.nix { inherit system pkgs; };
 makeTest {
   name = "tigervnc";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ lheckemann ];
+    maintainers = [ ];
   };
 
   nodes = {
diff --git a/nixos/tests/timezone.nix b/nixos/tests/timezone.nix
index 7fc9a5058eee9..5d0318e33daab 100644
--- a/nixos/tests/timezone.nix
+++ b/nixos/tests/timezone.nix
@@ -1,6 +1,6 @@
 import ./make-test-python.nix ({ pkgs, ...} : {
   name = "timezone";
-  meta.maintainers = with pkgs.lib.maintainers; [ lheckemann ];
+  meta.maintainers = with pkgs.lib.maintainers; [ ];
 
   nodes = {
     node_eutz = { pkgs, ... }: {
diff --git a/nixos/tests/tinywl.nix b/nixos/tests/tinywl.nix
index 9199866b57af7..2dc354812a75e 100644
--- a/nixos/tests/tinywl.nix
+++ b/nixos/tests/tinywl.nix
@@ -16,7 +16,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
         systemPackages = with pkgs; [ tinywl foot wayland-utils ];
       };
 
-      hardware.opengl.enable = true;
+      hardware.graphics.enable = true;
 
       # Automatically start TinyWL when logging in on tty1:
       programs.bash.loginShellInit = ''
diff --git a/nixos/tests/tomcat.nix b/nixos/tests/tomcat.nix
index df5cb033b78f0..c5e6e65ac600e 100644
--- a/nixos/tests/tomcat.nix
+++ b/nixos/tests/tomcat.nix
@@ -5,23 +5,24 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
   nodes.machine = { pkgs, ... }: {
     services.tomcat = {
       enable = true;
+      port = 8001;
       axis2.enable = true;
     };
   };
 
   testScript = ''
     machine.wait_for_unit("tomcat.service")
-    machine.wait_for_open_port(8080)
+    machine.wait_for_open_port(8001)
     machine.wait_for_file("/var/tomcat/webapps/examples");
 
     machine.succeed(
-        "curl -sS --fail http://localhost:8080/examples/servlets/servlet/HelloWorldExample | grep 'Hello World!'"
+        "curl -sS --fail http://localhost:8001/examples/servlets/servlet/HelloWorldExample | grep 'Hello World!'"
     )
     machine.succeed(
-        "curl -sS --fail http://localhost:8080/examples/jsp/jsp2/simpletag/hello.jsp | grep 'Hello, world!'"
+        "curl -sS --fail http://localhost:8001/examples/jsp/jsp2/simpletag/hello.jsp | grep 'Hello, world!'"
     )
     machine.succeed(
-        "curl -sS --fail http://localhost:8080/axis2/axis2-web/HappyAxis.jsp | grep 'Found Axis2'"
+        "curl -sS --fail http://localhost:8001/axis2/axis2-web/HappyAxis.jsp | grep 'Found Axis2'"
     )
   '';
 })
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index baefa67dbf535..a60cb3af5535c 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -208,6 +208,10 @@ builtins.mapAttrs (k: v: makeVaultwardenTest k v) {
           server.succeed('[ -d "/var/lib/vaultwarden/backups" ]')
           server.succeed('[ -f "/var/lib/vaultwarden/backups/db.sqlite3" ]')
           server.succeed('[ -d "/var/lib/vaultwarden/backups/attachments" ]')
+          server.succeed('[ -f "/var/lib/vaultwarden/backups/rsa_key.pem" ]')
+          server.succeed('[ -f "/var/lib/vaultwarden/backups/rsa_key.pub.pem" ]')
+          # Ensure only the db backed up with the backup command exists and not the other db files.
+          server.succeed('[ ! -f "/var/lib/vaultwarden/backups/db.sqlite3-shm" ]')
     '';
   };
 }
diff --git a/nixos/tests/wg-access-server.nix b/nixos/tests/wg-access-server.nix
new file mode 100644
index 0000000000000..84fdf43e7943b
--- /dev/null
+++ b/nixos/tests/wg-access-server.nix
@@ -0,0 +1,28 @@
+import ./make-test-python.nix ({ pkgs, lib, kernelPackages ? null, ... }:
+{
+  name = "wg-access-server";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ xanderio ];
+  };
+
+  nodes = {
+    server = {
+      services.wg-access-server = {
+        enable = true;
+        settings = {
+          adminUsername = "admin";
+        };
+        secretsFile = (pkgs.writers.writeYAML "secrets.yaml" {
+          adminPassword = "hunter2";
+        });
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    server.wait_for_unit("wg-access-server.service")
+  '';
+}
+)
diff --git a/nixos/tests/wstunnel.nix b/nixos/tests/wstunnel.nix
new file mode 100644
index 0000000000000..3bbc295568fb7
--- /dev/null
+++ b/nixos/tests/wstunnel.nix
@@ -0,0 +1,96 @@
+let
+  certs = import ./common/acme/server/snakeoil-certs.nix;
+  domain = certs.domain;
+in
+
+{
+  name = "wstunnel";
+
+  nodes = {
+    server = {
+      virtualisation.vlans = [ 1 ];
+
+      security.pki.certificateFiles = [ certs.ca.cert ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.1/24";
+      };
+
+      services.wstunnel = {
+        enable = true;
+        servers.my-server = {
+          listen = {
+            host = "10.0.0.1";
+            port = 443;
+          };
+          tlsCertificate = certs.${domain}.cert;
+          tlsKey = certs.${domain}.key;
+        };
+      };
+    };
+
+    client = {
+      virtualisation.vlans = [ 1 ];
+
+      security.pki.certificateFiles = [ certs.ca.cert ];
+
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+        firewall.enable = false;
+        extraHosts = ''
+          10.0.0.1 ${domain}
+        '';
+      };
+
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "10.0.0.2/24";
+      };
+
+      services.wstunnel = {
+        enable = true;
+        clients.my-client = {
+          autoStart = false;
+          connectTo = "wss://${domain}:443";
+          localToRemote = [
+            "tcp://8080:localhost:2080"
+          ];
+          remoteToLocal = [
+            "tcp://2081:localhost:8081"
+          ];
+        };
+      };
+    };
+  };
+
+  testScript = /* python */ ''
+    start_all()
+    server.wait_for_unit("wstunnel-server-my-server.service")
+    client.wait_for_open_port(443, "10.0.0.1")
+
+    client.systemctl("start wstunnel-client-my-client.service")
+    client.wait_for_unit("wstunnel-client-my-client.service")
+
+    with subtest("connection from client to server"):
+      server.succeed("nc -l 2080 >/tmp/msg &")
+      client.sleep(1)
+      client.succeed('nc -w1 localhost 8080 <<<"Hello from client"')
+      server.succeed('grep "Hello from client" /tmp/msg')
+
+    with subtest("connection from server to client"):
+      client.succeed("nc -l 8081 >/tmp/msg &")
+      server.sleep(1)
+      server.succeed('nc -w1 localhost 2081 <<<"Hello from server"')
+      client.succeed('grep "Hello from server" /tmp/msg')
+
+    client.systemctl("stop wstunnel-client-my-client.service")
+  '';
+}