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/default.nix7
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md32
-rw-r--r--nixos/lib/make-options-doc/default.nix99
-rw-r--r--nixos/lib/systemd-lib.nix15
-rw-r--r--nixos/lib/utils.nix4
-rw-r--r--nixos/modules/config/nix.nix11
-rw-r--r--nixos/modules/config/resolvconf.nix2
-rw-r--r--nixos/modules/hardware/video/webcam/ipu6.nix5
-rw-r--r--nixos/modules/i18n/input-method/fcitx5.nix4
-rw-r--r--nixos/modules/image/repart-image.nix85
-rw-r--r--nixos/modules/image/repart.nix42
-rw-r--r--nixos/modules/misc/mandoc.nix34
-rw-r--r--nixos/modules/module-list.nix4
-rw-r--r--nixos/modules/programs/_1password-gui.nix8
-rw-r--r--nixos/modules/programs/starship.nix29
-rw-r--r--nixos/modules/programs/steam.nix13
-rw-r--r--nixos/modules/security/pam.nix2
-rw-r--r--nixos/modules/services/audio/spotifyd.nix4
-rw-r--r--nixos/modules/services/databases/postgresql.md2
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/desktop-managers/plasma6.nix4
-rw-r--r--nixos/modules/services/desktops/pipewire/pipewire.nix10
-rw-r--r--nixos/modules/services/desktops/pipewire/wireplumber.nix32
-rw-r--r--nixos/modules/services/display-managers/greetd.nix2
-rw-r--r--nixos/modules/services/matrix/synapse.nix3
-rw-r--r--nixos/modules/services/misc/etebase-server.nix2
-rw-r--r--nixos/modules/services/misc/gitlab.nix11
-rw-r--r--nixos/modules/services/misc/llama-cpp.nix2
-rw-r--r--nixos/modules/services/misc/ollama.nix60
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix7
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/bind.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/bird.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/collectd.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/domain.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/fastly.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/flow.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/fritz.nix97
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/graphite.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/idrac.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/json.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/kea.nix22
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/keylight.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/knot.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/lnd.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/mail.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/minio.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/node.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nut.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/pihole.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/ping.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postfix.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postgres.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/process.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/pve.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/redis.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/restic.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/script.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/shelly.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/snmp.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/sql.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/statsd.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/tor.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unbound.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unifi.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/varnish.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/zfs.nix2
-rw-r--r--nixos/modules/services/monitoring/scrutiny.nix78
-rw-r--r--nixos/modules/services/networking/dnscache.nix6
-rw-r--r--nixos/modules/services/networking/mihomo.nix118
-rw-r--r--nixos/modules/services/networking/mycelium.nix133
-rw-r--r--nixos/modules/services/networking/networkmanager.nix27
-rw-r--r--nixos/modules/services/networking/tinyproxy.nix1
-rw-r--r--nixos/modules/services/web-apps/engelsystem.nix1
-rw-r--r--nixos/modules/services/web-apps/hedgedoc.nix8
-rw-r--r--nixos/modules/services/web-apps/komga.nix145
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix1
-rw-r--r--nixos/modules/services/web-apps/pretix.nix581
-rw-r--r--nixos/modules/services/web-apps/suwayomi-server.md3
-rw-r--r--nixos/modules/services/web-apps/suwayomi-server.nix11
-rw-r--r--nixos/modules/services/web-servers/garage.nix14
-rw-r--r--nixos/modules/services/x11/desktop-managers/budgie.nix9
-rw-r--r--nixos/modules/services/x11/desktop-managers/deepin.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/lxqt.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/mate.nix126
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix131
-rw-r--r--nixos/modules/services/x11/window-managers/nimdow.nix11
-rw-r--r--nixos/modules/services/x11/window-managers/xmonad.nix2
-rw-r--r--nixos/modules/services/x11/xserver.nix2
-rw-r--r--nixos/modules/system/boot/kernel.nix5
-rw-r--r--nixos/modules/system/boot/luksroot.nix6
-rw-r--r--nixos/modules/system/boot/networkd.nix4
-rw-r--r--nixos/modules/system/boot/stage-1.nix10
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix44
-rw-r--r--nixos/modules/system/boot/timesyncd.nix3
-rw-r--r--nixos/modules/system/boot/uki.nix15
-rw-r--r--nixos/modules/testing/test-instrumentation.nix10
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix4
-rw-r--r--nixos/modules/virtualisation/nixos-containers.nix6
-rw-r--r--nixos/tests/all-tests.nix13
-rw-r--r--nixos/tests/budgie.nix49
-rw-r--r--nixos/tests/drawterm.nix15
-rw-r--r--nixos/tests/etcd/etcd-cluster.nix (renamed from nixos/tests/etcd-cluster.nix)2
-rw-r--r--nixos/tests/etcd/etcd.nix (renamed from nixos/tests/etcd.nix)2
-rw-r--r--nixos/tests/freetube.nix2
-rw-r--r--nixos/tests/hibernate.nix2
-rw-r--r--nixos/tests/incus/default.nix1
-rw-r--r--nixos/tests/incus/storage.nix46
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix1
-rw-r--r--nixos/tests/installer.nix42
-rw-r--r--nixos/tests/kea.nix26
-rw-r--r--nixos/tests/keycloak.nix32
-rw-r--r--nixos/tests/krb5/default.nix3
-rw-r--r--nixos/tests/ladybird.nix2
-rw-r--r--nixos/tests/make-test-python.nix2
-rw-r--r--nixos/tests/mate-wayland.nix63
-rw-r--r--nixos/tests/mate.nix9
-rw-r--r--nixos/tests/mihomo.nix44
-rw-r--r--nixos/tests/mycelium/default.nix57
-rw-r--r--nixos/tests/mycelium/peer1.key1
-rw-r--r--nixos/tests/mycelium/peer2.key1
-rw-r--r--nixos/tests/nimdow.nix25
-rw-r--r--nixos/tests/nix-config.nix18
-rw-r--r--nixos/tests/nixops/default.nix18
-rw-r--r--nixos/tests/opensearch.nix30
-rw-r--r--nixos/tests/pg_anonymizer.nix94
-rw-r--r--nixos/tests/prometheus-exporters.nix48
-rw-r--r--nixos/tests/redlib.nix20
-rw-r--r--nixos/tests/systemd-machinectl.nix41
-rw-r--r--nixos/tests/vscodium.nix2
-rw-r--r--nixos/tests/web-apps/pretix.nix47
167 files changed, 2473 insertions, 598 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index a368b16201f8..5f51bb53ad7f 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -105,7 +105,9 @@ in rec {
       mkdir -p $dst
 
       cp ${../../../doc/style.css} $dst/style.css
-      cp ${../../../doc/overrides.css} $dst/overrides.css
+      cp ${../../../doc/anchor.min.js} $dst/anchor.min.js
+      cp ${../../../doc/anchor-use.js} $dst/anchor-use.js
+
       cp -r ${pkgs.documentation-highlighter} $dst/highlightjs
 
       ${prepareManualFromMD}
@@ -115,10 +117,11 @@ in rec {
         --revision ${lib.escapeShellArg revision} \
         --generator "nixos-render-docs ${lib.version}" \
         --stylesheet style.css \
-        --stylesheet overrides.css \
         --stylesheet highlightjs/mono-blue.css \
         --script ./highlightjs/highlight.pack.js \
         --script ./highlightjs/loader.js \
+        --script ./anchor.min.js \
+        --script ./anchor-use.js \
         --toc-depth 1 \
         --chunk-toc-depth 1 \
         ./manual.md \
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index 22689868cf02..19ff6f4485cd 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -82,6 +82,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [ollama](https://ollama.ai), server for running large language models locally.
 
+- [Mihomo](https://github.com/MetaCubeX/mihomo), a rule-based proxy in Go. Available as [services.mihomo.enable](#opt-services.mihomo.enable).
+
 - [hebbot](https://github.com/haecker-felix/hebbot), a Matrix bot to generate "This Week in X" like blog posts. Available as [services.hebbot](#opt-services.hebbot.enable).
 
 - [Python Matter Server](https://github.com/home-assistant-libs/python-matter-server), a
@@ -103,8 +105,12 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable).
 
+- [Pretix](https://pretix.eu/about/en/), an open source ticketing software for events. Available as [services.pretix]($opt-services-pretix.enable).
+
 - [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
 
+- [fritz-exporter](https://github.com/pdreker/fritz_exporter), a Prometheus exporter for extracting metrics from [FRITZ!](https://avm.de/produkte/) devices. Available as [services.prometheus.exporters.fritz](#opt-services.prometheus.exporters.fritz.enable).
+
 - [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`.
 
 - [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable).
@@ -123,13 +129,15 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
+- `k3s`: was updated to version [v1.29](https://github.com/k3s-io/k3s/releases/tag/v1.29.1%2Bk3s2), all previous versions (k3s_1_26, k3s_1_27, k3s_1_28) will be removed. See [changelog and upgrade notes](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md#urgent-upgrade-notes) for more information.
+
 - `himalaya` was updated to `v1.0.0-beta.3`, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta.3) for details.
 
 - The `power.ups` module now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS.
 
-- `unrar` was updated to v7. See [changelog](https://www.rarlab.com/unrar7notes.htm) for more information.
+- `pdns` was updated to version [v4.9.x](https://doc.powerdns.com/authoritative/changelog/4.9.html), which introduces breaking changes. Check out the [Upgrade Notes](https://doc.powerdns.com/authoritative/upgrading.html#to-4-9-0) for details.
 
-- `k3s` was updated to [v1.29](https://github.com/k3s-io/k3s/releases/tag/v1.29.1%2Bk3s2). See [changelog and upgrade notes](https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md#urgent-upgrade-notes) for more information.
+- `unrar` was updated to v7. See [changelog](https://www.rarlab.com/unrar7notes.htm) for more information.
 
 - `k9s` was updated to v0.31. There have been various breaking changes in the config file format,
   check out the changelog of [v0.29](https://github.com/derailed/k9s/releases/tag/v0.29.0),
@@ -150,6 +158,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `idris2` was updated to v0.7.0. This version introduces breaking changes. Check out the [changelog](https://github.com/idris-lang/Idris2/blob/v0.7.0/CHANGELOG.md#v070) for details.
 
+- `nvtop` family of packages was reorganized into nested attrset. `nvtop` has been renamed to `nvtopPackages.full`, and all `nvtop-{amd,nvidia,intel,msm}` packages are now named as `nvtopPackages.{amd,nvidia,intel,msm}`
+
 - `neo4j` has been updated to 5, you may want to read the [release notes for Neo4j 5](https://neo4j.com/release-notes/database/neo4j-5/)
 
 - `services.neo4j.allowUpgrade` was removed and no longer has any effect. Neo4j 5 supports automatic rolling upgrades.
@@ -167,6 +177,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - Invidious has changed its default database username from `kemal` to `invidious`. Setups involving an externally provisioned database (i.e. `services.invidious.database.createLocally == false`) should adjust their configuration accordingly. The old `kemal` user will not be removed automatically even when the database is provisioned automatically.(https://github.com/NixOS/nixpkgs/pull/265857)
 
+- `writeReferencesToFile` is deprecated in favour of the new trivial build helper `writeClosure`. The latter accepts a list of paths and has an unambiguous name and cleaner implementation.
+
 - `inetutils` now has a lower priority to avoid shadowing the commonly used `util-linux`. If one wishes to restore the default priority, simply use `lib.setPrio 5 inetutils` or override with `meta.priority = 5`.
 
 - `paperless`' `services.paperless.extraConfig` setting has been removed and converted to the freeform type and option named `services.paperless.settings`.
@@ -320,7 +332,14 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `addDriverRunpath` has been added to facilitate the deprecation of the old `addOpenGLRunpath` setuphook. This change is motivated by the evolution of the setuphook to include all hardware acceleration.
 
-- Cinnamon has been updated to 6.0. Please beware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release.
+- Cinnamon has been updated to 6.0. Please beware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release and could potentially [affect Xorg sessions](https://blog.linuxmint.com/?p=4639). We suggest a reboot when switching between sessions.
+
+- MATE has been updated to 1.28.
+  - To properly support panel plugins built with Wayland (in-process) support, we are introducing `services.xserver.desktopManager.mate.extraPanelApplets` option, please use that for installing panel applets.
+  - Similarly, please use `services.xserver.desktopManager.mate.extraCajaExtensions` option for installing Caja extensions.
+  - To use the Wayland session, enable `services.xserver.desktopManager.mate.enableWaylandSession`. This is opt-in for now as it is in early stage and introduces a new set of Wayfire closure. Due to [known issues with LightDM](https://github.com/canonical/lightdm/issues/63), we suggest using SDDM for display manager.
+
+- The Budgie module installs gnome-terminal by default (instead of mate-terminal).
 
 - New `boot.loader.systemd-boot.xbootldrMountPoint` allows setting up a separate [XBOOTLDR partition](https://uapi-group.org/specifications/specs/boot_loader_specification/) to store boot files. Useful on systems with a small EFI System partition that cannot be easily repartitioned.
 
@@ -347,6 +366,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [Lilypond](https://lilypond.org/index.html) and [Denemo](https://www.denemo.org) are now compiled with Guile 3.0.
 
+- The EC2 image module now enables the [Amazon SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) by default.
+
 - The following options of the Nextcloud module were moved into [`services.nextcloud.settings`](#opt-services.nextcloud.settings) and renamed to match the name from Nextcloud's `config.php`:
   - `logLevel` -> [`loglevel`](#opt-services.nextcloud.settings.loglevel),
   - `logType` -> [`log_type`](#opt-services.nextcloud.settings.log_type),
@@ -442,4 +463,7 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 - QtMultimedia has changed its default backend to `QT_MEDIA_BACKEND=ffmpeg` (previously `gstreamer` on Linux or `darwin` on MacOS).
   The previous native backends remain available but are now minimally maintained. Refer to [upstream documentation](https://doc.qt.io/qt-6/qtmultimedia-index.html#ffmpeg-as-the-default-backend) for further details about each platform.
 
-- The oil shell is now using the c++ version by default. The python based build is still available as `oil-python`
+- The oil shell's c++ version is now available as `oils-for-unix`. The python version is still available as `oil`
+
+- `documentation.man.mandoc` now by default uses `MANPATH` to set the directories where mandoc will search for manual pages.
+  This enables mandoc to find manual pages in Nix profiles. To set the manual search paths via the `mandoc.conf` configuration file like before, use `documentation.man.mandoc.settings.manpath` instead.
diff --git a/nixos/lib/make-options-doc/default.nix b/nixos/lib/make-options-doc/default.nix
index 284934a7608e..09a4022845e0 100644
--- a/nixos/lib/make-options-doc/default.nix
+++ b/nixos/lib/make-options-doc/default.nix
@@ -1,20 +1,95 @@
-/* Generate JSON, XML and DocBook documentation for given NixOS options.
+/**
+  Generates documentation for [nix modules](https://nix.dev/tutorials/module-system/module-system.html).
 
-   Minimal example:
+  It uses the declared `options` to generate documentation in various formats.
 
-    { pkgs,  }:
+  # Outputs
 
-    let
-      eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
-        baseModules = [
-          ../module.nix
-        ];
-        modules = [];
-      };
-    in pkgs.nixosOptionsDoc {
-      options = eval.options;
+  This function returns an attribute set with the following entries.
+
+  ## optionsCommonMark
+
+  Documentation in CommonMark text format.
+
+  ## optionsJSON
+
+  All options in a JSON format suitable for further automated processing.
+
+  `example.json`
+  ```json
+  {
+    ...
+    "fileSystems.<name>.options": {
+      "declarations": ["nixos/modules/tasks/filesystems.nix"],
+      "default": {
+        "_type": "literalExpression",
+        "text": "[\n  \"defaults\"\n]"
+      },
+      "description": "Options used to mount the file system.",
+      "example": {
+        "_type": "literalExpression",
+        "text": "[\n  \"data=journal\"\n]"
+      },
+      "loc": ["fileSystems", "<name>", "options"],
+      "readOnly": false,
+      "type": "non-empty (list of string (with check: non-empty))"
+      "relatedPackages": "- [`pkgs.tmux`](\n    https://search.nixos.org/packages?show=tmux&sort=relevance&query=tmux\n  )\n",
+    },
+    ...
+  }
+  ```
+
+  ## optionsDocBook
+
+  deprecated since 23.11 and will be removed in 24.05.
+
+  ## optionsAsciiDoc
+
+  Documentation rendered as AsciiDoc. This is useful for e.g. man pages.
+
+  > Note: NixOS itself uses this ouput to to build the configuration.nix man page"
+
+  ## optionsNix
+
+  All options as a Nix attribute set value, with the same schema as `optionsJSON`.
+
+  # Example
+
+  ## Example: NixOS configuration
+
+  ```nix
+  let
+    # Evaluate a NixOS configuration
+    eval = import (pkgs.path + "/nixos/lib/eval-config.nix") {
+      # Overriden explicitly here, this would include all modules from NixOS otherwise.
+      # See: docs of eval-config.nix for more details
+      baseModules = [];
+      modules = [
+        ./module.nix
+      ];
+    };
+  in
+    pkgs.nixosOptionsDoc {
+      inherit (eval) options;
     }
+  ```
+
+  ## Example: non-NixOS modules
+
+  `nixosOptionsDoc` can also be used to build documentation for non-NixOS modules.
 
+  ```nix
+  let
+    eval = lib.evalModules {
+      modules = [
+        ./module.nix
+      ];
+    };
+  in
+    pkgs.nixosOptionsDoc {
+      inherit (eval) options;
+    }
+  ```
 */
 { pkgs
 , lib
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index ef218e674ebf..c00b2d0f207c 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -73,13 +73,26 @@ in rec {
     optional (attr ? ${name} && (! isMacAddress attr.${name} && attr.${name} != "none"))
       "Systemd ${group} field `${name}` must be a valid MAC address or the special value `none`.";
 
-
+  isNumberOrRangeOf = check: v:
+    if isInt v
+    then check v
+    else let
+      parts = splitString "-" v;
+      lower = toIntBase10 (head parts);
+      upper = if tail parts != [] then toIntBase10 (head (tail parts)) else lower;
+    in
+      length parts <= 2 && lower <= upper && check lower && check upper;
   isPort = i: i >= 0 && i <= 65535;
+  isPortOrPortRange = isNumberOrRangeOf isPort;
 
   assertPort = name: group: attr:
     optional (attr ? ${name} && ! isPort attr.${name})
       "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number.";
 
+  assertPortOrPortRange = name: group: attr:
+    optional (attr ? ${name} && ! isPortOrPortRange attr.${name})
+      "Error on the systemd ${group} field `${name}': ${attr.name} is not a valid port number or range of port numbers.";
+
   assertValueOneOf = name: values: group: attr:
     optional (attr ? ${name} && !elem attr.${name} values)
       "Systemd ${group} field `${name}' cannot have value `${toString attr.${name}}'.";
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index 49ba2e5c8386..22a2c79843c6 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -64,8 +64,8 @@ rec {
     let
       s = if builtins.isPath arg then "${arg}"
         else if builtins.isString arg then arg
-        else if builtins.isInt arg || builtins.isFloat arg then toString arg
-        else throw "escapeSystemdExecArg only allows strings, paths and numbers";
+        else if builtins.isInt arg || builtins.isFloat arg || lib.isDerivation arg then toString arg
+        else throw "escapeSystemdExecArg only allows strings, paths, numbers and derivations";
     in
       replaceStrings [ "%" "$" ] [ "%%" "$$" ] (builtins.toJSON s);
 
diff --git a/nixos/modules/config/nix.nix b/nixos/modules/config/nix.nix
index e6a74bbb73fc..a40953a3a3c9 100644
--- a/nixos/modules/config/nix.nix
+++ b/nixos/modules/config/nix.nix
@@ -14,8 +14,10 @@ let
     concatStringsSep
     boolToString
     escape
+    filterAttrs
     floatToString
     getVersion
+    hasPrefix
     isBool
     isDerivation
     isFloat
@@ -95,14 +97,19 @@ let
 
       mkKeyValuePairs = attrs: concatStringsSep "\n" (mapAttrsToList mkKeyValue attrs);
 
+      isExtra = key: hasPrefix "extra-" key;
+
     in
     pkgs.writeTextFile {
       name = "nix.conf";
+      # workaround for https://github.com/NixOS/nix/issues/9487
+      # extra-* settings must come after their non-extra counterpart
       text = ''
         # WARNING: this file is generated from the nix.* options in
         # your NixOS configuration, typically
         # /etc/nixos/configuration.nix.  Do not edit it!
-        ${mkKeyValuePairs cfg.settings}
+        ${mkKeyValuePairs (filterAttrs (key: value: !(isExtra key)) cfg.settings)}
+        ${mkKeyValuePairs (filterAttrs (key: value: isExtra key) cfg.settings)}
         ${cfg.extraOptions}
       '';
       checkPhase = lib.optionalString cfg.checkConfig (
@@ -345,7 +352,7 @@ in
             show-trace = true;
 
             system-features = [ "big-parallel" "kvm" "recursive-nix" ];
-            sandbox-paths = { "/bin/sh" = "''${pkgs.busybox-sandbox-shell.out}/bin/busybox"; };
+            sandbox-paths = [ "/bin/sh=''${pkgs.busybox-sandbox-shell.out}/bin/busybox" ];
           }
         '';
         description = lib.mdDoc ''
diff --git a/nixos/modules/config/resolvconf.nix b/nixos/modules/config/resolvconf.nix
index e9ae4d651d26..3b8cc0cb8f42 100644
--- a/nixos/modules/config/resolvconf.nix
+++ b/nixos/modules/config/resolvconf.nix
@@ -28,6 +28,8 @@ let
     '' + optionalString cfg.useLocalResolver ''
       # This hosts runs a full-blown DNS resolver.
       name_servers='127.0.0.1'
+    '' + optionalString (cfg.useLocalResolver && config.networking.enableIPv6) ''
+      name_servers='::1'
     '' + cfg.extraConfig;
 
 in
diff --git a/nixos/modules/hardware/video/webcam/ipu6.nix b/nixos/modules/hardware/video/webcam/ipu6.nix
index c2dbdc217bd6..a7767e446bd4 100644
--- a/nixos/modules/hardware/video/webcam/ipu6.nix
+++ b/nixos/modules/hardware/video/webcam/ipu6.nix
@@ -30,7 +30,10 @@ in
       ipu6-drivers
     ];
 
-    hardware.firmware = [ pkgs.ipu6-camera-bins ];
+    hardware.firmware = with pkgs; [
+      ipu6-camera-bins
+      ivsc-firmware
+    ];
 
     services.udev.extraRules = ''
       SUBSYSTEM=="intel-ipu6-psys", MODE="0660", GROUP="video"
diff --git a/nixos/modules/i18n/input-method/fcitx5.nix b/nixos/modules/i18n/input-method/fcitx5.nix
index ee8d2652b1c7..755336220520 100644
--- a/nixos/modules/i18n/input-method/fcitx5.nix
+++ b/nixos/modules/i18n/input-method/fcitx5.nix
@@ -32,8 +32,8 @@ in
       };
       plasma6Support = mkOption {
         type = types.bool;
-        default = config.services.xserver.desktopManager.plasma6.enable;
-        defaultText = literalExpression "config.services.xserver.desktopManager.plasma6.enable";
+        default = config.services.desktopManager.plasma6.enable;
+        defaultText = literalExpression "config.services.desktopManager.plasma6.enable";
         description = lib.mdDoc ''
           Use qt6 versions of fcitx5 packages.
           Required for configuring fcitx5 in KDE System Settings.
diff --git a/nixos/modules/image/repart-image.nix b/nixos/modules/image/repart-image.nix
index 5ae523c43f58..83e766268cf0 100644
--- a/nixos/modules/image/repart-image.nix
+++ b/nixos/modules/image/repart-image.nix
@@ -2,8 +2,8 @@
 # NixOS module that can be imported.
 
 { lib
+, stdenvNoCC
 , runCommand
-, runCommandLocal
 , python3
 , black
 , ruff
@@ -26,15 +26,18 @@
 , xz
 
   # arguments
+, name
+, version
 , imageFileBasename
 , compression
 , fileSystems
-, partitions
+, partitionsJSON
 , split
 , seed
 , definitionsDirectory
 , sectorSize
 , mkfsEnv ? {}
+, createEmpty ? true
 }:
 
 let
@@ -52,11 +55,6 @@ let
     mypy --strict $out
   '';
 
-  amendedRepartDefinitions = runCommandLocal "amended-repart.d" {} ''
-    definitions=$(${amendRepartDefinitions} ${partitions} ${definitionsDirectory})
-    cp -r $definitions $out
-  '';
-
   fileSystemToolMapping = {
     "vfat" = [ dosfstools mtools ];
     "ext4" = [ e2fsprogs.bin ];
@@ -78,53 +76,88 @@ let
     "xz" = "xz --keep --verbose --threads=0 -${toString compression.level}";
   }."${compression.algorithm}";
 in
-
-runCommand imageFileBasename
-{
+  stdenvNoCC.mkDerivation (finalAttrs:
+  (if (version != null)
+  then { pname = name; inherit version; }
+  else { inherit name;  }
+  ) // {
   __structuredAttrs = true;
 
   nativeBuildInputs = [
     systemd
     fakeroot
     util-linux
+  ] ++ lib.optionals (compression.enable) [
     compressionPkg
   ] ++ fileSystemTools;
 
   env = mkfsEnv;
 
+  inherit partitionsJSON definitionsDirectory;
+
+  # relative path to the repart definitions that are read by systemd-repart
+  finalRepartDefinitions = "repart.d";
+
   systemdRepartFlags = [
     "--dry-run=no"
-    "--empty=create"
     "--size=auto"
     "--seed=${seed}"
-    "--definitions=${amendedRepartDefinitions}"
+    "--definitions=${finalAttrs.finalRepartDefinitions}"
     "--split=${lib.boolToString split}"
     "--json=pretty"
+  ] ++ lib.optionals createEmpty [
+    "--empty=create"
   ] ++ lib.optionals (sectorSize != null) [
     "--sector-size=${toString sectorSize}"
   ];
 
-  passthru = {
-    inherit amendRepartDefinitions amendedRepartDefinitions;
-  };
-} ''
-  mkdir -p $out
-  cd $out
+  dontUnpack = true;
+  dontConfigure = true;
+  doCheck = false;
+
+  patchPhase = ''
+    runHook prePatch
+
+    amendedRepartDefinitionsDir=$(${amendRepartDefinitions} $partitionsJSON $definitionsDirectory)
+    ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions
 
-  echo "Building image with systemd-repart..."
-  unshare --map-root-user fakeroot systemd-repart \
-    ''${systemdRepartFlags[@]} \
-    ${imageFileBasename}.raw \
-    | tee repart-output.json
+    runHook postPatch
+  '';
+
+  buildPhase = ''
+    runHook preBuild
+
+    echo "Building image with systemd-repart..."
+    unshare --map-root-user fakeroot systemd-repart \
+      ''${systemdRepartFlags[@]} \
+      ${imageFileBasename}.raw \
+      | tee repart-output.json
+
+    runHook postBuild
+  '';
+
+  installPhase = ''
+    runHook preInstall
 
+    mkdir -p $out
+  ''
   # Compression is implemented in the same derivation as opposed to in a
   # separate derivation to allow users to save disk space. Disk images are
   # already very space intensive so we want to allow users to mitigate this.
-  if ${lib.boolToString compression.enable}; then
+  + lib.optionalString compression.enable
+  ''
     for f in ${imageFileBasename}*; do
       echo "Compressing $f with ${compression.algorithm}..."
       # Keep the original file when compressing and only delete it afterwards
       ${compressionCommand} $f && rm $f
     done
-  fi
-''
+  '' + ''
+    mv -v repart-output.json ${imageFileBasename}* $out
+
+    runHook postInstall
+  '';
+
+  passthru = {
+    inherit amendRepartDefinitions;
+  };
+})
diff --git a/nixos/modules/image/repart.nix b/nixos/modules/image/repart.nix
index 90c9c7e51dfa..1a43297f4b43 100644
--- a/nixos/modules/image/repart.nix
+++ b/nixos/modules/image/repart.nix
@@ -211,6 +211,15 @@ in
       '';
     };
 
+    finalPartitions = lib.mkOption {
+      type = lib.types.attrs;
+      internal = true;
+      readOnly = true;
+      description = lib.mdDoc ''
+        Convenience option to access partitions with added closures.
+      '';
+    };
+
   };
 
   config = {
@@ -224,6 +233,16 @@ in
             "zstd" = ".zst";
             "xz" = ".xz";
           }."${cfg.compression.algorithm}";
+
+        makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
+
+        # Add the closure of the provided Nix store paths to cfg.partitions so
+        # that amend-repart-definitions.py can read it.
+        addClosure = _name: partitionConfig: partitionConfig // (
+          lib.optionalAttrs
+            (partitionConfig.storePaths or [ ] != [ ])
+            { closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
+        );
       in
       {
         name = lib.mkIf (config.system.image.id != null) (lib.mkOptionDefault config.system.image.id);
@@ -239,6 +258,8 @@ in
             "xz" = 3;
           }."${cfg.compression.algorithm}";
         };
+
+        finalPartitions = lib.mapAttrs addClosure cfg.partitions;
       };
 
     system.build.image =
@@ -247,36 +268,25 @@ in
           (f: f != null)
           (lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions);
 
-        makeClosure = paths: pkgs.closureInfo { rootPaths = paths; };
-
-        # Add the closure of the provided Nix store paths to cfg.partitions so
-        # that amend-repart-definitions.py can read it.
-        addClosure = _name: partitionConfig: partitionConfig // (
-          lib.optionalAttrs
-            (partitionConfig.storePaths or [ ] != [ ])
-            { closure = "${makeClosure partitionConfig.storePaths}/store-paths"; }
-        );
-
-        finalPartitions = lib.mapAttrs addClosure cfg.partitions;
 
         format = pkgs.formats.ini { };
 
         definitionsDirectory = utils.systemdUtils.lib.definitions
           "repart.d"
           format
-          (lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) finalPartitions);
+          (lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions);
 
-        partitions = pkgs.writeText "partitions.json" (builtins.toJSON finalPartitions);
+        partitionsJSON = pkgs.writeText "partitions.json" (builtins.toJSON cfg.finalPartitions);
 
         mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions;
       in
       pkgs.callPackage ./repart-image.nix {
         systemd = cfg.package;
-        inherit (cfg) imageFileBasename compression split seed sectorSize;
-        inherit fileSystems definitionsDirectory partitions mkfsEnv;
+        inherit (cfg) name version imageFileBasename compression split seed sectorSize;
+        inherit fileSystems definitionsDirectory partitionsJSON mkfsEnv;
       };
 
-    meta.maintainers = with lib.maintainers; [ nikstur ];
+    meta.maintainers = with lib.maintainers; [ nikstur willibutz ];
 
   };
 }
diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix
index 73646a60aabb..706e2ac2c283 100644
--- a/nixos/modules/misc/mandoc.nix
+++ b/nixos/modules/misc/mandoc.nix
@@ -17,6 +17,8 @@ let
       )
       output
   );
+
+  makeLeadingSlashes = map (path: if builtins.substring 0 1 path != "/" then "/${path}" else path);
 in
 {
   meta.maintainers = [ lib.maintainers.sternenseemann ];
@@ -29,6 +31,7 @@ in
         type = with lib.types; listOf str;
         default = [ "share/man" ];
         example = lib.literalExpression "[ \"share/man\" \"share/man/fr\" ]";
+        apply = makeLeadingSlashes;
         description = ''
           Change the paths included in the MANPATH environment variable,
           i. e. the directories where {manpage}`man(1)`
@@ -41,6 +44,28 @@ in
         '';
       };
 
+      cachePath = lib.mkOption {
+        type = with lib.types; listOf str;
+        default = cfg.manPath;
+        defaultText = lib.literalExpression "config.documentation.man.mandoc.manPath";
+        example = lib.literalExpression "[ \"share/man\" \"share/man/fr\" ]";
+        apply = makeLeadingSlashes;
+        description = ''
+          Change the paths where mandoc {manpage}`makewhatis(8)`generates the
+          manual page index caches. {option}`documentation.man.generateCaches`
+          should be enabled to allow cache generation. This list should only
+          include the paths to manpages installed in the system configuration,
+          i. e. /run/current-system/sw/share/man. {manpage}`makewhatis(8)`
+          creates a database in each directory using the files
+          `mansection/[arch/]title.section` and `catsection/[arch/]title.0`
+          in it. If a directory contains no manual pages, no database is
+          created in that directory.
+          This option only needs to be set manually if extra paths should be
+          indexed or {option}`documentation.man.manPath` contains paths that
+          can't be indexed.
+        '';
+      };
+
       package = lib.mkOption {
         type = lib.types.package;
         default = pkgs.mandoc;
@@ -178,19 +203,14 @@ in
       # TODO(@sternenseemman): fix symlinked directories not getting indexed,
       # see: https://inbox.vuxu.org/mandoc-tech/20210906171231.GF83680@athene.usta.de/T/#e85f773c1781e3fef85562b2794f9cad7b2909a3c
       extraSetup = lib.mkIf config.documentation.man.generateCaches ''
-        for man_path in ${
-          lib.concatMapStringsSep " " (path:
-            "$out/" + lib.escapeShellArg path
-            ) cfg.manPath} ${lib.concatMapStringsSep " " (path:
-            lib.escapeShellArg path) cfg.settings.manpath
-          }
+        for man_path in ${lib.concatMapStringsSep " " (path: "$out" + lib.escapeShellArg path) cfg.cachePath}
         do
           [[ -d "$man_path" ]] && ${makewhatis} -T utf8 $man_path
         done
       '';
 
       # tell mandoc the paths containing man pages
-      profileRelativeSessionVariables."MANPATH" = map (path: if builtins.substring 0 1 path != "/" then "/${path}" else path) cfg.manPath;
+      profileRelativeSessionVariables."MANPATH" = lib.mkIf (cfg.manPath != [ ]) cfg.manPath;
     };
   };
 }
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 41e369ac1c65..d17e638a0883 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -203,6 +203,7 @@
   ./programs/i3lock.nix
   ./programs/iotop.nix
   ./programs/java.nix
+  ./programs/joycond-cemuhook.nix
   ./programs/k3b.nix
   ./programs/k40-whisperer.nix
   ./programs/kbdlight.nix
@@ -1018,6 +1019,7 @@
   ./services/networking/lxd-image-server.nix
   ./services/networking/magic-wormhole-mailbox-server.nix
   ./services/networking/matterbridge.nix
+  ./services/networking/mihomo.nix
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
   ./services/networking/miredo.nix
@@ -1034,6 +1036,7 @@
   ./services/networking/multipath.nix
   ./services/networking/murmur.nix
   ./services/networking/mxisd.nix
+  ./services/networking/mycelium.nix
   ./services/networking/namecoind.nix
   ./services/networking/nar-serve.nix
   ./services/networking/nat.nix
@@ -1358,6 +1361,7 @@
   ./services/web-apps/plausible.nix
   ./services/web-apps/powerdns-admin.nix
   ./services/web-apps/pretalx.nix
+  ./services/web-apps/pretix.nix
   ./services/web-apps/prosody-filer.nix
   ./services/web-apps/rimgo.nix
   ./services/web-apps/sftpgo.nix
diff --git a/nixos/modules/programs/_1password-gui.nix b/nixos/modules/programs/_1password-gui.nix
index 83ef6037fb5a..eb2effee4326 100644
--- a/nixos/modules/programs/_1password-gui.nix
+++ b/nixos/modules/programs/_1password-gui.nix
@@ -51,14 +51,6 @@ in
           setuid = false;
           setgid = true;
         };
-
-        "1Password-KeyringHelper" = {
-          source = "${package}/share/1password/1Password-KeyringHelper";
-          owner = "root";
-          group = "onepassword";
-          setuid = true;
-          setgid = true;
-        };
       };
 
     };
diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix
index 34f6f0882c61..7f8d9eb3363d 100644
--- a/nixos/modules/programs/starship.nix
+++ b/nixos/modules/programs/starship.nix
@@ -12,7 +12,7 @@ let
       nativeBuildInputs = [ pkgs.yq ];
     } ''
     tomlq -s -t 'reduce .[] as $item ({}; . * $item)' \
-      ${lib.concatStringsSep " " (map (f: "${pkgs.starship}/share/starship/presets/${f}.toml") cfg.presets)} \
+      ${lib.concatStringsSep " " (map (f: "${cfg.package}/share/starship/presets/${f}.toml") cfg.presets)} \
       ${userSettingsFile} \
       > $out
   '';
@@ -26,23 +26,20 @@ let
 in
 {
   options.programs.starship = {
-    enable = lib.mkEnableOption (lib.mdDoc "the Starship shell prompt");
+    enable = lib.mkEnableOption "the Starship shell prompt";
 
-    interactiveOnly = lib.mkOption {
-      default = true;
-      example = false;
-      type = lib.types.bool;
-      description = lib.mdDoc ''
-        Whether to enable starship only when the shell is interactive.
-        Some plugins require this to be set to false to function correctly.
-      '';
-    };
+    package = lib.mkPackageOption pkgs "starship" { };
+
+    interactiveOnly = lib.mkEnableOption ''
+      starship only when the shell is interactive.
+      Some plugins require this to be set to false to function correctly
+    '' // { default = true; };
 
     presets = lib.mkOption {
       default = [ ];
       example = [ "nerd-font-symbols" ];
       type = with lib.types; listOf str;
-      description = lib.mdDoc ''
+      description = ''
         Presets files to be merged with settings in order.
       '';
     };
@@ -50,7 +47,7 @@ in
     settings = lib.mkOption {
       inherit (settingsFormat) type;
       default = { };
-      description = lib.mdDoc ''
+      description = ''
         Configuration included in `starship.toml`.
 
         See https://starship.rs/config/#prompt for documentation.
@@ -68,7 +65,7 @@ in
         if [[ ! -f "$HOME/.config/starship.toml" ]]; then
           export STARSHIP_CONFIG=${settingsFile}
         fi
-        eval "$(${pkgs.starship}/bin/starship init bash)"
+        eval "$(${cfg.package}/bin/starship init bash)"
       fi
     '';
 
@@ -81,7 +78,7 @@ in
         if not test -f "$HOME/.config/starship.toml";
           set -x STARSHIP_CONFIG ${settingsFile}
         end
-        eval (${pkgs.starship}/bin/starship init fish)
+        eval (${cfg.package}/bin/starship init fish)
       end
     '';
 
@@ -94,7 +91,7 @@ in
         if [[ ! -f "$HOME/.config/starship.toml" ]]; then
           export STARSHIP_CONFIG=${settingsFile}
         fi
-        eval "$(${pkgs.starship}/bin/starship init zsh)"
+        eval "$(${cfg.package}/bin/starship init zsh)"
       fi
     '';
   };
diff --git a/nixos/modules/programs/steam.nix b/nixos/modules/programs/steam.nix
index 31803f061dce..c93a34f61849 100644
--- a/nixos/modules/programs/steam.nix
+++ b/nixos/modules/programs/steam.nix
@@ -44,8 +44,8 @@ in {
       '';
       apply = steam: steam.override (prev: {
         extraEnv = (lib.optionalAttrs (cfg.extraCompatPackages != [ ]) {
-            STEAM_EXTRA_COMPAT_TOOLS_PATHS = makeBinPath cfg.extraCompatPackages;
-          }) // (prev.extraEnv or {});
+          STEAM_EXTRA_COMPAT_TOOLS_PATHS = makeSearchPathOutput "steamcompattool" "" cfg.extraCompatPackages;
+        }) // (prev.extraEnv or {});
         extraLibraries = pkgs: let
           prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
           additionalLibs = with config.hardware.opengl;
@@ -74,10 +74,17 @@ in {
     extraCompatPackages = mkOption {
       type = types.listOf types.package;
       default = [ ];
+      example = literalExpression ''
+        with pkgs; [
+          proton-ge-bin
+        ]
+      '';
       description = lib.mdDoc ''
         Extra packages to be used as compatibility tools for Steam on Linux. Packages will be included
         in the `STEAM_EXTRA_COMPAT_TOOLS_PATHS` environmental variable. For more information see
-        <https://github.com/ValveSoftware/steam-for-linux/issues/6310">.
+        https://github.com/ValveSoftware/steam-for-linux/issues/6310.
+
+        These packages must be Steam compatibility tools that have a `steamcompattool` output.
       '';
     };
 
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 560e5eff5c39..26dc724ae159 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -683,7 +683,7 @@ let
           (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; args = [
             dp9ik.authserver
           ]; })
-          { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${pkgs.fprintd}/lib/security/pam_fprintd.so"; }
+          { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${config.services.fprintd.package}/lib/security/pam_fprintd.so"; }
         ] ++
           # Modules in this block require having the password set in PAM_AUTHTOK.
           # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
diff --git a/nixos/modules/services/audio/spotifyd.nix b/nixos/modules/services/audio/spotifyd.nix
index 1194b6f200d7..04bb523e25b1 100644
--- a/nixos/modules/services/audio/spotifyd.nix
+++ b/nixos/modules/services/audio/spotifyd.nix
@@ -24,7 +24,7 @@ in
         type = types.lines;
         description = lib.mdDoc ''
           (Deprecated) Configuration for Spotifyd. For syntax and directives, see
-          <https://github.com/Spotifyd/spotifyd#Configuration>.
+          <https://docs.spotifyd.rs/config/File.html>.
         '';
       };
 
@@ -34,7 +34,7 @@ in
         example = { global.bitrate = 320; };
         description = lib.mdDoc ''
           Configuration for Spotifyd. For syntax and directives, see
-          <https://github.com/Spotifyd/spotifyd#Configuration>.
+          <https://docs.spotifyd.rs/config/File.html>.
         '';
       };
     };
diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md
index 7d141f12b5de..3ff1f00fa9cf 100644
--- a/nixos/modules/services/databases/postgresql.md
+++ b/nixos/modules/services/databases/postgresql.md
@@ -277,7 +277,7 @@ self: super: {
 Here's a recipe on how to override a particular plugin through an overlay:
 ```
 self: super: {
-  postgresql_15 = super.postgresql_15.override { this = self.postgresql_15; } // {
+  postgresql_15 = super.postgresql_15// {
     pkgs = super.postgresql_15.pkgs // {
       pg_repack = super.postgresql_15.pkgs.pg_repack.overrideAttrs (_: {
         name = "pg_repack-v20181024";
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index c4e76c82ba5c..c3f3b98ae5e7 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -14,7 +14,7 @@ let
       #     package = pkgs.postgresql_<major>;
       #   };
       # works.
-      base = if cfg.enableJIT && !cfg.package.jitSupport then cfg.package.withJIT else cfg.package;
+      base = if cfg.enableJIT then cfg.package.withJIT else cfg.package;
     in
     if cfg.extraPlugins == []
       then base
diff --git a/nixos/modules/services/desktop-managers/plasma6.nix b/nixos/modules/services/desktop-managers/plasma6.nix
index 1710d28954d6..e20b431f0b58 100644
--- a/nixos/modules/services/desktop-managers/plasma6.nix
+++ b/nixos/modules/services/desktop-managers/plasma6.nix
@@ -170,6 +170,7 @@ in {
         breeze.qt5
         plasma-integration.qt5
         pkgs.plasma5Packages.kwayland-integration
+        pkgs.plasma5Packages.kio
         kio-extras-kf5
       ]
       # Optional hardware support features
@@ -215,7 +216,7 @@ in {
       serif = ["Noto Serif"];
     };
 
-    programs.gnupg.agent.pinentryPackage = pkgs.pinentry-qt;
+    programs.gnupg.agent.pinentryPackage = mkDefault pkgs.pinentry-qt;
     programs.ssh.askPassword = mkDefault "${kdePackages.ksshaskpass.out}/bin/ksshaskpass";
 
     # Enable helpful DBus services.
@@ -252,6 +253,7 @@ in {
     services.xserver.displayManager.sddm = {
       package = kdePackages.sddm;
       theme = mkDefault "breeze";
+      wayland.compositor = "kwin";
       extraPackages = with kdePackages; [
         breeze-icons
         kirigami
diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix
index 09448833620c..182615cd4d6c 100644
--- a/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -95,6 +95,14 @@ in {
         enable = mkEnableOption (lib.mdDoc "JACK audio emulation");
       };
 
+      raopOpenFirewall = mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Opens UDP/6001-6002, required by RAOP/Airplay for timing and control data.
+        '';
+      };
+
       pulse = {
         enable = mkEnableOption (lib.mdDoc "PulseAudio server emulation");
       };
@@ -371,6 +379,8 @@ in {
     environment.sessionVariables.LD_LIBRARY_PATH =
       lib.mkIf cfg.jack.enable [ "${cfg.package.jack}/lib" ];
 
+    networking.firewall.allowedUDPPorts = lib.mkIf cfg.raopOpenFirewall [ 6001 6002 ];
+
     users = lib.mkIf cfg.systemWide {
       users.pipewire = {
         uid = config.ids.uids.pipewire;
diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix
index 009d68bd4f28..de177d0e4ef3 100644
--- a/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -56,24 +56,30 @@ in
 
   config =
     let
-      pwNotForAudioConfigPkg = pkgs.writeTextDir "share/wireplumber/main.lua.d/80-pw-not-for-audio.lua" ''
-        -- PipeWire is not used for audio, so prevent it from grabbing audio devices
-        alsa_monitor.enable = function() end
-      '';
-      systemwideConfigPkg = pkgs.writeTextDir "share/wireplumber/main.lua.d/80-systemwide.lua" ''
-        -- When running system-wide, these settings need to be disabled (they
-        -- use functions that aren't available on the system dbus).
-        alsa_monitor.properties["alsa.reserve"] = false
-        default_access.properties["enable-flatpak-portal"] = false
+      pwNotForAudioConfigPkg = pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/90-nixos-no-audio.conf" ''
+        # PipeWire is not used for audio, so WirePlumber should not be handling it
+        wireplumber.profiles = {
+          main = {
+            hardware.audio = disabled
+            hardware.bluetooth = disabled
+          }
+        }
       '';
-      systemwideBluetoothConfigPkg = pkgs.writeTextDir "share/wireplumber/bluetooth.lua.d/80-systemwide.lua" ''
-        -- When running system-wide, logind-integration needs to be disabled.
-        bluez_monitor.properties["with-logind"] = false
+
+      systemwideConfigPkg = pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/90-nixos-systemwide.conf" ''
+        # When running system-wide, we don't have logind to call ReserveDevice,
+        # And bluetooth logind integration needs to be disabled
+        wireplumber.profiles = {
+          main = {
+            support.reserve-device = disabled
+            monitor.bluez.seat-monitoring = disabled
+          }
+        }
       '';
 
       configPackages = cfg.configPackages
           ++ lib.optional (!pwUsedForAudio) pwNotForAudioConfigPkg
-          ++ lib.optionals config.services.pipewire.systemWide [ systemwideConfigPkg systemwideBluetoothConfigPkg ];
+          ++ lib.optional config.services.pipewire.systemWide systemwideConfigPkg;
 
       configs = pkgs.buildEnv {
         name = "wireplumber-configs";
diff --git a/nixos/modules/services/display-managers/greetd.nix b/nixos/modules/services/display-managers/greetd.nix
index c2d345152de9..5ce67c3fb3fd 100644
--- a/nixos/modules/services/display-managers/greetd.nix
+++ b/nixos/modules/services/display-managers/greetd.nix
@@ -61,6 +61,8 @@ in
     systemd.services."autovt@${tty}".enable = false;
 
     systemd.services.greetd = {
+      aliases = [ "display-manager.service" ];
+
       unitConfig = {
         Wants = [
           "systemd-user-sessions.service"
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index e3f9c7742cc7..7291c0fcbcdd 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -1232,7 +1232,8 @@ in {
             ProtectKernelTunables = true;
             ProtectProc = "invisible";
             ProtectSystem = "strict";
-            ReadWritePaths = [ cfg.dataDir cfg.settings.media_store_path ];
+            ReadWritePaths = [ cfg.dataDir cfg.settings.media_store_path ] ++
+              (map (listener: dirOf listener.path) (filter (listener: listener.path != null) cfg.settings.listeners));
             RemoveIPC = true;
             RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
             RestrictNamespaces = true;
diff --git a/nixos/modules/services/misc/etebase-server.nix b/nixos/modules/services/misc/etebase-server.nix
index f5a5e8a780d4..6ec3807f0fb2 100644
--- a/nixos/modules/services/misc/etebase-server.nix
+++ b/nixos/modules/services/misc/etebase-server.nix
@@ -177,6 +177,8 @@ in
 
     systemd.tmpfiles.rules = [
       "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
+    ] ++ lib.optionals (cfg.unixSocket != null) [
+      "d '${builtins.dirOf cfg.unixSocket}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
     ];
 
     systemd.services.etebase-server = {
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index ec347a75f063..e95ab0a112bc 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -1439,6 +1439,8 @@ in {
         nodejs
         gnupg
 
+        "${cfg.packages.gitlab}/share/gitlab/vendor/gems/sidekiq-${cfg.packages.gitlab.rubyEnv.gems.sidekiq.version}"
+
         # Needed for GitLab project imports
         gnutar
         gzip
@@ -1452,7 +1454,12 @@ in {
         TimeoutSec = "infinity";
         Restart = "always";
         WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
-        ExecStart="${cfg.packages.gitlab.rubyEnv}/bin/sidekiq -C \"${cfg.packages.gitlab}/share/gitlab/config/sidekiq_queues.yml\" -e production";
+        ExecStart = utils.escapeSystemdExecArgs [
+          "${cfg.packages.gitlab}/share/gitlab/bin/sidekiq-cluster"
+          "-e" "production"
+          "-r" "."
+          "*" # all queue groups
+        ];
       };
     };
 
@@ -1550,7 +1557,7 @@ in {
         gnutar
         gzip
         openssh
-        gitlab-workhorse
+        cfg.packages.gitlab-workhorse
       ];
       serviceConfig = {
         Type = "simple";
diff --git a/nixos/modules/services/misc/llama-cpp.nix b/nixos/modules/services/misc/llama-cpp.nix
index 4d76456fb2fd..305d4538e89a 100644
--- a/nixos/modules/services/misc/llama-cpp.nix
+++ b/nixos/modules/services/misc/llama-cpp.nix
@@ -56,7 +56,7 @@ in {
       serviceConfig = {
         Type = "idle";
         KillSignal = "SIGINT";
-        ExecStart = "${cfg.package}/bin/llama-cpp-server --log-disable --host ${cfg.host} --port ${builtins.toString cfg.port} -m ${cfg.model} ${utils.escapeSystemdExecArgs cfg.extraFlags}";
+        ExecStart = "${cfg.package}/bin/llama-server --log-disable --host ${cfg.host} --port ${builtins.toString cfg.port} -m ${cfg.model} ${utils.escapeSystemdExecArgs cfg.extraFlags}";
         Restart = "on-failure";
         RestartSec = 300;
 
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index 3ac3beb4de07..7a5661510e25 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -13,48 +13,60 @@ in
 {
   options = {
     services.ollama = {
-      enable = lib.mkEnableOption (
-        lib.mdDoc "Server for local large language models"
-      );
+      enable = lib.mkEnableOption "ollama server for local large language models";
+      package = lib.mkPackageOption pkgs "ollama" { };
       listenAddress = lib.mkOption {
         type = types.str;
         default = "127.0.0.1:11434";
-        description = lib.mdDoc ''
-          Specifies the bind address on which the ollama server HTTP interface listens.
+        example = "0.0.0.0:11111";
+        description = ''
+          The address which the ollama server HTTP interface binds and listens to.
         '';
       };
       acceleration = lib.mkOption {
         type = types.nullOr (types.enum [ "rocm" "cuda" ]);
         default = null;
         example = "rocm";
-        description = lib.mdDoc ''
-          Specifies the interface to use for hardware acceleration.
+        description = ''
+          What interface to use for hardware acceleration.
 
           - `rocm`: supported by modern AMD GPUs
           - `cuda`: supported by modern NVIDIA GPUs
         '';
       };
-      package = lib.mkPackageOption pkgs "ollama" { };
+      environmentVariables = lib.mkOption {
+        type = types.attrsOf types.str;
+        default = { };
+        example = {
+          HOME = "/tmp";
+          OLLAMA_LLM_LIBRARY = "cpu";
+        };
+        description = ''
+          Set arbitrary environment variables for the ollama service.
+
+          Be aware that these are only seen by the ollama server (systemd service),
+          not normal invocations like `ollama run`.
+          Since `ollama run` is mostly a shell around the ollama server, this is usually sufficient.
+        '';
+      };
     };
   };
 
   config = lib.mkIf cfg.enable {
-    systemd = {
-      services.ollama = {
-        wantedBy = [ "multi-user.target" ];
-        description = "Server for local large language models";
-        after = [ "network.target" ];
-        environment = {
-          HOME = "%S/ollama";
-          OLLAMA_MODELS = "%S/ollama/models";
-          OLLAMA_HOST = cfg.listenAddress;
-        };
-        serviceConfig = {
-          ExecStart = "${lib.getExe ollamaPackage} serve";
-          WorkingDirectory = "/var/lib/ollama";
-          StateDirectory = [ "ollama" ];
-          DynamicUser = true;
-        };
+    systemd.services.ollama = {
+      description = "Server for local large language models";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+      environment = cfg.environmentVariables // {
+        HOME = "%S/ollama";
+        OLLAMA_MODELS = "%S/ollama/models";
+        OLLAMA_HOST = cfg.listenAddress;
+      };
+      serviceConfig = {
+        ExecStart = "${lib.getExe ollamaPackage} serve";
+        WorkingDirectory = "%S/ollama";
+        StateDirectory = [ "ollama" ];
+        DynamicUser = true;
       };
     };
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 6be6ba7edf72..8c5ec2992eda 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -1,4 +1,4 @@
-{ config, pkgs, lib, options, ... }:
+{ config, pkgs, lib, options, utils, ... }:
 
 let
   inherit (lib) concatStrings foldl foldl' genAttrs literalExpression maintainers
@@ -35,6 +35,7 @@ let
     "dovecot"
     "fastly"
     "flow"
+    "fritz"
     "fritzbox"
     "graphite"
     "idrac"
@@ -94,10 +95,10 @@ let
     "zfs"
   ]
     (name:
-      import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options; }
+      import (./. + "/exporters/${name}.nix") { inherit config lib pkgs options utils; }
     )) // (mapAttrs
     (name: params:
-      import (./. + "/exporters/${params.name}.nix") { inherit config lib pkgs options; type = params.type ; })
+      import (./. + "/exporters/${params.name}.nix") { inherit config lib pkgs options utils; type = params.type ; })
     {
       exportarr-bazarr = {
         name = "exportarr";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix b/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix
index a8a9f84ea8ea..de6cda18bc37 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix b/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix
index bc67fe59b3b8..b3afdb596686 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/artifactory.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bind.nix b/nixos/modules/services/monitoring/prometheus/exporters/bind.nix
index bd2003f06504..100446c1a4eb 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/bind.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/bind.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bird.nix b/nixos/modules/services/monitoring/prometheus/exporters/bird.nix
index 5f6c36f4c567..fc52135e3b45 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/bird.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/bird.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
index 330d54126448..45f00a04a86c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
index ce2c391de523..e8399e1bec80 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/blackbox.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix b/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix
index 0515b72b13f9..6bfadc3b7632 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/buildkite-agent.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
index f67596f05a3a..3b2b123bbd07 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/collectd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix b/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix
index 437cece588a7..a4a917b473ce 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/dmarc.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix b/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix
index ece42a34cb06..4cfee7c54a41 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/dnsmasq.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/domain.nix b/nixos/modules/services/monitoring/prometheus/exporters/domain.nix
index 61e2fc80afde..b2c8e6664c0f 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/domain.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/domain.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
index 6fb438353a4c..df6b1ef3200c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/dovecot.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix b/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix
index 8511abbee1bd..c632b0290262 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/exportarr.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options, type }:
+{ config, lib, pkgs, options, type, ... }:
 
 let
   cfg = config.services.prometheus.exporters."exportarr-${type}";
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix b/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix
index 2a8b7fc0818d..097ea3959478 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/fastly.nix
@@ -2,6 +2,7 @@
 , lib
 , pkgs
 , options
+, ...
 }:
 
 let
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/flow.nix b/nixos/modules/services/monitoring/prometheus/exporters/flow.nix
index 81099aaf1704..42292abeada2 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/flow.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/flow.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fritz.nix b/nixos/modules/services/monitoring/prometheus/exporters/fritz.nix
new file mode 100644
index 000000000000..c3a962b576a5
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/fritz.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, utils, ... }:
+let
+  inherit (lib) mkOption types mdDoc;
+  cfg = config.services.prometheus.exporters.fritz;
+  yaml = pkgs.formats.yaml { };
+  configFile = yaml.generate "fritz-exporter.yaml" cfg.settings;
+in
+{
+  port = 9787;
+
+  extraOpts = {
+    settings = mkOption {
+      description = mdDoc "Configuration settings for fritz-exporter.";
+      type = types.submodule {
+        freeformType = yaml.type;
+
+        options = {
+          # Pull existing port option into config file.
+          port = mkOption {
+            type = types.port;
+            default = cfg.port;
+            internal = true;
+            visible = false;
+          };
+          # Pull existing listen address option into config file.
+          listen_address = mkOption {
+            type = types.str;
+            default = cfg.listenAddress;
+            internal = true;
+            visible = false;
+          };
+          log_level = mkOption {
+            type = types.enum [ "DEBUG" "INFO" "WARNING" "ERROR" "CRITICAL" ];
+            default = "INFO";
+            description = mdDoc ''
+              Log level to use for the exporter.
+            '';
+          };
+          devices = mkOption {
+            default = [];
+            description = "Fritz!-devices to monitor using the exporter.";
+            type = with types; listOf (submodule {
+              freeformType = yaml.type;
+
+              options = {
+                name = mkOption {
+                  type = types.str;
+                  default = "";
+                  description = mdDoc ''
+                    Name to use for the device.
+                  '';
+                };
+                hostname = mkOption {
+                  type = types.str;
+                  default = "fritz.box";
+                  description = mdDoc ''
+                    Hostname under which the target device is reachable.
+                  '';
+                };
+                username = mkOption {
+                  type = types.str;
+                  description = mdDoc ''
+                    Username to authenticate with the target device.
+                  '';
+                };
+                password_file = mkOption {
+                  type = types.path;
+                  description = mdDoc ''
+                    Path to a file which contains the password to authenticate with the target device.
+                    Needs to be readable by the user the exporter runs under.
+                  '';
+                };
+                host_info = mkOption {
+                  type = types.bool;
+                  description = mdDoc ''
+                    Enable extended host info for this device. *Warning*: This will heavily increase scrape time.
+                  '';
+                  default = false;
+                };
+              };
+            });
+          };
+        };
+      };
+    };
+  };
+
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = utils.escapeSystemdExecArgs ([
+        (lib.getExe pkgs.fritz-exporter)
+        "--config" configFile
+      ] ++ cfg.extraFlags);
+      DynamicUser = false;
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
index dc53d21406ff..7b881a8e2693 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/fritzbox.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix b/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix
index 34a887104212..07c06afe1409 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/graphite.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 let
   cfg = config.services.prometheus.exporters.graphite;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix b/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix
index f5604bc00ee0..78ae4826215c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/idrac.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 let
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix b/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix
index c5024a258e71..68fc63e40fcd 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/imap-mailstat.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix b/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
index 61c0c08d2250..d0d7f16bdadf 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/influxdb.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix b/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix
index 9adbe31d84d6..fe9734d33c7c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/ipmi.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix b/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix
index 024602718602..bc670ba9cc0e 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/jitsi.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/json.nix b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
index 473f3a7e47e3..7f78985d80cd 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/json.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/json.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix b/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix
index 15e0c9ecb177..72119d17fcb7 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/junos-czerwonk.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/kea.nix b/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
index 3abb6ff6bdf8..ccfdd98b8db9 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
@@ -1,7 +1,8 @@
 { config
 , lib
 , pkgs
-, options
+, utils
+, ...
 }:
 
 with lib;
@@ -9,18 +10,22 @@ with lib;
 let
   cfg = config.services.prometheus.exporters.kea;
 in {
+  imports = [
+    (mkRenamedOptionModule [ "controlSocketPaths" ] [ "targets" ])
+  ];
   port = 9547;
   extraOpts = {
-    controlSocketPaths = mkOption {
+    targets = mkOption {
       type = types.listOf types.str;
       example = literalExpression ''
         [
           "/run/kea/kea-dhcp4.socket"
           "/run/kea/kea-dhcp6.socket"
+          "http://127.0.0.1:8547"
         ]
       '';
       description = lib.mdDoc ''
-        Paths to kea control sockets
+        Paths or URLs to the Kea control socket.
       '';
     };
   };
@@ -32,12 +37,11 @@ in {
     serviceConfig = {
       User = "kea";
       DynamicUser = true;
-      ExecStart = ''
-        ${pkgs.prometheus-kea-exporter}/bin/kea-exporter \
-          --address ${cfg.listenAddress} \
-          --port ${toString cfg.port} \
-          ${concatStringsSep " " cfg.controlSocketPaths}
-      '';
+      ExecStart = utils.escapeSystemdExecArgs ([
+        (lib.getExe pkgs.prometheus-kea-exporter)
+        "--address" cfg.listenAddress
+        "--port" cfg.port
+      ] ++ cfg.extraFlags ++ cfg.targets);
       RuntimeDirectory = "kea";
       RuntimeDirectoryPreserve = true;
       RestrictAddressFamilies = [
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix b/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix
index dfa56343b871..afdb664a0de5 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/keylight.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/knot.nix b/nixos/modules/services/monitoring/prometheus/exporters/knot.nix
index 775848750803..0352aff8b013 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/knot.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/knot.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix b/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix
index 9f914b1dc146..66d9c02f904b 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/lnd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/mail.nix b/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
index 15079f5841f4..8c88f47ab86a 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix b/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix
index 54dab4b5581a..a8dba75251d8 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/mikrotik.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
index 82cc3fc314f2..e24d4f766e30 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/minio.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix b/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix
index 222ea3e5384f..0eb193c0021f 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/modemmanager.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix b/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix
index b36a09c60920..1ed6bbf0325d 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/mongodb.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix b/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix
index 849c514de681..c6da052ccdf3 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/mysqld.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 let
   cfg = config.services.prometheus.exporters.mysqld;
   inherit (lib) types mkOption mdDoc mkIf mkForce cli concatStringsSep optionalString escapeShellArgs;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix b/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix
index 28a3eb6a134c..82deea6864e8 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index 88dc79fc2503..339749226aa4 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix
index 674dc9dd4158..b79a034e1384 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
index dd8602e2c63d..9b8a0d2c6bc2 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/node.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nut.nix b/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
index e58a394456a3..a14e379079b0 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix b/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix
index 9e55cadae523..9587403c7802 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/pgbouncer.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix b/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix
index 8238f1ac1856..4ea5f64012c0 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/php-fpm.nix
@@ -2,6 +2,7 @@
 , lib
 , pkgs
 , options
+, ...
 }:
 
 let
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix b/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix
index 6f403b3e58c8..4b7eca7493a6 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/pihole.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/ping.nix b/nixos/modules/services/monitoring/prometheus/exporters/ping.nix
index af78b6bef625..bda5038a0c64 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/ping.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/ping.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
index 9f402b123110..ead8e806f85a 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix b/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix
index 755d771ecdff..514b2d0c8f2d 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postgres.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/process.nix b/nixos/modules/services/monitoring/prometheus/exporters/process.nix
index 278d6cd78074..86c71a88e28b 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/process.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/process.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/pve.nix b/nixos/modules/services/monitoring/prometheus/exporters/pve.nix
index 83e740320df2..96db49d9591f 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/pve.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/pve.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 let
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix b/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix
index f03b3c4df916..60243e0ed069 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/py-air-control.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/redis.nix b/nixos/modules/services/monitoring/prometheus/exporters/redis.nix
index befbcb21f766..71f94a700efd 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/redis.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/redis.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/restic.nix b/nixos/modules/services/monitoring/prometheus/exporters/restic.nix
index 977bd42e9812..12962af5f111 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/restic.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/restic.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix
index f9dcfad07d30..8169d4075a9f 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/rspamd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix b/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix
index 1f7235cb7830..42b659501161 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/rtl_433.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 let
   cfg = config.services.prometheus.exporters.rtl_433;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix b/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
index b9ab305f7c08..0d937ac6673f 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 let
   inherit (lib) mkOption types;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix b/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix
index 3b6ebf65b090..d4c929d88b9c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/scaphandre.nix
@@ -2,6 +2,7 @@
 , lib
 , pkgs
 , options
+, ...
 }:
 
 let
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/script.nix b/nixos/modules/services/monitoring/prometheus/exporters/script.nix
index eab0e1d8a6b5..f37fa456d27c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/script.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/script.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix b/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix
index b9cfd1b1e84a..1d2329dfbae1 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/shelly.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix b/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix
index 50e1321a1e9c..1040e9ecadbd 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/smartctl.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix b/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix
index 459f5842f546..2bacc9cd7cac 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/smokeping.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
index 452cb154bcf6..207446e39f49 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/snmp.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/sql.nix b/nixos/modules/services/monitoring/prometheus/exporters/sql.nix
index 678bc348679d..dbfa69678a0c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/sql.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/sql.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 with lib;
 let
   cfg = config.services.prometheus.exporters.sql;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix b/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix
index d9d732d8c125..94df86167e8c 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix b/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix
index b1d6760b40b3..337ebd4ed66f 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/surfboard.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/tor.nix b/nixos/modules/services/monitoring/prometheus/exporters/tor.nix
index 7a9167110a27..b91f69aded3d 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/tor.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/tor.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix b/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix
index f2336429d42f..2f4444a96c69 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unbound.nix
@@ -2,6 +2,7 @@
 , lib
 , pkgs
 , options
+, ...
 }:
 
 with lib;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
index 70f26d9783be..b7addcd56827 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unifi.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix b/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix
index 3b7f978528cd..aff1197a8775 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/unpoller.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix b/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix
index a019157c664b..7b21e5fc7cb7 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/v2ray.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
index a7e5b41dffc6..98fbba82c8e9 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/varnish.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
index 9b7590314936..127c8021a9f0 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix b/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix
index ff12a52d49a9..21f6354cc4a2 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/zfs.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, options }:
+{ config, lib, pkgs, options, ... }:
 
 with lib;
 
diff --git a/nixos/modules/services/monitoring/scrutiny.nix b/nixos/modules/services/monitoring/scrutiny.nix
index 2862cdd80128..031f5a30cada 100644
--- a/nixos/modules/services/monitoring/scrutiny.nix
+++ b/nixos/modules/services/monitoring/scrutiny.nix
@@ -2,7 +2,7 @@
 let
   inherit (lib) maintainers;
   inherit (lib.meta) getExe;
-  inherit (lib.modules) mkIf;
+  inherit (lib.modules) mkIf mkMerge;
   inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption;
   inherit (lib.types) bool enum nullOr port str submodule;
 
@@ -156,43 +156,47 @@ in
     };
   };
 
-  config = mkIf (cfg.enable || cfg.collector.enable) {
-    services.influxdb2.enable = cfg.influxdb.enable;
+  config = mkMerge [
+    (mkIf cfg.enable {
+      services.influxdb2.enable = cfg.influxdb.enable;
 
-    networking.firewall = mkIf cfg.openFirewall {
-      allowedTCPPorts = [ cfg.settings.web.listen.port ];
-    };
-
-    services.smartd = mkIf cfg.collector.enable {
-      enable = true;
-      extraOptions = [
-        "-A /var/log/smartd/"
-        "--interval=600"
-      ];
-    };
+      networking.firewall = mkIf cfg.openFirewall {
+        allowedTCPPorts = [ cfg.settings.web.listen.port ];
+      };
 
-    systemd = {
-      services = {
-        scrutiny = mkIf cfg.enable {
-          description = "Hard Drive S.M.A.R.T Monitoring, Historical Trends & Real World Failure Thresholds";
-          wantedBy = [ "multi-user.target" ];
-          after = [ "network.target" ];
-          environment = {
-            SCRUTINY_VERSION = "1";
-            SCRUTINY_WEB_DATABASE_LOCATION = "/var/lib/scrutiny/scrutiny.db";
-            SCRUTINY_WEB_SRC_FRONTEND_PATH = "${cfg.package}/share/scrutiny";
-          };
-          serviceConfig = {
-            DynamicUser = true;
-            ExecStart = "${getExe cfg.package} start --config ${settingsFormat.generate "scrutiny.yaml" cfg.settings}";
-            Restart = "always";
-            StateDirectory = "scrutiny";
-            StateDirectoryMode = "0750";
-          };
+      systemd.services.scrutiny = {
+        description = "Hard Drive S.M.A.R.T Monitoring, Historical Trends & Real World Failure Thresholds";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ] ++ lib.optional cfg.influxdb.enable "influxdb2.service";
+        wants = lib.optional cfg.influxdb.enable "influxdb2.service";
+        environment = {
+          SCRUTINY_VERSION = "1";
+          SCRUTINY_WEB_DATABASE_LOCATION = "/var/lib/scrutiny/scrutiny.db";
+          SCRUTINY_WEB_SRC_FRONTEND_PATH = "${cfg.package}/share/scrutiny";
+        };
+        serviceConfig = {
+          DynamicUser = true;
+          ExecStart = "${getExe cfg.package} start --config ${settingsFormat.generate "scrutiny.yaml" cfg.settings}";
+          Restart = "always";
+          StateDirectory = "scrutiny";
+          StateDirectoryMode = "0750";
         };
+      };
+    })
+    (mkIf cfg.collector.enable {
+      services.smartd = {
+        enable = true;
+        extraOptions = [
+          "-A /var/log/smartd/"
+          "--interval=600"
+        ];
+      };
 
-        scrutiny-collector = mkIf cfg.collector.enable {
+      systemd = {
+        services.scrutiny-collector = {
           description = "Scrutiny Collector Service";
+          after = lib.optional cfg.enable "scrutiny.service";
+          wants = lib.optional cfg.enable "scrutiny.service";
           environment = {
             COLLECTOR_VERSION = "1";
             COLLECTOR_API_ENDPOINT = cfg.collector.settings.api.endpoint;
@@ -203,13 +207,11 @@ in
           };
           startAt = cfg.collector.schedule;
         };
-      };
 
-      timers = mkIf cfg.collector.enable {
-        scrutiny-collector.timerConfig.Persistent = true;
+        timers.scrutiny-collector.timerConfig.Persistent = true;
       };
-    };
-  };
+    })
+  ];
 
   meta.maintainers = [ maintainers.jnsgruk ];
 }
diff --git a/nixos/modules/services/networking/dnscache.nix b/nixos/modules/services/networking/dnscache.nix
index eff13f69f470..4f5b77a5b685 100644
--- a/nixos/modules/services/networking/dnscache.nix
+++ b/nixos/modules/services/networking/dnscache.nix
@@ -86,7 +86,11 @@ in {
 
   config = mkIf config.services.dnscache.enable {
     environment.systemPackages = [ pkgs.djbdns ];
-    users.users.dnscache.isSystemUser = true;
+    users.users.dnscache = {
+        isSystemUser = true;
+        group = "dnscache";
+    };
+    users.groups.dnscache = {};
 
     systemd.services.dnscache = {
       description = "djbdns dnscache server";
diff --git a/nixos/modules/services/networking/mihomo.nix b/nixos/modules/services/networking/mihomo.nix
new file mode 100644
index 000000000000..ae700603b529
--- /dev/null
+++ b/nixos/modules/services/networking/mihomo.nix
@@ -0,0 +1,118 @@
+# NOTE:
+# cfg.configFile contains secrets such as proxy servers' credential!
+# we dont want plaintext secrets in world-readable `/nix/store`.
+
+{ lib
+, config
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.mihomo;
+in
+{
+  options.services.mihomo = {
+    enable = lib.mkEnableOption "Mihomo, A rule-based proxy in Go.";
+
+    package = lib.mkPackageOption pkgs "mihomo" { };
+
+    configFile = lib.mkOption {
+      default = null;
+      type = lib.types.nullOr lib.types.path;
+      description = "Configuration file to use.";
+    };
+
+    webui = lib.mkOption {
+      default = null;
+      type = lib.types.nullOr lib.types.path;
+      description = ''
+        Local web interface to use.
+
+        You can also use the following website, just in case:
+        - metacubexd:
+          - https://d.metacubex.one
+          - https://metacubex.github.io/metacubexd
+          - https://metacubexd.pages.dev
+        - yacd:
+          - https://yacd.haishan.me
+        - clash-dashboard (buggy):
+          - https://clash.razord.top
+      '';
+    };
+
+    extraOpts = lib.mkOption {
+      default = null;
+      type = lib.types.nullOr lib.types.str;
+      description = "Extra command line options to use.";
+    };
+
+    tunMode = lib.mkEnableOption ''
+      necessary permission for Mihomo's systemd service for TUN mode to function properly.
+
+      Keep in mind, that you still need to enable TUN mode manually in Mihomo's configuration.
+    '';
+  };
+
+  config = lib.mkIf cfg.enable {
+    ### systemd service
+    systemd.services."mihomo" = {
+      description = "Mihomo daemon, A rule-based proxy in Go.";
+      documentation = [ "https://wiki.metacubex.one/" ];
+      requires = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig =
+        {
+          ExecStart = lib.concatStringsSep " " [
+            (lib.getExe cfg.package)
+            "-d /var/lib/private/mihomo"
+            (lib.optionalString (cfg.configFile != null) "-f \${CREDENTIALS_DIRECTORY}/config.yaml")
+            (lib.optionalString (cfg.webui != null) "-ext-ui ${cfg.webui}")
+            (lib.optionalString (cfg.extraOpts != null) cfg.extraOpts)
+          ];
+
+          DynamicUser = true;
+          StateDirectory = "mihomo";
+          LoadCredential = "config.yaml:${cfg.configFile}";
+
+          ### Hardening
+          AmbientCapabilities = "";
+          CapabilityBoundingSet = "";
+          DeviceAllow = "";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateMounts = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProcSubset = "pid";
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          RestrictNamespaces = true;
+          RestrictAddressFamilies = "AF_INET AF_INET6";
+          SystemCallArchitectures = "native";
+          SystemCallFilter = "@system-service bpf";
+          UMask = "0077";
+        }
+        // lib.optionalAttrs cfg.tunMode {
+          AmbientCapabilities = "CAP_NET_ADMIN";
+          CapabilityBoundingSet = "CAP_NET_ADMIN";
+          PrivateDevices = false;
+          PrivateUsers = false;
+          RestrictAddressFamilies = "AF_INET AF_INET6 AF_NETLINK";
+        };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ Guanran928 ];
+}
diff --git a/nixos/modules/services/networking/mycelium.nix b/nixos/modules/services/networking/mycelium.nix
new file mode 100644
index 000000000000..9c4bca7c6861
--- /dev/null
+++ b/nixos/modules/services/networking/mycelium.nix
@@ -0,0 +1,133 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.mycelium;
+in
+{
+  options.services.mycelium = {
+    enable = lib.mkEnableOption "mycelium network";
+    peers = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      description = ''
+        List of peers to connect to, in the formats:
+         - `quic://[2001:0db8::1]:9651`
+         - `quic://192.0.2.1:9651`
+         - `tcp://[2001:0db8::1]:9651`
+         - `tcp://192.0.2.1:9651`
+
+        If addHostedPublicNodes is set to true, the hosted public nodes will also be added.
+      '';
+      default = [ ];
+    };
+    keyFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      description = ''
+        Optional path to a file containing the mycelium key material.
+        If unset, the default location (`/var/lib/mycelium/key.bin`) will be used.
+        If no key exist at this location, it will be generated on startup.
+      '';
+    };
+    openFirewall = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = "Open the firewall for mycelium";
+    };
+    package = lib.mkOption {
+      type = lib.types.package;
+      default = pkgs.mycelium;
+      defaultText = lib.literalExpression ''"''${pkgs.mycelium}"'';
+      description = "The mycelium package to use";
+    };
+    addHostedPublicNodes = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = ''
+        Adds the hosted peers from https://github.com/threefoldtech/mycelium#hosted-public-nodes.
+      '';
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ 9651 ];
+    networking.firewall.allowedUDPPorts = lib.optionals cfg.openFirewall [ 9650 9651 ];
+
+    systemd.services.mycelium = {
+      description = "Mycelium network";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      restartTriggers = [
+        cfg.keyFile
+      ];
+
+      unitConfig.Documentation = "https://github.com/threefoldtech/mycelium";
+
+      serviceConfig = {
+        User = "mycelium";
+        DynamicUser = true;
+        StateDirectory = "mycelium";
+        ProtectHome = true;
+        ProtectSystem = true;
+        LoadCredential = lib.mkIf (cfg.keyFile != null) "keyfile:${cfg.keyFile}";
+        SyslogIdentifier = "mycelium";
+        AmbientCapabilities = [ "CAP_NET_ADMIN" ];
+        MemoryDenyWriteExecute = true;
+        ProtectControlGroups = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged @keyring" ];
+        ExecStart = lib.concatStringsSep " " ([
+          (lib.getExe cfg.package)
+          (if (cfg.keyFile != null) then
+            "--key-file \${CREDENTIALS_DIRECTORY}/keyfile" else
+            "--key-file %S/mycelium/key.bin"
+          )
+          "--tun-name"
+          "mycelium"
+        ] ++
+        (lib.optional (cfg.addHostedPublicNodes || cfg.peers != [ ]) "--peers")
+        ++ cfg.peers ++ (lib.optionals cfg.addHostedPublicNodes [
+          "tcp://188.40.132.242:9651" # DE 01
+          "tcp://[2a01:4f8:221:1e0b::2]:9651"
+          "quic://188.40.132.242:9651"
+          "quic://[2a01:4f8:221:1e0b::2]:9651"
+
+          "tcp://136.243.47.186:9651" # DE 02
+          "tcp://[2a01:4f8:212:fa6::2]:9651"
+          "quic://136.243.47.186:9651"
+          "quic://[2a01:4f8:212:fa6::2]:9651"
+
+          "tcp://185.69.166.7:9651" # BE 03
+          "tcp://[2a02:1802:5e:0:8478:51ff:fee2:3331]:9651"
+          "quic://185.69.166.7:9651"
+          "quic://[2a02:1802:5e:0:8478:51ff:fee2:3331]:9651"
+
+          "tcp://185.69.166.8:9651" # BE 04
+          "tcp://[2a02:1802:5e:0:8c9e:7dff:fec9:f0d2]:9651"
+          "quic://185.69.166.8:9651"
+          "quic://[2a02:1802:5e:0:8c9e:7dff:fec9:f0d2]:9651"
+
+          "tcp://65.21.231.58:9651" # FI 05
+          "tcp://[2a01:4f9:6a:1dc5::2]:9651"
+          "quic://65.21.231.58:9651"
+          "quic://[2a01:4f9:6a:1dc5::2]:9651"
+
+          "tcp://65.109.18.113:9651" # FI 06
+          "tcp://[2a01:4f9:5a:1042::2]:9651"
+          "quic://65.109.18.113:9651"
+          "quic://[2a01:4f9:5a:1042::2]:9651"
+        ]));
+        Restart = "always";
+        RestartSec = 5;
+        TimeoutStopSec = 5;
+      };
+    };
+  };
+  meta = {
+    maintainers = with lib.maintainers; [ flokli lassulus ];
+  };
+}
+
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 63804a3b1c54..573a02cbda9e 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -101,7 +101,23 @@ let
     pre-down = "pre-down.d/";
   };
 
-  macAddressOpt = mkOption {
+  macAddressOptWifi = mkOption {
+    type = types.either types.str (types.enum [ "permanent" "preserve" "random" "stable" "stable-ssid" ]);
+    default = "preserve";
+    example = "00:11:22:33:44:55";
+    description = lib.mdDoc ''
+      Set the MAC address of the interface.
+
+      - `"XX:XX:XX:XX:XX:XX"`: MAC address of the interface
+      - `"permanent"`: Use the permanent MAC address of the device
+      - `"preserve"`: Don’t change the MAC address of the device upon activation
+      - `"random"`: Generate a randomized value upon each connect
+      - `"stable"`: Generate a stable, hashed MAC address
+      - `"stable-ssid"`: Generate a stable MAC addressed based on Wi-Fi network
+    '';
+  };
+
+  macAddressOptEth = mkOption {
     type = types.either types.str (types.enum [ "permanent" "preserve" "random" "stable" ]);
     default = "preserve";
     example = "00:11:22:33:44:55";
@@ -258,10 +274,10 @@ in
         '';
       };
 
-      ethernet.macAddress = macAddressOpt;
+      ethernet.macAddress = macAddressOptEth;
 
       wifi = {
-        macAddress = macAddressOpt;
+        macAddress = macAddressOptWifi;
 
         backend = mkOption {
           type = types.enum [ "wpa_supplicant" "iwd" ];
@@ -584,6 +600,7 @@ in
       description = "Ensure that NetworkManager declarative profiles are created";
       wantedBy = [ "multi-user.target" ];
       before = [ "network-online.target" ];
+      after = [ "NetworkManager.service" ];
       script = let
         path = id: "/run/NetworkManager/system-connections/${id}.nmconnection";
       in ''
@@ -593,9 +610,7 @@ in
           ${pkgs.envsubst}/bin/envsubst -i ${ini.generate (lib.escapeShellArg profile.n) profile.v} > ${path (lib.escapeShellArg profile.n)}
         '') (lib.mapAttrsToList (n: v: { inherit n v; }) cfg.ensureProfiles.profiles)
       + ''
-        if systemctl is-active --quiet NetworkManager; then
-          ${pkgs.networkmanager}/bin/nmcli connection reload
-        fi
+        ${pkgs.networkmanager}/bin/nmcli connection reload
       '';
       serviceConfig = {
         EnvironmentFile = cfg.ensureProfiles.environmentFiles;
diff --git a/nixos/modules/services/networking/tinyproxy.nix b/nixos/modules/services/networking/tinyproxy.nix
index 8ff12b52f10c..2b7509e99ca4 100644
--- a/nixos/modules/services/networking/tinyproxy.nix
+++ b/nixos/modules/services/networking/tinyproxy.nix
@@ -7,6 +7,7 @@ let
   mkValueStringTinyproxy = with lib; v:
         if true  ==         v then "yes"
         else if false ==    v then "no"
+        else if types.path.check v then ''"${v}"''
         else generators.mkValueStringDefault {} v;
   mkKeyValueTinyproxy = {
     mkValueString ? mkValueStringDefault {}
diff --git a/nixos/modules/services/web-apps/engelsystem.nix b/nixos/modules/services/web-apps/engelsystem.nix
index 669620debce5..ae7b2b9e7d0c 100644
--- a/nixos/modules/services/web-apps/engelsystem.nix
+++ b/nixos/modules/services/web-apps/engelsystem.nix
@@ -99,7 +99,6 @@ in {
       '';
 
     services.phpfpm.pools.engelsystem = {
-      phpPackage = pkgs.php81;
       user = "engelsystem";
       settings = {
         "listen.owner" = config.services.nginx.user;
diff --git a/nixos/modules/services/web-apps/hedgedoc.nix b/nixos/modules/services/web-apps/hedgedoc.nix
index adcfe80a7332..8b17c6cbc3be 100644
--- a/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixos/modules/services/web-apps/hedgedoc.nix
@@ -236,9 +236,9 @@ in
     };
 
     services.hedgedoc.settings = {
-      defaultNotePath = lib.mkDefault "${cfg.package}/public/default.md";
-      docsPath = lib.mkDefault "${cfg.package}/public/docs";
-      viewPath = lib.mkDefault "${cfg.package}/public/views";
+      defaultNotePath = lib.mkDefault "${cfg.package}/share/hedgedoc/public/default.md";
+      docsPath = lib.mkDefault "${cfg.package}/share/hedgedoc/public/docs";
+      viewPath = lib.mkDefault "${cfg.package}/share/hedgedoc/public/views";
     };
 
     systemd.services.hedgedoc = {
@@ -263,7 +263,7 @@ in
         Group = name;
 
         Restart = "always";
-        ExecStart = "${cfg.package}/bin/hedgedoc";
+        ExecStart = lib.getExe cfg.package;
         RuntimeDirectory = [ name ];
         StateDirectory = [ name ];
         WorkingDirectory = "/run/${name}";
diff --git a/nixos/modules/services/web-apps/komga.nix b/nixos/modules/services/web-apps/komga.nix
index 31f475fc7b04..d7ab2a9e612e 100644
--- a/nixos/modules/services/web-apps/komga.nix
+++ b/nixos/modules/services/web-apps/komga.nix
@@ -1,99 +1,122 @@
-{ config, pkgs, lib, ... }:
-
-with lib;
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
 
 let
   cfg = config.services.komga;
-
-in {
+  inherit (lib) mkOption mkEnableOption maintainers;
+  inherit (lib.types) port str bool;
+in
+{
   options = {
     services.komga = {
-      enable = mkEnableOption (lib.mdDoc "Komga, a free and open source comics/mangas media server");
+      enable = mkEnableOption "Komga, a free and open source comics/mangas media server";
 
       port = mkOption {
-        type = types.port;
+        type = port;
         default = 8080;
-        description = lib.mdDoc ''
-          The port that Komga will listen on.
-        '';
+        description = "The port that Komga will listen on.";
       };
 
       user = mkOption {
-        type = types.str;
+        type = str;
         default = "komga";
-        description = lib.mdDoc ''
-          User account under which Komga runs.
-        '';
+        description = "User account under which Komga runs.";
       };
 
       group = mkOption {
-        type = types.str;
+        type = str;
         default = "komga";
-        description = lib.mdDoc ''
-          Group under which Komga runs.
-        '';
+        description = "Group under which Komga runs.";
       };
 
       stateDir = mkOption {
-        type = types.str;
+        type = str;
         default = "/var/lib/komga";
-        description = lib.mdDoc ''
-          State and configuration directory Komga will use.
-        '';
+        description = "State and configuration directory Komga will use.";
       };
 
       openFirewall = mkOption {
-        type = types.bool;
+        type = bool;
         default = false;
-        description = lib.mdDoc ''
-          Whether to open the firewall for the port in {option}`services.komga.port`.
-        '';
+        description = "Whether to open the firewall for the port in {option}`services.komga.port`.";
       };
     };
   };
 
-  config = mkIf cfg.enable {
-
-    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
+  config =
+    let
+      inherit (lib) mkIf getExe;
+    in
+    mkIf cfg.enable {
 
-    users.groups = mkIf (cfg.group == "komga") {
-      komga = {};
-    };
+      networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
 
-    users.users = mkIf (cfg.user == "komga") {
-      komga = {
-        group = cfg.group;
-        home = cfg.stateDir;
-        description = "Komga Daemon user";
-        isSystemUser = true;
-      };
-    };
+      users.groups = mkIf (cfg.group == "komga") { komga = { }; };
 
-    systemd.services.komga = {
-      environment = {
-        SERVER_PORT = builtins.toString cfg.port;
-        KOMGA_CONFIGDIR = cfg.stateDir;
+      users.users = mkIf (cfg.user == "komga") {
+        komga = {
+          group = cfg.group;
+          home = cfg.stateDir;
+          description = "Komga Daemon user";
+          isSystemUser = true;
+        };
       };
 
-      description = "Komga is a free and open source comics/mangas media server";
-
-      wantedBy = [ "multi-user.target" ];
-      wants = [ "network-online.target" ];
-      after = [ "network-online.target" ];
-
-      serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
-
-        Type = "simple";
-        Restart = "on-failure";
-        ExecStart = "${pkgs.komga}/bin/komga";
-
-        StateDirectory = mkIf (cfg.stateDir == "/var/lib/komga") "komga";
+      systemd.services.komga = {
+        environment = {
+          SERVER_PORT = builtins.toString cfg.port;
+          KOMGA_CONFIGDIR = cfg.stateDir;
+        };
+
+        description = "Komga is a free and open source comics/mangas media server";
+
+        wantedBy = [ "multi-user.target" ];
+        wants = [ "network-online.target" ];
+        after = [ "network-online.target" ];
+
+        serviceConfig = {
+          User = cfg.user;
+          Group = cfg.group;
+
+          Type = "simple";
+          Restart = "on-failure";
+          ExecStart = getExe pkgs.komga;
+
+          StateDirectory = mkIf (cfg.stateDir == "/var/lib/komga") "komga";
+
+          RemoveIPC = true;
+          NoNewPrivileges = true;
+          CapabilityBoundingSet = "";
+          SystemCallFilter = [ "@system-service" ];
+          ProtectSystem = "full";
+          PrivateTmp = true;
+          ProtectProc = "invisible";
+          ProtectClock = true;
+          ProcSubset = "pid";
+          PrivateUsers = true;
+          PrivateDevices = true;
+          ProtectHostname = true;
+          ProtectKernelTunables = true;
+          RestrictAddressFamilies = [
+            "AF_INET"
+            "AF_INET6"
+            "AF_NETLINK"
+          ];
+          LockPersonality = true;
+          RestrictNamespaces = true;
+          ProtectKernelLogs = true;
+          ProtectControlGroups = true;
+          ProtectKernelModules = true;
+          SystemCallArchitectures = "native";
+          RestrictSUIDSGID = true;
+          RestrictRealtime = true;
+        };
       };
-
     };
-  };
 
   meta.maintainers = with maintainers; [ govanify ];
 }
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 5cda4a00a9de..7f998207c434 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -14,7 +14,6 @@ let
     expose_php = "Off";
     error_reporting = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
     display_errors = "stderr";
-    "opcache.enable_cli" = "1";
     "opcache.interned_strings_buffer" = "8";
     "opcache.max_accelerated_files" = "10000";
     "opcache.memory_consumption" = "128";
diff --git a/nixos/modules/services/web-apps/pretix.nix b/nixos/modules/services/web-apps/pretix.nix
new file mode 100644
index 000000000000..2355f8c450a1
--- /dev/null
+++ b/nixos/modules/services/web-apps/pretix.nix
@@ -0,0 +1,581 @@
+{ config
+, lib
+, pkgs
+, utils
+, ...
+}:
+
+let
+  inherit (lib)
+    concatMapStringsSep
+    escapeShellArgs
+    filter
+    filterAttrs
+    getExe
+    getExe'
+    isAttrs
+    isList
+    literalExpression
+    mapAttrs
+    mkDefault
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    optionals
+    optionalString
+    recursiveUpdate
+    types
+  ;
+
+  filterRecursiveNull = o:
+    if isAttrs o then
+      mapAttrs (_: v: filterRecursiveNull v) (filterAttrs (_: v: v != null) o)
+    else if isList o then
+      map filterRecursiveNull (filter (v: v != null) o)
+    else
+      o;
+
+  cfg = config.services.pretix;
+  format = pkgs.formats.ini { };
+
+  configFile = format.generate "pretix.cfg" (filterRecursiveNull cfg.settings);
+
+  finalPackage = cfg.package.override {
+    inherit (cfg) plugins;
+  };
+
+  pythonEnv = cfg.package.python.buildEnv.override {
+    extraLibs = with cfg.package.python.pkgs; [
+      (toPythonModule finalPackage)
+      gunicorn
+    ]
+    ++ lib.optionals (cfg.settings.memcached.location != null)
+      cfg.package.optional-dependencies.memcached
+    ;
+  };
+
+  withRedis = cfg.settings.redis.location != null;
+in
+{
+  meta = with lib; {
+    maintainers = with maintainers; [ hexa ];
+  };
+
+  options.services.pretix = {
+    enable = mkEnableOption "pretix";
+
+    package = mkPackageOption pkgs "pretix" { };
+
+    group = mkOption {
+      type = types.str;
+      default = "pretix";
+      description = ''
+        Group under which pretix should run.
+      '';
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "pretix";
+      description = ''
+        User under which pretix should run.
+      '';
+    };
+
+    environmentFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/keys/pretix-secrets.env";
+      description = ''
+        Environment file to pass secret configuration values.
+
+        Each line must follow the `PRETIX_SECTION_KEY=value` pattern.
+      '';
+    };
+
+    plugins = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression ''
+        with config.services.pretix.package.plugins; [
+          passbook
+          pages
+        ];
+      '';
+      description = ''
+        Pretix plugins to install into the Python environment.
+      '';
+    };
+
+    gunicorn.extraArgs = mkOption {
+      type = with types; listOf str;
+      default = [
+        "--name=pretix"
+      ];
+      example = [
+        "--name=pretix"
+        "--workers=4"
+        "--max-requests=1200"
+        "--max-requests-jitter=50"
+        "--log-level=info"
+      ];
+      description = ''
+        Extra arguments to pass to gunicorn.
+        See <https://docs.pretix.eu/en/latest/admin/installation/manual_smallscale.html#start-pretix-as-a-service> for details.
+      '';
+      apply = escapeShellArgs;
+    };
+
+    celery = {
+      extraArgs = mkOption {
+        type = with types; listOf str;
+        default = [ ];
+        description = ''
+          Extra arguments to pass to celery.
+
+          See <https://docs.celeryq.dev/en/stable/reference/cli.html#celery-worker> for more info.
+        '';
+        apply = utils.escapeSystemdExecArgs;
+      };
+    };
+
+    nginx = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        example = false;
+        description = ''
+          Whether to set up an nginx virtual host.
+        '';
+      };
+
+      domain = mkOption {
+        type = types.str;
+        example = "talks.example.com";
+        description = ''
+          The domain name under which to set up the virtual host.
+        '';
+      };
+    };
+
+    database.createLocally = mkOption {
+      type = types.bool;
+      default = true;
+      example = false;
+      description = ''
+        Whether to automatically set up the database on the local DBMS instance.
+
+        Only supported for PostgreSQL. Not required for sqlite.
+      '';
+    };
+
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = format.type;
+        options = {
+          pretix = {
+            instance_name = mkOption {
+              type = types.str;
+              example = "tickets.example.com";
+              description = ''
+                The name of this installation.
+              '';
+            };
+
+            url = mkOption {
+              type = types.str;
+              example = "https://tickets.example.com";
+              description = ''
+                The installation’s full URL, without a trailing slash.
+              '';
+            };
+
+            cachedir = mkOption {
+              type = types.path;
+              default = "/var/cache/pretix";
+              description = ''
+                Directory for storing temporary files.
+              '';
+            };
+
+            datadir = mkOption {
+              type = types.path;
+              default = "/var/lib/pretix";
+              description = ''
+                Directory for storing user uploads and similar data.
+              '';
+            };
+
+            logdir = mkOption {
+              type = types.path;
+              default = "/var/log/pretix";
+              description = ''
+                Directory for storing log files.
+              '';
+            };
+
+            currency = mkOption {
+              type = types.str;
+              default = "EUR";
+              example = "USD";
+              description = ''
+                Default currency for events in its ISO 4217 three-letter code.
+              '';
+            };
+
+            registration = mkOption {
+              type = types.bool;
+              default = false;
+              example = true;
+              description = ''
+                Whether to allow registration of new admin users.
+              '';
+            };
+          };
+
+          database = {
+            backend = mkOption {
+              type = types.enum [
+                "sqlite3"
+                "postgresql"
+              ];
+              default = "postgresql";
+              description = ''
+                Database backend to use.
+
+                Only postgresql is recommended for production setups.
+              '';
+            };
+
+            host = mkOption {
+              type = with types; nullOr types.path;
+              default = if cfg.settings.database.backend == "postgresql" then "/run/postgresql" else null;
+              defaultText = literalExpression ''
+                if config.services.pretix.settings..database.backend == "postgresql" then "/run/postgresql"
+                else null
+              '';
+              description = ''
+                Database host or socket path.
+              '';
+            };
+
+            name = mkOption {
+              type = types.str;
+              default = "pretix";
+              description = ''
+                Database name.
+              '';
+            };
+
+            user = mkOption {
+              type = types.str;
+              default = "pretix";
+              description = ''
+                Database username.
+              '';
+            };
+          };
+
+          mail = {
+            from = mkOption {
+              type = types.str;
+              example = "tickets@example.com";
+              description = ''
+                E-Mail address used in the `FROM` header of outgoing mails.
+              '';
+            };
+
+            host = mkOption {
+              type = types.str;
+              default = "localhost";
+              example = "mail.example.com";
+              description = ''
+                Hostname of the SMTP server use for mail delivery.
+              '';
+            };
+
+            port = mkOption {
+              type = types.port;
+              default = 25;
+              example = 587;
+              description = ''
+                Port of the SMTP server to use for mail delivery.
+              '';
+            };
+          };
+
+          celery = {
+            backend = mkOption {
+              type = types.str;
+              default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=1";
+              defaultText = literalExpression ''
+                optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=1"
+              '';
+              description = ''
+                URI to the celery backend used for the asynchronous job queue.
+              '';
+            };
+
+            broker = mkOption {
+              type = types.str;
+              default = "redis+socket://${config.services.redis.servers.pretix.unixSocket}?virtual_host=2";
+              defaultText = literalExpression ''
+                optionalString config.services.pretix.celery.enable "redis+socket://''${config.services.redis.servers.pretix.unixSocket}?virtual_host=2"
+              '';
+              description = ''
+                URI to the celery broker used for the asynchronous job queue.
+              '';
+            };
+          };
+
+          redis = {
+            location = mkOption {
+              type = with types; nullOr str;
+              default = "unix://${config.services.redis.servers.pretix.unixSocket}?db=0";
+              defaultText = literalExpression ''
+                "unix://''${config.services.redis.servers.pretix.unixSocket}?db=0"
+              '';
+              description = ''
+                URI to the redis server, used to speed up locking, caching and session storage.
+              '';
+            };
+
+            sessions = mkOption {
+              type = types.bool;
+              default = true;
+              example = false;
+              description = ''
+                Whether to use redis as the session storage.
+              '';
+            };
+          };
+
+          memcached = {
+            location = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              example = "127.0.0.1:11211";
+              description = ''
+                The `host:port` combination or the path to the UNIX socket of a memcached instance.
+
+                Can be used instead of Redis for caching.
+              '';
+            };
+          };
+
+          tools = {
+            pdftk = mkOption {
+              type = types.path;
+              default = getExe pkgs.pdftk;
+              defaultText = literalExpression ''
+                lib.getExe pkgs.pdftk
+              '';
+              description = ''
+                Path to the pdftk executable.
+              '';
+            };
+          };
+        };
+      };
+      default = { };
+      description = ''
+        pretix configuration as a Nix attribute set. All settings can also be passed
+        from the environment.
+
+        See <https://docs.pretix.eu/en/latest/admin/config.html> for possible options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # https://docs.pretix.eu/en/latest/admin/installation/index.html
+
+    environment.systemPackages = [
+      (pkgs.writeScriptBin "pretix-manage" ''
+        cd ${cfg.settings.pretix.datadir}
+        sudo=exec
+        if [[ "$USER" != ${cfg.user} ]]; then
+          sudo='exec /run/wrappers/bin/sudo -u ${cfg.user} ${optionalString withRedis "-g redis-pretix"} --preserve-env=PRETIX_CONFIG_FILE'
+        fi
+        export PRETIX_CONFIG_FILE=${configFile}
+        $sudo ${getExe' pythonEnv "pretix-manage"} "$@"
+      '')
+    ];
+
+    services = {
+      nginx = mkIf cfg.nginx.enable {
+        enable = true;
+        recommendedGzipSettings = mkDefault true;
+        recommendedOptimisation = mkDefault true;
+        recommendedProxySettings = mkDefault true;
+        recommendedTlsSettings = mkDefault true;
+        upstreams.pretix.servers."unix:/run/pretix/pretix.sock" = { };
+        virtualHosts.${cfg.nginx.domain} = {
+          # https://docs.pretix.eu/en/latest/admin/installation/manual_smallscale.html#ssl
+          extraConfig = ''
+            more_set_headers Referrer-Policy same-origin;
+            more_set_headers X-Content-Type-Options nosniff;
+          '';
+          locations = {
+            "/".proxyPass = "http://pretix";
+            "/media/" = {
+              alias = "${cfg.settings.pretix.datadir}/media/";
+              extraConfig = ''
+                access_log off;
+                expires 7d;
+              '';
+            };
+            "^~ /media/(cachedfiles|invoices)" = {
+              extraConfig = ''
+                deny all;
+                return 404;
+              '';
+            };
+            "/static/" = {
+              alias = "${finalPackage}/${cfg.package.python.sitePackages}/pretix/static.dist/";
+              extraConfig = ''
+                access_log off;
+                more_set_headers Cache-Control "public";
+                expires 365d;
+              '';
+            };
+          };
+        };
+      };
+
+      postgresql = mkIf (cfg.database.createLocally && cfg.settings.database.backend == "postgresql") {
+        enable = true;
+        ensureUsers = [ {
+          name = cfg.settings.database.user;
+          ensureDBOwnership = true;
+        } ];
+        ensureDatabases = [ cfg.settings.database.name ];
+      };
+
+      redis.servers.pretix.enable = withRedis;
+    };
+
+    systemd.services = let
+      commonUnitConfig = {
+        environment.PRETIX_CONFIG_FILE = configFile;
+        serviceConfig = {
+          User = "pretix";
+          Group = "pretix";
+          EnvironmentFile = optionals (cfg.environmentFile != null) [
+            cfg.environmentFile
+          ];
+          StateDirectory = [
+            "pretix"
+          ];
+          StateDirectoryMode = "0755";
+          CacheDirectory = "pretix";
+          LogsDirectory = "pretix";
+          WorkingDirectory = cfg.settings.pretix.datadir;
+          SupplementaryGroups = optionals withRedis [
+            "redis-pretix"
+          ];
+          AmbientCapabilities = "";
+          CapabilityBoundingSet = [ "" ];
+          DevicePolicy = "closed";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = false; # required by pdftk
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          ProcSubset = "pid";
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          ProtectSystem = "strict";
+          RemoveIPC = true;
+          RestrictAddressFamilies = [
+            "AF_INET"
+            "AF_INET6"
+            "AF_UNIX"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = [
+            "@system-service"
+            "~@privileged"
+            "@chown"
+          ];
+          UMask = "0022";
+        };
+      };
+    in {
+      pretix-web = recursiveUpdate commonUnitConfig {
+        description = "pretix web service";
+        after = [
+          "network.target"
+          "redis-pretix.service"
+          "postgresql.service"
+        ];
+        wantedBy = [ "multi-user.target" ];
+        preStart = ''
+          versionFile="${cfg.settings.pretix.datadir}/.version"
+          version=$(cat "$versionFile" 2>/dev/null || echo 0)
+
+          pluginsFile="${cfg.settings.pretix.datadir}/.plugins"
+          plugins=$(cat "$pluginsFile" 2>/dev/null || echo "")
+          configuredPlugins="${concatMapStringsSep "|" (package: package.name) cfg.plugins}"
+
+          if [[ $version != ${cfg.package.version} || $plugins != $configuredPlugins ]]; then
+            ${getExe' pythonEnv "pretix-manage"} migrate
+
+            echo "${cfg.package.version}" > "$versionFile"
+            echo "$configuredPlugins" > "$pluginsFile"
+          fi
+        '';
+        serviceConfig = {
+          TimeoutStartSec = "5min";
+          ExecStart = "${getExe' pythonEnv "gunicorn"} --bind unix:/run/pretix/pretix.sock ${cfg.gunicorn.extraArgs} pretix.wsgi";
+          RuntimeDirectory = "pretix";
+        };
+      };
+
+      pretix-periodic = recursiveUpdate commonUnitConfig {
+        description = "pretix periodic task runner";
+        # every 15 minutes
+        startAt = [ "*:3,18,33,48" ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${getExe' pythonEnv "pretix-manage"} runperiodic";
+        };
+      };
+
+      pretix-worker = recursiveUpdate commonUnitConfig {
+        description = "pretix asynchronous job runner";
+        after = [
+          "network.target"
+          "redis-pretix.service"
+          "postgresql.service"
+        ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig.ExecStart = "${getExe' pythonEnv "celery"} -A pretix.celery_app worker ${cfg.celery.extraArgs}";
+      };
+    };
+
+    systemd.sockets.pretix-web.socketConfig = {
+      ListenStream = "/run/pretix/pretix.sock";
+      SocketUser = "nginx";
+    };
+
+    users = {
+      groups."${cfg.group}" = {};
+      users."${cfg.user}" = {
+        isSystemUser = true;
+        createHome = true;
+        home = cfg.settings.pretix.datadir;
+        inherit (cfg) group;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/suwayomi-server.md b/nixos/modules/services/web-apps/suwayomi-server.md
index ff1e06c8a53a..18e7a631443f 100644
--- a/nixos/modules/services/web-apps/suwayomi-server.md
+++ b/nixos/modules/services/web-apps/suwayomi-server.md
@@ -101,6 +101,9 @@ Not all the configuration options are available directly in this module, but you
         port = 4567;
         autoDownloadNewChapters = false;
         maxSourcesInParallel" = 6;
+        extensionRepos = [
+          "https://raw.githubusercontent.com/MY_ACCOUNT/MY_REPO/repo/index.min.json"
+        ];
       };
     };
   };
diff --git a/nixos/modules/services/web-apps/suwayomi-server.nix b/nixos/modules/services/web-apps/suwayomi-server.nix
index 94dbe6f99356..99c6ea2a36e6 100644
--- a/nixos/modules/services/web-apps/suwayomi-server.nix
+++ b/nixos/modules/services/web-apps/suwayomi-server.nix
@@ -102,6 +102,17 @@ in
                 '';
               };
 
+              extensionRepos = mkOption {
+                type = types.listOf types.str;
+                default = [];
+                example = [
+                  "https://raw.githubusercontent.com/MY_ACCOUNT/MY_REPO/repo/index.min.json"
+                ];
+                description = mdDoc ''
+                  URL of repositories from which the extensions can be installed.
+                '';
+              };
+
               localSourcePath = mkOption {
                 type = types.path;
                 default = cfg.dataDir;
diff --git a/nixos/modules/services/web-servers/garage.nix b/nixos/modules/services/web-servers/garage.nix
index 48dd5b34757c..616be978b6e5 100644
--- a/nixos/modules/services/web-servers/garage.nix
+++ b/nixos/modules/services/web-servers/garage.nix
@@ -75,7 +75,19 @@ in
       source = configFile;
     };
 
-    environment.systemPackages = [ cfg.package ]; # For administration
+    # For administration
+    environment.systemPackages = [
+      (pkgs.writeScriptBin "garage" ''
+        # make it so all future variables set are automatically exported as environment variables
+        set -a
+
+        # source the set environmentFile (since systemd EnvironmentFile is supposed to be a minor subset of posix sh parsing) (with shell arg escaping to avoid quoting issues)
+        [ -f ${lib.escapeShellArg cfg.environmentFile} ] && . ${lib.escapeShellArg cfg.environmentFile}
+
+        # exec the program with quoted args (also with shell arg escaping for the program path to avoid quoting issues there)
+        exec ${lib.escapeShellArg (lib.getExe cfg.package)} "$@"
+      '')
+    ];
 
     systemd.services.garage = {
       description = "Garage Object Storage (S3 compatible)";
diff --git a/nixos/modules/services/x11/desktop-managers/budgie.nix b/nixos/modules/services/x11/desktop-managers/budgie.nix
index fe39097a22e8..466ef5c565b7 100644
--- a/nixos/modules/services/x11/desktop-managers/budgie.nix
+++ b/nixos/modules/services/x11/desktop-managers/budgie.nix
@@ -44,6 +44,8 @@ let
     enableSshSocket = config.services.openssh.startWhenNeeded;
   };
 in {
+  meta.maintainers = lib.teams.budgie.members;
+
   options = {
     services.xserver.desktopManager.budgie = {
       enable = mkEnableOption (mdDoc "the Budgie desktop");
@@ -144,7 +146,6 @@ in {
           mate.atril
           mate.engrampa
           mate.mate-calc
-          mate.mate-terminal
           mate.mate-system-monitor
           vlc
 
@@ -158,8 +159,11 @@ in {
         ] config.environment.budgie.excludePackages)
       ++ cfg.sessionPath;
 
+    # Both budgie-desktop-view and nemo defaults to this emulator.
+    programs.gnome-terminal.enable = mkDefault true;
+
     # Fonts.
-    fonts.packages = mkDefault [
+    fonts.packages = [
       pkgs.noto-fonts
       pkgs.hack-font
     ];
@@ -212,7 +216,6 @@ in {
     services.colord.enable = mkDefault true; # for BCC's Color panel.
     services.gnome.at-spi2-core.enable = mkDefault true; # for BCC's A11y panel.
     services.accounts-daemon.enable = mkDefault true; # for BCC's Users panel.
-    services.fprintd.enable = mkDefault true; # for BCC's Users panel.
     services.udisks2.enable = mkDefault true; # for BCC's Details panel.
 
     # For BCC's Online Accounts panel.
diff --git a/nixos/modules/services/x11/desktop-managers/deepin.nix b/nixos/modules/services/x11/desktop-managers/deepin.nix
index e6f221201013..902e3a9317dd 100644
--- a/nixos/modules/services/x11/desktop-managers/deepin.nix
+++ b/nixos/modules/services/x11/desktop-managers/deepin.nix
@@ -66,7 +66,7 @@ in
       services.upower.enable = mkDefault config.powerManagement.enable;
       networking.networkmanager.enable = mkDefault true;
       programs.dconf.enable = mkDefault true;
-      programs.gnupg.agent.pinentryPackage = pkgs.pinentry-qt;
+      programs.gnupg.agent.pinentryPackage = mkDefault pkgs.pinentry-qt;
 
       fonts.packages = with pkgs; [ noto-fonts ];
       xdg.mime.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix
index d3bdc4326a90..3d02deba6fc7 100644
--- a/nixos/modules/services/x11/desktop-managers/lxqt.nix
+++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix
@@ -62,7 +62,7 @@ in
     # Link some extra directories in /run/current-system/software/share
     environment.pathsToLink = [ "/share" ];
 
-    programs.gnupg.agent.pinentryPackage = pkgs.pinentry-qt;
+    programs.gnupg.agent.pinentryPackage = mkDefault pkgs.pinentry-qt;
 
     # virtual file systems support for PCManFM-QT
     services.gvfs.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/mate.nix b/nixos/modules/services/x11/desktop-managers/mate.nix
index f535a1d298b9..957eac7848e7 100644
--- a/nixos/modules/services/x11/desktop-managers/mate.nix
+++ b/nixos/modules/services/x11/desktop-managers/mate.nix
@@ -20,6 +20,22 @@ in
       };
 
       debug = mkEnableOption (lib.mdDoc "mate-session debug messages");
+
+      extraPanelApplets = mkOption {
+        default = [ ];
+        example = literalExpression "with pkgs.mate; [ mate-applets ]";
+        type = types.listOf types.package;
+        description = lib.mdDoc "Extra applets to add to mate-panel.";
+      };
+
+      extraCajaExtensions = mkOption {
+        default = [ ];
+        example = lib.literalExpression "with pkgs.mate; [ caja-extensions ]";
+        type = types.listOf types.package;
+        description = lib.mdDoc "Extra extensions to add to caja.";
+      };
+
+      enableWaylandSession = mkEnableOption (lib.mdDoc "MATE Wayland session");
     };
 
     environment.mate.excludePackages = mkOption {
@@ -31,55 +47,63 @@ in
 
   };
 
-  config = mkIf cfg.enable {
-
-    services.xserver.displayManager.sessionPackages = [
-      pkgs.mate.mate-session-manager
-    ];
-
-    # Let caja find extensions
-    environment.sessionVariables.CAJA_EXTENSION_DIRS = [ "${config.system.path}/lib/caja/extensions-2.0" ];
-
-    # Let mate-panel find applets
-    environment.sessionVariables."MATE_PANEL_APPLETS_DIR" = "${config.system.path}/share/mate-panel/applets";
-    environment.sessionVariables."MATE_PANEL_EXTRA_MODULES" = "${config.system.path}/lib/mate-panel/applets";
-
-    # Debugging
-    environment.sessionVariables.MATE_SESSION_DEBUG = mkIf cfg.debug "1";
-
-    environment.systemPackages = utils.removePackagesByName
-      (pkgs.mate.basePackages ++
-      pkgs.mate.extraPackages ++
-      [
-        pkgs.desktop-file-utils
-        pkgs.glib
-        pkgs.gtk3.out
-        pkgs.shared-mime-info
-        pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
-        pkgs.yelp # for 'Contents' in 'Help' menus
-      ])
-      config.environment.mate.excludePackages;
-
-    programs.dconf.enable = true;
-    # Shell integration for VTE terminals
-    programs.bash.vteIntegration = mkDefault true;
-    programs.zsh.vteIntegration = mkDefault true;
-
-    # Mate uses this for printing
-    programs.system-config-printer.enable = (mkIf config.services.printing.enable (mkDefault true));
-
-    services.gnome.at-spi2-core.enable = true;
-    services.gnome.gnome-keyring.enable = true;
-    services.udev.packages = [ pkgs.mate.mate-settings-daemon ];
-    services.gvfs.enable = true;
-    services.upower.enable = config.powerManagement.enable;
-    services.xserver.libinput.enable = mkDefault true;
-
-    security.pam.services.mate-screensaver.unixAuth = true;
-
-    xdg.portal.configPackages = mkDefault [ pkgs.mate.mate-desktop ];
-
-    environment.pathsToLink = [ "/share" ];
-  };
-
+  config = mkMerge [
+    (mkIf (cfg.enable || cfg.enableWaylandSession) {
+      services.xserver.displayManager.sessionPackages = [
+        pkgs.mate.mate-session-manager
+      ];
+
+      # Debugging
+      environment.sessionVariables.MATE_SESSION_DEBUG = mkIf cfg.debug "1";
+
+      environment.systemPackages = utils.removePackagesByName
+        (pkgs.mate.basePackages ++
+        pkgs.mate.extraPackages ++
+        [
+          (pkgs.mate.caja-with-extensions.override {
+            extensions = cfg.extraCajaExtensions;
+          })
+          (pkgs.mate.mate-panel-with-applets.override {
+            applets = cfg.extraPanelApplets;
+          })
+          pkgs.desktop-file-utils
+          pkgs.glib
+          pkgs.gtk3.out
+          pkgs.shared-mime-info
+          pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
+          pkgs.yelp # for 'Contents' in 'Help' menus
+        ])
+        config.environment.mate.excludePackages;
+
+      programs.dconf.enable = true;
+      # Shell integration for VTE terminals
+      programs.bash.vteIntegration = mkDefault true;
+      programs.zsh.vteIntegration = mkDefault true;
+
+      # Mate uses this for printing
+      programs.system-config-printer.enable = (mkIf config.services.printing.enable (mkDefault true));
+
+      services.gnome.at-spi2-core.enable = true;
+      services.gnome.gnome-keyring.enable = true;
+      services.udev.packages = [ pkgs.mate.mate-settings-daemon ];
+      services.gvfs.enable = true;
+      services.upower.enable = config.powerManagement.enable;
+      services.xserver.libinput.enable = mkDefault true;
+
+      security.pam.services.mate-screensaver.unixAuth = true;
+
+      xdg.portal.configPackages = mkDefault [ pkgs.mate.mate-desktop ];
+
+      environment.pathsToLink = [ "/share" ];
+    })
+    (mkIf cfg.enableWaylandSession {
+      programs.wayfire.enable = true;
+      programs.wayfire.plugins = [ pkgs.wayfirePlugins.firedecor ];
+
+      environment.sessionVariables.NIX_GSETTINGS_OVERRIDES_DIR = "${pkgs.mate.mate-gsettings-overrides}/share/gsettings-schemas/nixos-gsettings-overrides/glib-2.0/schemas";
+
+      environment.systemPackages = [ pkgs.mate.mate-wayland-session ];
+      services.xserver.displayManager.sessionPackages = [ pkgs.mate.mate-wayland-session ];
+    })
+  ];
 }
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index c884b4487e24..f516a29fb5db 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -336,7 +336,7 @@ in
         serif = [ "Noto Serif" ];
       };
 
-      programs.gnupg.agent.pinentryPackage = pkgs.pinentry-qt;
+      programs.gnupg.agent.pinentryPackage = mkDefault pkgs.pinentry-qt;
       programs.ssh.askPassword = mkDefault "${pkgs.plasma5Packages.ksshaskpass.out}/bin/ksshaskpass";
 
       # Enable helpful DBus services.
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 6bc964f4c6ed..3ba27b201507 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -131,7 +131,7 @@ in
         xfdesktop
       ] ++ optional cfg.enableScreensaver xfce4-screensaver) excludePackages;
 
-    programs.gnupg.agent.pinentryPackage = pkgs.pinentry-gtk2;
+    programs.gnupg.agent.pinentryPackage = mkDefault pkgs.pinentry-gtk2;
     programs.xfconf.enable = true;
     programs.thunar.enable = true;
 
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index 5b7f4bc58d80..a315a3ebf322 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -1,19 +1,24 @@
 { config, lib, pkgs, ... }:
 
-with lib;
 let
   xcfg = config.services.xserver;
   dmcfg = xcfg.displayManager;
   cfg = dmcfg.sddm;
   xEnv = config.systemd.services.display-manager.environment;
 
-  sddm = cfg.package.override(old: {
+  sddm = cfg.package.override (old: {
     withWayland = cfg.wayland.enable;
-    extraPackages = old.extraPackages or [] ++ cfg.extraPackages;
+    extraPackages = old.extraPackages or [ ] ++ cfg.extraPackages;
   });
 
   iniFmt = pkgs.formats.ini { };
 
+  inherit (lib)
+    concatMapStrings concatStringsSep getExe
+    attrNames getAttr optionalAttrs optionalString
+    mkRemovedOptionModule mkRenamedOptionModule mkIf mkEnableOption mkOption mkPackageOption types
+    ;
+
   xserverWrapper = pkgs.writeShellScript "xserver-wrapper" ''
     ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
     exec systemd-cat -t xserver-wrapper ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@"
@@ -38,12 +43,21 @@ let
       DefaultSession = optionalString (dmcfg.defaultSession != null) "${dmcfg.defaultSession}.desktop";
 
       DisplayServer = if cfg.wayland.enable then "wayland" else "x11";
+    } // optionalAttrs (cfg.wayland.compositor == "kwin") {
+      GreeterEnvironment = concatStringsSep " " [
+        "LANG=C.UTF-8"
+        "QT_WAYLAND_SHELL_INTEGRATION=layer-shell"
+      ];
+      InputMethod = ""; # needed if we are using --inputmethod with kwin
     };
 
     Theme = {
       Current = cfg.theme;
       ThemeDir = "/run/current-system/sw/share/sddm/themes";
       FacesDir = "/run/current-system/sw/share/sddm/faces";
+    } // optionalAttrs (cfg.theme == "breeze") {
+      CursorTheme = "breeze_cursors";
+      CursorSize = 24;
     };
 
     Users = {
@@ -69,7 +83,7 @@ let
       SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
       CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
     };
-  } // lib.optionalAttrs dmcfg.autoLogin.enable {
+  } // optionalAttrs dmcfg.autoLogin.enable {
     Autologin = {
       User = dmcfg.autoLogin.user;
       Session = autoLoginSessionName;
@@ -83,6 +97,34 @@ let
   autoLoginSessionName =
     "${dmcfg.sessionData.autologinSession}.desktop";
 
+  compositorCmds = {
+    kwin = concatStringsSep " " [
+      "${lib.getBin pkgs.kdePackages.kwin}/bin/kwin_wayland"
+      "--no-global-shortcuts"
+      "--no-kactivities"
+      "--no-lockscreen"
+      "--locale1"
+    ];
+    # This is basically the upstream default, but with Weston referenced by full path
+    # and the configuration generated from NixOS options.
+    weston =
+      let
+        westonIni = (pkgs.formats.ini { }).generate "weston.ini" {
+          libinput = {
+            enable-tap = xcfg.libinput.mouse.tapping;
+            left-handed = xcfg.libinput.mouse.leftHanded;
+          };
+          keyboard = {
+            keymap_model = xcfg.xkb.model;
+            keymap_layout = xcfg.xkb.layout;
+            keymap_variant = xcfg.xkb.variant;
+            keymap_options = xcfg.xkb.options;
+          };
+        };
+      in
+      "${getExe pkgs.weston} --shell=kiosk -c ${westonIni}";
+  };
+
 in
 {
   imports = [
@@ -111,7 +153,7 @@ in
         '';
       };
 
-      package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] {};
+      package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] { };
 
       enableHidpi = mkOption {
         type = types.bool;
@@ -145,7 +187,7 @@ in
 
       extraPackages = mkOption {
         type = types.listOf types.package;
-        default = [];
+        default = [ ];
         defaultText = "[]";
         description = lib.mdDoc ''
           Extra Qt plugins / QML libraries to add to the environment.
@@ -206,24 +248,16 @@ in
       wayland = {
         enable = mkEnableOption "experimental Wayland support";
 
+        compositor = mkOption {
+          description = lib.mdDoc "The compositor to use: ${lib.concatStringsSep ", " (builtins.attrNames compositorCmds)}";
+          type = types.enum (builtins.attrNames compositorCmds);
+          default = "weston";
+        };
+
         compositorCommand = mkOption {
           type = types.str;
           internal = true;
-
-          # This is basically the upstream default, but with Weston referenced by full path
-          # and the configuration generated from NixOS options.
-          default = let westonIni = (pkgs.formats.ini {}).generate "weston.ini" {
-              libinput = {
-                enable-tap = xcfg.libinput.mouse.tapping;
-                left-handed = xcfg.libinput.mouse.leftHanded;
-              };
-              keyboard = {
-                keymap_model = xcfg.xkb.model;
-                keymap_layout = xcfg.xkb.layout;
-                keymap_variant = xcfg.xkb.variant;
-                keymap_options = xcfg.xkb.options;
-              };
-            }; in "${pkgs.weston}/bin/weston --shell=kiosk -c ${westonIni}";
+          default = compositorCmds.${cfg.wayland.compositor};
           description = lib.mdDoc "Command used to start the selected compositor";
         };
       };
@@ -247,8 +281,6 @@ in
       }
     ];
 
-    services.xserver.displayManager.job.execCmd = "exec /run/current-system/sw/bin/sddm";
-
     security.pam.services = {
       sddm.text = ''
         auth      substack      login
@@ -293,30 +325,41 @@ in
       uid = config.ids.uids.sddm;
     };
 
-    environment.etc."sddm.conf".source = cfgFile;
-    environment.pathsToLink = [
-      "/share/sddm"
-    ];
+    environment = {
+      etc."sddm.conf".source = cfgFile;
+      pathsToLink = [
+        "/share/sddm"
+      ];
+      systemPackages = [ sddm ];
+    };
 
     users.groups.sddm.gid = config.ids.gids.sddm;
 
-    environment.systemPackages = [ sddm ];
-    services.dbus.packages = [ sddm ];
-    systemd.tmpfiles.packages = [ sddm ];
-
-    # We're not using the upstream unit, so copy these: https://github.com/sddm/sddm/blob/develop/services/sddm.service.in
-    systemd.services.display-manager.after = [
-      "systemd-user-sessions.service"
-      "getty@tty7.service"
-      "plymouth-quit.service"
-      "systemd-logind.service"
-    ];
-    systemd.services.display-manager.conflicts = [
-      "getty@tty7.service"
-    ];
+    services = {
+      dbus.packages = [ sddm ];
+      xserver = {
+        displayManager.job.execCmd = "exec /run/current-system/sw/bin/sddm";
+        # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
+        tty = null;
+        display = null;
+      };
+    };
 
-    # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
-    services.xserver.tty = null;
-    services.xserver.display = null;
+    systemd = {
+      tmpfiles.packages = [ sddm ];
+
+      # We're not using the upstream unit, so copy these: https://github.com/sddm/sddm/blob/develop/services/sddm.service.in
+      services.display-manager = {
+        after = [
+          "systemd-user-sessions.service"
+          "getty@tty7.service"
+          "plymouth-quit.service"
+          "systemd-logind.service"
+        ];
+        conflicts = [
+          "getty@tty7.service"
+        ];
+      };
+    };
   };
 }
diff --git a/nixos/modules/services/x11/window-managers/nimdow.nix b/nixos/modules/services/x11/window-managers/nimdow.nix
index de3192876024..9cee4bb271a5 100644
--- a/nixos/modules/services/x11/window-managers/nimdow.nix
+++ b/nixos/modules/services/x11/window-managers/nimdow.nix
@@ -8,16 +8,23 @@ in
 {
   options = {
     services.xserver.windowManager.nimdow.enable = mkEnableOption (lib.mdDoc "nimdow");
+    services.xserver.windowManager.nimdow.package = mkOption {
+      type = types.package;
+      default = pkgs.nimdow;
+      defaultText = "pkgs.nimdow";
+      description = lib.mdDoc "nimdow package to use";
+    };
   };
 
+
   config = mkIf cfg.enable {
     services.xserver.windowManager.session = singleton {
       name = "nimdow";
       start = ''
-        ${pkgs.nimdow}/bin/nimdow &
+        ${cfg.package}/bin/nimdow &
         waitPID=$!
       '';
     };
-    environment.systemPackages = [ pkgs.nimdow ];
+    environment.systemPackages = [ cfg.package pkgs.st ];
   };
 }
diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix
index c35446bf405b..2962f2851fa9 100644
--- a/nixos/modules/services/x11/window-managers/xmonad.nix
+++ b/nixos/modules/services/x11/window-managers/xmonad.nix
@@ -37,7 +37,7 @@ let
 
   xmonad = if (cfg.config != null) then xmonad-config else xmonad-vanilla;
 in {
-  meta.maintainers = with maintainers; [ lassulus xaverdh ivanbrennan ];
+  meta.maintainers = with maintainers; [ lassulus xaverdh ivanbrennan slotThe ];
 
   options = {
     services.xserver.windowManager.xmonad = {
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 3d7474e18263..4e0235f9ad1d 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -749,7 +749,7 @@ in
     boot.kernel.sysctl."fs.inotify.max_user_instances" = mkDefault 524288;
     boot.kernel.sysctl."fs.inotify.max_user_watches" = mkDefault 524288;
 
-    programs.gnupg.agent.pinentryPackage = lib.mkDefault pkgs.pinentry-gnome3;
+    programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3;
 
     systemd.defaultUnit = mkIf cfg.autorun "graphical.target";
 
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index b0ac857feb4b..950cff386d02 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -233,7 +233,9 @@ in
         symlinks because modprobe only supports one directory.
       '';
       # Convert the list of path to only one path.
-      apply = pkgs.aggregateModules;
+      apply = let
+        kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
+      in modules: (pkgs.aggregateModules modules).override { name = kernel-name + "-modules"; };
     };
 
     system.requiredKernelConfig = mkOption {
@@ -299,6 +301,7 @@ in
             "usbhid"
             "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
             "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft" "hid_cherry"
+            "hid_corsair"
 
           ] ++ optionals pkgs.stdenv.hostPlatform.isx86 [
             # Misc. x86 keyboard stuff.
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 86a3875e2c67..3020734783e7 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -982,8 +982,10 @@ in
         }
         { assertion = config.boot.initrd.systemd.enable -> !luks.fido2Support;
           message = ''
-            systemd stage 1 does not support configuring FIDO2 unlocking through `boot.initrd.luks.devices.<name>.fido2`.
-            Use systemd-cryptenroll(1) to configure FIDO2 support.
+            systemd stage 1 does not support configuring FIDO2 unlocking through `boot.initrd.luks.fido2Support`.
+            Use systemd-cryptenroll(1) to configure FIDO2 support, and set
+            `boot.initrd.luks.devices.''${DEVICE}.crypttabExtraOpts` as appropriate per crypttab(5)
+            (e.g. `fido2-device=auto`).
           '';
         }
         # TODO
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index b6b0f64b94c8..9b0d750d12ce 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -729,8 +729,8 @@ let
         (assertInt "FirewallMark")
         (assertRange "FirewallMark" 1 4294967295)
         (assertInt "Priority")
-        (assertPort "SourcePort")
-        (assertPort "DestinationPort")
+        (assertPortOrPortRange "SourcePort")
+        (assertPortOrPortRange "DestinationPort")
         (assertValueOneOf "InvertRule" boolValues)
         (assertValueOneOf "Family" ["ipv4" "ipv6" "both"])
         (assertInt "SuppressPrefixLength")
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 90a74c0ac578..02a3f5113cc0 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -13,15 +13,11 @@ let
 
   kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
 
-  modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
-  firmware = config.hardware.firmware;
-
-
   # Determine the set of modules that we need to mount the root FS.
   modulesClosure = pkgs.makeModulesClosure {
     rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
-    kernel = modulesTree;
-    firmware = firmware;
+    kernel = config.system.modulesTree;
+    firmware = config.hardware.firmware;
     allowMissing = false;
   };
 
@@ -688,7 +684,7 @@ in
 
   config = mkIf config.boot.initrd.enable {
     assertions = [
-      { assertion = any (fs: fs.mountPoint == "/") fileSystems;
+      { assertion = !config.boot.initrd.systemd.enable -> any (fs: fs.mountPoint == "/") fileSystems;
         message = "The ‘fileSystems’ option does not specify your root file system.";
       }
       { assertion = let inherit (config.boot) resumeDevice; in
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index f83837fbc6d4..e4f61db0cd02 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -91,13 +91,11 @@ let
   };
 
   kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
-  modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
-  firmware = config.hardware.firmware;
   # Determine the set of modules that we need to mount the root FS.
   modulesClosure = pkgs.makeModulesClosure {
     rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
-    kernel = modulesTree;
-    firmware = firmware;
+    kernel = config.system.modulesTree;
+    firmware = config.hardware.firmware;
     allowMissing = false;
   };
 
@@ -212,6 +210,19 @@ in {
       default = [];
     };
 
+    root = lib.mkOption {
+      type = lib.types.enum [ "fstab" "gpt-auto" ];
+      default = "fstab";
+      example = "gpt-auto";
+      description = ''
+        Controls how systemd will interpret the root FS in initrd. See
+        {manpage}`kernel-command-line(7)`. NixOS currently does not
+        allow specifying the root file system itself this
+        way. Instead, the `fstab` value is used in order to interpret
+        the root file system specified with the `fileSystems` option.
+      '';
+    };
+
     emergencyAccess = mkOption {
       type = with types; oneOf [ bool (nullOr (passwdEntry str)) ];
       description = lib.mdDoc ''
@@ -342,7 +353,12 @@ in {
   };
 
   config = mkIf (config.boot.initrd.enable && cfg.enable) {
-    assertions = map (name: {
+    assertions = [
+      {
+        assertion = cfg.root == "fstab" -> any (fs: fs.mountPoint == "/") (builtins.attrValues config.fileSystems);
+        message = "The ‘fileSystems’ option does not specify your root file system.";
+      }
+    ] ++ map (name: {
       assertion = lib.attrByPath name (throw "impossible") config.boot.initrd == "";
       message = ''
         systemd stage 1 does not support 'boot.initrd.${lib.concatStringsSep "." name}'. Please
@@ -371,7 +387,12 @@ in {
       "autofs"
       # systemd-cryptenroll
     ] ++ lib.optional cfg.enableTpm2 "tpm-tis"
-    ++ lib.optional (cfg.enableTpm2 && !(pkgs.stdenv.hostPlatform.isRiscV64 || pkgs.stdenv.hostPlatform.isArmv7)) "tpm-crb";
+    ++ lib.optional (cfg.enableTpm2 && !(pkgs.stdenv.hostPlatform.isRiscV64 || pkgs.stdenv.hostPlatform.isArmv7)) "tpm-crb"
+    ++ lib.optional cfg.package.withEfi "efivarfs";
+
+    boot.kernelParams = [
+      "root=${config.boot.initrd.systemd.root}"
+    ] ++ lib.optional (config.boot.resumeDevice != "") "resume=${config.boot.resumeDevice}";
 
     boot.initrd.systemd = {
       initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package];
@@ -495,7 +516,7 @@ in {
               case $o in
                   init=*)
                       IFS== read -r -a initParam <<< "$o"
-                      closure="$(dirname "''${initParam[1]}")"
+                      closure="''${initParam[1]}"
                       ;;
               esac
           done
@@ -506,6 +527,13 @@ in {
             exit 1
           fi
 
+          # Resolve symlinks in the init parameter. We need this for some boot loaders
+          # (e.g. boot.loader.generationsDir).
+          closure="$(chroot /sysroot ${pkgs.coreutils}/bin/realpath "$closure")"
+
+          # Assume the directory containing the init script is the closure.
+          closure="$(dirname "$closure")"
+
           # If we are not booting a NixOS closure (e.g. init=/bin/sh),
           # we don't know what root to prepare so we don't do anything
           if ! [ -x "/sysroot$(readlink "/sysroot$closure/prepare-root" || echo "$closure/prepare-root")" ]; then
@@ -554,7 +582,5 @@ in {
         serviceConfig.Type = "oneshot";
       };
     };
-
-    boot.kernelParams = lib.mkIf (config.boot.resumeDevice != "") [ "resume=${config.boot.resumeDevice}" ];
   };
 }
diff --git a/nixos/modules/system/boot/timesyncd.nix b/nixos/modules/system/boot/timesyncd.nix
index 2666e4cd6b28..ef17c1481abb 100644
--- a/nixos/modules/system/boot/timesyncd.nix
+++ b/nixos/modules/system/boot/timesyncd.nix
@@ -21,6 +21,9 @@ with lib;
         type = types.listOf types.str;
         description = lib.mdDoc ''
           The set of NTP servers from which to synchronise.
+          Note if this is set to an empty list, the defaults systemd itself is
+          compiled with ({0..4}.nixos.pool.ntp.org) apply,
+          In case you want to disable timesyncd altogether, use the `enable` option.
         '';
       };
       extraConfig = mkOption {
diff --git a/nixos/modules/system/boot/uki.nix b/nixos/modules/system/boot/uki.nix
index 0965b887c12e..c8d3c2f6605f 100644
--- a/nixos/modules/system/boot/uki.nix
+++ b/nixos/modules/system/boot/uki.nix
@@ -7,8 +7,6 @@ let
   inherit (pkgs.stdenv.hostPlatform) efiArch;
 
   format = pkgs.formats.ini { };
-  ukifyConfig = format.generate "ukify.conf" cfg.settings;
-
 in
 
 {
@@ -48,6 +46,15 @@ in
           contains and how it is built.
         '';
       };
+
+      configFile = lib.mkOption {
+        type = lib.types.path;
+        description = lib.mdDoc ''
+          The configuration file passed to {manpage}`ukify(1)` to create the UKI.
+
+          By default this configuration file is created from {option}`boot.uki.settings`.
+        '';
+      };
     };
 
     system.boot.loader.ukiFile = lib.mkOption {
@@ -80,6 +87,8 @@ in
       };
     };
 
+    boot.uki.configFile = lib.mkOptionDefault (format.generate "ukify.conf" cfg.settings);
+
     system.boot.loader.ukiFile =
       let
         name = config.boot.uki.name;
@@ -92,7 +101,7 @@ in
     system.build.uki = pkgs.runCommand config.system.boot.loader.ukiFile { } ''
       mkdir -p $out
       ${pkgs.buildPackages.systemdUkify}/lib/systemd/ukify build \
-        --config=${ukifyConfig} \
+        --config=${cfg.configFile} \
         --output="$out/${config.system.boot.loader.ukiFile}"
     '';
 
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 6aa718c1975d..50a54a006415 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -42,8 +42,10 @@ let
         # Otherwise we get errors on the terminal because bash tries to
         # setup things like job control.
         # Note: calling bash explicitly here instead of sh makes sure that
-        # we can also run non-NixOS guests during tests.
-        PS1= exec /usr/bin/env bash --norc /dev/hvc0
+        # we can also run non-NixOS guests during tests. This, however, is
+        # mostly futureproofing as the test instrumentation is still very
+        # tightly coupled to NixOS.
+        PS1= exec ${pkgs.coreutils}/bin/env bash --norc /dev/hvc0
       '';
       serviceConfig.KillSignal = "SIGHUP";
   };
@@ -121,7 +123,9 @@ in
           }
         ];
 
-        contents."/usr/bin/env".source = "${pkgs.coreutils}/bin/env";
+        storePaths = [
+          "${pkgs.coreutils}/bin/env"
+        ];
       })
     ];
 
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index c7fe1bed5159..77730178422c 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -79,6 +79,10 @@ in
       serviceConfig.StandardOutput = "journal+console";
     };
 
+    # Amazon-issued AMIs include the SSM Agent by default, so we do the same.
+    # https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html
+    services.amazon-ssm-agent.enable = true;
+
     # Allow root logins only using the SSH key that the user specified
     # at instance creation time.
     services.openssh.enable = true;
diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix
index d4fa707b2dd5..5db3a336f85d 100644
--- a/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixos/modules/virtualisation/nixos-containers.nix
@@ -509,6 +509,12 @@ in
                                 for details).
                               '';
                             }
+                            {
+                              assertion = !lib.strings.hasInfix "_" name;
+                              message = ''
+                                Names containing underscores are not allowed in nixos-containers. Please rename the container '${name}'
+                              '';
+                            }
                           ];
                         };
                       };
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index ac64b85dd486..2c08fdba6c98 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -290,8 +290,8 @@ in {
   activation-etc-overlay-mutable = runTest ./activation/etc-overlay-mutable.nix;
   activation-etc-overlay-immutable = runTest ./activation/etc-overlay-immutable.nix;
   activation-perlless = runTest ./activation/perlless.nix;
-  etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
-  etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
+  etcd = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./etcd/etcd.nix {};
+  etcd-cluster = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./etcd/etcd-cluster.nix {};
   etebase-server = handleTest ./etebase-server.nix {};
   etesync-dav = handleTest ./etesync-dav.nix {};
   evcc = handleTest ./evcc.nix {};
@@ -464,7 +464,7 @@ in {
   keymap = handleTest ./keymap.nix {};
   knot = handleTest ./knot.nix {};
   komga = handleTest ./komga.nix {};
-  krb5 = discoverTests (import ./krb5 {});
+  krb5 = discoverTests (import ./krb5);
   ksm = handleTest ./ksm.nix {};
   kthxbye = handleTest ./kthxbye.nix {};
   kubernetes = handleTestOn ["x86_64-linux"] ./kubernetes {};
@@ -513,6 +513,7 @@ in {
   mastodon = discoverTests (import ./web-apps/mastodon { inherit handleTestOn; });
   pixelfed = discoverTests (import ./web-apps/pixelfed { inherit handleTestOn; });
   mate = handleTest ./mate.nix {};
+  mate-wayland = handleTest ./mate-wayland.nix {};
   matter-server = handleTest ./matter-server.nix {};
   matomo = handleTest ./matomo.nix {};
   matrix-appservice-irc = handleTest ./matrix/appservice-irc.nix {};
@@ -528,6 +529,7 @@ in {
   memcached = handleTest ./memcached.nix {};
   merecat = handleTest ./merecat.nix {};
   metabase = handleTest ./metabase.nix {};
+  mihomo = handleTest ./mihomo.nix {};
   mindustry = handleTest ./mindustry.nix {};
   minecraft = handleTest ./minecraft.nix {};
   minecraft-server = handleTest ./minecraft-server.nix {};
@@ -560,6 +562,7 @@ in {
   munin = handleTest ./munin.nix {};
   mutableUsers = handleTest ./mutable-users.nix {};
   mxisd = handleTest ./mxisd.nix {};
+  mycelium = handleTest ./mycelium {};
   mympd = handleTest ./mympd.nix {};
   mysql = handleTest ./mysql/mysql.nix {};
   mysql-autobackup = handleTest ./mysql/mysql-autobackup.nix {};
@@ -579,6 +582,7 @@ in {
   ndppd = handleTest ./ndppd.nix {};
   nebula = handleTest ./nebula.nix {};
   netbird = handleTest ./netbird.nix {};
+  nimdow = handleTest ./nimdow.nix {};
   neo4j = handleTest ./neo4j.nix {};
   netdata = handleTest ./netdata.nix {};
   networking.networkd = handleTest ./networking.nix { networkd = true; };
@@ -613,6 +617,7 @@ in {
   nginx-variants = handleTest ./nginx-variants.nix {};
   nifi = handleTestOn ["x86_64-linux"] ./web-apps/nifi.nix {};
   nitter = handleTest ./nitter.nix {};
+  nix-config = handleTest ./nix-config.nix {};
   nix-ld = handleTest ./nix-ld.nix {};
   nix-serve = handleTest ./nix-serve.nix {};
   nix-serve-ssh = handleTest ./nix-serve-ssh.nix {};
@@ -683,6 +688,7 @@ in {
   peering-manager = handleTest ./web-apps/peering-manager.nix {};
   peertube = handleTestOn ["x86_64-linux"] ./web-apps/peertube.nix {};
   peroxide = handleTest ./peroxide.nix {};
+  pg_anonymizer = handleTest ./pg_anonymizer.nix {};
   pgadmin4 = handleTest ./pgadmin4.nix {};
   pgbouncer = handleTest ./pgbouncer.nix {};
   pgjwt = handleTest ./pgjwt.nix {};
@@ -727,6 +733,7 @@ in {
   pppd = handleTest ./pppd.nix {};
   predictable-interface-names = handleTest ./predictable-interface-names.nix {};
   pretalx = runTest ./web-apps/pretalx.nix;
+  pretix = runTest ./web-apps/pretix.nix;
   printing-socket = handleTest ./printing.nix { socket = true; };
   printing-service = handleTest ./printing.nix { socket = false; };
   privoxy = handleTest ./privoxy.nix {};
diff --git a/nixos/tests/budgie.nix b/nixos/tests/budgie.nix
index ca898bba1bc4..5228e869b056 100644
--- a/nixos/tests/budgie.nix
+++ b/nixos/tests/budgie.nix
@@ -29,6 +29,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   testScript = { nodes, ... }:
     let
       user = nodes.machine.users.users.alice;
+      env = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${toString user.uid}/bus DISPLAY=:0";
+      su = command: "su - ${user.name} -c '${env} ${command}'";
     in
     ''
       with subtest("Wait for login"):
@@ -47,21 +49,46 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}")
 
       with subtest("Check if Budgie session components actually start"):
-          machine.wait_until_succeeds("pgrep budgie-daemon")
+          for i in ["budgie-daemon", "budgie-panel", "budgie-wm", "budgie-desktop-view", "gsd-media-keys"]:
+              machine.wait_until_succeeds(f"pgrep -f {i}")
+          # We don't check xwininfo for budgie-wm.
+          # See https://github.com/NixOS/nixpkgs/pull/216737#discussion_r1155312754
           machine.wait_for_window("budgie-daemon")
-          machine.wait_until_succeeds("pgrep budgie-panel")
           machine.wait_for_window("budgie-panel")
-          # We don't check xwininfo for this one.
-          # See https://github.com/NixOS/nixpkgs/pull/216737#discussion_r1155312754
-          machine.wait_until_succeeds("pgrep budgie-wm")
 
-      with subtest("Open MATE terminal"):
-          machine.succeed("su - ${user.name} -c 'DISPLAY=:0 mate-terminal >&2 &'")
-          machine.wait_for_window("Terminal")
+      with subtest("Check if various environment variables are set"):
+          cmd = "xargs --null --max-args=1 echo < /proc/$(pgrep -xf /run/current-system/sw/bin/budgie-wm)/environ"
+          machine.succeed(f"{cmd} | grep 'XDG_CURRENT_DESKTOP' | grep 'Budgie:GNOME'")
+          machine.succeed(f"{cmd} | grep 'BUDGIE_PLUGIN_DATADIR' | grep '${pkgs.budgie.budgie-desktop-with-plugins.pname}'")
+
+      with subtest("Open run dialog"):
+          machine.send_key("alt-f2")
+          machine.wait_for_window("budgie-run-dialog")
+          machine.sleep(2)
+          machine.screenshot("run_dialog")
+          machine.send_key("esc")
+
+      with subtest("Open Budgie Control Center"):
+          machine.succeed("${su "budgie-control-center >&2 &"}")
+          machine.wait_for_window("Budgie Control Center")
+
+      with subtest("Lock the screen"):
+          machine.succeed("${su "budgie-screensaver-command -l >&2 &"}")
+          machine.wait_until_succeeds("${su "budgie-screensaver-command -q"} | grep 'The screensaver is active'")
+          machine.sleep(2)
+          machine.send_chars("${user.password}", delay=0.5)
+          machine.screenshot("budgie_screensaver")
+          machine.send_chars("\n")
+          machine.wait_until_succeeds("${su "budgie-screensaver-command -q"} | grep 'The screensaver is inactive'")
+          machine.sleep(2)
+
+      with subtest("Open GNOME terminal"):
+          machine.succeed("${su "gnome-terminal"}")
+          machine.wait_for_window("${user.name}@machine: ~")
 
-      with subtest("Check if budgie-wm has ever coredumped"):
-          machine.fail("coredumpctl --json=short | grep budgie-wm")
-          machine.sleep(20)
+      with subtest("Check if Budgie has ever coredumped"):
+          machine.fail("coredumpctl --json=short | grep budgie")
+          machine.sleep(10)
           machine.screenshot("screen")
     '';
 })
diff --git a/nixos/tests/drawterm.nix b/nixos/tests/drawterm.nix
index 1d444bb55433..3594343853c0 100644
--- a/nixos/tests/drawterm.nix
+++ b/nixos/tests/drawterm.nix
@@ -38,11 +38,24 @@ let
         def drawterm_running():
             machine.succeed("pgrep drawterm")
 
+        # cage is a bit wonky here.
+        # it seems to lag behind drawing
+        # and somehow needs a single input character
+        # in order to get the first prompt to show up.
+        # This is not present in any other compositor
+        # as far as I know, and after spending a couple
+        # hours with the upstream source trying to deduce
+        # how to perhaps fix it, I figured just polling is OK.
+        @polling_condition
+        def cpu_shown_up():
+            machine.send_chars(".")
+            machine.wait_for_text("cpu", 1)
+
         start_all()
 
         machine.wait_for_unit("graphical.target")
         drawterm_running.wait() # type: ignore[union-attr]
-        machine.wait_for_text("cpu")
+        cpu_shown_up.wait() # type: ignore[union-attr]
         machine.send_chars("cpu\n")
         machine.wait_for_text("auth")
         machine.send_chars("cpu\n")
diff --git a/nixos/tests/etcd-cluster.nix b/nixos/tests/etcd/etcd-cluster.nix
index c77c0dd73c25..734d56dbc223 100644
--- a/nixos/tests/etcd-cluster.nix
+++ b/nixos/tests/etcd/etcd-cluster.nix
@@ -1,6 +1,6 @@
 # This test runs simple etcd cluster
 
-import ./make-test-python.nix ({ pkgs, ... } : let
+import ../make-test-python.nix ({ pkgs, ... } : let
 
   runWithOpenSSL = file: cmd: pkgs.runCommand file {
     buildInputs = [ pkgs.openssl ];
diff --git a/nixos/tests/etcd.nix b/nixos/tests/etcd/etcd.nix
index 79857778ae1b..a32d0f9a55d1 100644
--- a/nixos/tests/etcd.nix
+++ b/nixos/tests/etcd/etcd.nix
@@ -1,6 +1,6 @@
 # This test runs simple etcd node
 
-import ./make-test-python.nix ({ pkgs, ... } : {
+import ../make-test-python.nix ({ pkgs, ... } : {
   name = "etcd";
 
   meta = with pkgs.lib.maintainers; {
diff --git a/nixos/tests/freetube.nix b/nixos/tests/freetube.nix
index faa534938227..10f0773cb884 100644
--- a/nixos/tests/freetube.nix
+++ b/nixos/tests/freetube.nix
@@ -40,4 +40,4 @@ let
       '';
     });
 in
-builtins.mapAttrs (k: v: mkTest k v { }) tests
+builtins.mapAttrs (k: v: mkTest k v) tests
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index 296aa9ba68b9..6de287f63e08 100644
--- a/nixos/tests/hibernate.nix
+++ b/nixos/tests/hibernate.nix
@@ -24,8 +24,8 @@ makeTest {
       virtualisation.useNixStoreImage = true;
 
       swapDevices = lib.mkOverride 0 [ { device = "/dev/vdc"; options = [ "x-systemd.makefs" ]; } ];
-      boot.resumeDevice = "/dev/vdc";
       boot.initrd.systemd.enable = systemdStage1;
+      virtualisation.useEFIBoot = true;
     };
   };
 
diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix
index 474a621c5ce9..32bc5396a164 100644
--- a/nixos/tests/incus/default.nix
+++ b/nixos/tests/incus/default.nix
@@ -14,6 +14,7 @@
   openvswitch = import ./openvswitch.nix { inherit system pkgs; };
   preseed = import ./preseed.nix { inherit system pkgs; };
   socket-activated = import ./socket-activated.nix { inherit system pkgs; };
+  storage = import ./storage.nix { inherit system pkgs; };
   ui = import ./ui.nix {inherit system pkgs;};
   virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { inherit system pkgs; };
 }
diff --git a/nixos/tests/incus/storage.nix b/nixos/tests/incus/storage.nix
new file mode 100644
index 000000000000..190f4f7451c2
--- /dev/null
+++ b/nixos/tests/incus/storage.nix
@@ -0,0 +1,46 @@
+import ../make-test-python.nix (
+  { pkgs, lib, ... }:
+
+  {
+    name = "incus-storage";
+
+    meta = {
+      maintainers = lib.teams.lxc.members;
+    };
+
+    nodes.machine =
+      { lib, ... }:
+      {
+        boot.supportedFilesystems = [ "zfs" ];
+        boot.zfs.forceImportRoot = false;
+        environment.systemPackages = [ pkgs.parted ];
+        networking.hostId = "01234567";
+        networking.nftables.enable = true;
+
+        virtualisation = {
+          emptyDiskImages = [ 2048 ];
+          incus.enable = true;
+        };
+      };
+
+    testScript = ''
+      machine.wait_for_unit("incus.service")
+
+      with subtest("Verify zfs pool created and usable"):
+        machine.succeed(
+            "zpool status",
+            "parted --script /dev/vdb mklabel gpt",
+            "zpool create zfs_pool /dev/vdb",
+        )
+
+        machine.succeed("incus storage create zfs_pool zfs source=zfs_pool/incus")
+        machine.succeed("zfs list zfs_pool/incus")
+        machine.succeed("incus storage volume create zfs_pool test_fs --type filesystem")
+        machine.succeed("incus storage volume create zfs_pool test_vol --type block")
+        machine.succeed("incus storage show zfs_pool")
+        machine.succeed("incus storage volume list zfs_pool")
+        machine.succeed("incus storage volume show zfs_pool test_fs")
+        machine.succeed("incus storage volume show zfs_pool test_vol")
+    '';
+  }
+)
diff --git a/nixos/tests/installer-systemd-stage-1.nix b/nixos/tests/installer-systemd-stage-1.nix
index 662017935412..d10256d91d7f 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -37,6 +37,7 @@
     clevisLuksFallback
     clevisZfs
     clevisZfsFallback
+    gptAutoRoot
     ;
 
 }
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 97bb7f8def59..1de886d6a0d1 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -82,6 +82,7 @@ let
   testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi, grubIdentifier
                   , postInstallCommands, preBootCommands, postBootCommands, extraConfig
                   , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest
+                  , disableFileSystems
                   }:
     let
       qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
@@ -163,7 +164,7 @@ let
       ${createPartitions}
 
       with subtest("Create the NixOS configuration"):
-          machine.succeed("nixos-generate-config --root /mnt")
+          machine.succeed("nixos-generate-config ${optionalString disableFileSystems "--no-filesystems"} --root /mnt")
           machine.succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2")
           machine.copy_from_host(
               "${ makeConfig {
@@ -433,6 +434,7 @@ let
     , testFlakeSwitch ? false
     , clevisTest ? false
     , clevisFallbackTest ? false
+    , disableFileSystems ? false
     }:
     makeTest {
       inherit enableOCR;
@@ -541,7 +543,8 @@ let
       testScript = testScriptFun {
         inherit bootLoader createPartitions postInstallCommands preBootCommands postBootCommands
                 grubDevice grubIdentifier grubUseEfi extraConfig
-                testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest;
+                testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest
+                disableFileSystems;
       };
     };
 
@@ -1414,4 +1417,39 @@ in {
       };
     };
   };
+
+  gptAutoRoot = let
+    rootPartType = {
+      ia32 = "44479540-F297-41B2-9AF7-D131D5F0458A";
+      x64 = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709";
+      arm = "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3";
+      aa64 = "B921B045-1DF0-41C3-AF44-4C6F280D3FAE";
+    }.${pkgs.stdenv.hostPlatform.efiArch};
+  in makeInstallerTest "gptAutoRoot" {
+    disableFileSystems = true;
+    createPartitions = ''
+      machine.succeed(
+        "sgdisk --zap-all /dev/vda",
+        "sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot
+        "sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap
+        "sgdisk --new=3:0:+5G --typecode=0:${rootPartType} /dev/vda", # /
+        "udevadm settle",
+
+        "mkfs.vfat /dev/vda1",
+        "mkswap /dev/vda2 -L swap",
+        "swapon -L swap",
+        "mkfs.ext4 -L root /dev/vda3",
+        "udevadm settle",
+
+        "mount /dev/vda3 /mnt",
+        "mkdir -p /mnt/boot",
+        "mount /dev/vda1 /mnt/boot"
+      )
+    '';
+    bootLoader = "systemd-boot";
+    extraConfig = ''
+      boot.initrd.systemd.root = "gpt-auto";
+      boot.initrd.supportedFilesystems = ["ext4"];
+    '';
+  };
 }
diff --git a/nixos/tests/kea.nix b/nixos/tests/kea.nix
index c8ecf771fa13..98a8e93a0760 100644
--- a/nixos/tests/kea.nix
+++ b/nixos/tests/kea.nix
@@ -44,6 +44,11 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: {
             name = "/var/lib/kea/dhcp4.leases";
           };
 
+          control-socket = {
+            socket-type = "unix";
+            socket-name = "/run/kea/dhcp4.sock";
+          };
+
           interfaces-config = {
             dhcp-socket-type = "raw";
             interfaces = [
@@ -89,6 +94,25 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: {
           };
         };
       };
+
+      services.kea.ctrl-agent = {
+        enable = true;
+        settings = {
+          http-host = "127.0.0.1";
+          http-port = 8000;
+          control-sockets.dhcp4 = {
+            socket-type = "unix";
+            socket-name = "/run/kea/dhcp4.sock";
+          };
+        };
+      };
+
+      services.prometheus.exporters.kea = {
+        enable = true;
+        controlSocketPaths = [
+          "http://127.0.0.1:8000"
+        ];
+      };
     };
 
     nameserver = { config, pkgs, ... }: {
@@ -182,5 +206,7 @@ import ./make-test-python.nix ({ pkgs, lib, ...}: {
     client.wait_until_succeeds("ping -c 5 10.0.0.1")
     router.wait_until_succeeds("ping -c 5 10.0.0.3")
     nameserver.wait_until_succeeds("kdig +short client.lan.nixos.test @10.0.0.2 | grep -q 10.0.0.3")
+    router.log(router.execute("curl 127.0.0.1:9547")[1])
+    router.succeed("curl --no-buffer 127.0.0.1:9547 | grep -qE '^kea_dhcp4_addresses_assigned_total.*1.0$'")
   '';
 })
diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix
index 228e57d1cdd6..67b412c80961 100644
--- a/nixos/tests/keycloak.nix
+++ b/nixos/tests/keycloak.nix
@@ -6,8 +6,8 @@ let
   certs = import ./common/acme/server/snakeoil-certs.nix;
   frontendUrl = "https://${certs.domain}";
 
-  keycloakTest = import ./make-test-python.nix (
-    { pkgs, databaseType, ... }:
+  keycloakTest = databaseType: import ./make-test-python.nix (
+    { pkgs, ... }:
     let
       initialAdminPassword = "h4Iho\"JFn't2>iQIR9";
       adminPasswordFile = pkgs.writeText "admin-password" "${initialAdminPassword}";
@@ -76,16 +76,18 @@ let
             enabled = true;
             realm = "test-realm";
             clients = [ client ];
-            users = [(
-              user // {
-                enabled = true;
-                credentials = [{
-                  type = "password";
-                  temporary = false;
-                  value = password;
-                }];
-              }
-            )];
+            users = [
+              (
+                user // {
+                  enabled = true;
+                  credentials = [{
+                    type = "password";
+                    temporary = false;
+                    value = password;
+                  }];
+                }
+              )
+            ];
           };
 
           realmDataJson = pkgs.writeText "realm-data.json" (builtins.toJSON realm);
@@ -177,7 +179,7 @@ let
   );
 in
 {
-  postgres = keycloakTest { databaseType = "postgresql"; };
-  mariadb = keycloakTest { databaseType = "mariadb"; };
-  mysql = keycloakTest { databaseType = "mysql"; };
+  postgres = keycloakTest "postgresql";
+  mariadb = keycloakTest "mariadb";
+  mysql = keycloakTest "mysql";
 }
diff --git a/nixos/tests/krb5/default.nix b/nixos/tests/krb5/default.nix
index ede085632c63..274ad580cebc 100644
--- a/nixos/tests/krb5/default.nix
+++ b/nixos/tests/krb5/default.nix
@@ -1,4 +1,3 @@
-{ system ? builtins.currentSystem }:
 {
-  example-config = import ./example-config.nix { inherit system; };
+  example-config = import ./example-config.nix;
 }
diff --git a/nixos/tests/ladybird.nix b/nixos/tests/ladybird.nix
index 4e9ab9a36d13..8ed0f47887c7 100644
--- a/nixos/tests/ladybird.nix
+++ b/nixos/tests/ladybird.nix
@@ -21,7 +21,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     ''
       machine.wait_for_x()
       machine.succeed("echo '<!DOCTYPE html><html><body><h1>Hello world</h1></body></html>' > page.html")
-      machine.execute("ladybird file://$(pwd)/page.html >&2 &")
+      machine.execute("Ladybird file://$(pwd)/page.html >&2 &")
       machine.wait_for_window("Ladybird")
       machine.sleep(5)
       machine.wait_for_text("Hello world")
diff --git a/nixos/tests/make-test-python.nix b/nixos/tests/make-test-python.nix
index 28569f1d2955..32531fffd2bf 100644
--- a/nixos/tests/make-test-python.nix
+++ b/nixos/tests/make-test-python.nix
@@ -1,5 +1,5 @@
 f: {
-  system ? builtins.currentSystem,
+  system,
   pkgs ? import ../.. { inherit system; config = {}; overlays = []; },
   ...
 } @ args:
diff --git a/nixos/tests/mate-wayland.nix b/nixos/tests/mate-wayland.nix
new file mode 100644
index 000000000000..df39ead286e1
--- /dev/null
+++ b/nixos/tests/mate-wayland.nix
@@ -0,0 +1,63 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "mate-wayland";
+
+  meta.maintainers = lib.teams.mate.members;
+
+  nodes.machine = { ... }: {
+    imports = [
+      ./common/user-account.nix
+    ];
+
+    services.xserver.enable = true;
+    services.xserver.displayManager = {
+      sddm.enable = true; # https://github.com/canonical/lightdm/issues/63
+      sddm.wayland.enable = true;
+      defaultSession = "MATE";
+      autoLogin = {
+        enable = true;
+        user = "alice";
+      };
+    };
+    services.xserver.desktopManager.mate.enableWaylandSession = true;
+
+    hardware.pulseaudio.enable = true;
+
+    # Need to switch to a different GPU driver than the default one (-vga std) so that wayfire can launch:
+    virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
+  };
+
+  enableOCR = true;
+
+  testScript = { nodes, ... }:
+    let
+      user = nodes.machine.users.users.alice;
+    in
+    ''
+      machine.wait_for_unit("display-manager.service")
+
+      with subtest("Wait for Wayland server"):
+          machine.wait_for_file("/run/user/${toString user.uid}/wayland-1")
+
+      with subtest("Check if MATE session components actually start"):
+          for i in ["wayfire", "mate-panel", "mate-wayland.sh", "mate-wayland-components.sh"]:
+              machine.wait_until_succeeds(f"pgrep -f {i}")
+          machine.wait_for_text('(Applications|Places|System)')
+          # It is expected that this applet doesn't work in Wayland
+          machine.wait_for_text('WorkspaceSwitcherApplet')
+
+      with subtest("Check if various environment variables are set"):
+          cmd = "xargs --null --max-args=1 echo < /proc/$(pgrep -xf mate-panel)/environ"
+          machine.succeed(f"{cmd} | grep 'XDG_SESSION_TYPE' | grep 'wayland'")
+          machine.succeed(f"{cmd} | grep 'XDG_SESSION_DESKTOP' | grep 'MATE'")
+          machine.succeed(f"{cmd} | grep 'MATE_PANEL_APPLETS_DIR' | grep '${pkgs.mate.mate-panel-with-applets.pname}'")
+
+      with subtest("Check if Wayfire config is properly configured"):
+          for i in ["button_style = mate", "firedecor", "mate-wayland-components.sh"]:
+              machine.wait_until_succeeds(f"cat /home/${user.name}/.config/mate/wayfire.ini | grep '{i}'")
+
+      with subtest("Check if Wayfire has ever coredumped"):
+          machine.fail("coredumpctl --json=short | grep wayfire")
+          machine.sleep(10)
+          machine.screenshot("screen")
+    '';
+})
diff --git a/nixos/tests/mate.nix b/nixos/tests/mate.nix
index 48582e18d520..1252ec43cf3d 100644
--- a/nixos/tests/mate.nix
+++ b/nixos/tests/mate.nix
@@ -54,6 +54,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           machine.wait_for_text('(Applications|Places|System)')
           machine.wait_for_text('(Computer|Home|Trash)')
 
+      with subtest("Check if various environment variables are set"):
+          machine.succeed("xargs --null --max-args=1 echo < /proc/$(pgrep -xf marco)/environ | grep 'XDG_CURRENT_DESKTOP' | grep 'MATE'")
+          # From mate-panel-with-applets packaging
+          machine.succeed("xargs --null --max-args=1 echo < /proc/$(pgrep -xf mate-panel)/environ | grep 'MATE_PANEL_APPLETS_DIR' | grep '${pkgs.mate.mate-panel-with-applets.pname}'")
+
+      with subtest("Check if applets are built with in-process support"):
+          # This is needed for Wayland support
+          machine.fail("pgrep -fa clock-applet")
+
       with subtest("Lock the screen"):
           machine.wait_until_succeeds("su - ${user.name} -c '${env} mate-screensaver-command -q' | grep 'The screensaver is inactive'")
           machine.succeed("su - ${user.name} -c '${env} mate-screensaver-command -l >&2 &'")
diff --git a/nixos/tests/mihomo.nix b/nixos/tests/mihomo.nix
new file mode 100644
index 000000000000..472d10050f7f
--- /dev/null
+++ b/nixos/tests/mihomo.nix
@@ -0,0 +1,44 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "mihomo";
+  meta.maintainers = with pkgs.lib.maintainers; [ Guanran928 ];
+
+  nodes.machine = {
+    environment.systemPackages = [ pkgs.curl ];
+
+    services.nginx = {
+      enable = true;
+      statusPage = true;
+    };
+
+    services.mihomo = {
+      enable = true;
+      configFile = pkgs.writeTextFile {
+        name = "config.yaml";
+        text = ''
+          mixed-port: 7890
+          external-controller: 127.0.0.1:9090
+          authentication:
+          - "user:supersecret"
+        '';
+      };
+    };
+  };
+
+  testScript = ''
+    # Wait until it starts
+    machine.wait_for_unit("nginx.service")
+    machine.wait_for_unit("mihomo.service")
+    machine.wait_for_open_port(80)
+    machine.wait_for_open_port(7890)
+    machine.wait_for_open_port(9090)
+
+    # Proxy
+    machine.succeed("curl --fail --max-time 10 --proxy http://user:supersecret@localhost:7890 http://localhost")
+    machine.succeed("curl --fail --max-time 10 --proxy socks5://user:supersecret@localhost:7890 http://localhost")
+    machine.fail("curl --fail --max-time 10 --proxy http://user:supervillain@localhost:7890 http://localhost")
+    machine.fail("curl --fail --max-time 10 --proxy socks5://user:supervillain@localhost:7890 http://localhost")
+
+    # Web UI
+    machine.succeed("curl --fail http://localhost:9090") == '{"hello":"clash"}'
+  '';
+})
diff --git a/nixos/tests/mycelium/default.nix b/nixos/tests/mycelium/default.nix
new file mode 100644
index 000000000000..f0d72436843c
--- /dev/null
+++ b/nixos/tests/mycelium/default.nix
@@ -0,0 +1,57 @@
+import ../make-test-python.nix ({ lib, ... }: let
+  peer1-ip = "531:c350:28c1:dfde:ea6d:77d1:a60b:7209";
+  peer2-ip = "49f:3942:3a55:d100:4c78:c558:c4f:695b";
+in
+  {
+    name = "mycelium";
+    meta.maintainers = with lib.maintainers; [ lassulus ];
+
+    nodes = {
+
+      peer1 = { config, pkgs, ... }: {
+        virtualisation.vlans = [ 1 ];
+        networking.interfaces.eth1.ipv4.addresses = [{
+          address = "192.168.1.11";
+          prefixLength = 24;
+        }];
+
+        services.mycelium = {
+          enable = true;
+          addHostedPublicNodes = false;
+          openFirewall = true;
+          keyFile = ./peer1.key;
+          peers = [
+            "quic://192.168.1.12:9651"
+            "tcp://192.168.1.12:9651"
+          ];
+        };
+      };
+
+      peer2 = { config, pkgs, ... }: {
+        virtualisation.vlans = [ 1 ];
+        networking.interfaces.eth1.ipv4.addresses = [{
+          address = "192.168.1.12";
+          prefixLength = 24;
+        }];
+
+        services.mycelium = {
+          enable = true;
+          addHostedPublicNodes = false;
+          openFirewall = true;
+          keyFile = ./peer2.key;
+        };
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      peer1.wait_for_unit("network-online.target")
+      peer2.wait_for_unit("network-online.target")
+      peer1.wait_for_unit("mycelium.service")
+      peer2.wait_for_unit("mycelium.service")
+
+      peer1.succeed("ping -c5 ${peer2-ip}")
+      peer2.succeed("ping -c5 ${peer1-ip}")
+    '';
+  })
diff --git a/nixos/tests/mycelium/peer1.key b/nixos/tests/mycelium/peer1.key
new file mode 100644
index 000000000000..db1cf9e72fe4
--- /dev/null
+++ b/nixos/tests/mycelium/peer1.key
@@ -0,0 +1 @@
+s	B0dRH5u?^
\ No newline at end of file
diff --git a/nixos/tests/mycelium/peer2.key b/nixos/tests/mycelium/peer2.key
new file mode 100644
index 000000000000..7e757de48efb
--- /dev/null
+++ b/nixos/tests/mycelium/peer2.key
@@ -0,0 +1 @@
+X1yGՅSAMe7]
\ No newline at end of file
diff --git a/nixos/tests/nimdow.nix b/nixos/tests/nimdow.nix
new file mode 100644
index 000000000000..cefe46edc5fb
--- /dev/null
+++ b/nixos/tests/nimdow.nix
@@ -0,0 +1,25 @@
+import ./make-test-python.nix ({ pkgs, ...} : {
+  name = "nimdow";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ marcusramberg ];
+  };
+
+  nodes.machine = { lib, ... }: {
+    imports = [ ./common/x11.nix ./common/user-account.nix ];
+    test-support.displayManager.auto.user = "alice";
+    services.xserver.displayManager.defaultSession = lib.mkForce "none+nimdow";
+    services.xserver.windowManager.nimdow.enable = true;
+  };
+
+  testScript = { ... }: ''
+    with subtest("ensure x starts"):
+        machine.wait_for_x()
+        machine.wait_for_file("/home/alice/.Xauthority")
+        machine.succeed("xauth merge ~alice/.Xauthority")
+
+    with subtest("ensure we can open a new terminal"):
+        machine.send_key("meta_l-ret")
+        machine.wait_for_window(r"alice.*?machine")
+        machine.screenshot("terminal")
+  '';
+})
diff --git a/nixos/tests/nix-config.nix b/nixos/tests/nix-config.nix
new file mode 100644
index 000000000000..907e886def35
--- /dev/null
+++ b/nixos/tests/nix-config.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+{
+  name = "nix-config";
+  nodes.machine = { pkgs, ... }: {
+    nix.settings = {
+      nix-path = [ "nonextra=/etc/value.nix" ];
+      extra-nix-path = [ "extra=/etc/value.nix" ];
+    };
+    environment.etc."value.nix".text = "42";
+  };
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("nix-daemon.socket")
+    # regression test for the workaround for https://github.com/NixOS/nix/issues/9487
+    print(machine.succeed("nix-instantiate --find-file extra"))
+    print(machine.succeed("nix-instantiate --find-file nonextra"))
+  '';
+})
diff --git a/nixos/tests/nixops/default.nix b/nixos/tests/nixops/default.nix
index 8477e5059fca..6468b8c38224 100644
--- a/nixos/tests/nixops/default.nix
+++ b/nixos/tests/nixops/default.nix
@@ -93,23 +93,5 @@ let
 
   inherit (import ../ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
 
-  /*
-    Return a store path with a closure containing everything including
-    derivations and all build dependency outputs, all the way down.
-  */
-  allDrvOutputs = pkg:
-    let name = "allDrvOutputs-${pkg.pname or pkg.name or "unknown"}";
-    in
-    pkgs.runCommand name { refs = pkgs.writeReferencesToFile pkg.drvPath; } ''
-      touch $out
-      while read ref; do
-        case $ref in
-          *.drv)
-            cat $ref >>$out
-            ;;
-        esac
-      done <$refs
-    '';
-
 in
 tests
diff --git a/nixos/tests/opensearch.nix b/nixos/tests/opensearch.nix
index 2887ac967765..7d37583464cb 100644
--- a/nixos/tests/opensearch.nix
+++ b/nixos/tests/opensearch.nix
@@ -1,7 +1,7 @@
 let
-  opensearchTest =
+  opensearchTest = extraSettings:
     import ./make-test-python.nix (
-      { pkgs, lib, extraSettings ? {} }: {
+      { pkgs, lib, ... }: {
         name = "opensearch";
         meta.maintainers = with pkgs.lib.maintainers; [ shyim ];
 
@@ -27,20 +27,18 @@ in
 {
   opensearch = opensearchTest {};
   opensearchCustomPathAndUser = opensearchTest {
-    extraSettings = {
-      services.opensearch.dataDir = "/var/opensearch_test";
-      services.opensearch.user = "open_search";
-      services.opensearch.group = "open_search";
-      systemd.tmpfiles.rules = [
-        "d /var/opensearch_test 0700 open_search open_search -"
-      ];
-      users = {
-        groups.open_search = {};
-        users.open_search = {
-          description = "OpenSearch daemon user";
-          group = "open_search";
-          isSystemUser = true;
-        };
+    services.opensearch.dataDir = "/var/opensearch_test";
+    services.opensearch.user = "open_search";
+    services.opensearch.group = "open_search";
+    systemd.tmpfiles.rules = [
+      "d /var/opensearch_test 0700 open_search open_search -"
+    ];
+    users = {
+      groups.open_search = { };
+      users.open_search = {
+        description = "OpenSearch daemon user";
+        group = "open_search";
+        isSystemUser = true;
       };
     };
   };
diff --git a/nixos/tests/pg_anonymizer.nix b/nixos/tests/pg_anonymizer.nix
new file mode 100644
index 000000000000..2960108e37c3
--- /dev/null
+++ b/nixos/tests/pg_anonymizer.nix
@@ -0,0 +1,94 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "pg_anonymizer";
+  meta.maintainers = lib.teams.flyingcircus.members;
+
+  nodes.machine = { pkgs, ... }: {
+    environment.systemPackages = [ pkgs.pg-dump-anon ];
+    services.postgresql = {
+      enable = true;
+      extraPlugins = ps: [ ps.anonymizer ];
+      settings.shared_preload_libraries = "anon";
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+    machine.wait_for_unit("postgresql.service")
+
+    with subtest("Setup"):
+        machine.succeed("sudo -u postgres psql --command 'create database demo'")
+        machine.succeed(
+            "sudo -u postgres psql -d demo -f ${pkgs.writeText "init.sql" ''
+              create extension anon cascade;
+              select anon.init();
+              create table player(id serial, name text, points int);
+              insert into player(id,name,points) values (1,'Foo', 23);
+              insert into player(id,name,points) values (2,'Bar',42);
+              security label for anon on column player.name is 'MASKED WITH FUNCTION anon.fake_last_name();';
+              security label for anon on column player.points is 'MASKED WITH VALUE NULL';
+            ''}"
+        )
+
+    def get_player_table_contents():
+        return [
+            x.split(',') for x in machine.succeed("sudo -u postgres psql -d demo --csv --command 'select * from player'").splitlines()[1:]
+        ]
+
+    def check_anonymized_row(row, id, original_name):
+        assert row[0] == id, f"Expected first row to have ID {id}, but got {row[0]}"
+        assert row[1] != original_name, f"Expected first row to have a name other than {original_name}"
+        assert not bool(row[2]), "Expected points to be NULL in first row"
+
+    def find_xsv_in_dump(dump, sep=','):
+        """
+        Expecting to find a CSV (for pg_dump_anon) or TSV (for pg_dump) structure, looking like
+
+            COPY public.player ...
+            1,Shields,
+            2,Salazar,
+            \.
+
+        in the given dump (the commas are tabs in case of pg_dump).
+              Extract the CSV lines and split by `sep`.
+        """
+
+        try:
+            from itertools import dropwhile, takewhile
+            return [x.split(sep) for x in list(takewhile(
+                lambda x: x != "\\.",
+                dropwhile(
+                    lambda x: not x.startswith("COPY public.player"),
+                    dump.splitlines()
+                )
+            ))[1:]]
+        except:
+            print(f"Dump to process: {dump}")
+            raise
+
+    def check_original_data(output):
+        assert output[0] == ['1','Foo','23'], f"Expected first row from player table to be 1,Foo,23; got {output[0]}"
+        assert output[1] == ['2','Bar','42'], f"Expected first row from player table to be 2,Bar,42; got {output[1]}"
+
+    def check_anonymized_rows(output):
+        check_anonymized_row(output[0], '1', 'Foo')
+        check_anonymized_row(output[1], '2', 'Bar')
+
+    with subtest("Check initial state"):
+        check_original_data(get_player_table_contents())
+
+    with subtest("Anonymous dumps"):
+        check_original_data(find_xsv_in_dump(
+            machine.succeed("sudo -u postgres pg_dump demo"),
+            sep='\t'
+        ))
+        check_anonymized_rows(find_xsv_in_dump(
+            machine.succeed("sudo -u postgres pg_dump_anon -U postgres -h /run/postgresql -d demo"),
+            sep=','
+        ))
+
+    with subtest("Anonymize"):
+        machine.succeed("sudo -u postgres psql -d demo --command 'select anon.anonymize_database();'")
+        check_anonymized_rows(get_player_table_contents())
+  '';
+})
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 632656ad5795..3dc368e320ff 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -418,54 +418,6 @@ let
       '';
     };
 
-    kea = let
-      controlSocketPathV4 = "/run/kea/dhcp4.sock";
-      controlSocketPathV6 = "/run/kea/dhcp6.sock";
-    in
-    {
-      exporterConfig = {
-        enable = true;
-        controlSocketPaths = [
-          controlSocketPathV4
-          controlSocketPathV6
-        ];
-      };
-      metricProvider = {
-        services.kea = {
-          dhcp4 = {
-            enable = true;
-            settings = {
-              control-socket = {
-                socket-type = "unix";
-                socket-name = controlSocketPathV4;
-              };
-            };
-          };
-          dhcp6 = {
-            enable = true;
-            settings = {
-              control-socket = {
-                socket-type = "unix";
-                socket-name = controlSocketPathV6;
-              };
-            };
-          };
-        };
-      };
-
-      exporterTest = ''
-        wait_for_unit("kea-dhcp4-server.service")
-        wait_for_unit("kea-dhcp6-server.service")
-        wait_for_file("${controlSocketPathV4}")
-        wait_for_file("${controlSocketPathV6}")
-        wait_for_unit("prometheus-kea-exporter.service")
-        wait_for_open_port(9547)
-        succeed(
-            "curl --fail localhost:9547/metrics | grep 'packets_received_total'"
-        )
-      '';
-    };
-
     knot = {
       exporterConfig = {
         enable = true;
diff --git a/nixos/tests/redlib.nix b/nixos/tests/redlib.nix
new file mode 100644
index 000000000000..e4bde25e30a6
--- /dev/null
+++ b/nixos/tests/redlib.nix
@@ -0,0 +1,20 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "redlib";
+  meta.maintainers = with lib.maintainers; [ soispha ];
+
+  nodes.machine = {
+    services.libreddit = {
+      package = pkgs.redlib;
+      enable = true;
+      # Test CAP_NET_BIND_SERVICE
+      port = 80;
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("libreddit.service")
+    machine.wait_for_open_port(80)
+    # Query a page that does not require Internet access
+    machine.succeed("curl --fail http://localhost:80/settings")
+  '';
+})
diff --git a/nixos/tests/systemd-machinectl.nix b/nixos/tests/systemd-machinectl.nix
index b8ed0c33e8e4..02b4d9c590b5 100644
--- a/nixos/tests/systemd-machinectl.nix
+++ b/nixos/tests/systemd-machinectl.nix
@@ -42,8 +42,18 @@ import ./make-test-python.nix ({ pkgs, ... }:
 
       virtualisation.additionalPaths = [ containerSystem ];
 
-      # not needed, but we want to test the nspawn file generation
-      systemd.nspawn.${containerName} = { };
+      systemd.tmpfiles.rules = [
+        "d /var/lib/machines/shared-decl 0755 root root - -"
+      ];
+      systemd.nspawn.shared-decl = {
+        execConfig = {
+          Boot = false;
+          Parameters = "${containerSystem}/init";
+        };
+        filesConfig = {
+          BindReadOnly = "/nix/store";
+        };
+      };
 
       systemd.services."systemd-nspawn@${containerName}" = {
         serviceConfig.Environment = [
@@ -52,14 +62,33 @@ import ./make-test-python.nix ({ pkgs, ... }:
         ];
         overrideStrategy = "asDropin";
       };
+
+      # open DHCP for container
+      networking.firewall.extraCommands = ''
+        ${pkgs.iptables}/bin/iptables -A nixos-fw -i ve-+ -p udp -m udp --dport 67 -j nixos-fw-accept
+      '';
     };
 
     testScript = ''
       start_all()
       machine.wait_for_unit("default.target");
 
-      # Install container
+      # Test machinectl start stop of shared-decl
+      machine.succeed("machinectl start shared-decl");
+      machine.wait_until_succeeds("systemctl -M shared-decl is-active default.target");
+      machine.succeed("machinectl stop shared-decl");
+
+      # create containers root
       machine.succeed("mkdir -p ${containerRoot}");
+
+      # start container with shared nix store by using same arguments as for systemd-nspawn@.service
+      machine.succeed("systemd-run systemd-nspawn --machine=${containerName} --network-veth -U --bind-ro=/nix/store ${containerSystem}/init")
+      machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
+
+      # Test machinectl stop
+      machine.succeed("machinectl stop ${containerName}");
+
+      # Install container
       # Workaround for nixos-install
       machine.succeed("chmod o+rx /var/lib/machines");
       machine.succeed("nixos-install --root ${containerRoot} --system ${containerSystem} --no-channel-copy --no-root-passwd");
@@ -77,6 +106,12 @@ import ./make-test-python.nix ({ pkgs, ... }:
       # Test nss_mymachines via nscd
       machine.succeed("getent hosts ${containerName}");
 
+      # Test systemd-nspawn network configuration to container
+      machine.succeed("networkctl --json=short status ve-${containerName} | ${pkgs.jq}/bin/jq -e '.OperationalState == \"routable\"'");
+
+      # Test systemd-nspawn network configuration to host
+      machine.succeed("machinectl shell ${containerName} /run/current-system/sw/bin/networkctl --json=short status host0 | ${pkgs.jq}/bin/jq -r '.OperationalState == \"routable\"'");
+
       # Test systemd-nspawn network configuration
       machine.succeed("ping -n -c 1 ${containerName}");
 
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index d817ce927ff8..76d5244b3ee3 100644
--- a/nixos/tests/vscodium.nix
+++ b/nixos/tests/vscodium.nix
@@ -76,4 +76,4 @@ let
     });
 
 in
-builtins.mapAttrs (k: v: mkTest k v { }) tests
+builtins.mapAttrs (k: v: mkTest k v) tests
diff --git a/nixos/tests/web-apps/pretix.nix b/nixos/tests/web-apps/pretix.nix
new file mode 100644
index 000000000000..559316f9b85c
--- /dev/null
+++ b/nixos/tests/web-apps/pretix.nix
@@ -0,0 +1,47 @@
+{
+  lib,
+  pkgs,
+  ...
+}:
+
+{
+  name = "pretix";
+  meta.maintainers = with lib.maintainers; [ hexa ];
+
+  nodes = {
+    pretix = {
+      networking.extraHosts = ''
+        127.0.0.1 tickets.local
+      '';
+
+      services.pretix = {
+        enable = true;
+        nginx.domain = "tickets.local";
+        plugins = with pkgs.pretix.plugins; [
+          passbook
+          pages
+        ];
+        settings = {
+          pretix = {
+            instance_name = "NixOS Test";
+            url = "http://tickets.local";
+          };
+          mail.from = "hello@tickets.local";
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    pretix.wait_for_unit("pretix-web.service")
+    pretix.wait_for_unit("pretix-worker.service")
+
+    pretix.wait_until_succeeds("curl -q --fail http://tickets.local")
+
+    pretix.succeed("pretix-manage --help")
+
+    pretix.log(pretix.succeed("systemd-analyze security pretix-web.service"))
+  '';
+}