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/administration/cleaning-store.chapter.md6
-rw-r--r--nixos/doc/manual/administration/container-networking.section.md12
-rw-r--r--nixos/doc/manual/administration/control-groups.chapter.md8
-rw-r--r--nixos/doc/manual/administration/declarative-containers.section.md28
-rw-r--r--nixos/doc/manual/administration/service-mgmt.chapter.md4
-rw-r--r--nixos/doc/manual/administration/system-state.chapter.md2
-rw-r--r--nixos/doc/manual/configuration/abstractions.section.md6
-rw-r--r--nixos/doc/manual/configuration/ad-hoc-network-config.section.md10
-rw-r--r--nixos/doc/manual/configuration/adding-custom-packages.section.md34
-rw-r--r--nixos/doc/manual/configuration/config-file.section.md62
-rw-r--r--nixos/doc/manual/configuration/customizing-packages.section.md52
-rw-r--r--nixos/doc/manual/configuration/declarative-packages.section.md4
-rw-r--r--nixos/doc/manual/configuration/file-systems.chapter.md10
-rw-r--r--nixos/doc/manual/configuration/firewall.section.md18
-rw-r--r--nixos/doc/manual/configuration/gpu-accel.chapter.md62
-rw-r--r--nixos/doc/manual/configuration/ipv4-config.section.md20
-rw-r--r--nixos/doc/manual/configuration/ipv6-config.section.md28
-rw-r--r--nixos/doc/manual/configuration/kubernetes.chapter.md30
-rw-r--r--nixos/doc/manual/configuration/linux-kernel.chapter.md54
-rw-r--r--nixos/doc/manual/configuration/luks-file-systems.section.md34
-rw-r--r--nixos/doc/manual/configuration/modularity.section.md12
-rw-r--r--nixos/doc/manual/configuration/network-manager.section.md16
-rw-r--r--nixos/doc/manual/configuration/overlayfs.section.md28
-rw-r--r--nixos/doc/manual/configuration/profiles.chapter.md8
-rw-r--r--nixos/doc/manual/configuration/profiles/demo.section.md2
-rw-r--r--nixos/doc/manual/configuration/profiles/graphical.section.md2
-rw-r--r--nixos/doc/manual/configuration/renaming-interfaces.section.md20
-rw-r--r--nixos/doc/manual/configuration/ssh.section.md10
-rw-r--r--nixos/doc/manual/configuration/sshfs-file-systems.section.md4
-rw-r--r--nixos/doc/manual/configuration/subversion.chapter.md51
-rw-r--r--nixos/doc/manual/configuration/user-mgmt.chapter.md28
-rw-r--r--nixos/doc/manual/configuration/wayland.chapter.md6
-rw-r--r--nixos/doc/manual/configuration/wireless.section.md40
-rw-r--r--nixos/doc/manual/configuration/x-windows.chapter.md148
-rw-r--r--nixos/doc/manual/configuration/xfce.chapter.md22
-rw-r--r--nixos/doc/manual/default.nix43
-rw-r--r--nixos/doc/manual/development/activation-script.section.md16
-rw-r--r--nixos/doc/manual/development/assertions.section.md4
-rw-r--r--nixos/doc/manual/development/etc-overlay.section.md8
-rw-r--r--nixos/doc/manual/development/meta-attributes.section.md4
-rw-r--r--nixos/doc/manual/development/non-switchable-systems.section.md2
-rw-r--r--nixos/doc/manual/development/option-declarations.section.md59
-rw-r--r--nixos/doc/manual/development/option-def.section.md78
-rw-r--r--nixos/doc/manual/development/option-types.section.md219
-rw-r--r--nixos/doc/manual/development/settings-options.section.md42
-rw-r--r--nixos/doc/manual/development/unit-handling.section.md12
-rw-r--r--nixos/doc/manual/development/writing-modules.chapter.md8
-rw-r--r--nixos/doc/manual/development/writing-nixos-tests.section.md16
-rw-r--r--nixos/doc/manual/installation/building-images-via-systemd-repart.chapter.md30
-rw-r--r--nixos/doc/manual/installation/changing-config.chapter.md4
-rw-r--r--nixos/doc/manual/installation/installing-behind-a-proxy.section.md6
-rw-r--r--nixos/doc/manual/installation/installing-from-other-distro.section.md18
-rw-r--r--nixos/doc/manual/installation/installing-virtualbox-guest.section.md8
-rw-r--r--nixos/doc/manual/installation/installing.chapter.md4
-rw-r--r--nixos/doc/manual/installation/upgrading.chapter.md10
-rw-r--r--nixos/doc/manual/release-notes/rl-1509.section.md8
-rw-r--r--nixos/doc/manual/release-notes/rl-1703.section.md4
-rw-r--r--nixos/doc/manual/release-notes/rl-1909.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.section.md34
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md4
-rw-r--r--nixos/doc/manual/release-notes/rl-2211.section.md10
-rw-r--r--nixos/doc/manual/release-notes/rl-2305.section.md64
-rw-r--r--nixos/doc/manual/release-notes/rl-2311.section.md44
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md151
-rw-r--r--nixos/lib/make-options-doc/default.nix119
-rw-r--r--nixos/lib/systemd-lib.nix79
-rw-r--r--nixos/lib/systemd-network-units.nix9
-rw-r--r--nixos/lib/systemd-types.nix107
-rw-r--r--nixos/lib/systemd-unit-options.nix137
-rw-r--r--nixos/lib/test-driver/test_driver/machine.py3
-rw-r--r--nixos/lib/testing/driver.nix20
-rw-r--r--nixos/lib/testing/interactive.nix4
-rw-r--r--nixos/lib/testing/meta.nix17
-rw-r--r--nixos/lib/testing/name.nix4
-rw-r--r--nixos/lib/testing/network.nix5
-rw-r--r--nixos/lib/testing/nodes.nix15
-rw-r--r--nixos/lib/testing/run.nix8
-rw-r--r--nixos/lib/testing/testScript.nix6
-rw-r--r--nixos/lib/utils.nix63
-rw-r--r--nixos/modules/config/mysql.nix2
-rw-r--r--nixos/modules/config/nix.nix11
-rw-r--r--nixos/modules/config/resolvconf.nix5
-rw-r--r--nixos/modules/config/users-groups.nix37
-rw-r--r--nixos/modules/config/xdg/portals/wlr.nix2
-rw-r--r--nixos/modules/hardware/cpu/amd-ryzen-smu.nix26
-rw-r--r--nixos/modules/hardware/ksm.nix2
-rw-r--r--nixos/modules/hardware/logitech.nix4
-rw-r--r--nixos/modules/hardware/uni-sync.nix117
-rw-r--r--nixos/modules/hardware/video/capture/mwprocapture.nix2
-rw-r--r--nixos/modules/hardware/video/nvidia.nix13
-rw-r--r--nixos/modules/hardware/video/webcam/ipu6.nix5
-rw-r--r--nixos/modules/i18n/input-method/default.md70
-rw-r--r--nixos/modules/i18n/input-method/fcitx5.nix4
-rw-r--r--nixos/modules/image/repart-image.nix105
-rw-r--r--nixos/modules/image/repart.nix80
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix29
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix20
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix18
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix28
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix20
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl13
-rw-r--r--nixos/modules/installer/tools/tools.nix6
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixos/modules/misc/documentation.nix58
-rw-r--r--nixos/modules/misc/mandoc.nix34
-rw-r--r--nixos/modules/module-list.nix45
-rw-r--r--nixos/modules/profiles/all-hardware.nix9
-rw-r--r--nixos/modules/profiles/macos-builder.nix5
-rw-r--r--nixos/modules/programs/_1password-gui.nix8
-rw-r--r--nixos/modules/programs/appgate-sdp.nix2
-rw-r--r--nixos/modules/programs/appimage.nix33
-rw-r--r--nixos/modules/programs/atop.nix2
-rw-r--r--nixos/modules/programs/bash/blesh.nix2
-rw-r--r--nixos/modules/programs/bcc.nix2
-rw-r--r--nixos/modules/programs/captive-browser.nix2
-rw-r--r--nixos/modules/programs/ccache.nix2
-rw-r--r--nixos/modules/programs/digitalbitbox/default.md36
-rw-r--r--nixos/modules/programs/firejail.nix2
-rw-r--r--nixos/modules/programs/fzf.nix42
-rw-r--r--nixos/modules/programs/gamescope.nix2
-rw-r--r--nixos/modules/programs/geary.nix2
-rw-r--r--nixos/modules/programs/git.nix4
-rw-r--r--nixos/modules/programs/goldwarden.nix50
-rw-r--r--nixos/modules/programs/iay.nix2
-rw-r--r--nixos/modules/programs/kubeswitch.nix56
-rw-r--r--nixos/modules/programs/lazygit.nix37
-rw-r--r--nixos/modules/programs/less.nix2
-rw-r--r--nixos/modules/programs/mepo.nix2
-rw-r--r--nixos/modules/programs/mininet.nix2
-rw-r--r--nixos/modules/programs/minipro.nix2
-rw-r--r--nixos/modules/programs/miriway.nix2
-rw-r--r--nixos/modules/programs/nano.nix2
-rw-r--r--nixos/modules/programs/nix-ld.nix42
-rw-r--r--nixos/modules/programs/nm-applet.nix2
-rw-r--r--nixos/modules/programs/noisetorch.nix2
-rw-r--r--nixos/modules/programs/oddjobd.nix22
-rw-r--r--nixos/modules/programs/partition-manager.nix22
-rw-r--r--nixos/modules/programs/plotinus.md6
-rw-r--r--nixos/modules/programs/projecteur.nix2
-rw-r--r--nixos/modules/programs/proxychains.nix2
-rw-r--r--nixos/modules/programs/rust-motd.nix2
-rw-r--r--nixos/modules/programs/ryzen-monitor-ng.nix35
-rw-r--r--nixos/modules/programs/sedutil.nix2
-rw-r--r--nixos/modules/programs/sniffnet.nix2
-rw-r--r--nixos/modules/programs/starship.nix29
-rw-r--r--nixos/modules/programs/steam.nix19
-rw-r--r--nixos/modules/programs/sysdig.nix2
-rw-r--r--nixos/modules/programs/thefuck.nix2
-rw-r--r--nixos/modules/programs/trippy.nix2
-rw-r--r--nixos/modules/programs/udevil.nix2
-rw-r--r--nixos/modules/programs/usbtop.nix2
-rw-r--r--nixos/modules/programs/wayland/cardboard.nix2
-rw-r--r--nixos/modules/programs/wayland/hyprland.nix26
-rw-r--r--nixos/modules/programs/wayland/labwc.nix2
-rw-r--r--nixos/modules/programs/wayland/river.nix2
-rw-r--r--nixos/modules/programs/wayland/sway.nix2
-rw-r--r--nixos/modules/programs/wayland/waybar.nix2
-rw-r--r--nixos/modules/programs/wayland/wayfire.nix2
-rw-r--r--nixos/modules/programs/weylus.nix2
-rw-r--r--nixos/modules/programs/xss-lock.nix1
-rw-r--r--nixos/modules/programs/yabar.nix2
-rw-r--r--nixos/modules/programs/zmap.nix2
-rw-r--r--nixos/modules/programs/zsh/oh-my-zsh.md8
-rw-r--r--nixos/modules/rename.nix3
-rw-r--r--nixos/modules/security/acme/default.md366
-rw-r--r--nixos/modules/security/ca.nix2
-rw-r--r--nixos/modules/security/pam.nix6
-rw-r--r--nixos/modules/security/sudo.nix4
-rw-r--r--nixos/modules/services/admin/docuum.nix45
-rw-r--r--nixos/modules/services/admin/salt/master.nix2
-rw-r--r--nixos/modules/services/admin/salt/minion.nix2
-rw-r--r--nixos/modules/services/audio/mpdscribble.nix2
-rw-r--r--nixos/modules/services/audio/roon-server.nix3
-rw-r--r--nixos/modules/services/backup/borgbackup.md31
-rw-r--r--nixos/modules/services/backup/borgbackup.nix4
-rw-r--r--nixos/modules/services/backup/restic-rest-server.nix37
-rw-r--r--nixos/modules/services/backup/znapzend.nix2
-rw-r--r--nixos/modules/services/continuous-integration/gitea-actions-runner.nix3
-rw-r--r--nixos/modules/services/databases/foundationdb.md20
-rw-r--r--nixos/modules/services/databases/hbase-standalone.nix2
-rw-r--r--nixos/modules/services/databases/lldap.nix2
-rw-r--r--nixos/modules/services/databases/postgresql.md50
-rw-r--r--nixos/modules/services/databases/postgresql.nix63
-rw-r--r--nixos/modules/services/databases/redis.nix5
-rw-r--r--nixos/modules/services/databases/tigerbeetle.md8
-rw-r--r--nixos/modules/services/databases/victoriametrics.nix2
-rw-r--r--nixos/modules/services/desktop-managers/plasma6.nix19
-rw-r--r--nixos/modules/services/desktops/blueman.nix2
-rw-r--r--nixos/modules/services/desktops/deepin/dde-api.nix2
-rw-r--r--nixos/modules/services/desktops/flatpak.md8
-rw-r--r--nixos/modules/services/desktops/flatpak.nix2
-rw-r--r--nixos/modules/services/desktops/neard.nix2
-rw-r--r--nixos/modules/services/desktops/pipewire/pipewire.nix149
-rw-r--r--nixos/modules/services/desktops/pipewire/wireplumber.nix113
-rw-r--r--nixos/modules/services/desktops/zeitgeist.nix2
-rw-r--r--nixos/modules/services/development/athens.md8
-rw-r--r--nixos/modules/services/development/blackfire.md2
-rw-r--r--nixos/modules/services/development/distccd.nix2
-rw-r--r--nixos/modules/services/development/gemstash.nix2
-rw-r--r--nixos/modules/services/development/livebook.md8
-rw-r--r--nixos/modules/services/display-managers/default.nix260
-rw-r--r--nixos/modules/services/display-managers/greetd.nix7
-rw-r--r--nixos/modules/services/display-managers/sddm.nix (renamed from nixos/modules/services/x11/display-managers/sddm.nix)184
-rw-r--r--nixos/modules/services/editors/emacs.md22
-rw-r--r--nixos/modules/services/finance/odoo.nix2
-rw-r--r--nixos/modules/services/games/archisteamfarm.nix2
-rw-r--r--nixos/modules/services/games/mchprs.nix2
-rw-r--r--nixos/modules/services/games/openarena.nix2
-rw-r--r--nixos/modules/services/games/terraria.nix7
-rw-r--r--nixos/modules/services/hardware/kanata.nix2
-rw-r--r--nixos/modules/services/hardware/lirc.nix2
-rw-r--r--nixos/modules/services/hardware/openrgb.nix2
-rw-r--r--nixos/modules/services/hardware/pcscd.nix2
-rw-r--r--nixos/modules/services/hardware/sane_extra_backends/brscan5.nix2
-rw-r--r--nixos/modules/services/hardware/udev.nix2
-rw-r--r--nixos/modules/services/hardware/undervolt.nix2
-rw-r--r--nixos/modules/services/hardware/vdr.nix2
-rw-r--r--nixos/modules/services/home-automation/ebusd.nix2
-rw-r--r--nixos/modules/services/home-automation/esphome.nix2
-rw-r--r--nixos/modules/services/home-automation/homeassistant-satellite.nix225
-rw-r--r--nixos/modules/services/home-automation/wyoming/faster-whisper.nix (renamed from nixos/modules/services/audio/wyoming/faster-whisper.nix)17
-rw-r--r--nixos/modules/services/home-automation/wyoming/openwakeword.nix (renamed from nixos/modules/services/audio/wyoming/openwakeword.nix)0
-rw-r--r--nixos/modules/services/home-automation/wyoming/piper.nix (renamed from nixos/modules/services/audio/wyoming/piper.nix)0
-rw-r--r--nixos/modules/services/home-automation/wyoming/satellite.nix244
-rw-r--r--nixos/modules/services/logging/awstats.nix2
-rw-r--r--nixos/modules/services/logging/fluentd.nix2
-rw-r--r--nixos/modules/services/logging/graylog.nix2
-rw-r--r--nixos/modules/services/logging/heartbeat.nix2
-rw-r--r--nixos/modules/services/logging/logcheck.nix2
-rw-r--r--nixos/modules/services/logging/logrotate.nix15
-rw-r--r--nixos/modules/services/logging/ulogd.nix2
-rw-r--r--nixos/modules/services/logging/vector.nix2
-rw-r--r--nixos/modules/services/mail/goeland.nix2
-rw-r--r--nixos/modules/services/mail/mailcatcher.nix2
-rw-r--r--nixos/modules/services/mail/mailhog.nix2
-rw-r--r--nixos/modules/services/mail/mailman.md4
-rw-r--r--nixos/modules/services/matrix/maubot.md90
-rw-r--r--nixos/modules/services/matrix/mautrix-meta.nix562
-rw-r--r--nixos/modules/services/matrix/mjolnir.md4
-rw-r--r--nixos/modules/services/matrix/synapse.md6
-rw-r--r--nixos/modules/services/matrix/synapse.nix5
-rw-r--r--nixos/modules/services/misc/anki-sync-server.md6
-rw-r--r--nixos/modules/services/misc/ankisyncd.nix2
-rw-r--r--nixos/modules/services/misc/autorandr.nix9
-rw-r--r--nixos/modules/services/misc/bepasty.nix2
-rw-r--r--nixos/modules/services/misc/calibre-server.nix2
-rw-r--r--nixos/modules/services/misc/confd.nix2
-rw-r--r--nixos/modules/services/misc/etebase-server.nix2
-rw-r--r--nixos/modules/services/misc/etesync-dav.nix2
-rw-r--r--nixos/modules/services/misc/evdevremapkeys.nix2
-rw-r--r--nixos/modules/services/misc/forgejo.md40
-rw-r--r--nixos/modules/services/misc/forgejo.nix6
-rw-r--r--nixos/modules/services/misc/geoipupdate.nix2
-rw-r--r--nixos/modules/services/misc/gitea.nix4
-rw-r--r--nixos/modules/services/misc/gitlab.md82
-rw-r--r--nixos/modules/services/misc/gitlab.nix30
-rw-r--r--nixos/modules/services/misc/gogs.nix2
-rw-r--r--nixos/modules/services/misc/gollum.nix2
-rw-r--r--nixos/modules/services/misc/graphical-desktop.nix54
-rw-r--r--nixos/modules/services/misc/greenclip.nix2
-rw-r--r--nixos/modules/services/misc/homepage-dashboard.nix2
-rw-r--r--nixos/modules/services/misc/invidious-router.nix121
-rw-r--r--nixos/modules/services/misc/jackett.nix2
-rw-r--r--nixos/modules/services/misc/languagetool.nix2
-rw-r--r--nixos/modules/services/misc/leaps.nix2
-rw-r--r--nixos/modules/services/misc/libreddit.nix2
-rw-r--r--nixos/modules/services/misc/lidarr.nix2
-rw-r--r--nixos/modules/services/misc/lifecycled.nix2
-rw-r--r--nixos/modules/services/misc/llama-cpp.nix4
-rw-r--r--nixos/modules/services/misc/logkeys.nix2
-rw-r--r--nixos/modules/services/misc/mbpfan.nix13
-rw-r--r--nixos/modules/services/misc/mollysocket.nix133
-rw-r--r--nixos/modules/services/misc/nitter.nix2
-rw-r--r--nixos/modules/services/misc/nix-optimise.nix8
-rw-r--r--nixos/modules/services/misc/nzbget.nix2
-rw-r--r--nixos/modules/services/misc/nzbhydra2.nix2
-rw-r--r--nixos/modules/services/misc/ollama.nix76
-rw-r--r--nixos/modules/services/misc/ombi.nix4
-rw-r--r--nixos/modules/services/misc/owncast.nix2
-rw-r--r--nixos/modules/services/misc/paperless.nix51
-rw-r--r--nixos/modules/services/misc/pinnwand.nix2
-rw-r--r--nixos/modules/services/misc/plikd.nix2
-rw-r--r--nixos/modules/services/misc/prowlarr.nix2
-rw-r--r--nixos/modules/services/misc/radarr.nix2
-rw-r--r--nixos/modules/services/misc/readarr.nix2
-rw-r--r--nixos/modules/services/misc/redmine.nix44
-rw-r--r--nixos/modules/services/misc/rippled.nix2
-rw-r--r--nixos/modules/services/misc/signald.nix2
-rw-r--r--nixos/modules/services/misc/sourcehut/default.md8
-rw-r--r--nixos/modules/services/misc/tandoor-recipes.nix6
-rw-r--r--nixos/modules/services/misc/wastebin.nix158
-rw-r--r--nixos/modules/services/misc/weechat.md4
-rw-r--r--nixos/modules/services/misc/workout-tracker.nix83
-rw-r--r--nixos/modules/services/monitoring/certspotter.md48
-rw-r--r--nixos/modules/services/monitoring/goss.md2
-rw-r--r--nixos/modules/services/monitoring/nezha-agent.nix103
-rw-r--r--nixos/modules/services/monitoring/parsedmarc.md124
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.md15
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix24
-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/nats.nix34
-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/monitoring/thanos.nix8
-rw-r--r--nixos/modules/services/monitoring/zabbix-proxy.nix4
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix4
-rw-r--r--nixos/modules/services/network-filesystems/davfs2.nix49
-rw-r--r--nixos/modules/services/network-filesystems/litestream/default.md2
-rw-r--r--nixos/modules/services/network-filesystems/nfsd.nix19
-rw-r--r--nixos/modules/services/networking/bind.nix2
-rw-r--r--nixos/modules/services/networking/cjdns.nix8
-rw-r--r--nixos/modules/services/networking/cloudflared.nix2
-rw-r--r--nixos/modules/services/networking/dnscache.nix6
-rw-r--r--nixos/modules/services/networking/dnsproxy.nix106
-rw-r--r--nixos/modules/services/networking/firefox-syncserver.md24
-rw-r--r--nixos/modules/services/networking/i2p.nix5
-rw-r--r--nixos/modules/services/networking/kea.nix1
-rw-r--r--nixos/modules/services/networking/microsocks.nix146
-rw-r--r--nixos/modules/services/networking/mihomo.nix118
-rw-r--r--nixos/modules/services/networking/mosquitto.md73
-rw-r--r--nixos/modules/services/networking/mycelium.nix142
-rw-r--r--nixos/modules/services/networking/netbird.md26
-rw-r--r--nixos/modules/services/networking/networkmanager.nix29
-rw-r--r--nixos/modules/services/networking/nsd.nix165
-rw-r--r--nixos/modules/services/networking/pleroma.md196
-rw-r--r--nixos/modules/services/networking/prosody.md60
-rw-r--r--nixos/modules/services/networking/radicale.nix2
-rw-r--r--nixos/modules/services/networking/scion/scion-control.nix69
-rw-r--r--nixos/modules/services/networking/scion/scion-daemon.nix64
-rw-r--r--nixos/modules/services/networking/scion/scion-dispatcher.nix74
-rw-r--r--nixos/modules/services/networking/scion/scion-router.nix49
-rw-r--r--nixos/modules/services/networking/scion/scion.nix39
-rw-r--r--nixos/modules/services/networking/soju.nix24
-rw-r--r--nixos/modules/services/networking/technitium-dns-server.nix109
-rw-r--r--nixos/modules/services/networking/tinc.nix14
-rw-r--r--nixos/modules/services/networking/tinyproxy.nix1
-rw-r--r--nixos/modules/services/networking/unifi.nix4
-rw-r--r--nixos/modules/services/networking/v2raya.nix2
-rw-r--r--nixos/modules/services/networking/yggdrasil.md6
-rw-r--r--nixos/modules/services/search/meilisearch.md4
-rw-r--r--nixos/modules/services/security/oauth2_proxy_nginx.nix35
-rw-r--r--nixos/modules/services/security/usbguard.nix4
-rw-r--r--nixos/modules/services/security/vaultwarden/default.nix78
-rw-r--r--nixos/modules/services/security/yubikey-agent.nix2
-rw-r--r--nixos/modules/services/system/cloud-init.nix14
-rw-r--r--nixos/modules/services/system/dbus.nix11
-rw-r--r--nixos/modules/services/system/nix-daemon.nix2
-rw-r--r--nixos/modules/services/video/photonvision.nix64
-rw-r--r--nixos/modules/services/video/unifi-video.nix2
-rw-r--r--nixos/modules/services/web-apps/akkoma.md284
-rw-r--r--nixos/modules/services/web-apps/c2fmzq-server.md4
-rw-r--r--nixos/modules/services/web-apps/castopod.md (renamed from nixos/modules/services/audio/castopod.md)21
-rw-r--r--nixos/modules/services/web-apps/castopod.nix (renamed from nixos/modules/services/audio/castopod.nix)73
-rw-r--r--nixos/modules/services/web-apps/davis.md32
-rw-r--r--nixos/modules/services/web-apps/davis.nix554
-rw-r--r--nixos/modules/services/web-apps/discourse.md228
-rw-r--r--nixos/modules/services/web-apps/engelsystem.nix1
-rw-r--r--nixos/modules/services/web-apps/gotosocial.md52
-rw-r--r--nixos/modules/services/web-apps/grocy.md6
-rw-r--r--nixos/modules/services/web-apps/healthchecks.nix3
-rw-r--r--nixos/modules/services/web-apps/hedgedoc.nix8
-rw-r--r--nixos/modules/services/web-apps/hledger-web.nix45
-rw-r--r--nixos/modules/services/web-apps/invidious.nix4
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.md4
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.nix11
-rw-r--r--nixos/modules/services/web-apps/kavita.nix67
-rw-r--r--nixos/modules/services/web-apps/keycloak.md24
-rw-r--r--nixos/modules/services/web-apps/komga.nix145
-rw-r--r--nixos/modules/services/web-apps/lemmy.md14
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix15
-rw-r--r--nixos/modules/services/web-apps/movim.nix711
-rw-r--r--nixos/modules/services/web-apps/nextcloud.md6
-rw-r--r--nixos/modules/services/web-apps/ocis.md113
-rw-r--r--nixos/modules/services/web-apps/ocis.nix201
-rw-r--r--nixos/modules/services/web-apps/outline.nix2
-rw-r--r--nixos/modules/services/web-apps/peertube.nix292
-rw-r--r--nixos/modules/services/web-apps/pict-rs.md4
-rw-r--r--nixos/modules/services/web-apps/plausible.md2
-rw-r--r--nixos/modules/services/web-apps/pretix.nix581
-rw-r--r--nixos/modules/services/web-apps/rss-bridge.nix61
-rw-r--r--nixos/modules/services/web-apps/silverbullet.nix123
-rw-r--r--nixos/modules/services/web-apps/slskd.nix326
-rw-r--r--nixos/modules/services/web-apps/suwayomi-server.md5
-rw-r--r--nixos/modules/services/web-apps/suwayomi-server.nix11
-rw-r--r--nixos/modules/services/web-apps/zabbix.nix4
-rw-r--r--nixos/modules/services/web-servers/garage.md2
-rw-r--r--nixos/modules/services/web-servers/garage.nix14
-rw-r--r--nixos/modules/services/x11/desktop-managers/budgie.nix11
-rw-r--r--nixos/modules/services/x11/desktop-managers/cinnamon.nix12
-rw-r--r--nixos/modules/services/x11/desktop-managers/deepin.nix6
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/enlightenment.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome.md110
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/lumina.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/pantheon.md37
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix23
-rw-r--r--nixos/modules/services/x11/desktop-managers/phosh.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix14
-rw-r--r--nixos/modules/services/x11/desktop-managers/surf-display.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/default.nix221
-rw-r--r--nixos/modules/services/x11/display-managers/gdm.nix20
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix4
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix20
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix3
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/nimdow.nix11
-rw-r--r--nixos/modules/services/x11/window-managers/ragnarwm.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/xmonad.nix2
-rw-r--r--nixos/modules/services/x11/xserver.nix65
-rw-r--r--nixos/modules/system/boot/clevis.md12
-rw-r--r--nixos/modules/system/boot/initrd-ssh.nix30
-rw-r--r--nixos/modules/system/boot/kernel.nix5
-rw-r--r--nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh8
-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/plymouth.nix6
-rw-r--r--nixos/modules/system/boot/stage-1.nix10
-rw-r--r--nixos/modules/system/boot/systemd.nix3
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix55
-rw-r--r--nixos/modules/system/boot/systemd/repart.nix6
-rw-r--r--nixos/modules/system/boot/timesyncd.nix3
-rw-r--r--nixos/modules/system/boot/uki.nix15
-rw-r--r--nixos/modules/tasks/filesystems/bcachefs.nix19
-rw-r--r--nixos/modules/tasks/filesystems/envfs.nix4
-rw-r--r--nixos/modules/tasks/filesystems/nfs.nix65
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix7
-rw-r--r--nixos/modules/testing/test-instrumentation.nix14
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix4
-rw-r--r--nixos/modules/virtualisation/digital-ocean-config.nix8
-rw-r--r--nixos/modules/virtualisation/incus.nix187
-rw-r--r--nixos/modules/virtualisation/lxd.nix4
-rw-r--r--nixos/modules/virtualisation/nixos-containers.nix11
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix9
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix120
-rw-r--r--nixos/release-combined.nix5
-rw-r--r--nixos/release.nix2
-rw-r--r--nixos/tests/agda.nix7
-rw-r--r--nixos/tests/all-tests.nix34
-rw-r--r--nixos/tests/armagetronad.nix12
-rw-r--r--nixos/tests/ayatana-indicators.nix2
-rw-r--r--nixos/tests/budgie.nix53
-rw-r--r--nixos/tests/castopod.nix272
-rw-r--r--nixos/tests/cinnamon-wayland.nix2
-rw-r--r--nixos/tests/cinnamon.nix4
-rw-r--r--nixos/tests/common/auto.nix10
-rw-r--r--nixos/tests/common/x11.nix2
-rw-r--r--nixos/tests/containers-require-bind-mounts.nix35
-rw-r--r--nixos/tests/davis.nix59
-rw-r--r--nixos/tests/docker-tools.nix8
-rw-r--r--nixos/tests/drawterm.nix15
-rw-r--r--nixos/tests/drbd.nix6
-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/firefoxpwa.nix36
-rw-r--r--nixos/tests/forgejo.nix6
-rw-r--r--nixos/tests/freetube.nix2
-rw-r--r--nixos/tests/gitlab.nix4
-rw-r--r--nixos/tests/gnome-flashback.nix11
-rw-r--r--nixos/tests/gnome-xorg.nix11
-rw-r--r--nixos/tests/gnome.nix9
-rw-r--r--nixos/tests/goss.nix8
-rw-r--r--nixos/tests/herbstluftwm.nix2
-rw-r--r--nixos/tests/hibernate.nix2
-rw-r--r--nixos/tests/hledger-web.nix2
-rw-r--r--nixos/tests/hydra/common.nix9
-rw-r--r--nixos/tests/i3wm.nix2
-rw-r--r--nixos/tests/incus/container.nix15
-rw-r--r--nixos/tests/incus/default.nix19
-rw-r--r--nixos/tests/incus/lxd-to-incus.nix2
-rw-r--r--nixos/tests/incus/storage.nix46
-rw-r--r--nixos/tests/installer-systemd-stage-1.nix3
-rw-r--r--nixos/tests/installer.nix441
-rw-r--r--nixos/tests/invidious.nix2
-rw-r--r--nixos/tests/k3s/multi-node.nix4
-rw-r--r--nixos/tests/k3s/single-node.nix4
-rw-r--r--nixos/tests/kavita.nix46
-rw-r--r--nixos/tests/kea.nix26
-rw-r--r--nixos/tests/kernel-generic.nix1
-rw-r--r--nixos/tests/kernel-rust.nix6
-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/lightdm.nix2
-rw-r--r--nixos/tests/lvm2/default.nix7
-rw-r--r--nixos/tests/lvm2/systemd-stage-1.nix6
-rw-r--r--nixos/tests/lvm2/thinpool.nix5
-rw-r--r--nixos/tests/lvm2/vdo.nix4
-rw-r--r--nixos/tests/maestral.nix9
-rw-r--r--nixos/tests/mate-wayland.nix63
-rw-r--r--nixos/tests/mate.nix9
-rw-r--r--nixos/tests/matrix/mautrix-meta-postgres.nix221
-rw-r--r--nixos/tests/matrix/mautrix-meta-sqlite.nix247
-rw-r--r--nixos/tests/mihomo.nix44
-rw-r--r--nixos/tests/miniflux.nix2
-rw-r--r--nixos/tests/miriway.nix6
-rw-r--r--nixos/tests/mollysocket.nix27
-rw-r--r--nixos/tests/monetdb.nix77
-rw-r--r--nixos/tests/mongodb.nix2
-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/nix-ld.nix52
-rw-r--r--nixos/tests/nixops/default.nix18
-rw-r--r--nixos/tests/nixos-rebuild-install-bootloader.nix2
-rw-r--r--nixos/tests/ocis.nix217
-rw-r--r--nixos/tests/oddjobd.nix23
-rw-r--r--nixos/tests/opensearch.nix30
-rw-r--r--nixos/tests/pantheon.nix13
-rw-r--r--nixos/tests/pg_anonymizer.nix2
-rw-r--r--nixos/tests/pgmanage.nix2
-rw-r--r--nixos/tests/photonvision.nix21
-rw-r--r--nixos/tests/plasma-bigscreen.nix6
-rw-r--r--nixos/tests/plasma5-systemd-start.nix9
-rw-r--r--nixos/tests/plasma5.nix6
-rw-r--r--nixos/tests/plasma6.nix8
-rw-r--r--nixos/tests/prometheus-exporters.nix48
-rw-r--r--nixos/tests/ragnarwm.nix2
-rw-r--r--nixos/tests/redis.nix117
-rw-r--r--nixos/tests/redlib.nix20
-rw-r--r--nixos/tests/redmine.nix1
-rw-r--r--nixos/tests/restic-rest-server.nix122
-rw-r--r--nixos/tests/scion/freestanding-deployment/README.rst12
-rw-r--r--nixos/tests/scion/freestanding-deployment/default.nix172
-rw-r--r--nixos/tests/scion/freestanding-deployment/topology1.json51
-rw-r--r--nixos/tests/scion/freestanding-deployment/topology2.json51
-rw-r--r--nixos/tests/scion/freestanding-deployment/topology3.json60
-rw-r--r--nixos/tests/scion/freestanding-deployment/topology4.json40
-rw-r--r--nixos/tests/scion/freestanding-deployment/topology5.json40
-rw-r--r--nixos/tests/sddm.nix8
-rw-r--r--nixos/tests/silverbullet.nix47
-rw-r--r--nixos/tests/soju.nix31
-rw-r--r--nixos/tests/switch-test.nix16
-rw-r--r--nixos/tests/systemd-machinectl.nix272
-rw-r--r--nixos/tests/systemd-user-linger.nix39
-rw-r--r--nixos/tests/technitium-dns-server.nix21
-rw-r--r--nixos/tests/tracee.nix93
-rw-r--r--nixos/tests/unifi.nix4
-rw-r--r--nixos/tests/ustreamer.nix75
-rw-r--r--nixos/tests/vaultwarden.nix2
-rw-r--r--nixos/tests/vscodium.nix2
-rw-r--r--nixos/tests/wastebin.nix19
-rw-r--r--nixos/tests/web-apps/mastodon/remote-databases.nix2
-rw-r--r--nixos/tests/web-apps/movim/default.nix8
-rw-r--r--nixos/tests/web-apps/movim/standard.nix102
-rw-r--r--nixos/tests/web-apps/peertube.nix28
-rw-r--r--nixos/tests/web-apps/pretix.nix47
-rw-r--r--nixos/tests/wmderland.nix2
-rw-r--r--nixos/tests/workout-tracker.nix29
-rw-r--r--nixos/tests/xfce.nix10
-rw-r--r--nixos/tests/xmonad-xdg-autostart.nix2
-rw-r--r--nixos/tests/xmonad.nix2
627 files changed, 14612 insertions, 4841 deletions
diff --git a/nixos/doc/manual/administration/cleaning-store.chapter.md b/nixos/doc/manual/administration/cleaning-store.chapter.md
index c9140d0869c77..4f404882055a0 100644
--- a/nixos/doc/manual/administration/cleaning-store.chapter.md
+++ b/nixos/doc/manual/administration/cleaning-store.chapter.md
@@ -21,8 +21,10 @@ You can tell NixOS in `configuration.nix` to run this unit automatically
 at certain points in time, for instance, every night at 03:15:
 
 ```nix
-nix.gc.automatic = true;
-nix.gc.dates = "03:15";
+{
+  nix.gc.automatic = true;
+  nix.gc.dates = "03:15";
+}
 ```
 
 The commands above do not remove garbage collector roots, such as old
diff --git a/nixos/doc/manual/administration/container-networking.section.md b/nixos/doc/manual/administration/container-networking.section.md
index 0873768376cc9..723cf211d8720 100644
--- a/nixos/doc/manual/administration/container-networking.section.md
+++ b/nixos/doc/manual/administration/container-networking.section.md
@@ -26,9 +26,11 @@ host to rewrite container traffic to use your external IP address. This
 can be accomplished using the following configuration on the host:
 
 ```nix
-networking.nat.enable = true;
-networking.nat.internalInterfaces = ["ve-+"];
-networking.nat.externalInterface = "eth0";
+{
+  networking.nat.enable = true;
+  networking.nat.internalInterfaces = ["ve-+"];
+  networking.nat.externalInterface = "eth0";
+}
 ```
 
 where `eth0` should be replaced with the desired external interface.
@@ -38,7 +40,9 @@ If you are using Network Manager, you need to explicitly prevent it from
 managing container interfaces:
 
 ```nix
-networking.networkmanager.unmanaged = [ "interface-name:ve-*" ];
+{
+  networking.networkmanager.unmanaged = [ "interface-name:ve-*" ];
+}
 ```
 
 You may need to restart your system for the changes to take effect.
diff --git a/nixos/doc/manual/administration/control-groups.chapter.md b/nixos/doc/manual/administration/control-groups.chapter.md
index abe8dd80b5ab0..8682236ca1a92 100644
--- a/nixos/doc/manual/administration/control-groups.chapter.md
+++ b/nixos/doc/manual/administration/control-groups.chapter.md
@@ -39,7 +39,9 @@ they were in the same cgroup, then the PostgreSQL process would get
 `configuration.nix`:
 
 ```nix
-systemd.services.httpd.serviceConfig.CPUShares = 512;
+{
+  systemd.services.httpd.serviceConfig.CPUShares = 512;
+}
 ```
 
 By default, every cgroup has 1024 CPU shares, so this will halve the CPU
@@ -52,7 +54,9 @@ limits can be specified in `configuration.nix`; for instance, to limit
 `httpd.service` to 512 MiB of RAM (excluding swap):
 
 ```nix
-systemd.services.httpd.serviceConfig.MemoryLimit = "512M";
+{
+  systemd.services.httpd.serviceConfig.MemoryLimit = "512M";
+}
 ```
 
 The command `systemd-cgtop` shows a continuously updated list of all
diff --git a/nixos/doc/manual/administration/declarative-containers.section.md b/nixos/doc/manual/administration/declarative-containers.section.md
index eaa50d3c663d4..f16fa8332b51e 100644
--- a/nixos/doc/manual/administration/declarative-containers.section.md
+++ b/nixos/doc/manual/administration/declarative-containers.section.md
@@ -5,13 +5,15 @@ You can also specify containers and their configuration in the host's
 shall be a container named `database` running PostgreSQL:
 
 ```nix
-containers.database =
-  { config =
-      { config, pkgs, ... }:
-      { services.postgresql.enable = true;
-      services.postgresql.package = pkgs.postgresql_14;
-      };
-  };
+{
+  containers.database =
+    { config =
+        { config, pkgs, ... }:
+        { services.postgresql.enable = true;
+        services.postgresql.package = pkgs.postgresql_14;
+        };
+    };
+}
 ```
 
 If you run `nixos-rebuild switch`, the container will be built. If the
@@ -25,11 +27,13 @@ cannot change the network configuration. You can give a container its
 own network as follows:
 
 ```nix
-containers.database = {
-  privateNetwork = true;
-  hostAddress = "192.168.100.10";
-  localAddress = "192.168.100.11";
-};
+{
+  containers.database = {
+    privateNetwork = true;
+    hostAddress = "192.168.100.10";
+    localAddress = "192.168.100.11";
+  };
+}
 ```
 
 This gives the container a private virtual Ethernet interface with IP
diff --git a/nixos/doc/manual/administration/service-mgmt.chapter.md b/nixos/doc/manual/administration/service-mgmt.chapter.md
index bc9bdbe3708bc..49e8d801fb80f 100644
--- a/nixos/doc/manual/administration/service-mgmt.chapter.md
+++ b/nixos/doc/manual/administration/service-mgmt.chapter.md
@@ -82,7 +82,9 @@ In order to enable a systemd *system* service with provided upstream
 package, use (e.g):
 
 ```nix
-systemd.packages = [ pkgs.packagekit ];
+{
+  systemd.packages = [ pkgs.packagekit ];
+}
 ```
 
 Usually NixOS modules written by the community do the above, plus take
diff --git a/nixos/doc/manual/administration/system-state.chapter.md b/nixos/doc/manual/administration/system-state.chapter.md
index 6840cc3902578..89013933cda5b 100644
--- a/nixos/doc/manual/administration/system-state.chapter.md
+++ b/nixos/doc/manual/administration/system-state.chapter.md
@@ -7,7 +7,7 @@ However, it is possible and not-uncommon to create [impermanent systems], whose
 `rootfs` is either a `tmpfs` or reset during boot. While NixOS itself supports
 this kind of configuration, special care needs to be taken.
 
-[impermanent systems]: https://nixos.wiki/wiki/Impermanence
+[impermanent systems]: https://wiki.nixos.org/wiki/Impermanence
 
 
 ```{=include=} sections
diff --git a/nixos/doc/manual/configuration/abstractions.section.md b/nixos/doc/manual/configuration/abstractions.section.md
index bf26e4c51ed37..5bc44aa722457 100644
--- a/nixos/doc/manual/configuration/abstractions.section.md
+++ b/nixos/doc/manual/configuration/abstractions.section.md
@@ -47,9 +47,9 @@ You can write a `let` wherever an expression is allowed. Thus, you also could ha
 ```nix
 {
   services.httpd.virtualHosts =
-    let commonConfig = ...; in
-    { "blog.example.org" = (commonConfig // { ... })
-      "wiki.example.org" = (commonConfig // { ... })
+    let commonConfig = { /* ... */ }; in
+    { "blog.example.org" = (commonConfig // { /* ... */ });
+      "wiki.example.org" = (commonConfig // { /* ... */ });
     };
 }
 ```
diff --git a/nixos/doc/manual/configuration/ad-hoc-network-config.section.md b/nixos/doc/manual/configuration/ad-hoc-network-config.section.md
index 4478d77f361d4..ecb06ad984a33 100644
--- a/nixos/doc/manual/configuration/ad-hoc-network-config.section.md
+++ b/nixos/doc/manual/configuration/ad-hoc-network-config.section.md
@@ -6,8 +6,10 @@ is useful for doing network configuration not covered by the existing NixOS
 modules. For instance, to statically configure an IPv6 address:
 
 ```nix
-networking.localCommands =
-  ''
-    ip -6 addr add 2001:610:685:1::1/64 dev eth0
-  '';
+{
+  networking.localCommands =
+    ''
+      ip -6 addr add 2001:610:685:1::1/64 dev eth0
+    '';
+}
 ```
diff --git a/nixos/doc/manual/configuration/adding-custom-packages.section.md b/nixos/doc/manual/configuration/adding-custom-packages.section.md
index 2340723e07c6b..f9a5221d6c930 100644
--- a/nixos/doc/manual/configuration/adding-custom-packages.section.md
+++ b/nixos/doc/manual/configuration/adding-custom-packages.section.md
@@ -23,7 +23,9 @@ Then you write and test the package as described in the Nixpkgs manual.
 Finally, you add it to [](#opt-environment.systemPackages), e.g.
 
 ```nix
-environment.systemPackages = [ pkgs.my-package ];
+{
+  environment.systemPackages = [ pkgs.my-package ];
+}
 ```
 
 and you run `nixos-rebuild`, specifying your own Nixpkgs tree:
@@ -38,24 +40,28 @@ tree. For instance, here is how you specify a build of the
 `configuration.nix`:
 
 ```nix
-environment.systemPackages =
-  let
-    my-hello = with pkgs; stdenv.mkDerivation rec {
-      name = "hello-2.8";
-      src = fetchurl {
-        url = "mirror://gnu/hello/${name}.tar.gz";
-        hash = "sha256-5rd/gffPfa761Kn1tl3myunD8TuM+66oy1O7XqVGDXM=";
+{
+  environment.systemPackages =
+    let
+      my-hello = with pkgs; stdenv.mkDerivation rec {
+        name = "hello-2.8";
+        src = fetchurl {
+          url = "mirror://gnu/hello/${name}.tar.gz";
+          hash = "sha256-5rd/gffPfa761Kn1tl3myunD8TuM+66oy1O7XqVGDXM=";
+        };
       };
-    };
-  in
-  [ my-hello ];
+    in
+    [ my-hello ];
+}
 ```
 
 Of course, you can also move the definition of `my-hello` into a
 separate Nix expression, e.g.
 
 ```nix
-environment.systemPackages = [ (import ./my-hello.nix) ];
+{
+  environment.systemPackages = [ (import ./my-hello.nix) ];
+}
 ```
 
 where `my-hello.nix` contains:
@@ -88,7 +94,9 @@ section](#module-services-flatpak). AppImages will not run "as-is" on NixOS.
 First you need to install `appimage-run`: add to `/etc/nixos/configuration.nix`
 
 ```nix
-environment.systemPackages = [ pkgs.appimage-run ];
+{
+  environment.systemPackages = [ pkgs.appimage-run ];
+}
 ```
 
 Then instead of running the AppImage "as-is", run `appimage-run foo.appimage`.
diff --git a/nixos/doc/manual/configuration/config-file.section.md b/nixos/doc/manual/configuration/config-file.section.md
index b010026c58286..e213aae29ae3d 100644
--- a/nixos/doc/manual/configuration/config-file.section.md
+++ b/nixos/doc/manual/configuration/config-file.section.md
@@ -5,7 +5,7 @@ The NixOS configuration file generally looks like this:
 ```nix
 { config, pkgs, ... }:
 
-{ option definitions
+{ /* option definitions */
 }
 ```
 
@@ -80,7 +80,9 @@ Strings
 :   Strings are enclosed in double quotes, e.g.
 
     ```nix
-    networking.hostName = "dexter";
+    {
+      networking.hostName = "dexter";
+    }
     ```
 
     Special characters can be escaped by prefixing them with a backslash
@@ -89,11 +91,13 @@ Strings
     Multi-line strings can be enclosed in *double single quotes*, e.g.
 
     ```nix
-    networking.extraHosts =
-      ''
-        127.0.0.2 other-localhost
-        10.0.0.1 server
-      '';
+    {
+      networking.extraHosts =
+        ''
+          127.0.0.2 other-localhost
+          10.0.0.1 server
+        '';
+    }
     ```
 
     The main difference is that it strips from each line a number of
@@ -108,8 +112,10 @@ Booleans
 :   These can be `true` or `false`, e.g.
 
     ```nix
-    networking.firewall.enable = true;
-    networking.firewall.allowPing = false;
+    {
+      networking.firewall.enable = true;
+      networking.firewall.allowPing = false;
+    }
     ```
 
 Integers
@@ -117,7 +123,9 @@ Integers
 :   For example,
 
     ```nix
-    boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 60;
+    {
+      boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 60;
+    }
     ```
 
     (Note that here the attribute name `net.ipv4.tcp_keepalive_time` is
@@ -132,11 +140,13 @@ Sets
     braces, as in the option definition
 
     ```nix
-    fileSystems."/boot" =
-      { device = "/dev/sda1";
-        fsType = "ext4";
-        options = [ "rw" "data=ordered" "relatime" ];
-      };
+    {
+      fileSystems."/boot" =
+        { device = "/dev/sda1";
+          fsType = "ext4";
+          options = [ "rw" "data=ordered" "relatime" ];
+        };
+    }
     ```
 
 Lists
@@ -145,13 +155,17 @@ Lists
     separated by whitespace, like this:
 
     ```nix
-    boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ];
+    {
+      boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ];
+    }
     ```
 
     List elements can be any other type, e.g. sets:
 
     ```nix
-    swapDevices = [ { device = "/dev/disk/by-label/swap"; } ];
+    {
+      swapDevices = [ { device = "/dev/disk/by-label/swap"; } ];
+    }
     ```
 
 Packages
@@ -161,12 +175,14 @@ Packages
     argument `pkgs`. Typical uses:
 
     ```nix
-    environment.systemPackages =
-      [ pkgs.thunderbird
-        pkgs.emacs
-      ];
-
-    services.postgresql.package = pkgs.postgresql_14;
+    {
+      environment.systemPackages =
+        [ pkgs.thunderbird
+          pkgs.emacs
+        ];
+
+      services.postgresql.package = pkgs.postgresql_14;
+    }
     ```
 
     The latter option definition changes the default PostgreSQL package
diff --git a/nixos/doc/manual/configuration/customizing-packages.section.md b/nixos/doc/manual/configuration/customizing-packages.section.md
index 76413b7d84fb8..a524ef266eaff 100644
--- a/nixos/doc/manual/configuration/customizing-packages.section.md
+++ b/nixos/doc/manual/configuration/customizing-packages.section.md
@@ -16,18 +16,20 @@ Examples include:
 
 You can use them like this:
 ```nix
-environment.systemPackages = with pkgs; [
-  sl
-  (pass.withExtensions (subpkgs: with subpkgs; [
-    pass-audit
-    pass-otp
-    pass-genphrase
-  ]))
-  (python3.withPackages (subpkgs: with subpkgs; [
-      requests
-  ]))
-  cowsay
-];
+{
+  environment.systemPackages = with pkgs; [
+    sl
+    (pass.withExtensions (subpkgs: with subpkgs; [
+      pass-audit
+      pass-otp
+      pass-genphrase
+    ]))
+    (python3.withPackages (subpkgs: with subpkgs; [
+        requests
+    ]))
+    cowsay
+  ];
+}
 ```
 :::
 
@@ -38,7 +40,9 @@ dependency on GTK 2. If you want to build it against GTK 3, you can
 specify that as follows:
 
 ```nix
-environment.systemPackages = [ (pkgs.emacs.override { gtk = pkgs.gtk3; }) ];
+{
+  environment.systemPackages = [ (pkgs.emacs.override { gtk = pkgs.gtk3; }) ];
+}
 ```
 
 The function `override` performs the call to the Nix function that
@@ -58,12 +62,14 @@ of the package, such as the source code. For instance, if you want to
 override the source code of Emacs, you can say:
 
 ```nix
-environment.systemPackages = [
-  (pkgs.emacs.overrideAttrs (oldAttrs: {
-    name = "emacs-25.0-pre";
-    src = /path/to/my/emacs/tree;
-  }))
-];
+{
+  environment.systemPackages = [
+    (pkgs.emacs.overrideAttrs (oldAttrs: {
+      name = "emacs-25.0-pre";
+      src = /path/to/my/emacs/tree;
+    }))
+  ];
+}
 ```
 
 Here, `overrideAttrs` takes the Nix derivation specified by `pkgs.emacs`
@@ -80,9 +86,11 @@ two instances of the package. If you want to have everything depend on
 your customised instance, you can apply a *global* override as follows:
 
 ```nix
-nixpkgs.config.packageOverrides = pkgs:
-  { emacs = pkgs.emacs.override { gtk = pkgs.gtk3; };
-  };
+{
+  nixpkgs.config.packageOverrides = pkgs:
+    { emacs = pkgs.emacs.override { gtk = pkgs.gtk3; };
+    };
+}
 ```
 
 The effect of this definition is essentially equivalent to modifying the
diff --git a/nixos/doc/manual/configuration/declarative-packages.section.md b/nixos/doc/manual/configuration/declarative-packages.section.md
index 480e250da8c73..6cdd520dcf150 100644
--- a/nixos/doc/manual/configuration/declarative-packages.section.md
+++ b/nixos/doc/manual/configuration/declarative-packages.section.md
@@ -7,7 +7,9 @@ following line to `configuration.nix` enables the Mozilla Thunderbird
 email application:
 
 ```nix
-environment.systemPackages = [ pkgs.thunderbird ];
+{
+  environment.systemPackages = [ pkgs.thunderbird ];
+}
 ```
 
 The effect of this specification is that the Thunderbird package from
diff --git a/nixos/doc/manual/configuration/file-systems.chapter.md b/nixos/doc/manual/configuration/file-systems.chapter.md
index 3dfdd20ac33ec..4bdd9c60e3278 100644
--- a/nixos/doc/manual/configuration/file-systems.chapter.md
+++ b/nixos/doc/manual/configuration/file-systems.chapter.md
@@ -6,10 +6,12 @@ Ext4 file system on device `/dev/disk/by-label/data` onto the mount
 point `/data`:
 
 ```nix
-fileSystems."/data" =
-  { device = "/dev/disk/by-label/data";
-    fsType = "ext4";
-  };
+{
+  fileSystems."/data" =
+    { device = "/dev/disk/by-label/data";
+      fsType = "ext4";
+    };
+}
 ```
 
 This will create an entry in `/etc/fstab`, which will generate a
diff --git a/nixos/doc/manual/configuration/firewall.section.md b/nixos/doc/manual/configuration/firewall.section.md
index dbf0ffb9273ee..9a71217944eee 100644
--- a/nixos/doc/manual/configuration/firewall.section.md
+++ b/nixos/doc/manual/configuration/firewall.section.md
@@ -5,14 +5,18 @@ and other unexpected packets. The firewall applies to both IPv4 and IPv6
 traffic. It is enabled by default. It can be disabled as follows:
 
 ```nix
-networking.firewall.enable = false;
+{
+  networking.firewall.enable = false;
+}
 ```
 
 If the firewall is enabled, you can open specific TCP ports to the
 outside world:
 
 ```nix
-networking.firewall.allowedTCPPorts = [ 80 443 ];
+{
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+}
 ```
 
 Note that TCP port 22 (ssh) is opened automatically if the SSH daemon is
@@ -22,10 +26,12 @@ enabled (`services.openssh.enable = true`). UDP ports can be opened through
 To open ranges of TCP ports:
 
 ```nix
-networking.firewall.allowedTCPPortRanges = [
-  { from = 4000; to = 4007; }
-  { from = 8000; to = 8010; }
-];
+{
+  networking.firewall.allowedTCPPortRanges = [
+    { from = 4000; to = 4007; }
+    { from = 8000; to = 8010; }
+  ];
+}
 ```
 
 Similarly, UDP port ranges can be opened through
diff --git a/nixos/doc/manual/configuration/gpu-accel.chapter.md b/nixos/doc/manual/configuration/gpu-accel.chapter.md
index aa63aec61669b..3b98bdd97c681 100644
--- a/nixos/doc/manual/configuration/gpu-accel.chapter.md
+++ b/nixos/doc/manual/configuration/gpu-accel.chapter.md
@@ -55,9 +55,11 @@ supported through the rocmPackages.clr.icd package. Adding this package to
 enables OpenCL support:
 
 ```nix
-hardware.opengl.extraPackages = [
-  rocmPackages.clr.icd
-];
+{
+  hardware.opengl.extraPackages = [
+    rocmPackages.clr.icd
+  ];
+}
 ```
 
 ### Intel {#sec-gpu-accel-opencl-intel}
@@ -74,9 +76,11 @@ to enable OpenCL support. For example, for Gen8 and later GPUs, the following
 configuration can be used:
 
 ```nix
-hardware.opengl.extraPackages = [
-  intel-compute-runtime
-];
+{
+  hardware.opengl.extraPackages = [
+    intel-compute-runtime
+  ];
+}
 ```
 
 ## Vulkan {#sec-gpu-accel-vulkan}
@@ -141,20 +145,22 @@ makes amdvlk the default driver and hides radv and lavapipe from the device list
 A specific driver can be forced as follows:
 
 ```nix
-hardware.opengl.extraPackages = [
-  pkgs.amdvlk
-];
-
-# To enable Vulkan support for 32-bit applications, also add:
-hardware.opengl.extraPackages32 = [
-  pkgs.driversi686Linux.amdvlk
-];
-
-# Force radv
-environment.variables.AMD_VULKAN_ICD = "RADV";
-# Or
-environment.variables.VK_ICD_FILENAMES =
-  "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json";
+{
+  hardware.opengl.extraPackages = [
+    pkgs.amdvlk
+  ];
+
+  # To enable Vulkan support for 32-bit applications, also add:
+  hardware.opengl.extraPackages32 = [
+    pkgs.driversi686Linux.amdvlk
+  ];
+
+  # Force radv
+  environment.variables.AMD_VULKAN_ICD = "RADV";
+  # Or
+  environment.variables.VK_ICD_FILENAMES =
+    "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json";
+}
 ```
 
 ## VA-API {#sec-gpu-accel-va-api}
@@ -178,17 +184,21 @@ $ nix-shell -p libva-utils --run vainfo
 Modern Intel GPUs use the iHD driver, which can be installed with:
 
 ```nix
-hardware.opengl.extraPackages = [
-  intel-media-driver
-];
+{
+  hardware.opengl.extraPackages = [
+    intel-media-driver
+  ];
+}
 ```
 
 Older Intel GPUs use the i965 driver, which can be installed with:
 
 ```nix
-hardware.opengl.extraPackages = [
-  intel-vaapi-driver
-];
+{
+  hardware.opengl.extraPackages = [
+    intel-vaapi-driver
+  ];
+}
 ```
 
 ## Common issues {#sec-gpu-accel-common-issues}
diff --git a/nixos/doc/manual/configuration/ipv4-config.section.md b/nixos/doc/manual/configuration/ipv4-config.section.md
index c73024b856d73..0464f53898554 100644
--- a/nixos/doc/manual/configuration/ipv4-config.section.md
+++ b/nixos/doc/manual/configuration/ipv4-config.section.md
@@ -5,18 +5,22 @@ configure network interfaces. However, you can configure an interface
 manually as follows:
 
 ```nix
-networking.interfaces.eth0.ipv4.addresses = [ {
-  address = "192.168.1.2";
-  prefixLength = 24;
-} ];
+{
+  networking.interfaces.eth0.ipv4.addresses = [ {
+    address = "192.168.1.2";
+    prefixLength = 24;
+  } ];
+}
 ```
 
 Typically you'll also want to set a default gateway and set of name
 servers:
 
 ```nix
-networking.defaultGateway = "192.168.1.1";
-networking.nameservers = [ "8.8.8.8" ];
+{
+  networking.defaultGateway = "192.168.1.1";
+  networking.nameservers = [ "8.8.8.8" ];
+}
 ```
 
 ::: {.note}
@@ -28,7 +32,9 @@ configuration is performed by `network-setup.service`.
 The host name is set using [](#opt-networking.hostName):
 
 ```nix
-networking.hostName = "cartman";
+{
+  networking.hostName = "cartman";
+}
 ```
 
 The default host name is `nixos`. Set it to the empty string (`""`) to
diff --git a/nixos/doc/manual/configuration/ipv6-config.section.md b/nixos/doc/manual/configuration/ipv6-config.section.md
index ce66f53ed4720..b4fe0d759b8ad 100644
--- a/nixos/doc/manual/configuration/ipv6-config.section.md
+++ b/nixos/doc/manual/configuration/ipv6-config.section.md
@@ -9,34 +9,42 @@ may be overridden on a per-interface basis by
 IPv6 support globally by setting:
 
 ```nix
-networking.enableIPv6 = false;
+{
+  networking.enableIPv6 = false;
+}
 ```
 
 You can disable IPv6 on a single interface using a normal sysctl (in
 this example, we use interface `eth0`):
 
 ```nix
-boot.kernel.sysctl."net.ipv6.conf.eth0.disable_ipv6" = true;
+{
+  boot.kernel.sysctl."net.ipv6.conf.eth0.disable_ipv6" = true;
+}
 ```
 
 As with IPv4 networking interfaces are automatically configured via
 DHCPv6. You can configure an interface manually:
 
 ```nix
-networking.interfaces.eth0.ipv6.addresses = [ {
-  address = "fe00:aa:bb:cc::2";
-  prefixLength = 64;
-} ];
+{
+  networking.interfaces.eth0.ipv6.addresses = [ {
+    address = "fe00:aa:bb:cc::2";
+    prefixLength = 64;
+  } ];
+}
 ```
 
 For configuring a gateway, optionally with explicitly specified
 interface:
 
 ```nix
-networking.defaultGateway6 = {
-  address = "fe00::1";
-  interface = "enp0s3";
-};
+{
+  networking.defaultGateway6 = {
+    address = "fe00::1";
+    interface = "enp0s3";
+  };
+}
 ```
 
 See [](#sec-ipv4) for similar examples and additional information.
diff --git a/nixos/doc/manual/configuration/kubernetes.chapter.md b/nixos/doc/manual/configuration/kubernetes.chapter.md
index f39726090e431..fba40b6487521 100644
--- a/nixos/doc/manual/configuration/kubernetes.chapter.md
+++ b/nixos/doc/manual/configuration/kubernetes.chapter.md
@@ -7,14 +7,16 @@ There are generally two ways of enabling Kubernetes on NixOS. One way is
 to enable and configure cluster components appropriately by hand:
 
 ```nix
-services.kubernetes = {
-  apiserver.enable = true;
-  controllerManager.enable = true;
-  scheduler.enable = true;
-  addonManager.enable = true;
-  proxy.enable = true;
-  flannel.enable = true;
-};
+{
+  services.kubernetes = {
+    apiserver.enable = true;
+    controllerManager.enable = true;
+    scheduler.enable = true;
+    addonManager.enable = true;
+    proxy.enable = true;
+    flannel.enable = true;
+  };
+}
 ```
 
 Another way is to assign cluster roles ("master" and/or "node") to
@@ -22,20 +24,26 @@ the host. This enables apiserver, controllerManager, scheduler,
 addonManager, kube-proxy and etcd:
 
 ```nix
-services.kubernetes.roles = [ "master" ];
+{
+  services.kubernetes.roles = [ "master" ];
+}
 ```
 
 While this will enable the kubelet and kube-proxy only:
 
 ```nix
-services.kubernetes.roles = [ "node" ];
+{
+  services.kubernetes.roles = [ "node" ];
+}
 ```
 
 Assigning both the master and node roles is usable if you want a single
 node Kubernetes cluster for dev or testing purposes:
 
 ```nix
-services.kubernetes.roles = [ "master" "node" ];
+{
+  services.kubernetes.roles = [ "master" "node" ];
+}
 ```
 
 Note: Assigning either role will also default both
diff --git a/nixos/doc/manual/configuration/linux-kernel.chapter.md b/nixos/doc/manual/configuration/linux-kernel.chapter.md
index 31d8d1a7d0cfe..3bc97446f452c 100644
--- a/nixos/doc/manual/configuration/linux-kernel.chapter.md
+++ b/nixos/doc/manual/configuration/linux-kernel.chapter.md
@@ -5,7 +5,9 @@ option `boot.kernelPackages`. For instance, this selects the Linux 3.10
 kernel:
 
 ```nix
-boot.kernelPackages = pkgs.linuxKernel.packages.linux_3_10;
+{
+  boot.kernelPackages = pkgs.linuxKernel.packages.linux_3_10;
+}
 ```
 
 Note that this not only replaces the kernel, but also packages that are
@@ -40,13 +42,15 @@ If you want to change the kernel configuration, you can use the
 instance, to enable support for the kernel debugger KGDB:
 
 ```nix
-nixpkgs.config.packageOverrides = pkgs: pkgs.lib.recursiveUpdate pkgs {
-  linuxKernel.kernels.linux_5_10 = pkgs.linuxKernel.kernels.linux_5_10.override {
-    extraConfig = ''
-      KGDB y
-    '';
+{
+  nixpkgs.config.packageOverrides = pkgs: pkgs.lib.recursiveUpdate pkgs {
+    linuxKernel.kernels.linux_5_10 = pkgs.linuxKernel.kernels.linux_5_10.override {
+      extraConfig = ''
+        KGDB y
+      '';
+    };
   };
-};
+}
 ```
 
 `extraConfig` takes a list of Linux kernel configuration options, one
@@ -59,14 +63,18 @@ by `udev`. You can force a module to be loaded via
 [](#opt-boot.kernelModules), e.g.
 
 ```nix
-boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ];
+{
+  boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ];
+}
 ```
 
 If the module is required early during the boot (e.g. to mount the root
 file system), you can use [](#opt-boot.initrd.kernelModules):
 
 ```nix
-boot.initrd.kernelModules = [ "cifs" ];
+{
+  boot.initrd.kernelModules = [ "cifs" ];
+}
 ```
 
 This causes the specified modules and their dependencies to be added to
@@ -76,7 +84,9 @@ Kernel runtime parameters can be set through
 [](#opt-boot.kernel.sysctl), e.g.
 
 ```nix
-boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 120;
+{
+  boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 120;
+}
 ```
 
 sets the kernel's TCP keepalive time to 120 seconds. To see the
@@ -89,7 +99,9 @@ Please refer to the Nixpkgs manual for the various ways of [building a custom ke
 To use your custom kernel package in your NixOS configuration, set
 
 ```nix
-boot.kernelPackages = pkgs.linuxPackagesFor yourCustomKernel;
+{
+  boot.kernelPackages = pkgs.linuxPackagesFor yourCustomKernel;
+}
 ```
 
 ## Rust {#sec-linux-rust}
@@ -99,15 +111,17 @@ default. For kernel versions 6.7 or newer, experimental Rust support
 can be enabled. In a NixOS configuration, set:
 
 ```nix
-boot.kernelPatches = [
-  {
-    name = "Rust Support";
-    patch = null;
-    features = {
-      rust = true;
-    };
-  }
-];
+{
+  boot.kernelPatches = [
+    {
+      name = "Rust Support";
+      patch = null;
+      features = {
+        rust = true;
+      };
+    }
+  ];
+}
 ```
 
 ## Developing kernel modules {#sec-linux-config-developing-modules}
diff --git a/nixos/doc/manual/configuration/luks-file-systems.section.md b/nixos/doc/manual/configuration/luks-file-systems.section.md
index 7615b95aef422..4d2f625073d4a 100644
--- a/nixos/doc/manual/configuration/luks-file-systems.section.md
+++ b/nixos/doc/manual/configuration/luks-file-systems.section.md
@@ -29,15 +29,19 @@ system is automatically mounted at boot time as `/`, add the following
 to `configuration.nix`:
 
 ```nix
-boot.initrd.luks.devices.crypted.device = "/dev/disk/by-uuid/3f6b0024-3a44-4fde-a43a-767b872abe5d";
-fileSystems."/".device = "/dev/mapper/crypted";
+{
+  boot.initrd.luks.devices.crypted.device = "/dev/disk/by-uuid/3f6b0024-3a44-4fde-a43a-767b872abe5d";
+  fileSystems."/".device = "/dev/mapper/crypted";
+}
 ```
 
 Should grub be used as bootloader, and `/boot` is located on an
 encrypted partition, it is necessary to add the following grub option:
 
 ```nix
-boot.loader.grub.enableCryptodisk = true;
+{
+  boot.loader.grub.enableCryptodisk = true;
+}
 ```
 
 ## FIDO2 {#sec-luks-file-systems-fido2}
@@ -68,8 +72,10 @@ To ensure that this file system is decrypted using the FIDO2 compatible
 key, add the following to `configuration.nix`:
 
 ```nix
-boot.initrd.luks.fido2Support = true;
-boot.initrd.luks.devices."/dev/sda2".fido2.credential = "f1d00200108b9d6e849a8b388da457688e3dd653b4e53770012d8f28e5d3b269865038c346802f36f3da7278b13ad6a3bb6a1452e24ebeeaa24ba40eef559b1b287d2a2f80b7";
+{
+  boot.initrd.luks.fido2Support = true;
+  boot.initrd.luks.devices."/dev/sda2".fido2.credential = "f1d00200108b9d6e849a8b388da457688e3dd653b4e53770012d8f28e5d3b269865038c346802f36f3da7278b13ad6a3bb6a1452e24ebeeaa24ba40eef559b1b287d2a2f80b7";
+}
 ```
 
 You can also use the FIDO2 passwordless setup, but for security reasons,
@@ -77,7 +83,9 @@ you might want to enable it only when your device is PIN protected, such
 as [Trezor](https://trezor.io/).
 
 ```nix
-boot.initrd.luks.devices."/dev/sda2".fido2.passwordLess = true;
+{
+  boot.initrd.luks.devices."/dev/sda2".fido2.passwordLess = true;
+}
 ```
 
 ### systemd Stage 1 {#sec-luks-file-systems-fido2-systemd}
@@ -88,13 +96,15 @@ unlocking the existing LUKS2 volume `root` using any enrolled FIDO2 compatible
 tokens.
 
 ```nix
-boot.initrd = {
-  luks.devices.root = {
-    crypttabExtraOpts = [ "fido2-device=auto" ];
-    device = "/dev/sda2";
+{
+  boot.initrd = {
+    luks.devices.root = {
+      crypttabExtraOpts = [ "fido2-device=auto" ];
+      device = "/dev/sda2";
+    };
+    systemd.enable = true;
   };
-  systemd.enable = true;
-};
+}
 ```
 
 All tokens that should be used for unlocking the LUKS2-encrypted volume must
diff --git a/nixos/doc/manual/configuration/modularity.section.md b/nixos/doc/manual/configuration/modularity.section.md
index f4a566d669735..ba3bc79a36311 100644
--- a/nixos/doc/manual/configuration/modularity.section.md
+++ b/nixos/doc/manual/configuration/modularity.section.md
@@ -16,7 +16,7 @@ including them from `configuration.nix`, e.g.:
 { imports = [ ./vpn.nix ./kde.nix ];
   services.httpd.enable = true;
   environment.systemPackages = [ pkgs.emacs ];
-  ...
+  # ...
 }
 ```
 
@@ -27,7 +27,7 @@ Here, we include two modules from the same directory, `vpn.nix` and
 { config, pkgs, ... }:
 
 { services.xserver.enable = true;
-  services.xserver.displayManager.sddm.enable = true;
+  services.displayManager.sddm.enable = true;
   services.xserver.desktopManager.plasma5.enable = true;
   environment.systemPackages = [ pkgs.vim ];
 }
@@ -42,7 +42,9 @@ merged last, so for list-type options, it will appear at the end of the
 merged list. If you want it to appear first, you can use `mkBefore`:
 
 ```nix
-boot.kernelModules = mkBefore [ "kvm-intel" ];
+{
+  boot.kernelModules = mkBefore [ "kvm-intel" ];
+}
 ```
 
 This causes the `kvm-intel` kernel module to be loaded before any other
@@ -60,7 +62,9 @@ When that happens, it's possible to force one definition take precedence
 over the others:
 
 ```nix
-services.httpd.adminAddr = pkgs.lib.mkForce "bob@example.org";
+{
+  services.httpd.adminAddr = pkgs.lib.mkForce "bob@example.org";
+}
 ```
 
 When using multiple modules, you may need to access configuration values
diff --git a/nixos/doc/manual/configuration/network-manager.section.md b/nixos/doc/manual/configuration/network-manager.section.md
index 4bda21d34a108..8e8dfabbf3cd5 100644
--- a/nixos/doc/manual/configuration/network-manager.section.md
+++ b/nixos/doc/manual/configuration/network-manager.section.md
@@ -4,7 +4,9 @@ To facilitate network configuration, some desktop environments use
 NetworkManager. You can enable NetworkManager by setting:
 
 ```nix
-networking.networkmanager.enable = true;
+{
+  networking.networkmanager.enable = true;
+}
 ```
 
 some desktop managers (e.g., GNOME) enable NetworkManager automatically
@@ -14,7 +16,9 @@ All users that should have permission to change network settings must
 belong to the `networkmanager` group:
 
 ```nix
-users.users.alice.extraGroups = [ "networkmanager" ];
+{
+  users.users.alice.extraGroups = [ "networkmanager" ];
+}
 ```
 
 NetworkManager is controlled using either `nmcli` or `nmtui`
@@ -32,9 +36,11 @@ can be used together if desired. To do this you need to instruct
 NetworkManager to ignore those interfaces like:
 
 ```nix
-networking.networkmanager.unmanaged = [
-   "*" "except:type:wwan" "except:type:gsm"
-];
+{
+  networking.networkmanager.unmanaged = [
+     "*" "except:type:wwan" "except:type:gsm"
+  ];
+}
 ```
 
 Refer to the option description for the exact syntax and references to
diff --git a/nixos/doc/manual/configuration/overlayfs.section.md b/nixos/doc/manual/configuration/overlayfs.section.md
index 592fb7c2e6f79..7027a6f426d4b 100644
--- a/nixos/doc/manual/configuration/overlayfs.section.md
+++ b/nixos/doc/manual/configuration/overlayfs.section.md
@@ -4,21 +4,23 @@ NixOS offers a convenient abstraction to create both read-only as well writable
 overlays.
 
 ```nix
-fileSystems = {
-  "/writable-overlay" = {
-    overlay = {
-      lowerdir = [ writableOverlayLowerdir ];
-      upperdir = "/.rw-writable-overlay/upper";
-      workdir = "/.rw-writable-overlay/work";
+{
+  fileSystems = {
+    "/writable-overlay" = {
+      overlay = {
+        lowerdir = [ writableOverlayLowerdir ];
+        upperdir = "/.rw-writable-overlay/upper";
+        workdir = "/.rw-writable-overlay/work";
+      };
+      # Mount the writable overlay in the initrd.
+      neededForBoot = true;
     };
-    # Mount the writable overlay in the initrd.
-    neededForBoot = true;
+    "/readonly-overlay".overlay.lowerdir = [
+      writableOverlayLowerdir
+      writableOverlayLowerdir2
+    ];
   };
-  "/readonly-overlay".overlay.lowerdir = [
-    writableOverlayLowerdir
-    writableOverlayLowerdir2
-  ];
-};
+}
 ```
 
 If `upperdir` and `workdir` are not null, they will be created before the
diff --git a/nixos/doc/manual/configuration/profiles.chapter.md b/nixos/doc/manual/configuration/profiles.chapter.md
index 9f6c11b0d59d5..6161d48e353f3 100644
--- a/nixos/doc/manual/configuration/profiles.chapter.md
+++ b/nixos/doc/manual/configuration/profiles.chapter.md
@@ -8,9 +8,11 @@ is to say, expected usage is to add them to the imports list of your
 `/etc/configuration.nix` as such:
 
 ```nix
-imports = [
-  <nixpkgs/nixos/modules/profiles/profile-name.nix>
-];
+{
+  imports = [
+    <nixpkgs/nixos/modules/profiles/profile-name.nix>
+  ];
+}
 ```
 
 Even if some of these profiles seem only useful in the context of
diff --git a/nixos/doc/manual/configuration/profiles/demo.section.md b/nixos/doc/manual/configuration/profiles/demo.section.md
index 0a0df483c1235..720fc101dc188 100644
--- a/nixos/doc/manual/configuration/profiles/demo.section.md
+++ b/nixos/doc/manual/configuration/profiles/demo.section.md
@@ -1,4 +1,4 @@
 # Demo {#sec-profile-demo}
 
 This profile just enables a `demo` user, with password `demo`, uid `1000`, `wheel` group and
-[autologin in the SDDM display manager](#opt-services.xserver.displayManager.autoLogin).
+[autologin in the SDDM display manager](#opt-services.displayManager.autoLogin).
diff --git a/nixos/doc/manual/configuration/profiles/graphical.section.md b/nixos/doc/manual/configuration/profiles/graphical.section.md
index aaea5c8c02887..3bd80b52e845a 100644
--- a/nixos/doc/manual/configuration/profiles/graphical.section.md
+++ b/nixos/doc/manual/configuration/profiles/graphical.section.md
@@ -4,7 +4,7 @@ Defines a NixOS configuration with the Plasma 5 desktop. It's used by the
 graphical installation CD.
 
 It sets [](#opt-services.xserver.enable),
-[](#opt-services.xserver.displayManager.sddm.enable),
+[](#opt-services.displayManager.sddm.enable),
 [](#opt-services.xserver.desktopManager.plasma5.enable),
 and [](#opt-services.xserver.libinput.enable) to true. It also
 includes glxinfo and firefox in the system packages list.
diff --git a/nixos/doc/manual/configuration/renaming-interfaces.section.md b/nixos/doc/manual/configuration/renaming-interfaces.section.md
index 5b515e9f82a03..4804e35f8a248 100644
--- a/nixos/doc/manual/configuration/renaming-interfaces.section.md
+++ b/nixos/doc/manual/configuration/renaming-interfaces.section.md
@@ -25,10 +25,12 @@ we assign the name `wan` to the interface with MAC address
 `52:54:00:12:01:01` using a netword link unit:
 
 ```nix
-systemd.network.links."10-wan" = {
-  matchConfig.PermanentMACAddress = "52:54:00:12:01:01";
-  linkConfig.Name = "wan";
-};
+{
+  systemd.network.links."10-wan" = {
+    matchConfig.PermanentMACAddress = "52:54:00:12:01:01";
+    linkConfig.Name = "wan";
+  };
+}
 ```
 
 Note that links are directly read by udev, *not networkd*, and will work
@@ -37,10 +39,12 @@ even if networkd is disabled.
 Alternatively, we can use a plain old udev rule:
 
 ```nix
-boot.initrd.services.udev.rules = ''
-  SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", \
-  ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="wan"
-'';
+{
+  boot.initrd.services.udev.rules = ''
+    SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", \
+    ATTR{address}=="52:54:00:12:01:01", KERNEL=="eth*", NAME="wan"
+  '';
+}
 ```
 
 ::: {.warning}
diff --git a/nixos/doc/manual/configuration/ssh.section.md b/nixos/doc/manual/configuration/ssh.section.md
index 9e239a8481789..8754e3d9ccaf4 100644
--- a/nixos/doc/manual/configuration/ssh.section.md
+++ b/nixos/doc/manual/configuration/ssh.section.md
@@ -3,7 +3,9 @@
 Secure shell (SSH) access to your machine can be enabled by setting:
 
 ```nix
-services.openssh.enable = true;
+{
+  services.openssh.enable = true;
+}
 ```
 
 By default, root logins using a password are disallowed. They can be
@@ -14,6 +16,8 @@ You can declaratively specify authorised RSA/DSA public keys for a user
 as follows:
 
 ```nix
-users.users.alice.openssh.authorizedKeys.keys =
-  [ "ssh-dss AAAAB3NzaC1kc3MAAACBAPIkGWVEt4..." ];
+{
+  users.users.alice.openssh.authorizedKeys.keys =
+    [ "ssh-dss AAAAB3NzaC1kc3MAAACBAPIkGWVEt4..." ];
+}
 ```
diff --git a/nixos/doc/manual/configuration/sshfs-file-systems.section.md b/nixos/doc/manual/configuration/sshfs-file-systems.section.md
index e2e37454b7ead..32b4aac78304d 100644
--- a/nixos/doc/manual/configuration/sshfs-file-systems.section.md
+++ b/nixos/doc/manual/configuration/sshfs-file-systems.section.md
@@ -26,8 +26,8 @@ To create a new key without a passphrase you can do:
 ```ShellSession
 $ ssh-keygen -t ed25519 -P '' -f example-key
 Generating public/private ed25519 key pair.
-Your identification has been saved in test-key
-Your public key has been saved in test-key.pub
+Your identification has been saved in example-key
+Your public key has been saved in example-key.pub
 The key fingerprint is:
 SHA256:yjxl3UbTn31fLWeyLYTAKYJPRmzknjQZoyG8gSNEoIE my-user@workstation
 ```
diff --git a/nixos/doc/manual/configuration/subversion.chapter.md b/nixos/doc/manual/configuration/subversion.chapter.md
index ff870f5c40b97..2436138669fe9 100644
--- a/nixos/doc/manual/configuration/subversion.chapter.md
+++ b/nixos/doc/manual/configuration/subversion.chapter.md
@@ -21,9 +21,11 @@ Apache HTTP, setting [](#opt-services.httpd.adminAddr)
 appropriately:
 
 ```nix
-services.httpd.enable = true;
-services.httpd.adminAddr = ...;
-networking.firewall.allowedTCPPorts = [ 80 443 ];
+{
+  services.httpd.enable = true;
+  services.httpd.adminAddr = "...";
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+}
 ```
 
 For a simple Subversion server with basic authentication, configure the
@@ -34,25 +36,28 @@ the `.authz` file describing access permission, and `AuthUserFile` to
 the password file.
 
 ```nix
-services.httpd.extraModules = [
-    # note that order is *super* important here
-    { name = "dav_svn"; path = "${pkgs.apacheHttpdPackages.subversion}/modules/mod_dav_svn.so"; }
-    { name = "authz_svn"; path = "${pkgs.apacheHttpdPackages.subversion}/modules/mod_authz_svn.so"; }
-  ];
-  services.httpd.virtualHosts = {
-    "svn" = {
-       hostName = HOSTNAME;
-       documentRoot = DOCUMENTROOT;
-       locations."/svn".extraConfig = ''
-           DAV svn
-           SVNParentPath REPO_PARENT
-           AuthzSVNAccessFile ACCESS_FILE
-           AuthName "SVN Repositories"
-           AuthType Basic
-           AuthUserFile PASSWORD_FILE
-           Require valid-user
-      '';
-    }
+{
+  services.httpd.extraModules = [
+      # note that order is *super* important here
+      { name = "dav_svn"; path = "${pkgs.apacheHttpdPackages.subversion}/modules/mod_dav_svn.so"; }
+      { name = "authz_svn"; path = "${pkgs.apacheHttpdPackages.subversion}/modules/mod_authz_svn.so"; }
+    ];
+    services.httpd.virtualHosts = {
+      "svn" = {
+         hostName = HOSTNAME;
+         documentRoot = DOCUMENTROOT;
+         locations."/svn".extraConfig = ''
+             DAV svn
+             SVNParentPath REPO_PARENT
+             AuthzSVNAccessFile ACCESS_FILE
+             AuthName "SVN Repositories"
+             AuthType Basic
+             AuthUserFile PASSWORD_FILE
+             Require valid-user
+        '';
+      };
+    };
+}
 ```
 
 The key `"svn"` is just a symbolic name identifying the virtual host.
@@ -90,7 +95,7 @@ $ htpasswd -s PASSWORD_FILE USER_NAME
 The file describing access permissions `ACCESS_FILE` will look something
 like the following:
 
-```nix
+```
 [/]
 * = r
 
diff --git a/nixos/doc/manual/configuration/user-mgmt.chapter.md b/nixos/doc/manual/configuration/user-mgmt.chapter.md
index 71d61ce4c641b..7d83121d41e09 100644
--- a/nixos/doc/manual/configuration/user-mgmt.chapter.md
+++ b/nixos/doc/manual/configuration/user-mgmt.chapter.md
@@ -6,13 +6,15 @@ management. In the declarative style, users are specified in
 account named `alice` shall exist:
 
 ```nix
-users.users.alice = {
-  isNormalUser = true;
-  home = "/home/alice";
-  description = "Alice Foobar";
-  extraGroups = [ "wheel" "networkmanager" ];
-  openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
-};
+{
+  users.users.alice = {
+    isNormalUser = true;
+    home = "/home/alice";
+    description = "Alice Foobar";
+    extraGroups = [ "wheel" "networkmanager" ];
+    openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
+  };
+}
 ```
 
 Note that `alice` is a member of the `wheel` and `networkmanager`
@@ -38,7 +40,9 @@ A user ID (uid) is assigned automatically. You can also specify a uid
 manually by adding
 
 ```nix
-uid = 1000;
+{
+  uid = 1000;
+}
 ```
 
 to the user specification.
@@ -47,7 +51,9 @@ Groups can be specified similarly. The following states that a group
 named `students` shall exist:
 
 ```nix
-users.groups.students.gid = 1000;
+{
+  users.groups.students.gid = 1000;
+}
 ```
 
 As with users, the group ID (gid) is optional and will be assigned
@@ -100,7 +106,9 @@ Instead of using a custom perl script to create users and groups, you can use
 systemd-sysusers:
 
 ```nix
-systemd.sysusers.enable = true;
+{
+  systemd.sysusers.enable = true;
+}
 ```
 
 The primary benefit of this is to remove a dependency on perl.
diff --git a/nixos/doc/manual/configuration/wayland.chapter.md b/nixos/doc/manual/configuration/wayland.chapter.md
index 0f195bd665673..27c027d385140 100644
--- a/nixos/doc/manual/configuration/wayland.chapter.md
+++ b/nixos/doc/manual/configuration/wayland.chapter.md
@@ -9,7 +9,9 @@ a Wayland Compositor such as sway without separately enabling a Wayland
 server:
 
 ```nix
+{
 programs.sway.enable = true;
+}
 ```
 
 This installs the sway compositor along with some essential utilities.
@@ -19,7 +21,9 @@ If you are using a wlroots-based compositor, like sway, and want to be
 able to share your screen, you might want to activate this option:
 
 ```nix
-xdg.portal.wlr.enable = true;
+{
+  xdg.portal.wlr.enable = true;
+}
 ```
 
 and configure Pipewire using
diff --git a/nixos/doc/manual/configuration/wireless.section.md b/nixos/doc/manual/configuration/wireless.section.md
index 3299d2d7ecb8a..df828698cf03d 100644
--- a/nixos/doc/manual/configuration/wireless.section.md
+++ b/nixos/doc/manual/configuration/wireless.section.md
@@ -7,25 +7,29 @@ skip the rest of this section on wireless networks.
 NixOS will start wpa_supplicant for you if you enable this setting:
 
 ```nix
-networking.wireless.enable = true;
+{
+  networking.wireless.enable = true;
+}
 ```
 
 NixOS lets you specify networks for wpa_supplicant declaratively:
 
 ```nix
-networking.wireless.networks = {
-  echelon = {                # SSID with no spaces or special characters
-    psk = "abcdefgh";
-  };
-  "echelon's AP" = {         # SSID with spaces and/or special characters
-    psk = "ijklmnop";
-  };
-  echelon = {                # Hidden SSID
-    hidden = true;
-    psk = "qrstuvwx";
+{
+  networking.wireless.networks = {
+    echelon = {                # SSID with no spaces or special characters
+      psk = "abcdefgh";
+    };
+    "echelon's AP" = {         # SSID with spaces and/or special characters
+      psk = "ijklmnop";
+    };
+    echelon = {                # Hidden SSID
+      hidden = true;
+      psk = "qrstuvwx";
+    };
+    free.wifi = {};            # Public wireless network
   };
-  free.wifi = {};            # Public wireless network
-};
+}
 ```
 
 Be aware that keys will be written to the nix store in plaintext! When
@@ -46,11 +50,13 @@ network={
 ```
 
 ```nix
-networking.wireless.networks = {
-  echelon = {
-    pskRaw = "dca6d6ed41f4ab5a984c9f55f6f66d4efdc720ebf66959810f4329bb391c5435";
+{
+  networking.wireless.networks = {
+    echelon = {
+      pskRaw = "dca6d6ed41f4ab5a984c9f55f6f66d4efdc720ebf66959810f4329bb391c5435";
+    };
   };
-};
+}
 ```
 
 or you can use it to directly generate the `wpa_supplicant.conf`:
diff --git a/nixos/doc/manual/configuration/x-windows.chapter.md b/nixos/doc/manual/configuration/x-windows.chapter.md
index bf1872ae01ace..c09e0877e8663 100644
--- a/nixos/doc/manual/configuration/x-windows.chapter.md
+++ b/nixos/doc/manual/configuration/x-windows.chapter.md
@@ -4,7 +4,9 @@ The X Window System (X11) provides the basis of NixOS' graphical user
 interface. It can be enabled as follows:
 
 ```nix
-services.xserver.enable = true;
+{
+  services.xserver.enable = true;
+}
 ```
 
 The X server will automatically detect and use the appropriate video
@@ -12,7 +14,9 @@ driver from a set of X.org drivers (such as `vesa` and `intel`). You can
 also specify a driver manually, e.g.
 
 ```nix
-services.xserver.videoDrivers = [ "r128" ];
+{
+  services.xserver.videoDrivers = [ "r128" ];
+}
 ```
 
 to enable X.org's `xf86-video-r128` driver.
@@ -22,15 +26,17 @@ Otherwise, you can only log into a plain undecorated `xterm` window.
 Thus you should pick one or more of the following lines:
 
 ```nix
-services.xserver.desktopManager.plasma5.enable = true;
-services.xserver.desktopManager.xfce.enable = true;
-services.xserver.desktopManager.gnome.enable = true;
-services.xserver.desktopManager.mate.enable = true;
-services.xserver.windowManager.xmonad.enable = true;
-services.xserver.windowManager.twm.enable = true;
-services.xserver.windowManager.icewm.enable = true;
-services.xserver.windowManager.i3.enable = true;
-services.xserver.windowManager.herbstluftwm.enable = true;
+{
+  services.xserver.desktopManager.plasma5.enable = true;
+  services.xserver.desktopManager.xfce.enable = true;
+  services.xserver.desktopManager.gnome.enable = true;
+  services.xserver.desktopManager.mate.enable = true;
+  services.xserver.windowManager.xmonad.enable = true;
+  services.xserver.windowManager.twm.enable = true;
+  services.xserver.windowManager.icewm.enable = true;
+  services.xserver.windowManager.i3.enable = true;
+  services.xserver.windowManager.herbstluftwm.enable = true;
+}
 ```
 
 NixOS's default *display manager* (the program that provides a graphical
@@ -38,22 +44,28 @@ login prompt and manages the X server) is LightDM. You can select an
 alternative one by picking one of the following lines:
 
 ```nix
-services.xserver.displayManager.sddm.enable = true;
-services.xserver.displayManager.gdm.enable = true;
+{
+  services.displayManager.sddm.enable = true;
+  services.xserver.displayManager.gdm.enable = true;
+}
 ```
 
 You can set the keyboard layout (and optionally the layout variant):
 
 ```nix
-services.xserver.xkb.layout = "de";
-services.xserver.xkb.variant = "neo";
+{
+  services.xserver.xkb.layout = "de";
+  services.xserver.xkb.variant = "neo";
+}
 ```
 
 The X server is started automatically at boot time. If you don't want
 this to happen, you can set:
 
 ```nix
-services.xserver.autorun = false;
+{
+  services.xserver.autorun = false;
+}
 ```
 
 The X server can then be started manually:
@@ -66,7 +78,9 @@ On 64-bit systems, if you want OpenGL for 32-bit programs such as in
 Wine, you should also set the following:
 
 ```nix
-hardware.opengl.driSupport32Bit = true;
+{
+  hardware.opengl.driSupport32Bit = true;
+}
 ```
 
 ## Auto-login {#sec-x11-auto-login}
@@ -84,16 +98,20 @@ desktop environment. If you wanted no desktop environment and i3 as your
 your window manager, you'd define:
 
 ```nix
-services.xserver.displayManager.defaultSession = "none+i3";
+{
+  services.displayManager.defaultSession = "none+i3";
+}
 ```
 
 Every display manager in NixOS supports auto-login, here is an example
 using lightdm for a user `alice`:
 
 ```nix
-services.xserver.displayManager.lightdm.enable = true;
-services.xserver.displayManager.autoLogin.enable = true;
-services.xserver.displayManager.autoLogin.user = "alice";
+{
+  services.xserver.displayManager.lightdm.enable = true;
+  services.displayManager.autoLogin.enable = true;
+  services.displayManager.autoLogin.user = "alice";
+}
 ```
 
 ## Intel Graphics drivers {#sec-x11--graphics-cards-intel}
@@ -119,18 +137,22 @@ drivers. Use the option
 to set one. The recommended configuration for modern systems is:
 
 ```nix
-services.xserver.videoDrivers = [ "modesetting" ];
+{
+  services.xserver.videoDrivers = [ "modesetting" ];
+}
 ```
 
 If you experience screen tearing no matter what, this configuration was
 reported to resolve the issue:
 
 ```nix
-services.xserver.videoDrivers = [ "intel" ];
-services.xserver.deviceSection = ''
-  Option "DRI" "2"
-  Option "TearFree" "true"
-'';
+{
+  services.xserver.videoDrivers = [ "intel" ];
+  services.xserver.deviceSection = ''
+    Option "DRI" "2"
+    Option "TearFree" "true"
+  '';
+}
 ```
 
 Note that this will likely downgrade the performance compared to
@@ -143,17 +165,19 @@ better 3D performance than the X.org drivers. It is not enabled by
 default because it's not free software. You can enable it as follows:
 
 ```nix
-services.xserver.videoDrivers = [ "nvidia" ];
+{
+  services.xserver.videoDrivers = [ "nvidia" ];
+}
 ```
 
-Or if you have an older card, you may have to use one of the legacy
-drivers:
+If you have an older card, you may have to use one of the legacy drivers:
 
 ```nix
-services.xserver.videoDrivers = [ "nvidiaLegacy470" ];
-services.xserver.videoDrivers = [ "nvidiaLegacy390" ];
-services.xserver.videoDrivers = [ "nvidiaLegacy340" ];
-services.xserver.videoDrivers = [ "nvidiaLegacy304" ];
+{
+  hardware.nvidia.package = config.boot.kernelPackages.nvidiaPackages.legacy_470;
+  hardware.nvidia.package = config.boot.kernelPackages.nvidiaPackages.legacy_390;
+  hardware.nvidia.package = config.boot.kernelPackages.nvidiaPackages.legacy_340;
+}
 ```
 
 You may need to reboot after enabling this driver to prevent a clash
@@ -168,7 +192,9 @@ performance. If you still want to use it anyway, you need to explicitly
 set:
 
 ```nix
-services.xserver.videoDrivers = [ "amdgpu-pro" ];
+{
+  services.xserver.videoDrivers = [ "amdgpu-pro" ];
+}
 ```
 
 You will need to reboot after enabling this driver to prevent a clash
@@ -180,14 +206,18 @@ Support for Synaptics touchpads (found in many laptops such as the Dell
 Latitude series) can be enabled as follows:
 
 ```nix
-services.xserver.libinput.enable = true;
+{
+  services.xserver.libinput.enable = true;
+}
 ```
 
 The driver has many options (see [](#ch-options)).
 For instance, the following disables tap-to-click behavior:
 
 ```nix
-services.xserver.libinput.touchpad.tapping = false;
+{
+  services.xserver.libinput.touchpad.tapping = false;
+}
 ```
 
 Note: the use of `services.xserver.synaptics` is deprecated since NixOS
@@ -200,9 +230,11 @@ GTK themes can be installed either to user profile or system-wide (via
 GTK ones, you can use the following configuration:
 
 ```nix
-qt.enable = true;
-qt.platformTheme = "gtk2";
-qt.style = "gtk2";
+{
+  qt.enable = true;
+  qt.platformTheme = "gtk2";
+  qt.style = "gtk2";
+}
 ```
 
 ## Custom XKB layouts {#custom-xkb-layouts}
@@ -219,7 +251,7 @@ Create a file called `us-greek` with the following content (under a
 directory called `symbols`; it's an XKB peculiarity that will help with
 testing):
 
-```nix
+```
 xkb_symbols "us-greek"
 {
   include "us(basic)"            // includes the base US keys
@@ -236,11 +268,13 @@ xkb_symbols "us-greek"
 A minimal layout specification must include the following:
 
 ```nix
-services.xserver.xkb.extraLayouts.us-greek = {
-  description = "US layout with alt-gr greek";
-  languages   = [ "eng" ];
-  symbolsFile = /yourpath/symbols/us-greek;
-};
+{
+  services.xserver.xkb.extraLayouts.us-greek = {
+    description = "US layout with alt-gr greek";
+    languages   = [ "eng" ];
+    symbolsFile = /yourpath/symbols/us-greek;
+  };
+}
 ```
 
 ::: {.note}
@@ -277,7 +311,7 @@ Use the *xev* utility from `pkgs.xorg.xev` to find the codes of the keys
 of interest, then create a `media-key` file to hold the keycodes
 definitions
 
-```nix
+```
 xkb_keycodes "media"
 {
  <volUp>   = 123;
@@ -287,7 +321,7 @@ xkb_keycodes "media"
 
 Now use the newly define keycodes in `media-sym`:
 
-```nix
+```
 xkb_symbols "media"
 {
  key.type = "ONE_LEVEL";
@@ -299,12 +333,14 @@ xkb_symbols "media"
 As before, to install the layout do
 
 ```nix
-services.xserver.xkb.extraLayouts.media = {
-  description  = "Multimedia keys remapping";
-  languages    = [ "eng" ];
-  symbolsFile  = /path/to/media-key;
-  keycodesFile = /path/to/media-sym;
-};
+{
+  services.xserver.xkb.extraLayouts.media = {
+    description  = "Multimedia keys remapping";
+    languages    = [ "eng" ];
+    symbolsFile  = /path/to/media-key;
+    keycodesFile = /path/to/media-sym;
+  };
+}
 ```
 
 ::: {.note}
@@ -320,7 +356,9 @@ workaround, you can set the keymap using `setxkbmap` at the start of the
 session with:
 
 ```nix
-services.xserver.displayManager.sessionCommands = "setxkbmap -keycodes media";
+{
+  services.xserver.displayManager.sessionCommands = "setxkbmap -keycodes media";
+}
 ```
 
 If you are manually starting the X server, you should set the argument
diff --git a/nixos/doc/manual/configuration/xfce.chapter.md b/nixos/doc/manual/configuration/xfce.chapter.md
index 9ec4a51d6e35e..302cf9fa093d1 100644
--- a/nixos/doc/manual/configuration/xfce.chapter.md
+++ b/nixos/doc/manual/configuration/xfce.chapter.md
@@ -3,21 +3,25 @@
 To enable the Xfce Desktop Environment, set
 
 ```nix
-services.xserver.desktopManager.xfce.enable = true;
-services.xserver.displayManager.defaultSession = "xfce";
+{
+  services.xserver.desktopManager.xfce.enable = true;
+  services.displayManager.defaultSession = "xfce";
+}
 ```
 
 Optionally, *picom* can be enabled for nice graphical effects, some
 example settings:
 
 ```nix
-services.picom = {
-  enable = true;
-  fade = true;
-  inactiveOpacity = 0.9;
-  shadow = true;
-  fadeDelta = 4;
-};
+{
+  services.picom = {
+    enable = true;
+    fade = true;
+    inactiveOpacity = 0.9;
+    shadow = true;
+    fadeDelta = 4;
+  };
+}
 ```
 
 Some Xfce programs are not installed automatically. To install them
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index a368b16201f8f..558fec4cab923 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -9,12 +9,20 @@
 , prefix ? ../../..
 }:
 
-with pkgs;
-
 let
-  inherit (lib) hasPrefix removePrefix;
-
-  lib = pkgs.lib;
+  inherit (pkgs) buildPackages runCommand docbook_xsl_ns;
+
+  inherit (pkgs.lib)
+    hasPrefix
+    removePrefix
+    flip
+    foldr
+    types
+    mkOption
+    escapeShellArg
+    concatMapStringsSep
+    sourceFilesBySuffices
+    ;
 
   common = import ./common.nix;
 
@@ -27,7 +35,7 @@ let
   # E.g. if some `options` came from modules in ${pkgs.customModules}/nix,
   # you'd need to include `extraSources = [ pkgs.customModules ]`
   prefixesToStrip = map (p: "${toString p}/") ([ prefix ] ++ extraSources);
-  stripAnyPrefixes = lib.flip (lib.foldr lib.removePrefix) prefixesToStrip;
+  stripAnyPrefixes = flip (foldr removePrefix) prefixesToStrip;
 
   optionsDoc = buildPackages.nixosOptionsDoc {
     inherit options revision baseOptionsJSON warningsAreErrors;
@@ -42,8 +50,8 @@ let
   testOptionsDoc = let
       eval = nixos-lib.evalTest {
         # Avoid evaluating a NixOS config prototype.
-        config.node.type = lib.types.deferredModule;
-        options._module.args = lib.mkOption { internal = true; };
+        config.node.type = types.deferredModule;
+        options._module.args = mkOption { internal = true; };
       };
     in buildPackages.nixosOptionsDoc {
       inherit (eval) options;
@@ -76,7 +84,7 @@ let
     substituteInPlace ./configuration/configuration.md \
       --replace \
           '@MODULE_CHAPTERS@' \
-          ${lib.escapeShellArg (lib.concatMapStringsSep "\n" (p: "${p.value}") config.meta.doc)}
+          ${escapeShellArg (concatMapStringsSep "\n" (p: "${p.value}") config.meta.doc)}
     substituteInPlace ./nixos-options.md \
       --replace \
         '@NIXOS_OPTIONS_JSON@' \
@@ -95,7 +103,7 @@ in rec {
   # Generate the NixOS manual.
   manualHTML = runCommand "nixos-manual-html"
     { nativeBuildInputs = [ buildPackages.nixos-render-docs ];
-      inputs = lib.sourceFilesBySuffices ./. [ ".md" ];
+      inputs = sourceFilesBySuffices ./. [ ".md" ];
       meta.description = "The NixOS manual in HTML format";
       allowedReferences = ["out"];
     }
@@ -105,20 +113,23 @@ 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}
 
       nixos-render-docs -j $NIX_BUILD_CORES manual html \
         --manpage-urls ${manpageUrls} \
-        --revision ${lib.escapeShellArg revision} \
-        --generator "nixos-render-docs ${lib.version}" \
+        --revision ${escapeShellArg revision} \
+        --generator "nixos-render-docs ${pkgs.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 \
@@ -144,7 +155,7 @@ in rec {
               xml:id="book-nixos-manual">
           <info>
             <title>NixOS Manual</title>
-            <subtitle>Version ${lib.version}</subtitle>
+            <subtitle>Version ${pkgs.lib.version}</subtitle>
           </info>
           <chapter>
             <title>Temporarily unavailable</title>
@@ -196,7 +207,7 @@ in rec {
       # Generate manpages.
       mkdir -p $out/share/man/man5
       nixos-render-docs -j $NIX_BUILD_CORES options manpage \
-        --revision ${lib.escapeShellArg revision} \
+        --revision ${escapeShellArg revision} \
         ${optionsJSON}/${common.outputPath}/options.json \
         $out/share/man/man5/configuration.nix.5
     '';
diff --git a/nixos/doc/manual/development/activation-script.section.md b/nixos/doc/manual/development/activation-script.section.md
index cc317a6a01aa8..f771c3524b796 100644
--- a/nixos/doc/manual/development/activation-script.section.md
+++ b/nixos/doc/manual/development/activation-script.section.md
@@ -17,13 +17,15 @@ activation script will take these dependencies into account and order the
 snippets accordingly. As a simple example:
 
 ```nix
-system.activationScripts.my-activation-script = {
-  deps = [ "etc" ];
-  # supportsDryActivation = true;
-  text = ''
-    echo "Hallo i bims"
-  '';
-};
+{
+  system.activationScripts.my-activation-script = {
+    deps = [ "etc" ];
+    # supportsDryActivation = true;
+    text = ''
+      echo "Hallo i bims"
+    '';
+  };
+}
 ```
 
 This example creates an activation script snippet that is run after the `etc`
diff --git a/nixos/doc/manual/development/assertions.section.md b/nixos/doc/manual/development/assertions.section.md
index cc6d81e56990b..eb5158c90f98c 100644
--- a/nixos/doc/manual/development/assertions.section.md
+++ b/nixos/doc/manual/development/assertions.section.md
@@ -18,7 +18,7 @@ This is an example of using `warnings`.
                This is known to cause some specific problems in certain situations.
                '' ]
       else [];
-  }
+  };
 }
 ```
 
@@ -35,6 +35,6 @@ This example, extracted from the [`syslogd` module](https://github.com/NixOS/nix
           message = "rsyslogd conflicts with syslogd";
         }
       ];
-  }
+  };
 }
 ```
diff --git a/nixos/doc/manual/development/etc-overlay.section.md b/nixos/doc/manual/development/etc-overlay.section.md
index e6f6d8d4ca1ef..d8588f508a26c 100644
--- a/nixos/doc/manual/development/etc-overlay.section.md
+++ b/nixos/doc/manual/development/etc-overlay.section.md
@@ -9,7 +9,9 @@ Instead of using a custom perl script to activate `/etc`, you activate it via an
 overlay filesystem:
 
 ```nix
-system.etc.overlay.enable = true;
+{
+  system.etc.overlay.enable = true;
+}
 ```
 
 Using an overlay has two benefits:
@@ -22,7 +24,9 @@ upper layer). However, you can also mount `/etc` immutably (i.e. read-only) by
 setting:
 
 ```nix
-system.etc.overlay.mutable = false;
+{
+  system.etc.overlay.mutable = false;
+}
 ```
 
 The overlay is atomically replaced during system switch. However, files that
diff --git a/nixos/doc/manual/development/meta-attributes.section.md b/nixos/doc/manual/development/meta-attributes.section.md
index 33b41fe74d297..b2ad23e58b94b 100644
--- a/nixos/doc/manual/development/meta-attributes.section.md
+++ b/nixos/doc/manual/development/meta-attributes.section.md
@@ -14,11 +14,11 @@ file.
 { config, lib, pkgs, ... }:
 {
   options = {
-    ...
+    # ...
   };
 
   config = {
-    ...
+    # ...
   };
 
   meta = {
diff --git a/nixos/doc/manual/development/non-switchable-systems.section.md b/nixos/doc/manual/development/non-switchable-systems.section.md
index 87bb46c789091..a51e8233f30b3 100644
--- a/nixos/doc/manual/development/non-switchable-systems.section.md
+++ b/nixos/doc/manual/development/non-switchable-systems.section.md
@@ -9,7 +9,7 @@ profile:
 
 ```nix
 { modulesPath, ... }: {
-  imports = [ "${modulesPath}/profiles/image-based-appliance.nix" ]
+  imports = [ "${modulesPath}/profiles/image-based-appliance.nix" ];
 }
 ```
 
diff --git a/nixos/doc/manual/development/option-declarations.section.md b/nixos/doc/manual/development/option-declarations.section.md
index 762070416187d..325f4d11cb083 100644
--- a/nixos/doc/manual/development/option-declarations.section.md
+++ b/nixos/doc/manual/development/option-declarations.section.md
@@ -6,14 +6,16 @@ hasn't been declared in any module. An option declaration generally
 looks like this:
 
 ```nix
-options = {
-  name = mkOption {
-    type = type specification;
-    default = default value;
-    example = example value;
-    description = lib.mdDoc "Description for use in the NixOS manual.";
+{
+  options = {
+    name = mkOption {
+      type = type specification;
+      default = default value;
+      example = example value;
+      description = "Description for use in the NixOS manual.";
+    };
   };
-};
+}
 ```
 
 The attribute names within the `name` attribute path must be camel
@@ -56,12 +58,9 @@ The function `mkOption` accepts the following arguments.
 
 `description`
 
-:   A textual description of the option, in [Nixpkgs-flavored Markdown](
-    https://nixos.org/nixpkgs/manual/#sec-contributing-markup) format, that will be
-    included in the NixOS manual. During the migration process from DocBook
-    it is necessary to mark descriptions written in CommonMark with `lib.mdDoc`.
-    The description may still be written in DocBook (without any marker), but this
-    is discouraged and will be deprecated in the future.
+:   A textual description of the option in [Nixpkgs-flavored Markdown](
+    https://nixos.org/nixpkgs/manual/#sec-contributing-markup) format that will be
+    included in the NixOS manual.
 
 ## Utility functions for common option patterns {#sec-option-declarations-util}
 
@@ -79,13 +78,13 @@ For example:
 ::: {#ex-options-declarations-util-mkEnableOption-magic .example}
 ### `mkEnableOption` usage
 ```nix
-lib.mkEnableOption (lib.mdDoc "magic")
+lib.mkEnableOption "magic"
 # is like
 lib.mkOption {
   type = lib.types.bool;
   default = false;
   example = true;
-  description = lib.mdDoc "Whether to enable magic.";
+  description = "Whether to enable magic.";
 }
 ```
 :::
@@ -133,7 +132,7 @@ lib.mkOption {
   type = lib.types.package;
   default = pkgs.hello;
   defaultText = lib.literalExpression "pkgs.hello";
-  description = lib.mdDoc "The hello package to use.";
+  description = "The hello package to use.";
 }
 ```
 :::
@@ -151,7 +150,7 @@ lib.mkOption {
   default = pkgs.ghc;
   defaultText = lib.literalExpression "pkgs.ghc";
   example = lib.literalExpression "pkgs.haskell.packages.ghc92.ghc.withPackages (hkgs: [ hkgs.primes ])";
-  description = lib.mdDoc "The GHC package to use.";
+  description = "The GHC package to use.";
 }
 ```
 :::
@@ -221,28 +220,34 @@ enforces that there can only be a single display manager enabled.
 ::: {#ex-option-declaration-eot-service .example}
 ### Extensible type placeholder in the service module
 ```nix
-services.xserver.displayManager.enable = mkOption {
-  description = "Display manager to use";
-  type = with types; nullOr (enum [ ]);
-};
+{
+  services.xserver.displayManager.enable = mkOption {
+    description = "Display manager to use";
+    type = with types; nullOr (enum [ ]);
+  };
+}
 ```
 :::
 
 ::: {#ex-option-declaration-eot-backend-gdm .example}
 ### Extending `services.xserver.displayManager.enable` in the `gdm` module
 ```nix
-services.xserver.displayManager.enable = mkOption {
-  type = with types; nullOr (enum [ "gdm" ]);
-};
+{
+  services.xserver.displayManager.enable = mkOption {
+    type = with types; nullOr (enum [ "gdm" ]);
+  };
+}
 ```
 :::
 
 ::: {#ex-option-declaration-eot-backend-sddm .example}
 ### Extending `services.xserver.displayManager.enable` in the `sddm` module
 ```nix
-services.xserver.displayManager.enable = mkOption {
-  type = with types; nullOr (enum [ "sddm" ]);
-};
+{
+  services.xserver.displayManager.enable = mkOption {
+    type = with types; nullOr (enum [ "sddm" ]);
+  };
+}
 ```
 :::
 
diff --git a/nixos/doc/manual/development/option-def.section.md b/nixos/doc/manual/development/option-def.section.md
index 6a3dc26b99be4..227f41d812ff1 100644
--- a/nixos/doc/manual/development/option-def.section.md
+++ b/nixos/doc/manual/development/option-def.section.md
@@ -4,9 +4,11 @@ Option definitions are generally straight-forward bindings of values to
 option names, like
 
 ```nix
-config = {
-  services.httpd.enable = true;
-};
+{
+  config = {
+    services.httpd.enable = true;
+  };
+}
 ```
 
 However, sometimes you need to wrap an option definition or set of
@@ -18,10 +20,12 @@ If a set of option definitions is conditional on the value of another
 option, you may need to use `mkIf`. Consider, for instance:
 
 ```nix
-config = if config.services.httpd.enable then {
-  environment.systemPackages = [ ... ];
-  ...
-} else {};
+{
+  config = if config.services.httpd.enable then {
+    environment.systemPackages = [ /* ... */ ];
+    # ...
+  } else {};
+}
 ```
 
 This definition will cause Nix to fail with an "infinite recursion"
@@ -30,30 +34,36 @@ on the value being constructed here. After all, you could also write the
 clearly circular and contradictory:
 
 ```nix
-config = if config.services.httpd.enable then {
-  services.httpd.enable = false;
-} else {
-  services.httpd.enable = true;
-};
+{
+  config = if config.services.httpd.enable then {
+    services.httpd.enable = false;
+  } else {
+    services.httpd.enable = true;
+  };
+}
 ```
 
 The solution is to write:
 
 ```nix
-config = mkIf config.services.httpd.enable {
-  environment.systemPackages = [ ... ];
-  ...
-};
+{
+  config = mkIf config.services.httpd.enable {
+    environment.systemPackages = [ /* ... */ ];
+    # ...
+  };
+}
 ```
 
 The special function `mkIf` causes the evaluation of the conditional to
 be "pushed down" into the individual definitions, as if you had written:
 
 ```nix
-config = {
-  environment.systemPackages = if config.services.httpd.enable then [ ... ] else [];
-  ...
-};
+{
+  config = {
+    environment.systemPackages = if config.services.httpd.enable then [ /* ... */ ] else [];
+    # ...
+  };
+}
 ```
 
 ## Setting Priorities {#sec-option-definitions-setting-priorities}
@@ -65,7 +75,9 @@ priority 100 and option defaults have priority 1500.
 You can specify an explicit priority by using `mkOverride`, e.g.
 
 ```nix
-services.openssh.enable = mkOverride 10 false;
+{
+  services.openssh.enable = mkOverride 10 false;
+}
 ```
 
 This definition causes all other definitions with priorities above 10 to
@@ -80,7 +92,9 @@ The functions `mkBefore` and `mkAfter` are equal to `mkOrder 500` and `mkOrder 1
 As an example,
 
 ```nix
-hardware.firmware = mkBefore [ myFirmware ];
+{
+  hardware.firmware = mkBefore [ myFirmware ];
+}
 ```
 
 This definition ensures that `myFirmware` comes before other unordered
@@ -97,13 +111,15 @@ they were declared in separate modules. This can be done using
 `mkMerge`:
 
 ```nix
-config = mkMerge
-  [ # Unconditional stuff.
-    { environment.systemPackages = [ ... ];
-    }
-    # Conditional stuff.
-    (mkIf config.services.bla.enable {
-      environment.systemPackages = [ ... ];
-    })
-  ];
+{
+  config = mkMerge
+    [ # Unconditional stuff.
+      { environment.systemPackages = [ /* ... */ ];
+      }
+      # Conditional stuff.
+      (mkIf config.services.bla.enable {
+        environment.systemPackages = [ /* ... */ ];
+      })
+    ];
+}
 ```
diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md
index 04edf99e70b00..b44a84553b37b 100644
--- a/nixos/doc/manual/development/option-types.section.md
+++ b/nixos/doc/manual/development/option-types.section.md
@@ -42,6 +42,9 @@ merging is handled.
 :   One element of the list *`l`*, e.g. `types.enum [ "left" "right" ]`.
     Multiple definitions cannot be merged.
 
+    If you want to pair these values with more information, possibly of
+    distinct types, consider using a [sum type](#sec-option-types-sums).
+
 `types.anything`
 
 :   A type that accepts any value and recursively merges attribute sets
@@ -279,6 +282,84 @@ Submodules are detailed in [Submodule](#section-option-types-submodule).
     more convenient and discoverable than expecting the module user to
     type-merge with the `attrsOf submodule` option.
 
+## Union types {#sec-option-types-unions}
+
+A union of types is a type such that a value is valid when it is valid for at least one of those types.
+
+If some values are instances of more than one of the types, it is not possible to distinguish which type they are meant to be instances of. If that's needed, consider using a [sum type](#sec-option-types-sums).
+
+`types.either` *`t1 t2`*
+
+:   Type *`t1`* or type *`t2`*, e.g. `with types; either int str`.
+    Multiple definitions cannot be merged.
+
+`types.oneOf` \[ *`t1 t2`* ... \]
+
+:   Type *`t1`* or type *`t2`* and so forth, e.g.
+    `with types; oneOf [ int str bool ]`. Multiple definitions cannot be
+    merged.
+
+`types.nullOr` *`t`*
+
+:   `null` or type *`t`*. Multiple definitions are merged according to
+    type *`t`*.
+
+
+## Sum types {#sec-option-types-sums}
+
+A sum type can be thought of, conceptually, as a *`types.enum`* where each valid item is paired with at least a type, through some value syntax.
+Nix does not have a built-in syntax for this pairing of a label and a type or value, so sum types may be represented in multiple ways.
+
+If the you're interested in can be distinguished without a label, you may simplify your value syntax with a [union type](#sec-option-types-unions) instead.
+
+`types.attrTag` *`{ attr1 = option1; attr2 = option2; ... }`*
+
+:   An attribute set containing one attribute, whose name must be picked from
+    the attribute set (`attr1`, etc) and whose value consists of definitions that are valid for the corresponding option (`option1`, etc).
+
+    This type appears in the documentation as _attribute-tagged union_.
+
+    Example:
+
+    ```nix
+    { lib, ... }:
+    let inherit (lib) type mkOption;
+    in {
+      options.toyRouter.rules = mkOption {
+        description = ''
+          Rules for a fictional packet routing service.
+        '';
+        type = types.attrsOf (
+          types.attrTag {
+            bounce = mkOption {
+              description = "Send back a packet explaining why it wasn't forwarded.";
+              type = types.submodule {
+                options.errorMessage = mkOption { … };
+              };
+            };
+            forward = mkOption {
+              description = "Forward the packet.";
+              type = types.submodule {
+                options.destination = mkOption { … };
+              };
+            };
+            ignore = types.mkOption {
+              description = "Drop the packet without sending anything back.";
+              type = types.submodule {};
+            };
+          });
+      };
+      config.toyRouter.rules = {
+        http = {
+          bounce = {
+            errorMessage = "Unencrypted HTTP is banned. You must always use https://.";
+          };
+        };
+        ssh = { drop = {}; };
+      };
+    }
+    ```
+
 ## Composed types {#sec-option-types-composed}
 
 Composed types are types that take a type as parameter. `listOf
@@ -318,11 +399,6 @@ Composed types are types that take a type as parameter. `listOf
     returned instead for the same `mkIf false` definition.
     :::
 
-`types.nullOr` *`t`*
-
-:   `null` or type *`t`*. Multiple definitions are merged according to
-    type *`t`*.
-
 `types.uniq` *`t`*
 
 :   Ensures that type *`t`* cannot be merged. It is used to ensure option
@@ -334,17 +410,6 @@ Composed types are types that take a type as parameter. `listOf
     the line `The option <option path> is defined multiple times.` and before
     a list of definition locations.
 
-`types.either` *`t1 t2`*
-
-:   Type *`t1`* or type *`t2`*, e.g. `with types; either int str`.
-    Multiple definitions cannot be merged.
-
-`types.oneOf` \[ *`t1 t2`* ... \]
-
-:   Type *`t1`* or type *`t2`* and so forth, e.g.
-    `with types; oneOf [ int str bool ]`. Multiple definitions cannot be
-    merged.
-
 `types.coercedTo` *`from f to`*
 
 :   Type *`to`* or type *`from`* which will be coerced to type *`to`* using
@@ -374,19 +439,21 @@ if you want to allow users to leave it undefined.
 ::: {#ex-submodule-direct .example}
 ### Directly defined submodule
 ```nix
-options.mod = mkOption {
-  description = "submodule example";
-  type = with types; submodule {
-    options = {
-      foo = mkOption {
-        type = int;
-      };
-      bar = mkOption {
-        type = str;
+{
+  options.mod = mkOption {
+    description = "submodule example";
+    type = with types; submodule {
+      options = {
+        foo = mkOption {
+          type = int;
+        };
+        bar = mkOption {
+          type = str;
+        };
       };
     };
   };
-};
+}
 ```
 :::
 
@@ -405,10 +472,12 @@ let
     };
   };
 in
-options.mod = mkOption {
-  description = "submodule example";
-  type = with types; submodule modOptions;
-};
+{
+  options.mod = mkOption {
+    description = "submodule example";
+    type = with types; submodule modOptions;
+  };
+}
 ```
 :::
 
@@ -421,29 +490,33 @@ multiple definitions of the submodule option set
 ::: {#ex-submodule-listof-declaration .example}
 ### Declaration of a list of submodules
 ```nix
-options.mod = mkOption {
-  description = "submodule example";
-  type = with types; listOf (submodule {
-    options = {
-      foo = mkOption {
-        type = int;
-      };
-      bar = mkOption {
-        type = str;
+{
+  options.mod = mkOption {
+    description = "submodule example";
+    type = with types; listOf (submodule {
+      options = {
+        foo = mkOption {
+          type = int;
+        };
+        bar = mkOption {
+          type = str;
+        };
       };
-    };
-  });
-};
+    });
+  };
+}
 ```
 :::
 
 ::: {#ex-submodule-listof-definition .example}
 ### Definition of a list of submodules
 ```nix
-config.mod = [
-  { foo = 1; bar = "one"; }
-  { foo = 2; bar = "two"; }
-];
+{
+  config.mod = [
+    { foo = 1; bar = "one"; }
+    { foo = 2; bar = "two"; }
+  ];
+}
 ```
 :::
 
@@ -455,27 +528,31 @@ multiple named definitions of the submodule option set
 ::: {#ex-submodule-attrsof-declaration .example}
 ### Declaration of attribute sets of submodules
 ```nix
-options.mod = mkOption {
-  description = "submodule example";
-  type = with types; attrsOf (submodule {
-    options = {
-      foo = mkOption {
-        type = int;
-      };
-      bar = mkOption {
-        type = str;
+{
+  options.mod = mkOption {
+    description = "submodule example";
+    type = with types; attrsOf (submodule {
+      options = {
+        foo = mkOption {
+          type = int;
+        };
+        bar = mkOption {
+          type = str;
+        };
       };
-    };
-  });
-};
+    });
+  };
+}
 ```
 :::
 
 ::: {#ex-submodule-attrsof-definition .example}
 ### Definition of attribute sets of submodules
 ```nix
-config.mod.one = { foo = 1; bar = "one"; };
-config.mod.two = { foo = 2; bar = "two"; };
+{
+  config.mod.one = { foo = 1; bar = "one"; };
+  config.mod.two = { foo = 2; bar = "two"; };
+}
 ```
 :::
 
@@ -495,10 +572,12 @@ Types are mainly characterized by their `check` and `merge` functions.
     ### Adding a type check
 
     ```nix
-    byte = mkOption {
-      description = "An integer between 0 and 255.";
-      type = types.addCheck types.int (x: x >= 0 && x <= 255);
-    };
+    {
+      byte = mkOption {
+        description = "An integer between 0 and 255.";
+        type = types.addCheck types.int (x: x >= 0 && x <= 255);
+      };
+    }
     ```
     :::
 
@@ -506,12 +585,14 @@ Types are mainly characterized by their `check` and `merge` functions.
     ### Overriding a type check
 
     ```nix
-    nixThings = mkOption {
-      description = "words that start with 'nix'";
-      type = types.str // {
-        check = (x: lib.hasPrefix "nix" x)
+    {
+      nixThings = mkOption {
+        description = "words that start with 'nix'";
+        type = types.str // {
+          check = (x: lib.hasPrefix "nix" x);
+        };
       };
-    };
+    }
     ```
     :::
 
diff --git a/nixos/doc/manual/development/settings-options.section.md b/nixos/doc/manual/development/settings-options.section.md
index 71ec9bbc88925..806eee5637907 100644
--- a/nixos/doc/manual/development/settings-options.section.md
+++ b/nixos/doc/manual/development/settings-options.section.md
@@ -248,28 +248,30 @@ up in the manual.
 ::: {#ex-settings-typed-attrs .example}
 ### Declaring a type-checked `settings` attribute
 ```nix
-settings = lib.mkOption {
-  type = lib.types.submodule {
+{
+  settings = lib.mkOption {
+    type = lib.types.submodule {
+
+      freeformType = settingsFormat.type;
+
+      # Declare an option for the port such that the type is checked and this option
+      # is shown in the manual.
+      options.port = lib.mkOption {
+        type = lib.types.port;
+        default = 8080;
+        description = ''
+          Which port this service should listen on.
+        '';
+      };
 
-    freeformType = settingsFormat.type;
-
-    # Declare an option for the port such that the type is checked and this option
-    # is shown in the manual.
-    options.port = lib.mkOption {
-      type = lib.types.port;
-      default = 8080;
-      description = ''
-        Which port this service should listen on.
-      '';
     };
-
+    default = {};
+    description = ''
+      Configuration for Foo, see
+      <link xlink:href="https://example.com/docs/foo"/>
+      for supported values.
+    '';
   };
-  default = {};
-  description = ''
-    Configuration for Foo, see
-    <link xlink:href="https://example.com/docs/foo"/>
-    for supported values.
-  '';
-};
+}
 ```
 :::
diff --git a/nixos/doc/manual/development/unit-handling.section.md b/nixos/doc/manual/development/unit-handling.section.md
index d5ba6a9529d01..1f6a30d6ef343 100644
--- a/nixos/doc/manual/development/unit-handling.section.md
+++ b/nixos/doc/manual/development/unit-handling.section.md
@@ -94,11 +94,13 @@ To make an existing sysinit service restart correctly during system switch, you
 have to declare:
 
 ```nix
-systemd.services.my-sysinit = {
-  requiredBy = [ "sysinit-reactivation.target" ];
-  before = [ "sysinit-reactivation.target" ];
-  restartTriggers = [ config.environment.etc."my-sysinit.d".source ];
-};
+{
+  systemd.services.my-sysinit = {
+    requiredBy = [ "sysinit-reactivation.target" ];
+    before = [ "sysinit-reactivation.target" ];
+    restartTriggers = [ config.environment.etc."my-sysinit.d".source ];
+  };
+}
 ```
 
 You need to configure appropriate `restartTriggers` specific to your service.
diff --git a/nixos/doc/manual/development/writing-modules.chapter.md b/nixos/doc/manual/development/writing-modules.chapter.md
index 20157a21e890f..67a5cc23a6aa5 100644
--- a/nixos/doc/manual/development/writing-modules.chapter.md
+++ b/nixos/doc/manual/development/writing-modules.chapter.md
@@ -28,7 +28,7 @@ NixOS modules:
 ```nix
 { config, pkgs, ... }:
 
-{ option definitions
+{ # option definitions
 }
 ```
 
@@ -43,15 +43,15 @@ is shown in [Example: Structure of NixOS Modules](#ex-module-syntax).
 
 {
   imports =
-    [ paths of other modules
+    [ # paths of other modules
     ];
 
   options = {
-    option declarations
+    # option declarations
   };
 
   config = {
-    option definitions
+    # option definitions
   };
 }
 ```
diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md
index 50886376c2409..3ce12f41c60fe 100644
--- a/nixos/doc/manual/development/writing-nixos-tests.section.md
+++ b/nixos/doc/manual/development/writing-nixos-tests.section.md
@@ -8,10 +8,10 @@ A NixOS test is a module that has the following structure:
   # One or more machines:
   nodes =
     { machine =
-        { config, pkgs, ... }: { … };
+        { config, pkgs, ... }: { /* ... */ };
       machine2 =
-        { config, pkgs, ... }: { … };
-      …
+        { config, pkgs, ... }: { /* ... */ };
+      # …
     };
 
   testScript =
@@ -46,16 +46,20 @@ Tests are invoked differently depending on whether the test is part of NixOS or
 Tests that are part of NixOS are added to [`nixos/tests/all-tests.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix).
 
 ```nix
+{
   hostname = runTest ./hostname.nix;
+}
 ```
 
 Overrides can be added by defining an anonymous module in `all-tests.nix`.
 
 ```nix
+{
   hostname = runTest {
     imports = [ ./hostname.nix ];
     defaults.networking.firewall.enable = false;
   };
+}
 ```
 
 You can run a test with attribute name `hostname` in `nixos/tests/all-tests.nix` by invoking:
@@ -161,7 +165,7 @@ For faster dev cycles it's also possible to disable the code-linters
   skipLint = true;
   nodes.machine =
     { config, pkgs, ... }:
-    { configuration…
+    { # configuration…
     };
 
   testScript =
@@ -177,12 +181,14 @@ linter directly (again, don't commit this within the Nixpkgs
 repository):
 
 ```nix
+{
   testScript =
     ''
       # fmt: off
       Python code…
       # fmt: on
     '';
+}
 ```
 
 Similarly, the type checking of test scripts can be disabled in the following
@@ -193,7 +199,7 @@ way:
   skipTypeCheck = true;
   nodes.machine =
     { config, pkgs, ... }:
-    { configuration…
+    { # configuration…
     };
 }
 ```
diff --git a/nixos/doc/manual/installation/building-images-via-systemd-repart.chapter.md b/nixos/doc/manual/installation/building-images-via-systemd-repart.chapter.md
index 10bee156d113a..5a552a54f5319 100644
--- a/nixos/doc/manual/installation/building-images-via-systemd-repart.chapter.md
+++ b/nixos/doc/manual/installation/building-images-via-systemd-repart.chapter.md
@@ -18,11 +18,11 @@ An example of how to build an image:
     partitions = {
       "esp" = {
         contents = {
-          ...
+          # ...
         };
         repartConfig = {
           Type = "esp";
-          ...
+          # ...
         };
       };
       "root" = {
@@ -30,7 +30,7 @@ An example of how to build an image:
         repartConfig = {
           Type = "root";
           Label = "nixos";
-          ...
+          # ...
         };
       };
     };
@@ -47,19 +47,21 @@ determined by the mount point, you have to set `stripNixStorePrefix = true;` so
 that the prefix is stripped from the paths before copying them into the image.
 
 ```nix
-fileSystems."/nix/store".device = "/dev/disk/by-partlabel/nix-store"
-
-image.repart.partitions = {
-  "store" = {
-    storePaths = [ config.system.build.toplevel ];
-    stripNixStorePrefix = true;
-    repartConfig = {
-      Type = "linux-generic";
-      Label = "nix-store";
-      ...
+{
+  fileSystems."/nix/store".device = "/dev/disk/by-partlabel/nix-store";
+
+  image.repart.partitions = {
+    "store" = {
+      storePaths = [ config.system.build.toplevel ];
+      stripNixStorePrefix = true;
+      repartConfig = {
+        Type = "linux-generic";
+        Label = "nix-store";
+        # ...
+      };
     };
   };
-};
+}
 ```
 
 ## Appliance Image {#sec-image-repart-appliance}
diff --git a/nixos/doc/manual/installation/changing-config.chapter.md b/nixos/doc/manual/installation/changing-config.chapter.md
index 9e56b15a880f6..07a0074d17e7b 100644
--- a/nixos/doc/manual/installation/changing-config.chapter.md
+++ b/nixos/doc/manual/installation/changing-config.chapter.md
@@ -87,7 +87,9 @@ set `mutableUsers = false`. Another way is to temporarily add the
 following to your configuration:
 
 ```nix
-users.users.your-user.initialHashedPassword = "test";
+{
+  users.users.your-user.initialHashedPassword = "test";
+}
 ```
 
 *Important:* delete the \$hostname.qcow2 file if you have started the
diff --git a/nixos/doc/manual/installation/installing-behind-a-proxy.section.md b/nixos/doc/manual/installation/installing-behind-a-proxy.section.md
index aca151531d0f4..691f9c9ccf6dd 100644
--- a/nixos/doc/manual/installation/installing-behind-a-proxy.section.md
+++ b/nixos/doc/manual/installation/installing-behind-a-proxy.section.md
@@ -7,8 +7,10 @@ To install NixOS behind a proxy, do the following before running
     keep the internet accessible after reboot.
 
     ```nix
-    networking.proxy.default = "http://user:password@proxy:port/";
-    networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
+    {
+      networking.proxy.default = "http://user:password@proxy:port/";
+      networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
+    }
     ```
 
 1.  Setup the proxy environment variables in the shell where you are
diff --git a/nixos/doc/manual/installation/installing-from-other-distro.section.md b/nixos/doc/manual/installation/installing-from-other-distro.section.md
index 921592fe53573..10ac2be4e161f 100644
--- a/nixos/doc/manual/installation/installing-from-other-distro.section.md
+++ b/nixos/doc/manual/installation/installing-from-other-distro.section.md
@@ -89,12 +89,14 @@ The first steps to all these are the same:
     want to add something like this to your `configuration.nix`:
 
     ```nix
-    boot.loader.grub.extraEntries = ''
-      menuentry "Ubuntu" {
-        search --set=ubuntu --fs-uuid 3cc3e652-0c1f-4800-8451-033754f68e6e
-        configfile "($ubuntu)/boot/grub/grub.cfg"
-      }
-    '';
+    {
+      boot.loader.grub.extraEntries = ''
+        menuentry "Ubuntu" {
+          search --set=ubuntu --fs-uuid 3cc3e652-0c1f-4800-8451-033754f68e6e
+          configfile "($ubuntu)/boot/grub/grub.cfg"
+        }
+      '';
+    }
     ```
 
     (You can find the appropriate UUID for your partition in
@@ -164,7 +166,9 @@ The first steps to all these are the same:
     `sudo passwd -l root` if you use `sudo`)
 
     ```nix
-    users.users.root.initialHashedPassword = "";
+    {
+      users.users.root.initialHashedPassword = "";
+    }
     ```
 
 1.  Build the NixOS closure and install it in the `system` profile:
diff --git a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
index 004838e586be6..4b9ae0a9c55f0 100644
--- a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
+++ b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
@@ -29,14 +29,18 @@ There are a few modifications you should make in configuration.nix.
 Enable booting:
 
 ```nix
-boot.loader.grub.device = "/dev/sda";
+{
+  boot.loader.grub.device = "/dev/sda";
+}
 ```
 
 Also remove the fsck that runs at startup. It will always fail to run,
 stopping your boot until you press `*`.
 
 ```nix
-boot.initrd.checkJournalingFS = false;
+{
+  boot.initrd.checkJournalingFS = false;
+}
 ```
 
 Shared folders can be given a name and a path in the host system in the
diff --git a/nixos/doc/manual/installation/installing.chapter.md b/nixos/doc/manual/installation/installing.chapter.md
index c7deb07352f1c..b6db40878ba76 100644
--- a/nixos/doc/manual/installation/installing.chapter.md
+++ b/nixos/doc/manual/installation/installing.chapter.md
@@ -376,7 +376,7 @@ Use the following commands:
 
     ```ShellSession
     # mkdir -p /mnt/boot
-    # mount /dev/disk/by-label/boot /mnt/boot
+    # mount -o umask=077 /dev/disk/by-label/boot /mnt/boot
     ```
 
 3.  If your machine has a limited amount of memory, you may want to
@@ -572,7 +572,7 @@ With a partitioned disk.
 # mkfs.fat -F 32 -n boot /dev/sda3        # (for UEFI systems only)
 # mount /dev/disk/by-label/nixos /mnt
 # mkdir -p /mnt/boot                      # (for UEFI systems only)
-# mount /dev/disk/by-label/boot /mnt/boot # (for UEFI systems only)
+# mount -o umask=077 /dev/disk/by-label/boot /mnt/boot # (for UEFI systems only)
 # nixos-generate-config --root /mnt
 # nano /mnt/etc/nixos/configuration.nix
 # nixos-install
diff --git a/nixos/doc/manual/installation/upgrading.chapter.md b/nixos/doc/manual/installation/upgrading.chapter.md
index 79cd4e55be5cc..09338bf8723d2 100644
--- a/nixos/doc/manual/installation/upgrading.chapter.md
+++ b/nixos/doc/manual/installation/upgrading.chapter.md
@@ -101,8 +101,10 @@ You can keep a NixOS system up-to-date automatically by adding the
 following to `configuration.nix`:
 
 ```nix
-system.autoUpgrade.enable = true;
-system.autoUpgrade.allowReboot = true;
+{
+  system.autoUpgrade.enable = true;
+  system.autoUpgrade.allowReboot = true;
+}
 ```
 
 This enables a periodically executed systemd service named
@@ -114,5 +116,7 @@ the new generation contains a different kernel, initrd or kernel
 modules. You can also specify a channel explicitly, e.g.
 
 ```nix
-system.autoUpgrade.channel = "https://channels.nixos.org/nixos-23.11";
+{
+  system.autoUpgrade.channel = "https://channels.nixos.org/nixos-23.11";
+}
 ```
diff --git a/nixos/doc/manual/release-notes/rl-1509.section.md b/nixos/doc/manual/release-notes/rl-1509.section.md
index f47d13008185e..c2ac89a095184 100644
--- a/nixos/doc/manual/release-notes/rl-1509.section.md
+++ b/nixos/doc/manual/release-notes/rl-1509.section.md
@@ -253,9 +253,9 @@ Installing Haskell _libraries_ this way, however, is no longer supported. See th
 
   {
     options = {
-      foo = mkOption { … };
+      foo = mkOption { /* … */ };
     };
-    config = mkIf config.foo { … };
+    config = mkIf config.foo { /* … */ };
   }
   ```
 
@@ -268,9 +268,9 @@ Installing Haskell _libraries_ this way, however, is no longer supported. See th
 
   {
     options = {
-      foo = mkOption { option declaration };
+      foo = mkOption { /* option declaration */ };
     };
-    config = mkIf config.foo { option definition };
+    config = mkIf config.foo { /* option definition */ };
   }
   ```
 
diff --git a/nixos/doc/manual/release-notes/rl-1703.section.md b/nixos/doc/manual/release-notes/rl-1703.section.md
index b82c41e28ca34..e20d84d306e82 100644
--- a/nixos/doc/manual/release-notes/rl-1703.section.md
+++ b/nixos/doc/manual/release-notes/rl-1703.section.md
@@ -246,7 +246,7 @@ When upgrading from a previous release, please be aware of the following incompa
   let
     pkgs = import <nixpkgs> {};
   in
-    pkgs.overridePackages (self: super: ...)
+    pkgs.overridePackages (self: super: { /* ... */ })
   ```
 
   should be replaced by:
@@ -255,7 +255,7 @@ When upgrading from a previous release, please be aware of the following incompa
   let
     pkgs = import <nixpkgs> {};
   in
-    import pkgs.path { overlays = [(self: super: ...)]; }
+    import pkgs.path { overlays = [(self: super: { /* ... */ })]; }
   ```
 
 - Autoloading connection tracking helpers is now disabled by default. This default was also changed in the Linux kernel and is considered insecure if not configured properly in your firewall. If you need connection tracking helpers (i.e. for active FTP) please enable `networking.firewall.autoLoadConntrackHelpers` and tune `networking.firewall.connectionTrackingModules` to suit your needs.
diff --git a/nixos/doc/manual/release-notes/rl-1909.section.md b/nixos/doc/manual/release-notes/rl-1909.section.md
index 2bd04f8dd40a3..49fc98c313ac3 100644
--- a/nixos/doc/manual/release-notes/rl-1909.section.md
+++ b/nixos/doc/manual/release-notes/rl-1909.section.md
@@ -230,7 +230,7 @@ When upgrading from a previous release, please be aware of the following incompa
 
 - The `documentation` module gained an option named `documentation.nixos.includeAllModules` which makes the generated configuration.nix 5 manual page include all options from all NixOS modules included in a given `configuration.nix` configuration file. Currently, it is set to `false` by default as enabling it frequently prevents evaluation. But the plan is to eventually have it set to `true` by default. Please set it to `true` now in your `configuration.nix` and fix all the bugs it uncovers.
 
-- The `vlc` package gained support for Chromecast streaming, enabled by default. TCP port 8010 must be open for it to work, so something like `networking.firewall.allowedTCPPorts = [ 8010 ];` may be required in your configuration. Also consider enabling [ Accelerated Video Playback](https://nixos.wiki/wiki/Accelerated_Video_Playback) for better transcoding performance.
+- The `vlc` package gained support for Chromecast streaming, enabled by default. TCP port 8010 must be open for it to work, so something like `networking.firewall.allowedTCPPorts = [ 8010 ];` may be required in your configuration. Also consider enabling [ Accelerated Video Playback](https://wiki.nixos.org/wiki/Accelerated_Video_Playback) for better transcoding performance.
 
 - The following changes apply if the `stateVersion` is changed to 19.09 or higher. For `stateVersion = "19.03"` or lower the old behavior is preserved.
 
diff --git a/nixos/doc/manual/release-notes/rl-2009.section.md b/nixos/doc/manual/release-notes/rl-2009.section.md
index eac02a8ff445b..900c20dbe717d 100644
--- a/nixos/doc/manual/release-notes/rl-2009.section.md
+++ b/nixos/doc/manual/release-notes/rl-2009.section.md
@@ -334,22 +334,18 @@ When upgrading from a previous release, please be aware of the following incompa
 - The remaining configuration flags can now be set directly on the `php` attribute. For example, instead of
 
   ```nix
-  {
-    php.override {
-      config.php.embed = true;
-      config.php.apxs2 = false;
-    }
+  php.override {
+    config.php.embed = true;
+    config.php.apxs2 = false;
   }
   ```
 
   you should now write
 
   ```nix
-  {
-    php.override {
-      embedSupport = true;
-      apxs2Support = false;
-    }
+  php.override {
+    embedSupport = true;
+    apxs2Support = false;
   }
   ```
 
@@ -383,9 +379,10 @@ When upgrading from a previous release, please be aware of the following incompa
   {
     specialisation.example-sub-configuration = {
       configuration = {
-        ...
+        # ...
       };
-  };
+    };
+  }
   ```
 
   Replace a `nesting.children` entry with:
@@ -395,9 +392,10 @@ When upgrading from a previous release, please be aware of the following incompa
     specialisation.example-sub-configuration = {
       inheritParentConfig = false;
       configuration = {
-        ...
+        # ...
       };
-  };
+    };
+  }
   ```
 
   To switch to a specialised configuration at runtime you need to run:
@@ -469,7 +467,7 @@ When upgrading from a previous release, please be aware of the following incompa
     services.bitcoind = {
       enable = true;
       extraConfig = "...";
-      ...
+      # ...
     };
   }
   ```
@@ -483,7 +481,7 @@ When upgrading from a previous release, please be aware of the following incompa
       dataDir = "/var/lib/bitcoind";
       user = "bitcoin";
       extraConfig = "...";
-      ...
+      # ...
     };
   }
   ```
@@ -502,7 +500,7 @@ When upgrading from a previous release, please be aware of the following incompa
   {
     services.dokuwiki = {
       enable = true;
-      ...
+      # ...
     };
   }
   ```
@@ -517,7 +515,7 @@ When upgrading from a previous release, please be aware of the following incompa
         forceSSL = true;
         enableACME = true;
       };
-      ...
+      # ...
     };
   }
   ```
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 6f5a807f478a2..3a2c70fb7a31b 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -462,6 +462,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 
   Before:
   ```nix
+  {
     services.keycloak = {
       enable = true;
       httpPort = "8080";
@@ -471,10 +472,12 @@ In addition to numerous new and upgraded packages, this release has the followin
         "subsystem=undertow"."server=default-server"."http-listener=default".proxy-address-forwarding = true;
       };
     };
+  }
   ```
 
   After:
   ```nix
+  {
     services.keycloak = {
       enable = true;
       settings = {
@@ -485,6 +488,7 @@ In addition to numerous new and upgraded packages, this release has the followin
       };
       database.passwordFile = "/run/keys/db_password";
     };
+  }
   ```
 
 - The MoinMoin wiki engine (`services.moinmoin`) has been removed, because Python 2 is being retired from nixpkgs.
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index 1c73d0c9790d5..77cb6c9baadbc 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -254,10 +254,12 @@ In addition to numerous new and upgraded packages, this release includes the fol
 
 - `services.github-runner` and `services.github-runners.<name>` gained the option `serviceOverrides` which allows overriding the systemd `serviceConfig`. If you have been overriding the systemd service configuration (i.e., by defining `systemd.services.github-runner.serviceConfig`), you have to use the `serviceOverrides` option now. Example:
 
-  ```
-  services.github-runner.serviceOverrides.SupplementaryGroups = [
-    "docker"
-  ];
+  ```nix
+  {
+    services.github-runner.serviceOverrides.SupplementaryGroups = [
+      "docker"
+    ];
+  }
   ```
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index 21c798b3b4a46..ce874a6e0b2d6 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -25,7 +25,9 @@ In addition to numerous new and updated packages, this release has the following
 
 - NixOS now defaults to using [nsncd](https://github.com/twosigma/nsncd), a non-caching reimplementation of nscd in Rust, as its NSS lookup dispatcher. This replaces the buggy and deprecated nscd implementation provided through glibc. When you find problems, you can switch back by disabling it:
   ```nix
-  services.nscd.enableNsncd = false;
+  {
+    services.nscd.enableNsncd = false;
+  }
   ```
 
 - The internal option `boot.bootspec.enable` is now enabled by default because [RFC 0125](https://github.com/NixOS/rfcs/pull/125) was merged. This means you will have a bootspec document called `boot.json` generated for each system and specialisation in the top-level. This is useful to enable advanced boot use cases in NixOS, such as Secure Boot.
@@ -190,11 +192,13 @@ In addition to numerous new and updated packages, this release has the following
 - MAC-then-encrypt algorithms were removed from the default selection of `services.openssh.settings.Macs`. If you still require these [MACs](https://en.wikipedia.org/wiki/Message_authentication_code), for example when you are relying on libssh2 (e.g. VLC) or the SSH library shipped on the iPhone, you can re-add them like this:
 
   ```nix
-  services.openssh.settings.Macs = [
-    "hmac-sha2-512"
-    "hmac-sha2-256"
-    "umac-128@openssh.com"
-  ];
+  {
+    services.openssh.settings.Macs = [
+      "hmac-sha2-512"
+      "hmac-sha2-256"
+      "umac-128@openssh.com"
+    ];
+  }
   ```
 
 - `podman` now uses the `netavark` network stack. Users will need to delete all of their local containers, images, volumes, etc, by running `podman system reset --force` once before upgrading their systems.
@@ -227,21 +231,25 @@ In addition to numerous new and updated packages, this release has the following
 - The attributes used by `services.snapper.configs.<name>` have changed. Migrate from this:
 
   ```nix
-  services.snapper.configs.example = {
-    subvolume = "/example";
-    extraConfig = ''
-      ALLOW_USERS="alice"
-    '';
-  };
+  {
+    services.snapper.configs.example = {
+      subvolume = "/example";
+      extraConfig = ''
+        ALLOW_USERS="alice"
+      '';
+    };
+  }
   ```
 
   to this:
 
   ```nix
-  services.snapper.configs.example = {
-    SUBVOLUME = "/example";
-    ALLOW_USERS = [ "alice" ];
-  };
+  {
+    services.snapper.configs.example = {
+      SUBVOLUME = "/example";
+      ALLOW_USERS = [ "alice" ];
+    };
+  }
   ```
 
 - The default module options for [services.snapserver.openFirewall](#opt-services.snapserver.openFirewall), [services.tmate-ssh-server.openFirewall](#opt-services.tmate-ssh-server.openFirewall) and [services.unifi-video.openFirewall](#opt-services.unifi-video.openFirewall) have been changed from `true` to `false`. You will need to explicitly set this option to `true`, or configure your firewall.
@@ -446,15 +454,17 @@ In addition to numerous new and updated packages, this release has the following
 - NixOS swap partitions with random encryption can now control the sector size, cipher, and key size used to set up the plain encryption device over the underlying block device rather than allowing them to be determined by `cryptsetup(8)`. One can use these features like so:
 
   ```nix
-  swapDevices = [ {
-    device = "/dev/disk/by-partlabel/swapspace";
-    randomEncryption = {
-      enable = true;
-      cipher = "aes-xts-plain64";
-      keySize = 512;
-      sectorSize = 4096;
-    };
-  } ];
+  {
+    swapDevices = [ {
+      device = "/dev/disk/by-partlabel/swapspace";
+      randomEncryption = {
+        enable = true;
+        cipher = "aes-xts-plain64";
+        keySize = 512;
+        sectorSize = 4096;
+      };
+    } ];
+  }
   ```
 
 - New option `security.pam.zfs` to enable unlocking and mounting of encrypted ZFS home dataset at login.
@@ -465,7 +475,9 @@ In addition to numerous new and updated packages, this release has the following
 
 - PostgreSQL has added opt-in support for [JIT compilation](https://www.postgresql.org/docs/current/jit-reason.html). It can be enabled like this:
   ```nix
-  services.postgresql.enableJIT = true;
+  {
+    services.postgresql.enableJIT = true;
+  }
   ```
 
 - `services.netdata` offers a [`services.netdata.deadlineBeforeStopSec`](#opt-services.netdata.deadlineBeforeStopSec) option which will control the deadline (in seconds) after which systemd will consider your netdata instance as dead if it didn't start in the elapsed time. It is helpful when your netdata instance takes longer to start because of a large amount of state or upgrades.
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md
index 1aef1828908f8..d837e0ff68b7c 100644
--- a/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -700,11 +700,13 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2
   will probably be removed eventually.
 
   ```nix
-  qt = {
-    enable = true;
-    platformTheme = "gnome";
-    style = "adwaita";
-  };
+  {
+    qt = {
+      enable = true;
+      platformTheme = "gnome";
+      style = "adwaita";
+    };
+  }
   ```
 
 - DocBook option documentation is no longer supported, all module documentation
@@ -885,11 +887,13 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2
   to a compatible major version, so they can move at their own pace.
 
   ```nix
-  python = python3.override {
-    packageOverrides = self: super: {
-      django = super.django_3;
+  {
+    python = python3.override {
+      packageOverrides = self: super: {
+        django = super.django_3;
+      };
     };
-  };
+  }
   ```
 
 - The `qemu-vm.nix` module by default now identifies block devices via
@@ -997,7 +1001,7 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2
   Satellite](https://github.com/synesthesiam/homeassistant-satellite), a
   streaming audio satellite for Home Assistant voice pipelines, where you can
   reuse existing mic and speaker hardware. Available as
-  [services.homeassistant-satellite](#opt-services.homeassistant-satellite.enable).
+  `services.homeassistant-satellite`.
 
 - [Apache Guacamole](https://guacamole.apache.org/), a cross-platform,
   clientless remote desktop gateway. Available as
@@ -1228,16 +1232,18 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2
 - CoreDNS may be built with external plugins now. This may be done by
   overriding `externalPlugins` and `vendorHash` arguments like this:
 
-  ```
-  services.coredns = {
-    enable = true;
-    package = pkgs.coredns.override {
-      externalPlugins = [
-        {name = "fanout"; repo = "github.com/networkservicemesh/fanout"; version = "v1.9.1";}
-      ];
-      vendorHash = "<SRI hash>";
+  ```nix
+  {
+    services.coredns = {
+      enable = true;
+      package = pkgs.coredns.override {
+        externalPlugins = [
+          {name = "fanout"; repo = "github.com/networkservicemesh/fanout"; version = "v1.9.1";}
+        ];
+        vendorHash = "<SRI hash>";
+      };
     };
-  };
+  }
   ```
 
   To get the necessary SRI hash, set `vendorHash = "";`. The build will fail
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index 710172699abf9..9ef92632094d5 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -32,9 +32,16 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - Julia environments can now be built with arbitrary packages from the ecosystem using the `.withPackages` function. For example: `julia.withPackages ["Plots"]`.
 
+- The PipeWire and WirePlumber modules have removed support for using
+`environment.etc."pipewire/..."` and `environment.etc."wireplumber/..."`.
+Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for PipeWire and
+`services.pipewire.wireplumber.configPackages` for WirePlumber instead."
+
 - A new option `systemd.sysusers.enable` was added. If enabled, users and
   groups are created with systemd-sysusers instead of with a custom perl script.
 
+- The default dbus implementation has transitioned to dbus-broker from the classic dbus daemon for better performance and reliability. Users can revert to the classic dbus daemon by setting `services.dbus.implementation = "dbus";`. For detailed deviations, refer to [dbus-broker's deviations page](https://github.com/bus1/dbus-broker/wiki/Deviations).
+
 - A new option `virtualisation.containers.cdi` was added. It contains `static` and `dynamic` attributes (corresponding to `/etc/cdi` and `/run/cdi` respectively) to configure the Container Device Interface (CDI).
 
 - `virtualisation.docker.enableNvidia` and `virtualisation.podman.enableNvidia` options are deprecated. `virtualisation.containers.cdi.dynamic.nvidia.enable` should be used instead. This option will expose GPUs on containers with the `--device` CLI option. This is supported by Docker 25, Podman 3.2.0 and Singularity 4. Any container runtime that supports the CDI specification will take advantage of this feature.
@@ -52,38 +59,59 @@ In addition to numerous new and upgraded packages, this release has the followin
   without perl). Previously, the NixOS activation depended on two perl scripts
   which can now be replaced via an opt-in mechanism. To make your system
   perlless, you can use the new perlless profile:
-  ```
+  ```nix
   { modulesPath, ... }: {
     imports = [ "${modulesPath}/profiles/perlless.nix" ];
   }
   ```
 
+- The initial Incus LTS release (v6.0.x) is now available through `virtualisation.incus` as the default. Users who wish to continue using the non-LTS release will need to set `virtualisation.incus.package = pkgs.incus`. Stable release users are encouraged to stay on the LTS release as non-LTS releases will by default not be backported.
+
+- Canonical LXD has been upgraded to v5.21.x, an LTS release. The LTS release is now the only supported LXD release. Users are encouraged to [migrate to Incus](https://linuxcontainers.org/incus/docs/main/howto/server_migrate_lxd/) for better support on NixOS.
+
+- lua interpreters default LUA_PATH and LUA_CPATH are not overriden by nixpkgs
+  anymore, we patch LUA_ROOT instead which is more respectful to upstream.
+
 - Plasma 6 is now available and can be installed with `services.xserver.desktopManager.plasma6.enable = true;`. Plasma 5 will likely be deprecated in the next release (24.11). Note that Plasma 6 runs as Wayland by default, and the X11 session needs to be explicitly selected if necessary.
 
 ## New Services {#sec-release-24.05-new-services}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
+- [ownCloud Infinite Scale Stack](https://owncloud.com/infinite-scale-4-0/), a modern and scalable rewrite of ownCloud.
+
 - [Handheld Daemon](https://github.com/hhd-dev/hhd), support for gaming handhelds like the Legion Go, ROG Ally, and GPD Win. Available as [services.handheld-daemon](#opt-services.handheld-daemon.enable).
 
 - [Guix](https://guix.gnu.org), a functional package manager inspired by Nix. Available as [services.guix](#opt-services.guix.enable).
 
+- [PhotonVision](https://photonvision.org/), a free, fast, and easy-to-use computer vision solution for the FIRST® Robotics Competition.
+
 - [pyLoad](https://pyload.net/), a FOSS download manager written in Python. Available as [services.pyload](#opt-services.pyload.enable)
 
 - [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable).
 
+- [ryzen-monitor-ng](https://github.com/mann1x/ryzen_monitor_ng), a desktop AMD CPU power monitor and controller, similar to Ryzen Master but for Linux. Available as [programs.ryzen-monitor-ng](#opt-programs.ryzen-monitor-ng.enable)
+
+- [ryzen-smu](https://gitlab.com/leogx9r/ryzen_smu), Linux kernel driver to expose the SMU (System Management Unit) for certain AMD Ryzen Processors. Includes the userspace program `monitor_cpu`. Available at [hardward.cpu.amd.ryzen-smu](#opt-hardware.cpu.amd.ryzen-smu.enable)
+
 - systemd's gateway, upload, and remote services, which provides ways of sending journals across the network. Enable using [services.journald.gateway](#opt-services.journald.gateway.enable), [services.journald.upload](#opt-services.journald.upload.enable), and [services.journald.remote](#opt-services.journald.remote.enable).
 
 - [GNS3](https://www.gns3.com/), a network software emulator. Available as [services.gns3-server](#opt-services.gns3-server.enable).
 
 - [pretalx](https://github.com/pretalx/pretalx), a conference planning tool. Available as [services.pretalx](#opt-services.pretalx.enable).
 
+- [dnsproxy](https://github.com/AdguardTeam/dnsproxy), a simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support. Available as [services.dnsproxy](#opt-services.dnsproxy.enable).
+
 - [rspamd-trainer](https://gitlab.com/onlime/rspamd-trainer), script triggered by a helper which reads mails from a specific mail inbox and feeds them into rspamd for spam/ham training.
 
 - [ollama](https://ollama.ai), server for running large language models locally.
 
+- [Mihomo](https://github.com/MetaCubeX/mihomo/tree/Alpha), 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).
 
+- [Workout-tracker](https://github.com/jovandeginste/workout-tracker), a workout tracking web application for personal use.
+
 - [Python Matter Server](https://github.com/home-assistant-libs/python-matter-server), a
   Matter Controller Server exposing websocket connections for use with other services, notably Home Assistant.
   Available as [services.matter-server](#opt-services.matter-server.enable)
@@ -91,8 +119,12 @@ In addition to numerous new and upgraded packages, this release has the followin
 - [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
 The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server softwares.
 
+- [mautrix-meta](https://github.com/mautrix/meta), a Matrix <-> Facebook and Matrix <-> Instagram hybrid puppeting/relaybot bridge. Available as services.mautrix-meta
+
 - [transfer-sh](https://github.com/dutchcoders/transfer.sh), a tool that supports easy and fast file sharing from the command-line. Available as [services.transfer-sh](#opt-services.transfer-sh.enable).
 
+- [MollySocket](https://github.com/mollyim/mollysocket) which allows getting Signal notifications via UnifiedPush.
+
 - [Suwayomi Server](https://github.com/Suwayomi/Suwayomi-Server), a free and open source manga reader server that runs extensions built for [Tachiyomi](https://tachiyomi.org). Available as [services.suwayomi-server](#opt-services.suwayomi-server.enable).
 
 - [ping_exporter](https://github.com/czerwonk/ping_exporter), a Prometheus exporter for ICMP echo requests. Available as [services.prometheus.exporters.ping](#opt-services.prometheus.exporters.ping.enable).
@@ -103,22 +135,40 @@ 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).
+
+- [microsocks](https://github.com/rofl0r/microsocks), a tiny, portable SOCKS5 server with very moderate resource usage. Available as [services.microsocks]($opt-services-microsocks.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`.
 
+- [wyoming-satellite](https://github.com/rhasspy/wyoming-satellite), a voice assistant satellite for Home Assistant using the Wyoming protocol. Available as [services.wyoming.satellite]($opt-services.wyoming.satellite.enable).
+
 - [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable).
 
+- binfmt option for AppImage-run to support running [AppImage](https://appimage.org/)'s seamlessly on NixOS.. Available as [programs.appimage.binfmt](#opt-programs.appimage.binfmt).
+
 - [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable)
 
 - [RustDesk](https://rustdesk.com), a full-featured open source remote control alternative for self-hosting and security with minimal configuration. Alternative to TeamViewer.
 
 - [Scrutiny](https://github.com/AnalogJ/scrutiny), a S.M.A.R.T monitoring tool for hard disks with a web frontend.
 
+- [davis](https://github.com/tchapi/davis), a simple CardDav and CalDav server inspired by Baïkal. Available as [services.davis]($opt-services-davis.enable).
+
 - [systemd-lock-handler](https://git.sr.ht/~whynothugo/systemd-lock-handler/), a bridge between logind D-Bus events and systemd targets. Available as [services.systemd-lock-handler.enable](#opt-services.systemd-lock-handler.enable).
 
+- [wastebin](https://github.com/matze/wastebin), a pastebin server written in rust. Available as [services.wastebin](#opt-services.wastebin.enable).
+
 - [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable)
 
+- [Uni-Sync](https://github.com/EightB1ts/uni-sync), a synchronization tool for Lian Li Uni Controllers. Available as [hardware.uni-sync](#opt-hardware.uni-sync.enable)
+
+- [prometheus-nats-exporter](https://github.com/nats-io/prometheus-nats-exporter), a Prometheus exporter for NATS. Available as [services.prometheus.exporters.nats](#opt-services.prometheus.exporters.nats.enable).
+
 ## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -129,8 +179,19 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - 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.
 
+- `programs.nix-ld.libraries` no longer sets `baseLibraries` via the option's default but in config and now merges any additional libraries with the default ones.
+  This means that `lib.mkForce` must be used to clear the list of default libraries.
+
+- `cudaPackages.autoAddOpenGLRunpathHook` and `cudaPackages.autoAddDriverRunpath` have been deprecated for `pkgs.autoAddDriverRunpath`. Functionality has not changed, but the setuphook has been renamed and moved to the top-level package scope.
+
+- `cudaPackages.autoFixElfFiles` has been deprecated for `pkgs.autoFixElfFiles`. Functionality has not changed, but the setuphook has been renamed and moved to the top-level package scope.
+
+- `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.
+
 - `unrar` was updated to v7. See [changelog](https://www.rarlab.com/unrar7notes.htm) for more information.
 
+- `git-town` was updated from version `11` to `13`. See the [changelog](https://github.com/git-town/git-town/blob/main/CHANGELOG.md#1300-2024-03-22) for breaking changes.
+
 - `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),
   [v0.30](https://github.com/derailed/k9s/releases/tag/v0.30.0) and
@@ -150,10 +211,16 @@ 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.
 
+- `unifiLTS`, `unifi5` and `unifi6` have been removed, as they require MongoDB versions which are end-of-life. All these versions can be upgraded to `unifi7` directly.
+
+- `mongodb-4_4` has been removed as it has reached end of life. Consequently, `unifi7` and `unifi8` now use MongoDB 5.0 by default.
+
 - `nitter` requires a `guest_accounts.jsonl` to be provided as a path or loaded into the default location at `/var/lib/nitter/guest_accounts.jsonl`. See [Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) for details.
 
 - `boot.supportedFilesystems` and `boot.initrd.supportedFilesystems` are now attribute sets instead of lists. Assignment from lists as done previously is still supported, but checking whether a filesystem is enabled must now by done using `supportedFilesystems.fs or false` instead of using `lib.elem "fs" supportedFilesystems` as was done previously.
@@ -165,18 +232,37 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   "mysecret"` becomes `services.aria2.rpcSecretFile = "/path/to/secret_file"`
   where the file `secret_file` contains the string `mysecret`.
 
+- `openssh`, `openssh_hpn` and `openssh_gssapi` are now compiled without support for the DSA signature algorithm as it is being deprecated upstream. Users still relying on DSA keys should consider upgrading
+  to another signature algorithm. It is however possible, for the time being, to restore the DSA keys support using `override` to set `dsaKeysSupport = true`.
+
+- `buildGoModule` now throws error when `vendorHash` is not specified. `vendorSha256`, deprecated in Nixpkgs 23.11, is now ignored and is no longer a `vendorHash` alias.
+
 - 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`.
 
+- `davfs2`' `services.davfs2.extraConfig` setting has been deprecated and converted to the freeform type option named `services.davfs2.settings` according to RFC42.
+
 - `services.homepage-dashboard` now takes it's configuration using native Nix expressions, rather than dumping templated configurations into `/var/lib/homepage-dashboard` where they were previously managed manually. There are now new options which allow the configuration of bookmarks, services, widgets and custom CSS/JS natively in Nix.
 
 - `hare` may now be cross-compiled. For that to work, however, `haredoc` needed to stop being built together with it. Thus, the latter is now its own package with the name of `haredoc`.
 
 - The legacy and long deprecated systemd target `network-interfaces.target` has been removed. Use `network.target` instead.
 
+- `azure-cli` now has extension support. For example, to install the `aks-preview` extension, use
+
+  ```nix
+  environment.systemPackages = [
+    (azure-cli.withExtensions [ azure-cli.extensions.aks-preview ]);
+  ];
+  ```
+  To make the `azure-cli` immutable and prevent clashes in case `azure-cli` is also installed via other package managers, some configuration files were moved into the derivation.
+  This can be disabled by overriding `withImmutableConfig = false` when building `azure-cli`.
+
 - `services.frp.settings` now generates the frp configuration file in TOML format as [recommended by upstream](https://github.com/fatedier/frp#configuration-files), instead of the legacy INI format. This has also introduced other changes in the configuration file structure and options.
   - The `settings.common` section in the configuration is no longer valid and all the options form inside it now goes directly under `settings`.
   - The `_` separating words in the configuration options is removed so the options are now in camel case. For example: `server_addr` becomes `serverAddr`, `server_port` becomes `serverPort` etc.
@@ -197,13 +283,14 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   Example:
 
   ```nix
+  {
     locations."/".extraConfig = ''
       add_header Alt-Svc 'h3=":$server_port"; ma=86400';
     '';
     locations."^~ /assets/".extraConfig = ''
       add_header Alt-Svc 'h3=":$server_port"; ma=86400';
     '';
-
+  }
   ```
 
 - The package `optparse-bash` is now dropped due to upstream inactivity. Alternatives available in Nixpkgs include [`argc`](https://github.com/sigoden/argc), [`argbash`](https://github.com/matejak/argbash), [`bashly`](https://github.com/DannyBen/bashly) and [`gum`](https://github.com/charmbracelet/gum), to name a few.
@@ -228,12 +315,21 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - The `cudaPackages` package scope has been updated to `cudaPackages_12`.
 
+- The `halloy` package was updated past 2024.5 which introduced a breaking change by switching the config format from YAML to TOML. See https://github.com/squidowl/halloy/releases/tag/2024.5 for details.
+
 - Ada packages (libraries and tools) have been moved into the `gnatPackages` scope. `gnatPackages` uses the default GNAT compiler, `gnat12Packages` and `gnat13Packages` use the respective matching compiler version.
 
 - `spark2014` has been renamed to `gnatprove`. A version of `gnatprove` matching different GNAT versions is available from the different `gnatPackages` sets.
 
 - `services.resolved.fallbackDns` can now be used to disable the upstream fallback servers entirely by setting it to an empty list. To get the previous behaviour of the upstream defaults set it to null, the new default, instead.
 
+- `services.hledger-web.capabilities` options has been replaced by a new option `services.hledger-web.allow`.
+
+  - `allow = "view"` means `capabilities = { view = true; }`;
+  - `allow = "add"` means `capabilities = { view = true; add = true; }`;
+  - `allow = "edit"` means `capabilities = { view = true; add = true; edit = true }`;
+  - `allow = "sandstorm"` reads permissions from the `X-Sandstorm-Permissions` request header.
+
 - `xxd` has been moved from `vim` default output to its own output to reduce closure size. The canonical way to reference it across all platforms is `unixtools.xxd`.
 
 - The `stalwart-mail` package has been updated to v0.5.3, which includes [breaking changes](https://github.com/stalwartlabs/mail-server/blob/v0.5.3/UPGRADING.md).
@@ -253,6 +349,10 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 - `services.archisteamfarm` no longer uses the abbreviation `asf` for its state directory (`/var/lib/asf`), user and group (both `asf`). Instead the long name `archisteamfarm` is used.
   Configurations with `system.stateVersion` 23.11 or earlier, default to the old stateDirectory until the 24.11 release and must either set the option explicitly or move the data to the new directory.
 
+- `xfsprogs` was updated to version 6.6.0, which enables reverse mapping (rmapbt) and large extent counts (nrext64) by default.
+   Support for these features was added in kernel 4.9 and 5.19 and nrext64 was deemed stable in kernel 6.5.
+   Format your filesystems with `mkfs.xfs -i nrext64=0`, if they need to be readable by GRUB2 before 2.12 or kernels older than 5.19.
+
 - `networking.iproute2.enable` now does not set `environment.etc."iproute2/rt_tables".text`.
 
   Setting `environment.etc."iproute2/{CONFIG_FILE_NAME}".text` will override the whole configuration file instead of appending it to the upstream configuration file.
@@ -311,16 +411,28 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   This means that configuration now has to be done using [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) instead of command line arguments.
   This has the further implication that the `livebook` service configuration has changed:
 
-  - The `erlang_node_short_name`, `erlang_node_name`, `port` and `options` configuration parameters are gone, and have been replaced with an `environment` parameter.
+- The `erlang_node_short_name`, `erlang_node_name`, `port` and `options` configuration parameters are gone, and have been replaced with an `environment` parameter.
     Use the appropriate [environment variables](https://hexdocs.pm/livebook/readme.html#environment-variables) inside `environment` to configure the service instead.
 
+- The `crystal` package has been updated to 1.11.x, which has some breaking changes.
+  Refer to crystal's changelog for more information. ([v1.10](https://github.com/crystal-lang/crystal/blob/master/CHANGELOG.md#1100-2023-10-09), [v1.11](https://github.com/crystal-lang/crystal/blob/master/CHANGELOG.md#1110-2024-01-08))
+
+- The `erlang-ls` package no longer ships the `els_dap` binary as of v0.51.0.
+
 ## Other Notable Changes {#sec-release-24.05-notable-changes}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
 - `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.
 
@@ -332,10 +444,15 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 - The Matrix homeserver [Synapse](https://element-hq.github.io/synapse/) module now supports configuring UNIX domain socket [listeners](#opt-services.matrix-synapse.settings.listeners) through the `path` option.
   The default replication worker on the main instance has been migrated away from TCP sockets to UNIX domain sockets.
 
+- The initrd ssh daemon module got a new option to add authorized keys via a list of files using `boot.initrd.network.ssh.authorizedKeyFiles`.
+
 - Programs written in [Nim](https://nim-lang.org/) are built with libraries selected by lockfiles.
   The `nimPackages` and `nim2Packages` sets have been removed.
   See https://nixos.org/manual/nixpkgs/unstable#nim for more information.
 
+- Programs written in [D](https://dlang.org/) using the `dub` build system and package manager can now be built using `buildDubPackage` utilizing lockfiles provided by the new `dub-to-nix` helper program.
+  See the [D section](https://nixos.org/manual/nixpkgs/unstable#dlang) in the manual for more information.
+
 - [Portunus](https://github.com/majewsky/portunus) has been updated to major version 2.
   This version of Portunus supports strong password hashes, but the legacy hash SHA-256 is also still supported to ensure a smooth migration of existing user accounts.
   After upgrading, follow the instructions on the [upstream release notes](https://github.com/majewsky/portunus/releases/tag/v2.0.0) to upgrade all user accounts to strong password hashes.
@@ -347,6 +464,10 @@ 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.
 
+- Garage has been updated to v1.x.x. Users should read the [upstream release notes](https://git.deuxfleurs.fr/Deuxfleurs/garage/releases/tag/v1.0.0) and follow the documentation when changing over their `services.garage.package` and performing this manual upgrade.
+
+- 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),
@@ -409,10 +530,19 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 - `services.zfs.zed.enableMail` now uses the global `sendmail` wrapper defined by an email module
   (such as msmtp or Postfix). It no longer requires using a special ZFS build with email support.
 
+- `castopod` has some migration actions to be taken in case of a S3 setup. Some new features may also need some manual migration actions. See [https://code.castopod.org/adaures/castopod/-/releases](https://code.castopod.org/adaures/castopod/-/releases) for more informations.
+
 - `nextcloud-setup.service` no longer changes the group of each file & directory inside `/var/lib/nextcloud/{config,data,store-apps}` if one of these directories has the wrong owner group. This was part of transitioning the group used for `/var/lib/nextcloud`, but isn't necessary anymore.
 
+- `services.kavita` now uses the freeform option `services.kavita.settings` for the application settings file.
+  The options `services.kavita.ipAdresses` and `services.kavita.port` now exist at `services.kavita.settings.IpAddresses`
+  and `services.kavita.settings.IpAddresses`. The file at `services.kavita.tokenKeyFile` now needs to contain a secret with
+  512+ bits instead of 128+ bits.
+
 - The `krb5` module has been rewritten and moved to `security.krb5`, moving all options but `security.krb5.enable` and `security.krb5.package` into `security.krb5.settings`.
 
+- `services.soju` now has a wrapper for the `sojuctl` command, pointed at the service config file. It also has the new option `adminSocket.enable`, which creates a unix admin socket at `/run/soju/admin`.
+
 - Gitea 1.21 upgrade has several breaking changes, including:
   - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*`
   - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command.
@@ -425,6 +555,12 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - The module `services.github-runner` has been removed. To configure a single GitHub Actions Runner refer to `services.github-runners.*`. Note that this will trigger a new runner registration.
 
+- The `services.slskd` has been refactored to include more configuation options in
+  the freeform `services.slskd.settings` option, and some defaults (including listen ports)
+  have been changed to match the upstream defaults. Additionally, disk logging is now
+  disabled by default, and the log rotation timer has been removed.
+  The nginx virtualhost option is now of the `vhost-options` type.
+
 - The `btrbk` module now automatically selects and provides required compression
   program depending on the configured `stream_compress` option. Since this
   replaces the need for the `extraPackages` option, this option will be
@@ -442,4 +578,9 @@ 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 `drbd` out-of-tree Linux kernel driver has been added in version `9.2.7`. With it the DRBD 9.x features can be used instead of the 8.x features provided by the `8.4.11` in-tree driver.
+
+- 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 284934a7608ef..17e03baf3bb75 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
@@ -42,9 +117,7 @@
 # deprecated since 23.11.
 # TODO remove in a while.
 , allowDocBook ? false
-# whether lib.mdDoc is required for descriptions to be read as markdown.
-# deprecated since 23.11.
-# TODO remove in a while.
+# TODO remove in a while (see https://github.com/NixOS/nixpkgs/issues/300735)
 , markdownByDefault ? true
 }:
 
@@ -157,19 +230,5 @@ in rec {
       echo "file json-br $dst/options.json.br" >> $out/nix-support/hydra-build-products
     '';
 
-  optionsDocBook = lib.warn "optionsDocBook is deprecated since 23.11 and will be removed in 24.05"
-    (pkgs.runCommand "options-docbook.xml" {
-      nativeBuildInputs = [
-        pkgs.nixos-render-docs
-      ];
-    } ''
-      nixos-render-docs -j $NIX_BUILD_CORES options docbook \
-        --manpage-urls ${pkgs.path + "/doc/manpage-urls.json"} \
-        --revision ${lib.escapeShellArg revision} \
-        --document-type ${lib.escapeShellArg documentType} \
-        --varlist-id ${lib.escapeShellArg variablelistId} \
-        --id-prefix ${lib.escapeShellArg optionIdPrefix} \
-        ${optionsJSON}/share/doc/nixos/options.json \
-        "$out"
-    '');
+  optionsDocBook = throw "optionsDocBook has been removed in 24.05";
 }
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index ef218e674ebf1..b67495609ff5a 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -1,8 +1,47 @@
 { config, lib, pkgs }:
 
-with lib;
-
 let
+  inherit (lib)
+    all
+    attrByPath
+    attrNames
+    concatLists
+    concatMap
+    concatMapStrings
+    concatStrings
+    concatStringsSep
+    const
+    elem
+    filter
+    filterAttrs
+    flip
+    head
+    isInt
+    isList
+    length
+    makeBinPath
+    makeSearchPathOutput
+    mapAttrs
+    mapAttrsToList
+    mkAfter
+    mkIf
+    optional
+    optionalAttrs
+    optionalString
+    range
+    replaceStrings
+    reverseList
+    splitString
+    stringLength
+    stringToCharacters
+    tail
+    toIntBase10
+    trace
+    types
+    ;
+
+  inherit (lib.strings) toJSON;
+
   cfg = config.systemd;
   lndir = "${pkgs.buildPackages.xorg.lndir}/bin/lndir";
   systemd = cfg.package;
@@ -10,7 +49,7 @@ in rec {
 
   shellEscape = s: (replaceStrings [ "\\" ] [ "\\\\" ] s);
 
-  mkPathSafeName = lib.replaceStrings ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
+  mkPathSafeName = replaceStrings ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
 
   # a type for options that take a unit name
   unitNameType = types.strMatching "[a-zA-Z0-9@%:_.\\-]+[.](service|socket|device|mount|automount|swap|target|path|timer|scope|slice)";
@@ -73,13 +112,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}}'.";
@@ -120,7 +172,7 @@ in rec {
     )) attrs;
     errors = concatMap (c: c group defs) checks;
   in if errors == [] then true
-     else builtins.trace (concatStringsSep "\n" errors) false;
+     else trace (concatStringsSep "\n" errors) false;
 
   toOption = x:
     if x == true then "true"
@@ -207,7 +259,7 @@ in rec {
       # upstream unit.
       for i in ${toString (mapAttrsToList
           (n: v: v.unit)
-          (lib.filterAttrs (n: v: (attrByPath [ "overrideStrategy" ] "asDropinIfExists" v) == "asDropinIfExists") units))}; do
+          (filterAttrs (n: v: (attrByPath [ "overrideStrategy" ] "asDropinIfExists" v) == "asDropinIfExists") units))}; do
         fn=$(basename $i/*)
         if [ -e $out/$fn ]; then
           if [ "$(readlink -f $i/$fn)" = /dev/null ]; then
@@ -230,7 +282,7 @@ in rec {
       # treated as drop-in file.
       for i in ${toString (mapAttrsToList
           (n: v: v.unit)
-          (lib.filterAttrs (n: v: v ? overrideStrategy && v.overrideStrategy == "asDropin") units))}; do
+          (filterAttrs (n: v: v ? overrideStrategy && v.overrideStrategy == "asDropin") units))}; do
         fn=$(basename $i/*)
         mkdir -p $out/$fn.d
         ln -s $i/$fn $out/$fn.d/overrides.conf
@@ -371,7 +423,7 @@ in rec {
   commonUnitText = def: lines: ''
       [Unit]
       ${attrsToSection def.unitConfig}
-    '' + lines + lib.optionalString (def.wantedBy != [ ]) ''
+    '' + lines + optionalString (def.wantedBy != [ ]) ''
 
       [Install]
       WantedBy=${concatStringsSep " " def.wantedBy}
@@ -393,7 +445,7 @@ in rec {
       '' + (let env = cfg.globalEnvironment // def.environment;
         in concatMapStrings (n:
           let s = optionalString (env.${n} != null)
-            "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
+            "Environment=${toJSON "${n}=${env.${n}}"}\n";
           # systemd max line length is now 1MiB
           # https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
           in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env))
@@ -462,15 +514,20 @@ in rec {
   # in that attrset are determined by the supplied format.
   definitions = directoryName: format: definitionAttrs:
     let
-      listOfDefinitions = lib.mapAttrsToList
+      listOfDefinitions = mapAttrsToList
         (name: format.generate "${name}.conf")
         definitionAttrs;
     in
     pkgs.runCommand directoryName { } ''
       mkdir -p $out
-      ${(lib.concatStringsSep "\n"
+      ${(concatStringsSep "\n"
         (map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions)
       )}
     '';
 
+  # The maximum number of characters allowed in a GPT partition label. This
+  # limit is specified by UEFI and enforced by systemd-repart.
+  # Corresponds to GPT_LABEL_MAX from systemd's gpt.h.
+  GPTMaxLabelLength = 36;
+
 }
diff --git a/nixos/lib/systemd-network-units.nix b/nixos/lib/systemd-network-units.nix
index 1d5f823f3678d..986586a61a705 100644
--- a/nixos/lib/systemd-network-units.nix
+++ b/nixos/lib/systemd-network-units.nix
@@ -1,8 +1,13 @@
 { lib, systemdUtils }:
 
-with lib;
-
 let
+  inherit (lib)
+    concatMapStrings
+    concatStringsSep
+    flip
+    optionalString
+    ;
+
   attrsToSection = systemdUtils.lib.attrsToSection;
   commonMatchText = def:
     optionalString (def.matchConfig != { }) ''
diff --git a/nixos/lib/systemd-types.nix b/nixos/lib/systemd-types.nix
index a109f248b1704..c4c5771cff822 100644
--- a/nixos/lib/systemd-types.nix
+++ b/nixos/lib/systemd-types.nix
@@ -1,47 +1,90 @@
 { lib, systemdUtils, pkgs }:
 
-with systemdUtils.lib;
-with systemdUtils.unitOptions;
-with lib;
+let
+  inherit (systemdUtils.lib)
+    automountConfig
+    makeUnit
+    mountConfig
+    stage1ServiceConfig
+    stage2ServiceConfig
+    unitConfig
+    ;
+
+  inherit (systemdUtils.unitOptions)
+    concreteUnitOptions
+    stage1AutomountOptions
+    stage1CommonUnitOptions
+    stage1MountOptions
+    stage1PathOptions
+    stage1ServiceOptions
+    stage1SliceOptions
+    stage1SocketOptions
+    stage1TimerOptions
+    stage2AutomountOptions
+    stage2CommonUnitOptions
+    stage2MountOptions
+    stage2PathOptions
+    stage2ServiceOptions
+    stage2SliceOptions
+    stage2SocketOptions
+    stage2TimerOptions
+    ;
+
+  inherit (lib)
+    mkDefault
+    mkDerivedConfig
+    mkEnableOption
+    mkIf
+    mkOption
+    ;
+
+  inherit (lib.types)
+    attrsOf
+    lines
+    listOf
+    nullOr
+    path
+    submodule
+    ;
+in
 
 rec {
-  units = with types;
-    attrsOf (submodule ({ name, config, ... }: {
-      options = concreteUnitOptions;
-      config = { unit = mkDefault (systemdUtils.lib.makeUnit name config); };
-    }));
+  units = attrsOf (submodule ({ name, config, ... }: {
+    options = concreteUnitOptions;
+    config = { unit = mkDefault (makeUnit name config); };
+  }));
 
-  services = with types; attrsOf (submodule [ stage2ServiceOptions unitConfig stage2ServiceConfig ]);
-  initrdServices = with types; attrsOf (submodule [ stage1ServiceOptions unitConfig stage1ServiceConfig ]);
+  services = attrsOf (submodule [ stage2ServiceOptions unitConfig stage2ServiceConfig ]);
+  initrdServices = attrsOf (submodule [ stage1ServiceOptions unitConfig stage1ServiceConfig ]);
 
-  targets = with types; attrsOf (submodule [ stage2CommonUnitOptions unitConfig ]);
-  initrdTargets = with types; attrsOf (submodule [ stage1CommonUnitOptions unitConfig ]);
+  targets = attrsOf (submodule [ stage2CommonUnitOptions unitConfig ]);
+  initrdTargets = attrsOf (submodule [ stage1CommonUnitOptions unitConfig ]);
 
-  sockets = with types; attrsOf (submodule [ stage2SocketOptions unitConfig ]);
-  initrdSockets = with types; attrsOf (submodule [ stage1SocketOptions unitConfig ]);
+  sockets = attrsOf (submodule [ stage2SocketOptions unitConfig ]);
+  initrdSockets = attrsOf (submodule [ stage1SocketOptions unitConfig ]);
 
-  timers = with types; attrsOf (submodule [ stage2TimerOptions unitConfig ]);
-  initrdTimers = with types; attrsOf (submodule [ stage1TimerOptions unitConfig ]);
+  timers = attrsOf (submodule [ stage2TimerOptions unitConfig ]);
+  initrdTimers = attrsOf (submodule [ stage1TimerOptions unitConfig ]);
 
-  paths = with types; attrsOf (submodule [ stage2PathOptions unitConfig ]);
-  initrdPaths = with types; attrsOf (submodule [ stage1PathOptions unitConfig ]);
+  paths = attrsOf (submodule [ stage2PathOptions unitConfig ]);
+  initrdPaths = attrsOf (submodule [ stage1PathOptions unitConfig ]);
 
-  slices = with types; attrsOf (submodule [ stage2SliceOptions unitConfig ]);
-  initrdSlices = with types; attrsOf (submodule [ stage1SliceOptions unitConfig ]);
+  slices = attrsOf (submodule [ stage2SliceOptions unitConfig ]);
+  initrdSlices = attrsOf (submodule [ stage1SliceOptions unitConfig ]);
 
-  mounts = with types; listOf (submodule [ stage2MountOptions unitConfig mountConfig ]);
-  initrdMounts = with types; listOf (submodule [ stage1MountOptions unitConfig mountConfig ]);
+  mounts = listOf (submodule [ stage2MountOptions unitConfig mountConfig ]);
+  initrdMounts = listOf (submodule [ stage1MountOptions unitConfig mountConfig ]);
 
-  automounts = with types; listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]);
-  initrdAutomounts = with types; attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]);
+  automounts = listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]);
+  initrdAutomounts = attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]);
 
-  initrdContents = types.attrsOf (types.submodule ({ config, options, name, ... }: {
+  initrdContents = attrsOf (submodule ({ config, options, name, ... }: {
     options = {
-      enable = mkEnableOption (lib.mdDoc "copying of this file and symlinking it") // { default = true; };
+      enable = (mkEnableOption "copying of this file and symlinking it") // { default = true; };
 
       target = mkOption {
-        type = types.path;
-        description = lib.mdDoc ''
+        type = path;
+        description = ''
           Path of the symlink.
         '';
         default = name;
@@ -49,13 +92,13 @@ rec {
 
       text = mkOption {
         default = null;
-        type = types.nullOr types.lines;
-        description = lib.mdDoc "Text of the file.";
+        type = nullOr lines;
+        description = "Text of the file.";
       };
 
       source = mkOption {
-        type = types.path;
-        description = lib.mdDoc "Path of the source file.";
+        type = path;
+        description = "Path of the source file.";
       };
     };
 
diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix
index e4953ba72dd92..fc990a87f0c20 100644
--- a/nixos/lib/systemd-unit-options.nix
+++ b/nixos/lib/systemd-unit-options.nix
@@ -1,9 +1,32 @@
 { lib, systemdUtils }:
 
-with systemdUtils.lib;
-with lib;
-
 let
+  inherit (systemdUtils.lib)
+    assertValueOneOf
+    automountConfig
+    checkUnitConfig
+    makeJobScript
+    mountConfig
+    serviceConfig
+    unitConfig
+    unitNameType
+    ;
+
+  inherit (lib)
+    any
+    concatMap
+    filterOverrides
+    isList
+    mergeEqualOption
+    mkIf
+    mkMerge
+    mkOption
+    mkOptionType
+    singleton
+    toList
+    types
+    ;
+
   checkService = checkUnitConfig "Service" [
     (assertValueOneOf "Type" [
       "exec" "simple" "forking" "oneshot" "dbus" "notify" "notify-reload" "idle"
@@ -31,7 +54,7 @@ in rec {
     enable = mkOption {
       default = true;
       type = types.bool;
-      description = lib.mdDoc ''
+      description = ''
         If set to false, this unit will be a symlink to
         /dev/null. This is primarily useful to prevent specific
         template instances
@@ -45,7 +68,7 @@ in rec {
     overrideStrategy = mkOption {
       default = "asDropinIfExists";
       type = types.enum [ "asDropinIfExists" "asDropin" ];
-      description = lib.mdDoc ''
+      description = ''
         Defines how unit configuration is provided for systemd:
 
         `asDropinIfExists` creates a unit file when no unit file is provided by the package
@@ -61,7 +84,7 @@ in rec {
     requiredBy = mkOption {
       default = [];
       type = types.listOf unitNameType;
-      description = lib.mdDoc ''
+      description = ''
         Units that require (i.e. depend on and need to go down with) this unit.
         As discussed in the `wantedBy` option description this also creates
         `.requires` symlinks automatically.
@@ -71,7 +94,7 @@ in rec {
     upheldBy = mkOption {
       default = [];
       type = types.listOf unitNameType;
-      description = lib.mdDoc ''
+      description = ''
         Keep this unit running as long as the listed units are running. This is a continuously
         enforced version of wantedBy.
       '';
@@ -80,7 +103,7 @@ in rec {
     wantedBy = mkOption {
       default = [];
       type = types.listOf unitNameType;
-      description = lib.mdDoc ''
+      description = ''
         Units that want (i.e. depend on) this unit. The default method for
         starting a unit by default at boot time is to set this option to
         `["multi-user.target"]` for system services. Likewise for user units
@@ -98,7 +121,7 @@ in rec {
     aliases = mkOption {
       default = [];
       type = types.listOf unitNameType;
-      description = lib.mdDoc "Aliases of that unit.";
+      description = "Aliases of that unit.";
     };
 
   };
@@ -108,12 +131,12 @@ in rec {
     text = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc "Text of this systemd unit.";
+      description = "Text of this systemd unit.";
     };
 
     unit = mkOption {
       internal = true;
-      description = lib.mdDoc "The generated unit.";
+      description = "The generated unit.";
     };
 
   };
@@ -124,19 +147,19 @@ in rec {
       description = mkOption {
         default = "";
         type = types.singleLineStr;
-        description = lib.mdDoc "Description of this unit used in systemd messages and progress indicators.";
+        description = "Description of this unit used in systemd messages and progress indicators.";
       };
 
       documentation = mkOption {
         default = [];
         type = types.listOf types.str;
-        description = lib.mdDoc "A list of URIs referencing documentation for this unit or its configuration.";
+        description = "A list of URIs referencing documentation for this unit or its configuration.";
       };
 
       requires = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           Start the specified units when this unit is started, and stop
           this unit when the specified units are stopped or fail.
         '';
@@ -145,7 +168,7 @@ in rec {
       wants = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           Start the specified units when this unit is started.
         '';
       };
@@ -153,7 +176,7 @@ in rec {
       upholds = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           Keeps the specified running while this unit is running. A continuous version of `wants`.
         '';
       };
@@ -161,7 +184,7 @@ in rec {
       after = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           If the specified units are started at the same time as
           this unit, delay this unit until they have started.
         '';
@@ -170,7 +193,7 @@ in rec {
       before = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           If the specified units are started at the same time as
           this unit, delay them until this unit has started.
         '';
@@ -179,7 +202,7 @@ in rec {
       bindsTo = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           Like ‘requires’, but in addition, if the specified units
           unexpectedly disappear, this unit will be stopped as well.
         '';
@@ -188,7 +211,7 @@ in rec {
       partOf = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           If the specified units are stopped or restarted, then this
           unit is stopped or restarted as well.
         '';
@@ -197,7 +220,7 @@ in rec {
       conflicts = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           If the specified units are started, then this unit is stopped
           and vice versa.
         '';
@@ -206,7 +229,7 @@ in rec {
       requisite = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           Similar to requires. However if the units listed are not started,
           they will not be started and the transaction will fail.
         '';
@@ -216,7 +239,7 @@ in rec {
         default = {};
         example = { RequiresMountsFor = "/data"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Unit]` section of the unit.  See
           {manpage}`systemd.unit(5)` for details.
@@ -226,7 +249,7 @@ in rec {
       onFailure = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           A list of one or more units that are activated when
           this unit enters the "failed" state.
         '';
@@ -235,7 +258,7 @@ in rec {
       onSuccess = mkOption {
         default = [];
         type = types.listOf unitNameType;
-        description = lib.mdDoc ''
+        description = ''
           A list of one or more units that are activated when
           this unit enters the "inactive" state.
         '';
@@ -243,7 +266,7 @@ in rec {
 
       startLimitBurst = mkOption {
          type = types.int;
-         description = lib.mdDoc ''
+         description = ''
            Configure unit start rate limiting. Units which are started
            more than startLimitBurst times within an interval time
            interval are not permitted to start any more.
@@ -252,7 +275,7 @@ in rec {
 
       startLimitIntervalSec = mkOption {
          type = types.int;
-         description = lib.mdDoc ''
+         description = ''
            Configure unit start rate limiting. Units which are started
            more than startLimitBurst times within an interval time
            interval are not permitted to start any more.
@@ -271,7 +294,7 @@ in rec {
       restartTriggers = mkOption {
         default = [];
         type = types.listOf types.unspecified;
-        description = lib.mdDoc ''
+        description = ''
           An arbitrary list of items such as derivations.  If any item
           in the list changes between reconfigurations, the service will
           be restarted.
@@ -281,7 +304,7 @@ in rec {
       reloadTriggers = mkOption {
         default = [];
         type = types.listOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           An arbitrary list of items such as derivations.  If any item
           in the list changes between reconfigurations, the service will
           be reloaded.  If anything but a reload trigger changes in the
@@ -299,13 +322,13 @@ in rec {
         default = {};
         type = with types; attrsOf (nullOr (oneOf [ str path package ]));
         example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
-        description = lib.mdDoc "Environment variables passed to the service's processes.";
+        description = "Environment variables passed to the service's processes.";
       };
 
       path = mkOption {
         default = [];
         type = with types; listOf (oneOf [ package str ]);
-        description = lib.mdDoc ''
+        description = ''
           Packages added to the service's {env}`PATH`
           environment variable.  Both the {file}`bin`
           and {file}`sbin` subdirectories of each
@@ -319,7 +342,7 @@ in rec {
           { RestartSec = 5;
           };
         type = types.addCheck (types.attrsOf unitOption) checkService;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Service]` section of the unit.  See
           {manpage}`systemd.service(5)` for details.
@@ -329,14 +352,14 @@ in rec {
       script = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc "Shell commands executed as the service's main process.";
+        description = "Shell commands executed as the service's main process.";
       };
 
       scriptArgs = mkOption {
         type = types.str;
         default = "";
         example = "%i";
-        description = lib.mdDoc ''
+        description = ''
           Arguments passed to the main process script.
           Can contain specifiers (`%` placeholders expanded by systemd, see {manpage}`systemd.unit(5)`).
         '';
@@ -345,7 +368,7 @@ in rec {
       preStart = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Shell commands executed before the service's main process
           is started.
         '';
@@ -354,7 +377,7 @@ in rec {
       postStart = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Shell commands executed after the service's main process
           is started.
         '';
@@ -363,7 +386,7 @@ in rec {
       reload = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Shell commands executed when the service's main process
           is reloaded.
         '';
@@ -372,7 +395,7 @@ in rec {
       preStop = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Shell commands executed to stop the service.
         '';
       };
@@ -380,7 +403,7 @@ in rec {
       postStop = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           Shell commands executed after the service's main process
           has exited.
         '';
@@ -389,7 +412,7 @@ in rec {
       jobScripts = mkOption {
         type = with types; coercedTo path singleton (listOf path);
         internal = true;
-        description = lib.mdDoc "A list of all job script derivations of this unit.";
+        description = "A list of all job script derivations of this unit.";
         default = [];
       };
 
@@ -434,7 +457,7 @@ in rec {
       restartIfChanged = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether the service should be restarted during a NixOS
           configuration switch if its definition has changed.
         '';
@@ -443,7 +466,7 @@ in rec {
       reloadIfChanged = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether the service should be reloaded during a NixOS
           configuration switch if its definition has changed.  If
           enabled, the value of {option}`restartIfChanged` is
@@ -459,7 +482,7 @@ in rec {
       stopIfChanged = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           If set, a changed unit is restarted by calling
           {command}`systemctl stop` in the old configuration,
           then {command}`systemctl start` in the new one.
@@ -475,7 +498,7 @@ in rec {
         type = with types; either str (listOf str);
         default = [];
         example = "Sun 14:00:00";
-        description = lib.mdDoc ''
+        description = ''
           Automatically start this unit at the given date/time, which
           must be in the format described in
           {manpage}`systemd.time(7)`.  This is equivalent
@@ -502,7 +525,7 @@ in rec {
         default = [];
         type = types.listOf types.str;
         example = [ "0.0.0.0:993" "/run/my-socket" ];
-        description = lib.mdDoc ''
+        description = ''
           For each item in this list, a `ListenStream`
           option in the `[Socket]` section will be created.
         '';
@@ -512,7 +535,7 @@ in rec {
         default = [];
         type = types.listOf types.str;
         example = [ "0.0.0.0:993" "/run/my-socket" ];
-        description = lib.mdDoc ''
+        description = ''
           For each item in this list, a `ListenDatagram`
           option in the `[Socket]` section will be created.
         '';
@@ -522,7 +545,7 @@ in rec {
         default = {};
         example = { ListenStream = "/run/my-socket"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Socket]` section of the unit.  See
           {manpage}`systemd.socket(5)` for details.
@@ -554,7 +577,7 @@ in rec {
         default = {};
         example = { OnCalendar = "Sun 14:00:00"; Unit = "foo.service"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Timer]` section of the unit.  See
           {manpage}`systemd.timer(5)` and
@@ -587,7 +610,7 @@ in rec {
         default = {};
         example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Path]` section of the unit.  See
           {manpage}`systemd.path(5)` for details.
@@ -618,13 +641,13 @@ in rec {
       what = mkOption {
         example = "/dev/sda1";
         type = types.str;
-        description = lib.mdDoc "Absolute path of device node, file or other resource. (Mandatory)";
+        description = "Absolute path of device node, file or other resource. (Mandatory)";
       };
 
       where = mkOption {
         example = "/mnt";
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Absolute path of a directory of the mount point.
           Will be created if it doesn't exist. (Mandatory)
         '';
@@ -634,21 +657,21 @@ in rec {
         default = "";
         example = "ext4";
         type = types.str;
-        description = lib.mdDoc "File system type.";
+        description = "File system type.";
       };
 
       options = mkOption {
         default = "";
         example = "noatime";
         type = types.commas;
-        description = lib.mdDoc "Options used to mount the file system.";
+        description = "Options used to mount the file system.";
       };
 
       mountConfig = mkOption {
         default = {};
         example = { DirectoryMode = "0775"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Mount]` section of the unit.  See
           {manpage}`systemd.mount(5)` for details.
@@ -678,7 +701,7 @@ in rec {
       where = mkOption {
         example = "/mnt";
         type = types.str;
-        description = lib.mdDoc ''
+        description = ''
           Absolute path of a directory of the mount point.
           Will be created if it doesn't exist. (Mandatory)
         '';
@@ -688,7 +711,7 @@ in rec {
         default = {};
         example = { DirectoryMode = "0775"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Automount]` section of the unit.  See
           {manpage}`systemd.automount(5)` for details.
@@ -719,7 +742,7 @@ in rec {
         default = {};
         example = { MemoryMax = "2G"; };
         type = types.attrsOf unitOption;
-        description = lib.mdDoc ''
+        description = ''
           Each attribute in this set specifies an option in the
           `[Slice]` section of the unit.  See
           {manpage}`systemd.slice(5)` for details.
diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py
index df8628bce9568..652cc600fad59 100644
--- a/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixos/lib/test-driver/test_driver/machine.py
@@ -165,8 +165,6 @@ class StartCommand:
         )
         if not allow_reboot:
             qemu_opts += " -no-reboot"
-        # TODO: qemu script already catpures this env variable, legacy?
-        qemu_opts += " " + os.environ.get("QEMU_OPTS", "")
 
         return (
             f"{self._cmd}"
@@ -1250,6 +1248,5 @@ class Machine:
             check_return=False,
             check_output=False,
         )
-        self.wait_for_console_text(r"systemd\[1\]:.*Switching root\.")
         self.connected = False
         self.connect()
diff --git a/nixos/lib/testing/driver.nix b/nixos/lib/testing/driver.nix
index b6f01c38191d2..7eb06e023918b 100644
--- a/nixos/lib/testing/driver.nix
+++ b/nixos/lib/testing/driver.nix
@@ -1,6 +1,6 @@
 { config, lib, hostPkgs, ... }:
 let
-  inherit (lib) mkOption types literalMD mdDoc;
+  inherit (lib) mkOption types literalMD;
 
   # Reifies and correctly wraps the python test driver for
   # the respective qemu version and with or without ocr support
@@ -104,13 +104,13 @@ in
   options = {
 
     driver = mkOption {
-      description = mdDoc "Package containing a script that runs the test.";
+      description = "Package containing a script that runs the test.";
       type = types.package;
       defaultText = literalMD "set by the test framework";
     };
 
     hostPkgs = mkOption {
-      description = mdDoc "Nixpkgs attrset used outside the nodes.";
+      description = "Nixpkgs attrset used outside the nodes.";
       type = types.raw;
       example = lib.literalExpression ''
         import nixpkgs { inherit system config overlays; }
@@ -118,14 +118,14 @@ in
     };
 
     qemu.package = mkOption {
-      description = mdDoc "Which qemu package to use for the virtualisation of [{option}`nodes`](#test-opt-nodes).";
+      description = "Which qemu package to use for the virtualisation of [{option}`nodes`](#test-opt-nodes).";
       type = types.package;
       default = hostPkgs.qemu_test;
       defaultText = "hostPkgs.qemu_test";
     };
 
     globalTimeout = mkOption {
-      description = mdDoc ''
+      description = ''
         A global timeout for the complete test, expressed in seconds.
         Beyond that timeout, every resource will be killed and released and the test will fail.
 
@@ -137,7 +137,7 @@ in
     };
 
     enableOCR = mkOption {
-      description = mdDoc ''
+      description = ''
         Whether to enable Optical Character Recognition functionality for
         testing graphical programs. See [Machine objects](`ssec-machine-objects`).
       '';
@@ -146,7 +146,7 @@ in
     };
 
     extraPythonPackages = mkOption {
-      description = mdDoc ''
+      description = ''
         Python packages to add to the test driver.
 
         The argument is a Python package set, similar to `pkgs.pythonPackages`.
@@ -159,7 +159,7 @@ in
     };
 
     extraDriverArgs = mkOption {
-      description = mdDoc ''
+      description = ''
         Extra arguments to pass to the test driver.
 
         They become part of [{option}`driver`](#test-opt-driver) via `wrapProgram`.
@@ -171,7 +171,7 @@ in
     skipLint = mkOption {
       type = types.bool;
       default = false;
-      description = mdDoc ''
+      description = ''
         Do not run the linters. This may speed up your iteration cycle, but it is not something you should commit.
       '';
     };
@@ -179,7 +179,7 @@ in
     skipTypeCheck = mkOption {
       type = types.bool;
       default = false;
-      description = mdDoc ''
+      description = ''
         Disable type checking. This must not be enabled for new NixOS tests.
 
         This may speed up your iteration cycle, unless you're working on the [{option}`testScript`](#test-opt-testScript).
diff --git a/nixos/lib/testing/interactive.nix b/nixos/lib/testing/interactive.nix
index 317ed4241882b..0b12096692828 100644
--- a/nixos/lib/testing/interactive.nix
+++ b/nixos/lib/testing/interactive.nix
@@ -1,11 +1,11 @@
 { config, lib, moduleType, hostPkgs, ... }:
 let
-  inherit (lib) mkOption types mdDoc;
+  inherit (lib) mkOption types;
 in
 {
   options = {
     interactive = mkOption {
-      description = mdDoc ''
+      description = ''
         Tests [can be run interactively](#sec-running-nixos-tests-interactively)
         using the program in the test derivation's `.driverInteractive` attribute.
 
diff --git a/nixos/lib/testing/meta.nix b/nixos/lib/testing/meta.nix
index 805b7520edff3..bdf313e5b119f 100644
--- a/nixos/lib/testing/meta.nix
+++ b/nixos/lib/testing/meta.nix
@@ -1,11 +1,11 @@
 { lib, ... }:
 let
-  inherit (lib) types mkOption mdDoc;
+  inherit (lib) types mkOption;
 in
 {
   options = {
     meta = lib.mkOption {
-      description = mdDoc ''
+      description = ''
         The [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes that will be set on the returned derivations.
 
         Not all [`meta`](https://nixos.org/manual/nixpkgs/stable/#chap-meta) attributes are supported, but more can be added as desired.
@@ -16,24 +16,31 @@ in
           maintainers = lib.mkOption {
             type = types.listOf types.raw;
             default = [];
-            description = mdDoc ''
+            description = ''
               The [list of maintainers](https://nixos.org/manual/nixpkgs/stable/#var-meta-maintainers) for this test.
             '';
           };
           timeout = lib.mkOption {
             type = types.nullOr types.int;
             default = 3600;  # 1 hour
-            description = mdDoc ''
+            description = ''
               The [{option}`test`](#test-opt-test)'s [`meta.timeout`](https://nixos.org/manual/nixpkgs/stable/#var-meta-timeout) in seconds.
             '';
           };
           broken = lib.mkOption {
             type = types.bool;
             default = false;
-            description = mdDoc ''
+            description = ''
               Sets the [`meta.broken`](https://nixos.org/manual/nixpkgs/stable/#var-meta-broken) attribute on the [{option}`test`](#test-opt-test) derivation.
             '';
           };
+          platforms = lib.mkOption {
+            type = types.listOf types.raw;
+            default = lib.platforms.linux ++ lib.platforms.darwin;
+            description = ''
+              Sets the [`meta.platforms`](https://nixos.org/manual/nixpkgs/stable/#var-meta-platforms) attribute on the [{option}`test`](#test-opt-test) derivation.
+            '';
+          };
         };
       };
       default = {};
diff --git a/nixos/lib/testing/name.nix b/nixos/lib/testing/name.nix
index 0af593169eeca..0682873c7bcd0 100644
--- a/nixos/lib/testing/name.nix
+++ b/nixos/lib/testing/name.nix
@@ -1,10 +1,10 @@
 { lib, ... }:
 let
-  inherit (lib) mkOption types mdDoc;
+  inherit (lib) mkOption types;
 in
 {
   options.name = mkOption {
-    description = mdDoc ''
+    description = ''
       The name of the test.
 
       This is used in the derivation names of the [{option}`driver`](#test-opt-driver) and [{option}`test`](#test-opt-test) runner.
diff --git a/nixos/lib/testing/network.nix b/nixos/lib/testing/network.nix
index 1edc9e276530c..0f1615a0ad3b9 100644
--- a/nixos/lib/testing/network.nix
+++ b/nixos/lib/testing/network.nix
@@ -5,7 +5,6 @@ let
     attrNames concatMap concatMapStrings flip forEach head
     listToAttrs mkDefault mkOption nameValuePair optionalString
     range toLower types zipListsWith zipLists
-    mdDoc
     ;
 
   nodeNumbers =
@@ -89,7 +88,7 @@ let
         default = name;
         # We need to force this in specilisations, otherwise it'd be
         # readOnly = true;
-        description = mdDoc ''
+        description = ''
           The `name` in `nodes.<name>`; stable across `specialisations`.
         '';
       };
@@ -98,7 +97,7 @@ let
         type = types.int;
         readOnly = true;
         default = nodeNumbers.${config.virtualisation.test.nodeName};
-        description = mdDoc ''
+        description = ''
           A unique number assigned for each node in `nodes`.
         '';
       };
diff --git a/nixos/lib/testing/nodes.nix b/nixos/lib/testing/nodes.nix
index 7941d69e38d2b..9aecca10ac6b2 100644
--- a/nixos/lib/testing/nodes.nix
+++ b/nixos/lib/testing/nodes.nix
@@ -5,7 +5,6 @@ let
     literalExpression
     literalMD
     mapAttrs
-    mdDoc
     mkDefault
     mkIf
     mkOption mkForce
@@ -76,7 +75,7 @@ in
     nodes = mkOption {
       type = types.lazyAttrsOf config.node.type;
       visible = "shallow";
-      description = mdDoc ''
+      description = ''
         An attribute set of NixOS configuration modules.
 
         The configurations are augmented by the [`defaults`](#test-opt-defaults) option.
@@ -88,7 +87,7 @@ in
     };
 
     defaults = mkOption {
-      description = mdDoc ''
+      description = ''
         NixOS configuration that is applied to all [{option}`nodes`](#test-opt-nodes).
       '';
       type = types.deferredModule;
@@ -96,7 +95,7 @@ in
     };
 
     extraBaseModules = mkOption {
-      description = mdDoc ''
+      description = ''
         NixOS configuration that, like [{option}`defaults`](#test-opt-defaults), is applied to all [{option}`nodes`](#test-opt-nodes) and can not be undone with [`specialisation.<name>.inheritParentConfig`](https://search.nixos.org/options?show=specialisation.%3Cname%3E.inheritParentConfig&from=0&size=50&sort=relevance&type=packages&query=specialisation).
       '';
       type = types.deferredModule;
@@ -104,7 +103,7 @@ in
     };
 
     node.pkgs = mkOption {
-      description = mdDoc ''
+      description = ''
         The Nixpkgs to use for the nodes.
 
         Setting this will make the `nixpkgs.*` options read-only, to avoid mistakenly testing with a Nixpkgs configuration that diverges from regular use.
@@ -117,7 +116,7 @@ in
     };
 
     node.pkgsReadOnly = mkOption {
-      description = mdDoc ''
+      description = ''
         Whether to make the `nixpkgs.*` options read-only. This is only relevant when [`node.pkgs`](#test-opt-node.pkgs) is set.
 
         Set this to `false` when any of the [`nodes`](#test-opt-nodes) needs to configure any of the `nixpkgs.*` options. This will slow down evaluation of your test a bit.
@@ -130,7 +129,7 @@ in
     node.specialArgs = mkOption {
       type = types.lazyAttrsOf types.raw;
       default = { };
-      description = mdDoc ''
+      description = ''
         An attribute set of arbitrary values that will be made available as module arguments during the resolution of module `imports`.
 
         Note that it is not possible to override these from within the NixOS configurations. If you argument is not relevant to `imports`, consider setting {option}`defaults._module.args.<name>` instead.
@@ -139,7 +138,7 @@ in
 
     nodesCompat = mkOption {
       internal = true;
-      description = mdDoc ''
+      description = ''
         Basically `_module.args.nodes`, but with backcompat and warnings added.
 
         This will go away.
diff --git a/nixos/lib/testing/run.nix b/nixos/lib/testing/run.nix
index de5a9b97e61d5..218292121ee77 100644
--- a/nixos/lib/testing/run.nix
+++ b/nixos/lib/testing/run.nix
@@ -1,12 +1,12 @@
 { config, hostPkgs, lib, ... }:
 let
-  inherit (lib) types mkOption mdDoc;
+  inherit (lib) types mkOption;
 in
 {
   options = {
     passthru = mkOption {
       type = types.lazyAttrsOf types.raw;
-      description = mdDoc ''
+      description = ''
         Attributes to add to the returned derivations,
         which are not necessarily part of the build.
 
@@ -18,7 +18,7 @@ in
 
     rawTestDerivation = mkOption {
       type = types.package;
-      description = mdDoc ''
+      description = ''
         Unfiltered version of `test`, for troubleshooting the test framework and `testBuildFailure` in the test framework's test suite.
         This is not intended for general use. Use `test` instead.
       '';
@@ -28,7 +28,7 @@ in
     test = mkOption {
       type = types.package;
       # TODO: can the interactive driver be configured to access the network?
-      description = mdDoc ''
+      description = ''
         Derivation that runs the test as its "build" process.
 
         This implies that NixOS tests run isolated from the network, making them
diff --git a/nixos/lib/testing/testScript.nix b/nixos/lib/testing/testScript.nix
index 5c36d754d79d7..09964777bd924 100644
--- a/nixos/lib/testing/testScript.nix
+++ b/nixos/lib/testing/testScript.nix
@@ -1,13 +1,13 @@
 testModuleArgs@{ config, lib, hostPkgs, nodes, moduleType, ... }:
 let
-  inherit (lib) mkOption types mdDoc;
+  inherit (lib) mkOption types;
   inherit (types) either str functionTo;
 in
 {
   options = {
     testScript = mkOption {
       type = either str (functionTo str);
-      description = mdDoc ''
+      description = ''
         A series of python declarations and statements that you write to perform
         the test.
       '';
@@ -25,7 +25,7 @@ in
     };
     withoutTestScriptReferences = mkOption {
       type = moduleType;
-      description = mdDoc ''
+      description = ''
         A parallel universe where the testScript is invalid and has no references.
       '';
       internal = true;
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index 49ba2e5c83868..4992113bdbd25 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -1,9 +1,44 @@
-{ lib, config, pkgs }: with lib;
+{ lib, config, pkgs }:
+
+let
+  inherit (lib)
+    any
+    attrNames
+    concatMapStringsSep
+    concatStringsSep
+    elem
+    escapeShellArg
+    filter
+    flatten
+    getName
+    hasPrefix
+    hasSuffix
+    imap0
+    imap1
+    isAttrs
+    isDerivation
+    isFloat
+    isInt
+    isList
+    isPath
+    isString
+    listToAttrs
+    nameValuePair
+    optionalString
+    removePrefix
+    removeSuffix
+    replaceStrings
+    stringToCharacters
+    types
+    ;
+
+  inherit (lib.strings) toJSON normalizePath escapeC;
+in
 
 rec {
 
   # Copy configuration files to avoid having the entire sources in the system closure
-  copyFile = filePath: pkgs.runCommand (builtins.unsafeDiscardStringContext (builtins.baseNameOf filePath)) {} ''
+  copyFile = filePath: pkgs.runCommand (builtins.unsafeDiscardStringContext (baseNameOf filePath)) {} ''
     cp ${filePath} $out
   '';
 
@@ -46,11 +81,11 @@ rec {
   escapeSystemdPath = s: let
     replacePrefix = p: r: s: (if (hasPrefix p s) then r + (removePrefix p s) else s);
     trim = s: removeSuffix "/" (removePrefix "/" s);
-    normalizedPath = strings.normalizePath s;
+    normalizedPath = normalizePath s;
   in
     replaceStrings ["/"] ["-"]
-    (replacePrefix "." (strings.escapeC ["."] ".")
-    (strings.escapeC (stringToCharacters " !\"#$%&'()*+,;<=>=@[\\]^`{|}~-")
+    (replacePrefix "." (escapeC ["."] ".")
+    (escapeC (stringToCharacters " !\"#$%&'()*+,;<=>=@[\\]^`{|}~-")
     (if normalizedPath == "/" then normalizedPath else trim normalizedPath)));
 
   # Quotes an argument for use in Exec* service lines.
@@ -62,12 +97,12 @@ rec {
   # substitution for the directive.
   escapeSystemdExecArg = arg:
     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";
+      s = if isPath arg then "${arg}"
+        else if isString arg then arg
+        else if isInt arg || isFloat arg || isDerivation arg then toString arg
+        else throw "escapeSystemdExecArg only allows strings, paths, numbers and derivations";
     in
-      replaceStrings [ "%" "$" ] [ "%%" "$$" ] (builtins.toJSON s);
+      replaceStrings [ "%" "$" ] [ "%%" "$$" ] (toJSON s);
 
   # Quotes a list of arguments into a single string for use in a Exec*
   # line.
@@ -197,7 +232,7 @@ rec {
                (attrNames secrets))
     + "\n"
     + "${pkgs.jq}/bin/jq >'${output}' "
-    + lib.escapeShellArg (stringOrDefault
+    + escapeShellArg (stringOrDefault
           (concatStringsSep
             " | "
             (imap1 (index: name: ''${name} = $ENV.secret${toString index}'')
@@ -205,7 +240,7 @@ rec {
           ".")
     + ''
        <<'EOF'
-      ${builtins.toJSON set}
+      ${toJSON set}
       EOF
       (( ! $inherit_errexit_enabled )) && shopt -u inherit_errexit
     '';
@@ -222,9 +257,9 @@ rec {
   */
   removePackagesByName = packages: packagesToRemove:
     let
-      namesToRemove = map lib.getName packagesToRemove;
+      namesToRemove = map getName packagesToRemove;
     in
-      lib.filter (x: !(builtins.elem (lib.getName x) namesToRemove)) packages;
+      filter (x: !(elem (getName x) namesToRemove)) packages;
 
   systemdUtils = {
     lib = import ./systemd-lib.nix { inherit lib config pkgs; };
diff --git a/nixos/modules/config/mysql.nix b/nixos/modules/config/mysql.nix
index 4f72d22c4f0ec..30741a9ad406f 100644
--- a/nixos/modules/config/mysql.nix
+++ b/nixos/modules/config/mysql.nix
@@ -10,7 +10,7 @@ in
 
   options = {
     users.mysql = {
-      enable = mkEnableOption (lib.mdDoc "Authentication against a MySQL/MariaDB database");
+      enable = mkEnableOption (lib.mdDoc "authentication against a MySQL/MariaDB database");
       host = mkOption {
         type = types.str;
         example = "localhost";
diff --git a/nixos/modules/config/nix.nix b/nixos/modules/config/nix.nix
index e6a74bbb73fcc..a40953a3a3c92 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 e9ae4d651d264..8a58b218f867b 100644
--- a/nixos/modules/config/resolvconf.nix
+++ b/nixos/modules/config/resolvconf.nix
@@ -10,7 +10,8 @@ let
 
   resolvconfOptions = cfg.extraOptions
     ++ optional cfg.dnsSingleRequest "single-request"
-    ++ optional cfg.dnsExtensionMechanism "edns0";
+    ++ optional cfg.dnsExtensionMechanism "edns0"
+    ++ optional cfg.useLocalResolver "trust-ad";
 
   configText =
     ''
@@ -27,7 +28,7 @@ let
       resolv_conf_options='${concatStringsSep " " resolvconfOptions}'
     '' + optionalString cfg.useLocalResolver ''
       # This hosts runs a full-blown DNS resolver.
-      name_servers='127.0.0.1'
+      name_servers='127.0.0.1${optionalString config.networking.enableIPv6 " ::1"}'
     '' + cfg.extraConfig;
 
 in
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 02cd1a17f538a..07d9591cb446b 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -496,6 +496,7 @@ let
     in
       filter types.shellPackage.check shells;
 
+  lingeringUsers = map (u: u.name) (attrValues (flip filterAttrs cfg.users (n: u: u.linger)));
 in {
   imports = [
     (mkAliasOptionModuleMD [ "users" "extraUsers" ] [ "users" "users" ])
@@ -695,24 +696,31 @@ in {
       '';
     } else ""; # keep around for backwards compatibility
 
-    system.activationScripts.update-lingering = let
-      lingerDir = "/var/lib/systemd/linger";
-      lingeringUsers = map (u: u.name) (attrValues (flip filterAttrs cfg.users (n: u: u.linger)));
-      lingeringUsersFile = builtins.toFile "lingering-users"
-        (concatStrings (map (s: "${s}\n")
-          (sort (a: b: a < b) lingeringUsers)));  # this sorting is important for `comm` to work correctly
-    in stringAfter [ "users" ] ''
-      if [ -e ${lingerDir} ] ; then
+    systemd.services.linger-users = lib.mkIf ((builtins.length lingeringUsers) > 0) {
+      wantedBy = ["multi-user.target"];
+      after = ["systemd-logind.service"];
+      requires = ["systemd-logind.service"];
+
+      script = let
+        lingerDir = "/var/lib/systemd/linger";
+        lingeringUsersFile = builtins.toFile "lingering-users"
+          (concatStrings (map (s: "${s}\n")
+            (sort (a: b: a < b) lingeringUsers)));  # this sorting is important for `comm` to work correctly
+      in ''
+        mkdir -vp ${lingerDir}
         cd ${lingerDir}
-        for user in ${lingerDir}/*; do
-          if ! id "$user" >/dev/null 2>&1; then
+        for user in $(ls); do
+          if ! id "$user" >/dev/null; then
+            echo "Removing linger for missing user $user"
             rm --force -- "$user"
           fi
         done
-        ls ${lingerDir} | sort | comm -3 -1 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl disable-linger
-        ls ${lingerDir} | sort | comm -3 -2 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl  enable-linger
-      fi
-    '';
+        ls | sort | comm -3 -1 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl disable-linger
+        ls | sort | comm -3 -2 ${lingeringUsersFile} - | xargs -r ${pkgs.systemd}/bin/loginctl  enable-linger
+      '';
+
+      serviceConfig.Type = "oneshot";
+    };
 
     # Warn about user accounts with deprecated password hashing schemes
     # This does not work when the users and groups are created by
@@ -863,7 +871,6 @@ in {
           }
           {
             assertion = let
-              xor = a: b: a && !b || b && !a;
               isEffectivelySystemUser = user.isSystemUser || (user.uid != null && user.uid < 1000);
             in xor isEffectivelySystemUser user.isNormalUser;
             message = ''
diff --git a/nixos/modules/config/xdg/portals/wlr.nix b/nixos/modules/config/xdg/portals/wlr.nix
index d84ae794e3bc2..2274be9cc40a9 100644
--- a/nixos/modules/config/xdg/portals/wlr.nix
+++ b/nixos/modules/config/xdg/portals/wlr.nix
@@ -15,7 +15,7 @@ in
 
   options.xdg.portal.wlr = {
     enable = mkEnableOption (lib.mdDoc ''
-      desktop portal for wlroots-based desktops
+      desktop portal for wlroots-based desktops.
 
       This will add the `xdg-desktop-portal-wlr` package into
       the {option}`xdg.portal.extraPortals` option, and provide the
diff --git a/nixos/modules/hardware/cpu/amd-ryzen-smu.nix b/nixos/modules/hardware/cpu/amd-ryzen-smu.nix
new file mode 100644
index 0000000000000..b1a5895aaa24a
--- /dev/null
+++ b/nixos/modules/hardware/cpu/amd-ryzen-smu.nix
@@ -0,0 +1,26 @@
+{ config
+, lib
+, ...
+}:
+let
+  inherit (lib) mkEnableOption mkIf;
+  cfg = config.hardware.cpu.amd.ryzen-smu;
+  ryzen-smu = config.boot.kernelPackages.ryzen-smu;
+in
+{
+  options.hardware.cpu.amd.ryzen-smu = {
+    enable = mkEnableOption ''
+        ryzen_smu, a linux kernel driver that exposes access to the SMU (System Management Unit) for certain AMD Ryzen Processors.
+
+        WARNING: Damage cause by use of your AMD processor outside of official AMD specifications or outside of factory settings are not covered under any AMD product warranty and may not be covered by your board or system manufacturer's warranty
+      '';
+  };
+
+  config = mkIf cfg.enable {
+    boot.kernelModules = [ "ryzen-smu" ];
+    boot.extraModulePackages = [ ryzen-smu ];
+    environment.systemPackages = [ ryzen-smu ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ Cryolitia phdyellow ];
+}
diff --git a/nixos/modules/hardware/ksm.nix b/nixos/modules/hardware/ksm.nix
index 82d94e6ab57c5..d3b2ae37f41bf 100644
--- a/nixos/modules/hardware/ksm.nix
+++ b/nixos/modules/hardware/ksm.nix
@@ -11,7 +11,7 @@ in {
   ];
 
   options.hardware.ksm = {
-    enable = mkEnableOption (lib.mdDoc "Kernel Same-Page Merging");
+    enable = mkEnableOption (lib.mdDoc "Linux kernel Same-Page Merging");
     sleep = mkOption {
       type = types.nullOr types.int;
       default = null;
diff --git a/nixos/modules/hardware/logitech.nix b/nixos/modules/hardware/logitech.nix
index 9b06eb8a8b016..cb1f9b585762d 100644
--- a/nixos/modules/hardware/logitech.nix
+++ b/nixos/modules/hardware/logitech.nix
@@ -19,7 +19,7 @@ in
   options.hardware.logitech = {
 
     lcd = {
-      enable = mkEnableOption (lib.mdDoc "Logitech LCD Devices");
+      enable = mkEnableOption (lib.mdDoc "support for Logitech LCD Devices");
 
       startWhenNeeded = mkOption {
         type = types.bool;
@@ -41,7 +41,7 @@ in
     };
 
     wireless = {
-      enable = mkEnableOption (lib.mdDoc "Logitech Wireless Devices");
+      enable = mkEnableOption (lib.mdDoc "support for Logitech Wireless Devices");
 
       enableGraphical = mkOption {
         type = types.bool;
diff --git a/nixos/modules/hardware/uni-sync.nix b/nixos/modules/hardware/uni-sync.nix
new file mode 100644
index 0000000000000..69411619bc94f
--- /dev/null
+++ b/nixos/modules/hardware/uni-sync.nix
@@ -0,0 +1,117 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+with lib; let
+  cfg = config.hardware.uni-sync;
+in
+{
+  meta.maintainers = with maintainers; [ yunfachi ];
+
+  options.hardware.uni-sync = {
+    enable = mkEnableOption (mdDoc "udev rules and software for Lian Li Uni Controllers");
+    package = mkPackageOption pkgs "uni-sync" { };
+
+    devices = mkOption {
+      default = [ ];
+      example = literalExpression ''
+        [
+          {
+            device_id = "VID:1111/PID:11111/SN:1111111111";
+            sync_rgb = true;
+            channels = [
+              {
+                mode = "PWM";
+              }
+              {
+                mode = "Manual";
+                speed = 100;
+              }
+              {
+                mode = "Manual";
+                speed = 54;
+              }
+              {
+                mode = "Manual";
+                speed = 0;
+              }
+            ];
+          }
+          {
+            device_id = "VID:1010/PID:10101/SN:1010101010";
+            sync_rgb = false;
+            channels = [
+              {
+                mode = "Manual";
+                speed = 0;
+              }
+            ];
+          }
+        ]
+      '';
+      description = mdDoc "List of controllers with their configurations.";
+      type = types.listOf (types.submodule {
+        options = {
+          device_id = mkOption {
+            type = types.str;
+            example = "VID:1111/PID:11111/SN:1111111111";
+            description = mdDoc "Unique device ID displayed at each startup.";
+          };
+          sync_rgb = mkOption {
+            type = types.bool;
+            default = false;
+            example = true;
+            description = mdDoc "Enable ARGB header sync.";
+          };
+          channels = mkOption {
+            default = [ ];
+            example = literalExpression ''
+              [
+                {
+                  mode = "PWM";
+                }
+                {
+                  mode = "Manual";
+                  speed = 100;
+                }
+                {
+                  mode = "Manual";
+                  speed = 54;
+                }
+                {
+                  mode = "Manual";
+                  speed = 0;
+                }
+              ]
+            '';
+            description = mdDoc "List of channels connected to the controller.";
+            type = types.listOf (types.submodule {
+              options = {
+                mode = mkOption {
+                  type = types.enum [ "Manual" "PWM" ];
+                  default = "Manual";
+                  example = "PWM";
+                  description = mdDoc "\"PWM\" to enable PWM sync. \"Manual\" to set speed.";
+                };
+                speed = mkOption {
+                  type = types.int;
+                  default = "50";
+                  example = "100";
+                  description = mdDoc "Fan speed as percentage (clamped between 0 and 100).";
+                };
+              };
+            });
+          };
+        };
+      });
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.etc."uni-sync/uni-sync.json".text = mkIf (cfg.devices != [ ]) (builtins.toJSON { configs = cfg.devices; });
+
+    environment.systemPackages = [ cfg.package ];
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/hardware/video/capture/mwprocapture.nix b/nixos/modules/hardware/video/capture/mwprocapture.nix
index ddd3f3ec7f32f..187f97c25d1b2 100644
--- a/nixos/modules/hardware/video/capture/mwprocapture.nix
+++ b/nixos/modules/hardware/video/capture/mwprocapture.nix
@@ -12,7 +12,7 @@ in
 
 {
 
-  options.hardware.mwProCapture.enable = mkEnableOption (lib.mdDoc "Magewell Pro Capture family kernel module");
+  options.hardware.mwProCapture.enable = mkEnableOption (lib.mdDoc "the Magewell Pro Capture family kernel module");
 
   config = mkIf cfg.enable {
 
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 3b983f768f91a..352c8d8ead54d 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -396,6 +396,9 @@ in {
             modules = [nvidia_x11.bin];
             display = !offloadCfg.enable;
             deviceSection =
+              ''
+                Option "SidebandSocketPath" "/run/nvidia-xdriver/"
+              '' +
               lib.optionalString primeEnabled
               ''
                 BusID "${pCfg.nvidiaBusId}"
@@ -533,8 +536,14 @@ in {
 
         hardware.firmware = lib.optional cfg.open nvidia_x11.firmware;
 
-        systemd.tmpfiles.rules =
-          lib.optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
+        systemd.tmpfiles.rules = [
+          # Remove the following log message:
+          #    (WW) NVIDIA: Failed to bind sideband socket to
+          #    (WW) NVIDIA:     '/var/run/nvidia-xdriver-b4f69129' Permission denied
+          #
+          # https://bbs.archlinux.org/viewtopic.php?pid=1909115#p1909115
+          "d /run/nvidia-xdriver 0770 root users"
+        ] ++ lib.optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
           "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
 
         boot = {
diff --git a/nixos/modules/hardware/video/webcam/ipu6.nix b/nixos/modules/hardware/video/webcam/ipu6.nix
index c2dbdc217bd60..a7767e446bd4f 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/default.md b/nixos/modules/i18n/input-method/default.md
index 42cb8a8d7b6a7..6d12462b788e4 100644
--- a/nixos/modules/i18n/input-method/default.md
+++ b/nixos/modules/i18n/input-method/default.md
@@ -22,11 +22,13 @@ friendly input method user interface.
 
 The following snippet can be used to configure IBus:
 
-```
-i18n.inputMethod = {
-  enabled = "ibus";
-  ibus.engines = with pkgs.ibus-engines; [ anthy hangul mozc ];
-};
+```nix
+{
+  i18n.inputMethod = {
+    enabled = "ibus";
+    ibus.engines = with pkgs.ibus-engines; [ anthy hangul mozc ];
+  };
+}
 ```
 
 `i18n.inputMethod.ibus.engines` is optional and can be used
@@ -48,8 +50,10 @@ Available extra IBus engines are:
     methods, it must appear in the list of engines along with
     `table`. For example:
 
-    ```
-    ibus.engines = with pkgs.ibus-engines; [ table table-others ];
+    ```nix
+    {
+      ibus.engines = with pkgs.ibus-engines; [ table table-others ];
+    }
     ```
 
 To use any input method, the package must be added in the configuration, as
@@ -74,11 +78,13 @@ built-in Input Method Engine, Pinyin, QuWei and Table-based input methods.
 
 The following snippet can be used to configure Fcitx:
 
-```
-i18n.inputMethod = {
-  enabled = "fcitx5";
-  fcitx5.addons = with pkgs; [ fcitx5-mozc fcitx5-hangul fcitx5-m17n ];
-};
+```nix
+{
+  i18n.inputMethod = {
+    enabled = "fcitx5";
+    fcitx5.addons = with pkgs; [ fcitx5-mozc fcitx5-hangul fcitx5-m17n ];
+  };
+}
 ```
 
 `i18n.inputMethod.fcitx5.addons` is optional and can be
@@ -110,10 +116,12 @@ phonetic Korean characters (hangul) and pictographic Korean characters
 
 The following snippet can be used to configure Nabi:
 
-```
-i18n.inputMethod = {
-  enabled = "nabi";
-};
+```nix
+{
+  i18n.inputMethod = {
+    enabled = "nabi";
+  };
+}
 ```
 
 ## Uim {#module-services-input-methods-uim}
@@ -123,10 +131,12 @@ framework. Applications can use it through so-called bridges.
 
 The following snippet can be used to configure uim:
 
-```
-i18n.inputMethod = {
-  enabled = "uim";
-};
+```nix
+{
+  i18n.inputMethod = {
+    enabled = "uim";
+  };
+}
 ```
 
 Note: The [](#opt-i18n.inputMethod.uim.toolbar) option can be
@@ -141,10 +151,12 @@ etc...
 
 The following snippet can be used to configure Hime:
 
-```
-i18n.inputMethod = {
-  enabled = "hime";
-};
+```nix
+{
+  i18n.inputMethod = {
+    enabled = "hime";
+  };
+}
 ```
 
 ## Kime {#module-services-input-methods-kime}
@@ -153,8 +165,10 @@ Kime is Korean IME. it's built with Rust language and let you get simple, safe,
 
 The following snippet can be used to configure Kime:
 
-```
-i18n.inputMethod = {
-  enabled = "kime";
-};
+```nix
+{
+  i18n.inputMethod = {
+    enabled = "kime";
+  };
+}
 ```
diff --git a/nixos/modules/i18n/input-method/fcitx5.nix b/nixos/modules/i18n/input-method/fcitx5.nix
index ee8d2652b1c72..7553362205203 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 5ae523c43f589..59d5fc26efe9b 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,18 +26,40 @@
 , xz
 
   # arguments
+, name
+, version
 , imageFileBasename
 , compression
 , fileSystems
-, partitions
+, partitionsJSON
 , split
 , seed
 , definitionsDirectory
 , sectorSize
 , mkfsEnv ? {}
+, createEmpty ? true
 }:
 
 let
+  systemdArch = let
+    inherit (stdenvNoCC) hostPlatform;
+  in
+    if hostPlatform.isAarch32 then "arm"
+    else if hostPlatform.isAarch64 then "arm64"
+    else if hostPlatform.isx86_32 then "x86"
+    else if hostPlatform.isx86_64 then "x86-64"
+    else if hostPlatform.isMips32 then "mips-le"
+    else if hostPlatform.isMips64 then "mips64-le"
+    else if hostPlatform.isPower then "ppc"
+    else if hostPlatform.isPower64 then "ppc64"
+    else if hostPlatform.isRiscV32 then "riscv32"
+    else if hostPlatform.isRiscV64 then "riscv64"
+    else if hostPlatform.isS390 then "s390"
+    else if hostPlatform.isS390x then "s390x"
+    else if hostPlatform.isLoongArch64 then "loongarch64"
+    else if hostPlatform.isAlpha then "alpha"
+    else hostPlatform.parsed.cpu.name;
+
   amendRepartDefinitions = runCommand "amend-repart-definitions.py"
     {
       # TODO: ruff does not splice properly in nativeBuildInputs
@@ -52,11 +74,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 +95,89 @@ 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 = [
+    "--architecture=${systemdArch}"
     "--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
 
-  echo "Building image with systemd-repart..."
-  unshare --map-root-user fakeroot systemd-repart \
-    ''${systemdRepartFlags[@]} \
-    ${imageFileBasename}.raw \
-    | tee repart-output.json
+    amendedRepartDefinitionsDir=$(${amendRepartDefinitions} $partitionsJSON $definitionsDirectory)
+    ln -vs $amendedRepartDefinitionsDir $finalRepartDefinitions
 
+    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 90c9c7e51dfa3..569d4a4b00215 100644
--- a/nixos/modules/image/repart.nix
+++ b/nixos/modules/image/repart.nix
@@ -6,6 +6,8 @@
 let
   cfg = config.image.repart;
 
+  inherit (utils.systemdUtils.lib) GPTMaxLabelLength;
+
   partitionOptions = {
     options = {
       storePaths = lib.mkOption {
@@ -211,10 +213,55 @@ in
       '';
     };
 
+    finalPartitions = lib.mkOption {
+      type = lib.types.attrs;
+      internal = true;
+      readOnly = true;
+      description = lib.mdDoc ''
+        Convenience option to access partitions with added closures.
+      '';
+    };
+
   };
 
   config = {
 
+    assertions = lib.mapAttrsToList (fileName: partitionConfig:
+      let
+        inherit (partitionConfig) repartConfig;
+        labelLength = builtins.stringLength repartConfig.Label;
+      in
+      {
+        assertion = repartConfig ? Label -> GPTMaxLabelLength >= labelLength;
+        message = ''
+          The partition label '${repartConfig.Label}'
+          defined for '${fileName}' is ${toString labelLength} characters long,
+          but the maximum label length supported by UEFI is ${toString
+          GPTMaxLabelLength}.
+        '';
+      }
+    ) cfg.partitions;
+
+    warnings = lib.filter (v: v != null) (lib.mapAttrsToList (fileName: partitionConfig:
+      let
+        inherit (partitionConfig) repartConfig;
+        suggestedMaxLabelLength = GPTMaxLabelLength - 2;
+        labelLength = builtins.stringLength repartConfig.Label;
+      in
+        if (repartConfig ? Label && labelLength >= suggestedMaxLabelLength) then ''
+          The partition label '${repartConfig.Label}'
+          defined for '${fileName}' is ${toString labelLength} characters long.
+          The suggested maximum label length is ${toString
+          suggestedMaxLabelLength}.
+
+          If you use sytemd-sysupdate style A/B updates, this might
+          not leave enough space to increment the version number included in
+          the label in a future release. For example, if your label is
+          ${toString GPTMaxLabelLength} characters long (the maximum enforced by UEFI) and
+          you're at version 9, you cannot increment this to 10.
+        '' else null
+    ) cfg.partitions);
+
     image.repart =
       let
         version = config.image.repart.version;
@@ -224,6 +271,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 +296,8 @@ in
             "xz" = 3;
           }."${cfg.compression.algorithm}";
         };
+
+        finalPartitions = lib.mapAttrs addClosure cfg.partitions;
       };
 
     system.build.image =
@@ -247,36 +306,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/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix
index d1a4c27432c2b..1de5ba113875a 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix
@@ -35,20 +35,19 @@
     QT_QPA_PLATFORM = "$([[ $XDG_SESSION_TYPE = \"wayland\" ]] && echo \"wayland\")";
   };
 
-  services.xserver.displayManager = {
-    gdm = {
-      enable = true;
-      # autoSuspend makes the machine automatically suspend after inactivity.
-      # It's possible someone could/try to ssh'd into the machine and obviously
-      # have issues because it's inactive.
-      # See:
-      # * https://github.com/NixOS/nixpkgs/pull/63790
-      # * https://gitlab.gnome.org/GNOME/gnome-control-center/issues/22
-      autoSuspend = false;
-    };
-    autoLogin = {
-      enable = true;
-      user = "nixos";
-    };
+  services.xserver.displayManager.gdm = {
+    enable = true;
+    # autoSuspend makes the machine automatically suspend after inactivity.
+    # It's possible someone could/try to ssh'd into the machine and obviously
+    # have issues because it's inactive.
+    # See:
+    # * https://github.com/NixOS/nixpkgs/pull/63790
+    # * https://gitlab.gnome.org/GNOME/gnome-control-center/issues/22
+    autoSuspend = false;
+  };
+
+  services.displayManager.autoLogin = {
+    enable = true;
+    user = "nixos";
   };
 }
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix
index a4c46d58c85a4..61e94ffed8894 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma5.nix
@@ -8,18 +8,16 @@
 
   isoImage.edition = "plasma5";
 
-  services.xserver = {
-    desktopManager.plasma5 = {
-      enable = true;
-    };
+  services.xserver.desktopManager.plasma5 = {
+    enable = true;
+  };
 
-    # Automatically login as nixos.
-    displayManager = {
-      sddm.enable = true;
-      autoLogin = {
-        enable = true;
-        user = "nixos";
-      };
+  # Automatically login as nixos.
+  services.displayManager = {
+    sddm.enable = true;
+    autoLogin = {
+      enable = true;
+      user = "nixos";
     };
   };
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix
index 11118db3aae2a..bdcf751bf6290 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix
@@ -7,16 +7,14 @@
 
   isoImage.edition = "plasma6";
 
-  services.xserver = {
-    desktopManager.plasma6.enable = true;
-
-    # Automatically login as nixos.
-    displayManager = {
-      sddm.enable = true;
-      autoLogin = {
-        enable = true;
-        user = "nixos";
-      };
+  services.desktopManager.plasma6.enable = true;
+
+  # Automatically login as nixos.
+  services.displayManager = {
+    sddm.enable = true;
+    autoLogin = {
+      enable = true;
+      user = "nixos";
     };
   };
 
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
index 573b31b439c2d..b3c605e3f94d1 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-gnome.nix
@@ -16,21 +16,19 @@
     enable = true;
   };
 
-  services.xserver.displayManager = {
-    gdm = {
-      enable = true;
-      # autoSuspend makes the machine automatically suspend after inactivity.
-      # It's possible someone could/try to ssh'd into the machine and obviously
-      # have issues because it's inactive.
-      # See:
-      # * https://github.com/NixOS/nixpkgs/pull/63790
-      # * https://gitlab.gnome.org/GNOME/gnome-control-center/issues/22
-      autoSuspend = false;
-    };
-    autoLogin = {
-      enable = true;
-      user = "nixos";
-    };
+  services.xserver.displayManager.gdm = {
+    enable = true;
+    # autoSuspend makes the machine automatically suspend after inactivity.
+    # It's possible someone could/try to ssh'd into the machine and obviously
+    # have issues because it's inactive.
+    # See:
+    # * https://github.com/NixOS/nixpkgs/pull/63790
+    # * https://gitlab.gnome.org/GNOME/gnome-control-center/issues/22
+    autoSuspend = false;
   };
 
+  services.displayManager.autoLogin = {
+    enable = true;
+    user = "nixos";
+  };
 }
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix b/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix
index 5c7617c9f8c1a..ce111bcebd5c9 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-graphical-plasma5.nix
@@ -8,18 +8,16 @@
 
   isoImage.edition = "plasma5";
 
-  services.xserver = {
-    desktopManager.plasma5 = {
-      enable = true;
-    };
+  services.xserver.desktopManager.plasma5 = {
+    enable = true;
+  };
 
-    # Automatically login as nixos.
-    displayManager = {
-      sddm.enable = true;
-      autoLogin = {
-        enable = true;
-        user = "nixos";
-      };
+  # Automatically login as nixos.
+  services.displayManager = {
+    sddm.enable = true;
+    autoLogin = {
+      enable = true;
+      user = "nixos";
     };
   };
 
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 2f9edba4f0c9c..cf4173638b1a2 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -257,7 +257,7 @@ foreach my $path (glob "/sys/class/{block,mmc_host}/*") {
 
 # Add bcache module, if needed.
 my @bcacheDevices = glob("/dev/bcache*");
-@bcacheDevices = grep(!qr#dev/bcachefs.*#, @bcacheDevices);
+@bcacheDevices = grep(!m#dev/bcachefs.*#, @bcacheDevices);
 if (scalar @bcacheDevices > 0) {
     push @initrdAvailableKernelModules, "bcache";
 }
@@ -453,6 +453,17 @@ EOF
         }
     }
 
+    # Preserve umask (fmask, dmask) settings for vfat filesystems.
+    # (The default is to mount these world-readable, but that's a security risk
+    # for the EFI System Partition.)
+    if ($fsType eq "vfat") {
+        for (@superOptions) {
+            if ($_ =~ /fmask|dmask/) {
+                push @extraOptions, $_;
+            }
+        }
+    }
+
     # is this a stratis fs?
     my $stableDevPath = findStableDevPath $device;
     my $stratisPool;
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index a7d11370d445e..3ef9a8cdca202 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -177,8 +177,12 @@ in
         # services.printing.enable = true;
 
         # Enable sound.
-        # sound.enable = true;
         # hardware.pulseaudio.enable = true;
+        # OR
+        # services.pipewire = {
+        #   enable = true;
+        #   pulse.enable = true;
+        # };
 
         # Enable touchpad support (enabled default in most desktopManager).
         # services.xserver.libinput.enable = true;
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 01931b2acfca4..289a8cf9e5062 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -40,7 +40,7 @@ with lib;
   # If you prefer another desktop manager or display manager, you may want
   # to disable the default.
   # services.xserver.desktopManager.plasma5.enable = lib.mkForce false;
-  # services.xserver.displayManager.sddm.enable = lib.mkForce false;
+  # services.displayManager.sddm.enable = lib.mkForce false;
 
   # Enable GDM/GNOME by uncommenting above two lines and two lines below.
   # services.xserver.displayManager.gdm.enable = true;
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
index f3e698468e642..2a25f8e564684 100644
--- a/nixos/modules/misc/documentation.nix
+++ b/nixos/modules/misc/documentation.nix
@@ -1,8 +1,32 @@
 { config, options, lib, pkgs, utils, modules, baseModules, extraModules, modulesPath, specialArgs, ... }:
 
-with lib;
-
 let
+  inherit (lib)
+    cleanSourceFilter
+    concatMapStringsSep
+    evalModules
+    filter
+    functionArgs
+    hasSuffix
+    isAttrs
+    isDerivation
+    isFunction
+    isPath
+    literalExpression
+    mapAttrs
+    mkIf
+    mkMerge
+    mkOption
+    mkRemovedOptionModule
+    mkRenamedOptionModule
+    optional
+    optionalAttrs
+    optionals
+    partition
+    removePrefix
+    types
+    warn
+    ;
 
   cfg = config.documentation;
   allOpts = options;
@@ -13,7 +37,7 @@ let
       instance = f (mapAttrs (n: _: abort "evaluating ${n} for `meta` failed") (functionArgs f));
     in
       cfg.nixos.options.splitBuild
-        && builtins.isPath m
+        && isPath m
         && isFunction f
         && instance ? options
         && instance.meta.buildDocsInSandbox or true;
@@ -51,12 +75,12 @@ let
           (name: value:
             let
               wholeName = "${namePrefix}.${name}";
-              guard = lib.warn "Attempt to evaluate package ${wholeName} in option documentation; this is not supported and will eventually be an error. Use `mkPackageOption{,MD}` or `literalExpression` instead.";
+              guard = warn "Attempt to evaluate package ${wholeName} in option documentation; this is not supported and will eventually be an error. Use `mkPackageOption{,MD}` or `literalExpression` instead.";
             in if isAttrs value then
               scrubDerivations wholeName value
               // optionalAttrs (isDerivation value) {
                 outPath = guard "\${${wholeName}}";
-                drvPath = guard drvPath;
+                drvPath = guard value.drvPath;
               }
             else value
           )
@@ -176,7 +200,7 @@ in
       enable = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to install documentation of packages from
           {option}`environment.systemPackages` into the generated system path.
 
@@ -188,7 +212,7 @@ in
       man.enable = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to install manual pages.
           This also includes `man` outputs.
         '';
@@ -197,7 +221,7 @@ in
       man.generateCaches = mkOption {
         type = types.bool;
         default = false;
-        description = mdDoc ''
+        description = ''
           Whether to generate the manual page index caches.
           This allows searching for a page or
           keyword using utilities like {manpage}`apropos(1)`
@@ -209,7 +233,7 @@ in
       info.enable = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to install info pages and the {command}`info` command.
           This also includes "info" outputs.
         '';
@@ -218,7 +242,7 @@ in
       doc.enable = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to install documentation distributed in packages' `/share/doc`.
           Usually plain text and/or HTML.
           This also includes "doc" outputs.
@@ -228,7 +252,7 @@ in
       dev.enable = mkOption {
         type = types.bool;
         default = false;
-        description = mdDoc ''
+        description = ''
           Whether to install documentation targeted at developers.
           * This includes man pages targeted at developers if {option}`documentation.man.enable` is
             set (this also includes "devman" outputs).
@@ -242,7 +266,7 @@ in
       nixos.enable = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to install NixOS's own documentation.
 
           - This includes man pages like
@@ -256,7 +280,7 @@ in
       nixos.extraModules = mkOption {
         type = types.listOf types.raw;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           Modules for which to show options even when not imported.
         '';
       };
@@ -264,7 +288,7 @@ in
       nixos.options.splitBuild = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Whether to split the option docs build into a cacheable and an uncacheable part.
           Splitting the build can substantially decrease the amount of time needed to build
           the manual, but some user modules may be incompatible with this splitting.
@@ -274,7 +298,7 @@ in
       nixos.options.warningsAreErrors = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           Treat warning emitted during the option documentation build (eg for missing option
           descriptions) as errors.
         '';
@@ -283,7 +307,7 @@ in
       nixos.includeAllModules = mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           Whether the generated NixOS's documentation should include documentation for all
           the options from all the NixOS modules included in the current
           `configuration.nix`. Disabling this will make the manual
@@ -294,7 +318,7 @@ in
       nixos.extraModuleSources = mkOption {
         type = types.listOf (types.either types.path types.str);
         default = [ ];
-        description = lib.mdDoc ''
+        description = ''
           Which extra NixOS module paths the generated NixOS's documentation should strip
           from options.
         '';
diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix
index 73646a60aabb2..706e2ac2c2836 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 41e369ac1c650..786f838bc6c6f 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -54,6 +54,7 @@
   ./hardware/corectrl.nix
   ./hardware/cpu/amd-microcode.nix
   ./hardware/cpu/amd-sev.nix
+  ./hardware/cpu/amd-ryzen-smu.nix
   ./hardware/cpu/intel-microcode.nix
   ./hardware/cpu/intel-sgx.nix
   ./hardware/cpu/x86-msr.nix
@@ -98,6 +99,7 @@
   ./hardware/tuxedo-keyboard.nix
   ./hardware/ubertooth.nix
   ./hardware/uinput.nix
+  ./hardware/uni-sync.nix
   ./hardware/usb-modeswitch.nix
   ./hardware/usb-storage.nix
   ./hardware/video/amdgpu-pro.nix
@@ -142,6 +144,7 @@
   ./programs/adb.nix
   ./programs/alvr.nix
   ./programs/appgate-sdp.nix
+  ./programs/appimage.nix
   ./programs/atop.nix
   ./programs/ausweisapp.nix
   ./programs/autojump.nix
@@ -193,6 +196,7 @@
   ./programs/gnome-disks.nix
   ./programs/gnome-terminal.nix
   ./programs/gnupg.nix
+  ./programs/goldwarden.nix
   ./programs/gpaste.nix
   ./programs/gphoto2.nix
   ./programs/haguichi.nix
@@ -203,11 +207,14 @@
   ./programs/i3lock.nix
   ./programs/iotop.nix
   ./programs/java.nix
+  ./programs/joycond-cemuhook.nix
   ./programs/k3b.nix
   ./programs/k40-whisperer.nix
   ./programs/kbdlight.nix
   ./programs/kclock.nix
   ./programs/kdeconnect.nix
+  ./programs/lazygit.nix
+  ./programs/kubeswitch.nix
   ./programs/less.nix
   ./programs/liboping.nix
   ./programs/light.nix
@@ -247,6 +254,7 @@
   ./programs/regreet.nix
   ./programs/rog-control-center.nix
   ./programs/rust-motd.nix
+  ./programs/ryzen-monitor-ng.nix
   ./programs/screen.nix
   ./programs/seahorse.nix
   ./programs/sedutil.nix
@@ -329,6 +337,7 @@
   ./security/systemd-confinement.nix
   ./security/tpm2.nix
   ./security/wrappers/default.nix
+  ./services/admin/docuum.nix
   ./services/admin/meshcentral.nix
   ./services/admin/oxidized.nix
   ./services/admin/pgadmin.nix
@@ -338,7 +347,6 @@
   ./services/amqp/rabbitmq.nix
   ./services/audio/alsa.nix
   ./services/audio/botamusique.nix
-  ./services/audio/castopod.nix
   ./services/audio/gmediarender.nix
   ./services/audio/gonic.nix
   ./services/audio/goxlr-utility.nix
@@ -360,9 +368,6 @@
   ./services/audio/spotifyd.nix
   ./services/audio/squeezelite.nix
   ./services/audio/tts.nix
-  ./services/audio/wyoming/faster-whisper.nix
-  ./services/audio/wyoming/openwakeword.nix
-  ./services/audio/wyoming/piper.nix
   ./services/audio/ympd.nix
   ./services/backup/automysqlbackup.nix
   ./services/backup/bacula.nix
@@ -507,7 +512,9 @@
   ./services/development/nixseparatedebuginfod.nix
   ./services/development/rstudio-server/default.nix
   ./services/development/zammad.nix
+  ./services/display-managers/default.nix
   ./services/display-managers/greetd.nix
+  ./services/display-managers/sddm.nix
   ./services/editors/emacs.nix
   ./services/editors/haste.nix
   ./services/editors/infinoted.nix
@@ -585,8 +592,11 @@
   ./services/home-automation/evcc.nix
   ./services/home-automation/govee2mqtt.nix
   ./services/home-automation/home-assistant.nix
-  ./services/home-automation/homeassistant-satellite.nix
   ./services/home-automation/matter-server.nix
+  ./services/home-automation/wyoming/faster-whisper.nix
+  ./services/home-automation/wyoming/openwakeword.nix
+  ./services/home-automation/wyoming/piper.nix
+  ./services/home-automation/wyoming/satellite.nix
   ./services/home-automation/zigbee2mqtt.nix
   ./services/home-automation/zwave-js.nix
   ./services/logging/SystemdJournal2Gelf.nix
@@ -648,6 +658,7 @@
   ./services/matrix/hebbot.nix
   ./services/matrix/maubot.nix
   ./services/matrix/mautrix-facebook.nix
+  ./services/matrix/mautrix-meta.nix
   ./services/matrix/mautrix-telegram.nix
   ./services/matrix/mautrix-whatsapp.nix
   ./services/matrix/mjolnir.nix
@@ -703,6 +714,7 @@
   ./services/misc/gogs.nix
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
+  ./services/misc/graphical-desktop.nix
   ./services/misc/greenclip.nix
   ./services/misc/guix
   ./services/misc/headphones.nix
@@ -710,6 +722,7 @@
   ./services/misc/homepage-dashboard.nix
   ./services/misc/ihaskell.nix
   ./services/misc/input-remapper.nix
+  ./services/misc/invidious-router.nix
   ./services/misc/irkerd.nix
   ./services/misc/jackett.nix
   ./services/misc/jellyfin.nix
@@ -726,6 +739,7 @@
   ./services/misc/mbpfan.nix
   ./services/misc/mediatomb.nix
   ./services/misc/metabase.nix
+  ./services/misc/mollysocket.nix
   ./services/misc/moonraker.nix
   ./services/misc/mqtt2influxdb.nix
   ./services/misc/n8n.nix
@@ -793,7 +807,9 @@
   ./services/misc/transfer-sh.nix
   ./services/misc/tzupdate.nix
   ./services/misc/uhub.nix
+  ./services/misc/wastebin.nix
   ./services/misc/weechat.nix
+  ./services/misc/workout-tracker.nix
   ./services/misc/xmr-stak.nix
   ./services/misc/xmrig.nix
   ./services/misc/zoneminder.nix
@@ -833,6 +849,7 @@
   ./services/monitoring/munin.nix
   ./services/monitoring/nagios.nix
   ./services/monitoring/netdata.nix
+  ./services/monitoring/nezha-agent.nix
   ./services/monitoring/ocsinventory-agent.nix
   ./services/monitoring/opentelemetry-collector.nix
   ./services/monitoring/osquery.nix
@@ -943,6 +960,7 @@
   ./services/networking/dnscrypt-wrapper.nix
   ./services/networking/dnsdist.nix
   ./services/networking/dnsmasq.nix
+  ./services/networking/dnsproxy.nix
   ./services/networking/doh-proxy-rust.nix
   ./services/networking/ejabberd.nix
   ./services/networking/envoy.nix
@@ -1018,6 +1036,8 @@
   ./services/networking/lxd-image-server.nix
   ./services/networking/magic-wormhole-mailbox-server.nix
   ./services/networking/matterbridge.nix
+  ./services/networking/microsocks.nix
+  ./services/networking/mihomo.nix
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
   ./services/networking/miredo.nix
@@ -1034,6 +1054,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
@@ -1102,6 +1123,11 @@
   ./services/networking/rpcbind.nix
   ./services/networking/rxe.nix
   ./services/networking/sabnzbd.nix
+  ./services/networking/scion/scion.nix
+  ./services/networking/scion/scion-control.nix
+  ./services/networking/scion/scion-daemon.nix
+  ./services/networking/scion/scion-dispatcher.nix
+  ./services/networking/scion/scion-router.nix
   ./services/networking/seafile.nix
   ./services/networking/searx.nix
   ./services/networking/shadowsocks.nix
@@ -1139,6 +1165,7 @@
   ./services/networking/tayga.nix
   ./services/networking/tcpcrypt.nix
   ./services/networking/teamspeak3.nix
+  ./services/networking/technitium-dns-server.nix
   ./services/networking/teleport.nix
   ./services/networking/tetrd.nix
   ./services/networking/tftpd.nix
@@ -1270,6 +1297,7 @@
   ./services/video/go2rtc/default.nix
   ./services/video/frigate.nix
   ./services/video/mirakurun.nix
+  ./services/video/photonvision.nix
   ./services/video/replay-sorcery.nix
   ./services/video/mediamtx.nix
   ./services/video/unifi-video.nix
@@ -1285,12 +1313,14 @@
   ./services/web-apps/bookstack.nix
   ./services/web-apps/c2fmzq-server.nix
   ./services/web-apps/calibre-web.nix
+  ./services/web-apps/castopod.nix
   ./services/web-apps/coder.nix
   ./services/web-apps/changedetection-io.nix
   ./services/web-apps/chatgpt-retrieval-plugin.nix
   ./services/web-apps/cloudlog.nix
   ./services/web-apps/code-server.nix
   ./services/web-apps/convos.nix
+  ./services/web-apps/davis.nix
   ./services/web-apps/dex.nix
   ./services/web-apps/discourse.nix
   ./services/web-apps/documize.nix
@@ -1337,12 +1367,14 @@
   ./services/web-apps/miniflux.nix
   ./services/web-apps/monica.nix
   ./services/web-apps/moodle.nix
+  ./services/web-apps/movim.nix
   ./services/web-apps/netbox.nix
   ./services/web-apps/nextcloud.nix
   ./services/web-apps/nextcloud-notify_push.nix
   ./services/web-apps/nexus.nix
   ./services/web-apps/nifi.nix
   ./services/web-apps/node-red.nix
+  ./services/web-apps/ocis.nix
   ./services/web-apps/onlyoffice.nix
   ./services/web-apps/openvscode-server.nix
   ./services/web-apps/mobilizon.nix
@@ -1358,6 +1390,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
@@ -1365,6 +1398,7 @@
   ./services/web-apps/rss-bridge.nix
   ./services/web-apps/selfoss.nix
   ./services/web-apps/shiori.nix
+  ./services/web-apps/silverbullet.nix
   ./services/web-apps/slskd.nix
   ./services/web-apps/snipe-it.nix
   ./services/web-apps/sogo.nix
@@ -1418,7 +1452,6 @@
   ./services/x11/display-managers/default.nix
   ./services/x11/display-managers/gdm.nix
   ./services/x11/display-managers/lightdm.nix
-  ./services/x11/display-managers/sddm.nix
   ./services/x11/display-managers/slim.nix
   ./services/x11/display-managers/startx.nix
   ./services/x11/display-managers/sx.nix
diff --git a/nixos/modules/profiles/all-hardware.nix b/nixos/modules/profiles/all-hardware.nix
index 4857ea4dbeae8..249b767593f2d 100644
--- a/nixos/modules/profiles/all-hardware.nix
+++ b/nixos/modules/profiles/all-hardware.nix
@@ -58,15 +58,7 @@ in
       # Hyper-V support.
       "hv_storvsc"
     ] ++ lib.optionals pkgs.stdenv.hostPlatform.isAarch [
-      # Most of the following falls into two categories:
-      #  - early KMS / early display
-      #  - early storage (e.g. USB) support
-
-      # Allows using framebuffer configured by the initial boot firmware
-      "simplefb"
-
       # Allwinner support
-
       # Required for early KMS
       "sun4i-drm"
       "sun8i-mixer" # Audio, but required for kms
@@ -75,7 +67,6 @@ in
       "pwm-sun4i"
 
       # Broadcom
-
       "vc4"
     ] ++ lib.optionals pkgs.stdenv.isAarch64 [
       # Most of the following falls into two categories:
diff --git a/nixos/modules/profiles/macos-builder.nix b/nixos/modules/profiles/macos-builder.nix
index 6c2602881d6b5..786e26cf98f7f 100644
--- a/nixos/modules/profiles/macos-builder.nix
+++ b/nixos/modules/profiles/macos-builder.nix
@@ -145,6 +145,8 @@ in
         # This installCredentials script is written so that it's as easy as
         # possible for a user to audit before confirming the `sudo`
         installCredentials = hostPkgs.writeShellScript "install-credentials" ''
+          set -euo pipefail
+
           KEYS="''${1}"
           INSTALL=${hostPkgs.coreutils}/bin/install
           "''${INSTALL}" -g nixbld -m 600 "''${KEYS}/${user}_${keyType}" ${privateKey}
@@ -154,6 +156,9 @@ in
         hostPkgs = config.virtualisation.host.pkgs;
 
         script = hostPkgs.writeShellScriptBin "create-builder" (
+          ''
+            set -euo pipefail
+          '' +
           # When running as non-interactively as part of a DarwinConfiguration the working directory
           # must be set to a writeable directory.
         (if cfg.workingDirectory != "." then ''
diff --git a/nixos/modules/programs/_1password-gui.nix b/nixos/modules/programs/_1password-gui.nix
index 83ef6037fb5a3..eb2effee4326f 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/appgate-sdp.nix b/nixos/modules/programs/appgate-sdp.nix
index bdd538dc2f1f3..5b958b686552e 100644
--- a/nixos/modules/programs/appgate-sdp.nix
+++ b/nixos/modules/programs/appgate-sdp.nix
@@ -5,7 +5,7 @@ with lib;
 {
   options = {
     programs.appgate-sdp = {
-      enable = mkEnableOption (lib.mdDoc "AppGate SDP VPN client");
+      enable = mkEnableOption (lib.mdDoc "the AppGate SDP VPN client");
     };
   };
 
diff --git a/nixos/modules/programs/appimage.nix b/nixos/modules/programs/appimage.nix
new file mode 100644
index 0000000000000..0011c2ff578dc
--- /dev/null
+++ b/nixos/modules/programs/appimage.nix
@@ -0,0 +1,33 @@
+{ lib, config, pkgs, ... }:
+
+let
+  cfg = config.programs.appimage;
+in
+
+{
+  options.programs.appimage = {
+    enable = lib.mkEnableOption "appimage-run wrapper script for executing appimages on NixOS";
+    binfmt = lib.mkEnableOption "binfmt registration to run appimages via appimage-run seamlessly";
+    package = lib.mkPackageOption pkgs "appimage-run" {
+      example = ''
+        pkgs.appimage-run.override {
+          extraPkgs = pkgs: [ pkgs.ffmpeg pkgs.imagemagick ];
+        }
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    boot.binfmt.registrations.appimage = lib.mkIf cfg.binfmt {
+      wrapInterpreterInShell = false;
+      interpreter = lib.getExe cfg.package;
+      recognitionType = "magic";
+      offset = 0;
+      mask = ''\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff'';
+      magicOrExtension = ''\x7fELF....AI\x02'';
+    };
+    environment.systemPackages = [ cfg.package ];
+  };
+
+  meta.maintainers = with lib.maintainers; [ jopejoe1 atemu ];
+}
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
index 003cfdbfc8fad..26bfc519934b3 100644
--- a/nixos/modules/programs/atop.nix
+++ b/nixos/modules/programs/atop.nix
@@ -14,7 +14,7 @@ in
 
     programs.atop = rec {
 
-      enable = mkEnableOption (lib.mdDoc "Atop");
+      enable = mkEnableOption (lib.mdDoc "Atop, a tool for monitoring system resources");
 
       package = mkPackageOption pkgs "atop" { };
 
diff --git a/nixos/modules/programs/bash/blesh.nix b/nixos/modules/programs/bash/blesh.nix
index 8fa51bef77443..f23dff1405e25 100644
--- a/nixos/modules/programs/bash/blesh.nix
+++ b/nixos/modules/programs/bash/blesh.nix
@@ -4,7 +4,7 @@ let
   cfg = config.programs.bash.blesh;
 in {
   options = {
-    programs.bash.blesh.enable = mkEnableOption (mdDoc "blesh");
+    programs.bash.blesh.enable = mkEnableOption (mdDoc "blesh, a full-featured line editor written in pure Bash");
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/programs/bcc.nix b/nixos/modules/programs/bcc.nix
index ff29d56bedb9c..f425fd45232dc 100644
--- a/nixos/modules/programs/bcc.nix
+++ b/nixos/modules/programs/bcc.nix
@@ -1,6 +1,6 @@
 { config, pkgs, lib, ... }:
 {
-  options.programs.bcc.enable = lib.mkEnableOption (lib.mdDoc "bcc");
+  options.programs.bcc.enable = lib.mkEnableOption (lib.mdDoc "bcc, tools for BPF-based Linux IO analysis, networking, monitoring, and more");
 
   config = lib.mkIf config.programs.bcc.enable {
     environment.systemPackages = [ pkgs.bcc ];
diff --git a/nixos/modules/programs/captive-browser.nix b/nixos/modules/programs/captive-browser.nix
index 1c3ee7638ee02..ae00403284105 100644
--- a/nixos/modules/programs/captive-browser.nix
+++ b/nixos/modules/programs/captive-browser.nix
@@ -49,7 +49,7 @@ in
 
   options = {
     programs.captive-browser = {
-      enable = mkEnableOption (lib.mdDoc "captive browser");
+      enable = mkEnableOption (lib.mdDoc "captive browser, a dedicated Chrome instance to log into captive portals without messing with DNS settings");
 
       package = mkPackageOption pkgs "captive-browser" { };
 
diff --git a/nixos/modules/programs/ccache.nix b/nixos/modules/programs/ccache.nix
index 7972b2ac4a569..fbe8eb6c179e0 100644
--- a/nixos/modules/programs/ccache.nix
+++ b/nixos/modules/programs/ccache.nix
@@ -5,7 +5,7 @@ let
 in {
   options.programs.ccache = {
     # host configuration
-    enable = lib.mkEnableOption (lib.mdDoc "CCache");
+    enable = lib.mkEnableOption (lib.mdDoc "CCache, a compiler cache for fast recompilation of C/C++ code");
     cacheDir = lib.mkOption {
       type = lib.types.path;
       description = lib.mdDoc "CCache directory";
diff --git a/nixos/modules/programs/digitalbitbox/default.md b/nixos/modules/programs/digitalbitbox/default.md
index 9bca14e97ffef..5147bb971e3a0 100644
--- a/nixos/modules/programs/digitalbitbox/default.md
+++ b/nixos/modules/programs/digitalbitbox/default.md
@@ -4,8 +4,10 @@ Digital Bitbox is a hardware wallet and second-factor authenticator.
 
 The `digitalbitbox` programs module may be installed by setting
 `programs.digitalbitbox` to `true` in a manner similar to
-```
-programs.digitalbitbox.enable = true;
+```nix
+{
+  programs.digitalbitbox.enable = true;
+}
 ```
 and bundles the `digitalbitbox` package (see [](#sec-digitalbitbox-package)),
 which contains the `dbb-app` and `dbb-cli` binaries, along with the hardware
@@ -21,27 +23,33 @@ For more information, see <https://digitalbitbox.com/start_linux>.
 
 The binaries, `dbb-app` (a GUI tool) and `dbb-cli` (a CLI tool), are available
 through the `digitalbitbox` package which could be installed as follows:
-```
-environment.systemPackages = [
-  pkgs.digitalbitbox
-];
+```nix
+{
+  environment.systemPackages = [
+    pkgs.digitalbitbox
+  ];
+}
 ```
 
 ## Hardware {#sec-digitalbitbox-hardware-module}
 
 The digitalbitbox hardware package enables the udev rules for Digital Bitbox
 devices and may be installed as follows:
-```
-hardware.digitalbitbox.enable = true;
+```nix
+{
+  hardware.digitalbitbox.enable = true;
+}
 ```
 
 In order to alter the udev rules, one may provide different values for the
 `udevRule51` and `udevRule52` attributes by means of overriding as follows:
-```
-programs.digitalbitbox = {
-  enable = true;
-  package = pkgs.digitalbitbox.override {
-    udevRule51 = "something else";
+```nix
+{
+  programs.digitalbitbox = {
+    enable = true;
+    package = pkgs.digitalbitbox.override {
+      udevRule51 = "something else";
+    };
   };
-};
+}
 ```
diff --git a/nixos/modules/programs/firejail.nix b/nixos/modules/programs/firejail.nix
index 046c31ce64f6b..792ed9fe56807 100644
--- a/nixos/modules/programs/firejail.nix
+++ b/nixos/modules/programs/firejail.nix
@@ -40,7 +40,7 @@ let
 
 in {
   options.programs.firejail = {
-    enable = mkEnableOption (lib.mdDoc "firejail");
+    enable = mkEnableOption (lib.mdDoc "firejail, a sandboxing tool for Linux");
 
     wrappedBinaries = mkOption {
       type = types.attrsOf (types.either types.path (types.submodule {
diff --git a/nixos/modules/programs/fzf.nix b/nixos/modules/programs/fzf.nix
index 7c4f338e29b30..acc23d75df7b6 100644
--- a/nixos/modules/programs/fzf.nix
+++ b/nixos/modules/programs/fzf.nix
@@ -1,32 +1,38 @@
 { pkgs, config, lib, ... }:
-with lib;
+
 let
   cfg = config.programs.fzf;
 in
 {
   options = {
     programs.fzf = {
-      fuzzyCompletion = mkEnableOption (mdDoc "fuzzy completion with fzf");
-      keybindings = mkEnableOption (mdDoc "fzf keybindings");
+      fuzzyCompletion = lib.mkEnableOption (lib.mdDoc "fuzzy completion with fzf");
+      keybindings = lib.mkEnableOption (lib.mdDoc "fzf keybindings");
     };
   };
-  config = {
-    environment.systemPackages = optional (cfg.keybindings || cfg.fuzzyCompletion) pkgs.fzf;
 
-    programs.bash.interactiveShellInit = optionalString cfg.fuzzyCompletion ''
-      source ${pkgs.fzf}/share/fzf/completion.bash
-    '' + optionalString cfg.keybindings ''
-      source ${pkgs.fzf}/share/fzf/key-bindings.bash
-    '';
+  config = lib.mkIf (cfg.keybindings || cfg.fuzzyCompletion) {
+    environment.systemPackages = lib.mkIf (cfg.keybindings || cfg.fuzzyCompletion) [ pkgs.fzf ];
+
+    programs = {
+      bash.interactiveShellInit = lib.optionalString cfg.fuzzyCompletion ''
+        source ${pkgs.fzf}/share/fzf/completion.bash
+      '' + lib.optionalString cfg.keybindings ''
+        source ${pkgs.fzf}/share/fzf/key-bindings.bash
+      '';
 
-    programs.zsh.interactiveShellInit = optionalString (!config.programs.zsh.ohMyZsh.enable)
-      (optionalString cfg.fuzzyCompletion ''
-        source ${pkgs.fzf}/share/fzf/completion.zsh
-      '' + optionalString cfg.keybindings ''
-        source ${pkgs.fzf}/share/fzf/key-bindings.zsh
-      '');
+      zsh = {
+        interactiveShellInit = lib.optionalString (!config.programs.zsh.ohMyZsh.enable)
+        (lib.optionalString cfg.fuzzyCompletion ''
+          source ${pkgs.fzf}/share/fzf/completion.zsh
+        '' + lib.optionalString cfg.keybindings ''
+          source ${pkgs.fzf}/share/fzf/key-bindings.zsh
+        '');
 
-    programs.zsh.ohMyZsh.plugins = lib.mkIf (cfg.keybindings || cfg.fuzzyCompletion) [ "fzf" ];
+        ohMyZsh.plugins = lib.mkIf config.programs.zsh.ohMyZsh.enable [ "fzf" ];
+      };
+    };
   };
-  meta.maintainers = with maintainers; [ laalsaas ];
+
+  meta.maintainers = with lib.maintainers; [ laalsaas ];
 }
diff --git a/nixos/modules/programs/gamescope.nix b/nixos/modules/programs/gamescope.nix
index 594e5be5fd583..33ab16cc87078 100644
--- a/nixos/modules/programs/gamescope.nix
+++ b/nixos/modules/programs/gamescope.nix
@@ -21,7 +21,7 @@ with lib; let
 in
 {
   options.programs.gamescope = {
-    enable = mkEnableOption (mdDoc "gamescope");
+    enable = mkEnableOption (mdDoc "gamescope, the SteamOS session compositing window manager");
 
     package = mkPackageOption pkgs "gamescope" { };
 
diff --git a/nixos/modules/programs/geary.nix b/nixos/modules/programs/geary.nix
index d9454a2247fd6..5aa71434090ed 100644
--- a/nixos/modules/programs/geary.nix
+++ b/nixos/modules/programs/geary.nix
@@ -11,7 +11,7 @@ in {
   };
 
   options = {
-    programs.geary.enable = mkEnableOption (lib.mdDoc "Geary, a Mail client for GNOME 3");
+    programs.geary.enable = mkEnableOption (lib.mdDoc "Geary, a Mail client for GNOME");
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/programs/git.nix b/nixos/modules/programs/git.nix
index 8fb69cbae28fe..3029dd9e1bfd7 100644
--- a/nixos/modules/programs/git.nix
+++ b/nixos/modules/programs/git.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     programs.git = {
-      enable = mkEnableOption (lib.mdDoc "git");
+      enable = mkEnableOption (lib.mdDoc "git, a distributed version control system");
 
       package = mkPackageOption pkgs "git" {
         example = "gitFull";
@@ -59,7 +59,7 @@ in
       };
 
       lfs = {
-        enable = mkEnableOption (lib.mdDoc "git-lfs");
+        enable = mkEnableOption (lib.mdDoc "git-lfs (Large File Storage)");
 
         package = mkPackageOption pkgs "git-lfs" { };
       };
diff --git a/nixos/modules/programs/goldwarden.nix b/nixos/modules/programs/goldwarden.nix
new file mode 100644
index 0000000000000..26f9a87c1986f
--- /dev/null
+++ b/nixos/modules/programs/goldwarden.nix
@@ -0,0 +1,50 @@
+{ lib, config, pkgs, ... }:
+let
+  cfg = config.programs.goldwarden;
+in
+{
+  options.programs.goldwarden = {
+    enable = lib.mkEnableOption "Goldwarden";
+    package = lib.mkPackageOption pkgs "goldwarden" {};
+    useSshAgent = lib.mkEnableOption "Goldwarden's SSH Agent" // { default = true; };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [{
+       assertion = cfg.useSshAgent -> !config.programs.ssh.startAgent;
+       message = "Only one ssh-agent can be used at a time.";
+    }];
+
+    environment = {
+      etc = lib.mkIf config.programs.chromium.enable {
+        "chromium/native-messaging-hosts/com.8bit.bitwarden.json".source = "${cfg.package}/etc/chromium/native-messaging-hosts/com.8bit.bitwarden.json";
+        "opt/chrome/native-messaging-hosts/com.8bit.bitwarden.json".source = "${cfg.package}/etc/chrome/native-messaging-hosts/com.8bit.bitwarden.json";
+      };
+
+      extraInit = lib.mkIf cfg.useSshAgent ''
+        if [ -z "$SSH_AUTH_SOCK" -a -n "$HOME" ]; then
+          export SSH_AUTH_SOCK="$HOME/.goldwarden-ssh-agent.sock"
+        fi
+      '';
+
+      systemPackages = [
+        # for cli and polkit action
+        cfg.package
+        # binary exec's into pinentry which should match the DE
+        config.programs.gnupg.agent.pinentryPackage
+      ];
+    };
+
+    programs.firefox.nativeMessagingHosts.packages = [ cfg.package ];
+
+    # see https://github.com/quexten/goldwarden/blob/main/cmd/goldwarden.service
+    systemd.user.services.goldwarden = {
+      description = "Goldwarden daemon";
+      wantedBy = [ "graphical-session.target" ];
+      after = [ "graphical-session.target" ];
+      serviceConfig.ExecStart = "${lib.getExe cfg.package} daemonize";
+      path = [ config.programs.gnupg.agent.pinentryPackage ];
+      unitConfig.ConditionUser = "!@system";
+    };
+  };
+}
diff --git a/nixos/modules/programs/iay.nix b/nixos/modules/programs/iay.nix
index 1fa00e43795ad..a27f2520fd6ac 100644
--- a/nixos/modules/programs/iay.nix
+++ b/nixos/modules/programs/iay.nix
@@ -5,7 +5,7 @@ let
   inherit (lib) mkEnableOption mkIf mkOption mkPackageOption optionalString types;
 in {
   options.programs.iay = {
-    enable = mkEnableOption (lib.mdDoc "iay");
+    enable = mkEnableOption (lib.mdDoc "iay, a minimalistic shell prompt");
     package = mkPackageOption pkgs "iay" {};
 
     minimalPrompt = mkOption {
diff --git a/nixos/modules/programs/kubeswitch.nix b/nixos/modules/programs/kubeswitch.nix
new file mode 100644
index 0000000000000..ba2d25fbeb455
--- /dev/null
+++ b/nixos/modules/programs/kubeswitch.nix
@@ -0,0 +1,56 @@
+{
+  config,
+  pkgs,
+  lib,
+  ...
+}:
+let
+  cfg = config.programs.kubeswitch;
+in
+{
+  options = {
+    programs.kubeswitch = {
+      enable = lib.mkEnableOption (lib.mdDoc "kubeswitch");
+
+      commandName = lib.mkOption {
+        type = lib.types.str;
+        default = "kswitch";
+        description = "The name of the command to use";
+      };
+
+      package = lib.mkOption {
+        type = lib.types.package;
+        default = pkgs.kubeswitch;
+        defaultText = lib.literalExpression "pkgs.kubeswitch";
+        description = "The package to install for kubeswitch";
+      };
+    };
+  };
+
+  config =
+    let
+      shell_files = pkgs.stdenv.mkDerivation rec {
+        name = "kubeswitch-shell-files";
+        phases = [ "installPhase" ];
+        installPhase = ''
+          mkdir -p $out/share
+          for shell in bash zsh; do
+            ${cfg.package}/bin/switcher init $shell | sed 's/switch(/${cfg.commandName}(/' > $out/share/${cfg.commandName}_init.$shell
+            ${cfg.package}/bin/switcher --cmd ${cfg.commandName} completion $shell > $out/share/${cfg.commandName}_completion.$shell
+          done
+        '';
+      };
+    in
+    lib.mkIf cfg.enable {
+      environment.systemPackages = [ cfg.package ];
+
+      programs.bash.interactiveShellInit = ''
+        source ${shell_files}/share/${cfg.commandName}_init.bash
+        source ${shell_files}/share/${cfg.commandName}_completion.bash
+      '';
+      programs.zsh.interactiveShellInit = ''
+        source ${shell_files}/share/${cfg.commandName}_init.zsh
+        source ${shell_files}/share/${cfg.commandName}_completion.zsh
+      '';
+    };
+}
diff --git a/nixos/modules/programs/lazygit.nix b/nixos/modules/programs/lazygit.nix
new file mode 100644
index 0000000000000..3e36a0e0c4a8f
--- /dev/null
+++ b/nixos/modules/programs/lazygit.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.programs.lazygit;
+
+  settingsFormat = pkgs.formats.yaml { };
+in
+{
+  options.programs.lazygit = {
+    enable = lib.mkEnableOption "lazygit, a simple terminal UI for git commands";
+
+    package = lib.mkPackageOption pkgs "lazygit" { };
+
+    settings = lib.mkOption {
+      inherit (settingsFormat) type;
+      default = { };
+      description = ''
+        Lazygit configuration.
+
+        See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md for documentation.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment = {
+      systemPackages = [ cfg.package ];
+      etc = lib.mkIf (cfg.settings != { }) {
+        "xdg/lazygit/config.yml".source = settingsFormat.generate "lazygit-config.yml" cfg.settings;
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ linsui ];
+  };
+}
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
index 81c68307aee14..6aa08d136a113 100644
--- a/nixos/modules/programs/less.nix
+++ b/nixos/modules/programs/less.nix
@@ -35,7 +35,7 @@ in
 
       # note that environment.nix sets PAGER=less, and
       # therefore also enables this module
-      enable = mkEnableOption (lib.mdDoc "less");
+      enable = mkEnableOption (lib.mdDoc "less, a file pager");
 
       configFile = mkOption {
         type = types.nullOr types.path;
diff --git a/nixos/modules/programs/mepo.nix b/nixos/modules/programs/mepo.nix
index 4b1706a2a0e53..92ce6e5fa578a 100644
--- a/nixos/modules/programs/mepo.nix
+++ b/nixos/modules/programs/mepo.nix
@@ -5,7 +5,7 @@ let
 in
 {
   options.programs.mepo = {
-    enable = mkEnableOption (mdDoc "Mepo");
+    enable = mkEnableOption (mdDoc "Mepo, a fast, simple and hackable OSM map viewer");
 
     locationBackends = {
       gpsd = mkOption {
diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix
index 3568736854d8e..128a9cd45320c 100644
--- a/nixos/modules/programs/mininet.nix
+++ b/nixos/modules/programs/mininet.nix
@@ -8,7 +8,7 @@ let
   cfg = config.programs.mininet;
 in
 {
-  options.programs.mininet.enable = mkEnableOption (lib.mdDoc "Mininet");
+  options.programs.mininet.enable = mkEnableOption (lib.mdDoc "Mininet, an emulator for rapid prototyping of Software Defined Networks");
 
   config = mkIf cfg.enable {
 
diff --git a/nixos/modules/programs/minipro.nix b/nixos/modules/programs/minipro.nix
index 8cb64866a84cc..56920656f23df 100644
--- a/nixos/modules/programs/minipro.nix
+++ b/nixos/modules/programs/minipro.nix
@@ -8,7 +8,7 @@ in
     programs.minipro = {
       enable = lib.mkEnableOption (lib.mdDoc "minipro") // {
         description = lib.mdDoc ''
-          Installs minipro and its udev rules.
+          Whether to enable minipro and its udev rules.
           Users of the `plugdev` group can interact with connected MiniPRO chip programmers.
         '';
       };
diff --git a/nixos/modules/programs/miriway.nix b/nixos/modules/programs/miriway.nix
index e8a10770b6a34..010ab984fc1b0 100644
--- a/nixos/modules/programs/miriway.nix
+++ b/nixos/modules/programs/miriway.nix
@@ -71,7 +71,7 @@ in {
     programs.xwayland.enable = lib.mkDefault true;
 
     # To make the Miriway session available if a display manager like SDDM is enabled:
-    services.xserver.displayManager.sessionPackages = [ pkgs.miriway ];
+    services.displayManager.sessionPackages = [ pkgs.miriway ];
   };
 
   meta.maintainers = with lib.maintainers; [ OPNA2608 ];
diff --git a/nixos/modules/programs/nano.nix b/nixos/modules/programs/nano.nix
index 461681b598631..225fa4f2568de 100644
--- a/nixos/modules/programs/nano.nix
+++ b/nixos/modules/programs/nano.nix
@@ -7,7 +7,7 @@ in
 {
   options = {
     programs.nano = {
-      enable = lib.mkEnableOption (lib.mdDoc "nano") // {
+      enable = lib.mkEnableOption (lib.mdDoc "nano, a small user-friendly console text editor") // {
         default = true;
       };
 
diff --git a/nixos/modules/programs/nix-ld.nix b/nixos/modules/programs/nix-ld.nix
index 6f36ce33640cd..b095437733cce 100644
--- a/nixos/modules/programs/nix-ld.nix
+++ b/nixos/modules/programs/nix-ld.nix
@@ -3,7 +3,7 @@ let
   cfg = config.programs.nix-ld;
 
   nix-ld-libraries = pkgs.buildEnv {
-    name = "lb-library-path";
+    name = "ld-library-path";
     pathsToLink = [ "/lib" ];
     paths = map lib.getLib cfg.libraries;
     # TODO make glibc here configurable?
@@ -13,25 +13,6 @@ let
     extraPrefix = "/share/nix-ld";
     ignoreCollisions = true;
   };
-
-  # We currently take all libraries from systemd and nix as the default.
-  # Is there a better list?
-  baseLibraries = with pkgs; [
-    zlib
-    zstd
-    stdenv.cc.cc
-    curl
-    openssl
-    attr
-    libssh
-    bzip2
-    libxml2
-    acl
-    libsodium
-    util-linux
-    xz
-    systemd
-  ];
 in
 {
   meta.maintainers = [ lib.maintainers.mic92 ];
@@ -41,7 +22,7 @@ in
     libraries = lib.mkOption {
       type = lib.types.listOf lib.types.package;
       description = lib.mdDoc "Libraries that automatically become available to all programs. The default set includes common libraries.";
-      default = baseLibraries;
+      default = [ ];
       defaultText = lib.literalExpression "baseLibraries derived from systemd and nix dependencies.";
     };
   };
@@ -57,5 +38,24 @@ in
       NIX_LD = "/run/current-system/sw/share/nix-ld/lib/ld.so";
       NIX_LD_LIBRARY_PATH = "/run/current-system/sw/share/nix-ld/lib";
     };
+
+    # We currently take all libraries from systemd and nix as the default.
+    # Is there a better list?
+    programs.nix-ld.libraries = with pkgs; [
+      zlib
+      zstd
+      stdenv.cc.cc
+      curl
+      openssl
+      attr
+      libssh
+      bzip2
+      libxml2
+      acl
+      libsodium
+      util-linux
+      xz
+      systemd
+    ];
   };
 }
diff --git a/nixos/modules/programs/nm-applet.nix b/nixos/modules/programs/nm-applet.nix
index 4b09b1884d7e6..3ff27cd742771 100644
--- a/nixos/modules/programs/nm-applet.nix
+++ b/nixos/modules/programs/nm-applet.nix
@@ -6,7 +6,7 @@
   };
 
   options.programs.nm-applet = {
-    enable = lib.mkEnableOption (lib.mdDoc "nm-applet");
+    enable = lib.mkEnableOption (lib.mdDoc "nm-applet, a NetworkManager control applet for GNOME");
 
     indicator = lib.mkOption {
       type = lib.types.bool;
diff --git a/nixos/modules/programs/noisetorch.nix b/nixos/modules/programs/noisetorch.nix
index d8135877d02f2..84bc78ce3839d 100644
--- a/nixos/modules/programs/noisetorch.nix
+++ b/nixos/modules/programs/noisetorch.nix
@@ -6,7 +6,7 @@ let cfg = config.programs.noisetorch;
 in
 {
   options.programs.noisetorch = {
-    enable = mkEnableOption (lib.mdDoc "noisetorch + setcap wrapper");
+    enable = mkEnableOption (lib.mdDoc "noisetorch (+ setcap wrapper), a virtual microphone device with noise suppression");
 
     package = mkPackageOption pkgs "noisetorch" { };
   };
diff --git a/nixos/modules/programs/oddjobd.nix b/nixos/modules/programs/oddjobd.nix
index 08bb8b2684731..019ca58a6048e 100644
--- a/nixos/modules/programs/oddjobd.nix
+++ b/nixos/modules/programs/oddjobd.nix
@@ -4,26 +4,28 @@ let
   cfg = config.programs.oddjobd;
 in
 {
-  options.programs.oddjobd = {
-    enable = lib.mkEnableOption "oddjob";
-    package = lib.mkPackageOption pkgs "oddjob" {};
+  options = {
+    programs.oddjobd = {
+      enable = lib.mkEnableOption "oddjob, a D-Bus service which runs odd jobs on behalf of client applications";
+      package = lib.mkPackageOption pkgs "oddjob" {};
+    };
   };
 
   config = lib.mkIf cfg.enable {
-    systemd.packages = [ cfg.package ];
-
     systemd.services.oddjobd = {
-      wantedBy = [ "multi-user.target"];
-      after = [ "network.target"];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "dbus.service" ];
       description = "DBUS Odd-job Daemon";
       enable = true;
       documentation = [ "man:oddjobd(8)" "man:oddjobd.conf(5)" ];
       serviceConfig = {
-        Type = "dbus";
-        BusName = "org.freedesktop.oddjob";
-        ExecStart = "${lib.getBin cfg.package}/bin/oddjobd";
+        Type = "simple";
+        PIDFile = "/run/oddjobd.pid";
+        ExecStart = "${lib.getBin cfg.package}/bin/oddjobd -n -p /run/oddjobd.pid -t 300";
       };
     };
+
+    services.dbus.packages = [ cfg.package ];
   };
 
   meta.maintainers = with lib.maintainers; [ SohamG ];
diff --git a/nixos/modules/programs/partition-manager.nix b/nixos/modules/programs/partition-manager.nix
index cf0491ff028fc..63b637f9952b8 100644
--- a/nixos/modules/programs/partition-manager.nix
+++ b/nixos/modules/programs/partition-manager.nix
@@ -1,19 +1,21 @@
 { config, lib, pkgs, ... }:
 
-with lib;
+let
+  cfg = config.programs.partition-manager;
+in {
+  meta.maintainers = [ lib.maintainers.oxalica ];
 
-{
-  meta.maintainers = [ maintainers.oxalica ];
-
-  ###### interface
   options = {
-    programs.partition-manager.enable = mkEnableOption (lib.mdDoc "KDE Partition Manager");
+    programs.partition-manager = {
+      enable = lib.mkEnableOption (lib.mdDoc "KDE Partition Manager");
+
+      package = lib.mkPackageOption pkgs [ "libsForQt5" "partitionmanager" ] { };
+    };
   };
 
-  ###### implementation
-  config = mkIf config.programs.partition-manager.enable {
-    services.dbus.packages = [ pkgs.libsForQt5.kpmcore ];
+  config = lib.mkIf config.programs.partition-manager.enable {
+    services.dbus.packages = [ cfg.package.kpmcore ];
     # `kpmcore` need to be installed to pull in polkit actions.
-    environment.systemPackages = [ pkgs.libsForQt5.kpmcore pkgs.libsForQt5.partitionmanager ];
+    environment.systemPackages = [ cfg.package.kpmcore cfg.package ];
   };
 }
diff --git a/nixos/modules/programs/plotinus.md b/nixos/modules/programs/plotinus.md
index fac3bbad1e085..0a2c688c722c3 100644
--- a/nixos/modules/programs/plotinus.md
+++ b/nixos/modules/programs/plotinus.md
@@ -12,6 +12,8 @@ palette provides a searchable list of of all menu items in the application.
 
 To enable Plotinus, add the following to your
 {file}`configuration.nix`:
-```
-programs.plotinus.enable = true;
+```nix
+{
+  programs.plotinus.enable = true;
+}
 ```
diff --git a/nixos/modules/programs/projecteur.nix b/nixos/modules/programs/projecteur.nix
index 140de0209e680..8f7985a3750d2 100644
--- a/nixos/modules/programs/projecteur.nix
+++ b/nixos/modules/programs/projecteur.nix
@@ -5,7 +5,7 @@ let
 in
 {
   options.programs.projecteur = {
-    enable = lib.mkEnableOption (lib.mdDoc "projecteur");
+    enable = lib.mkEnableOption (lib.mdDoc "projecteur, an application for the Logitech Spotlight device (and similar)");
     package = lib.mkPackageOption pkgs "projecteur" { };
   };
 
diff --git a/nixos/modules/programs/proxychains.nix b/nixos/modules/programs/proxychains.nix
index acd41f3552448..db693cd83b8f2 100644
--- a/nixos/modules/programs/proxychains.nix
+++ b/nixos/modules/programs/proxychains.nix
@@ -49,7 +49,7 @@ in {
 
     programs.proxychains = {
 
-      enable = mkEnableOption (lib.mdDoc "installing proxychains configuration");
+      enable = mkEnableOption (lib.mdDoc "proxychains configuration");
 
       package = mkPackageOption pkgs "proxychains" {
         example = "proxychains-ng";
diff --git a/nixos/modules/programs/rust-motd.nix b/nixos/modules/programs/rust-motd.nix
index 4c9b1018596b7..49f834596b67e 100644
--- a/nixos/modules/programs/rust-motd.nix
+++ b/nixos/modules/programs/rust-motd.nix
@@ -24,7 +24,7 @@ let
     '';
 in {
   options.programs.rust-motd = {
-    enable = mkEnableOption (lib.mdDoc "rust-motd");
+    enable = mkEnableOption (lib.mdDoc "rust-motd, a Message Of The Day (MOTD) generator");
     enableMotdInSSHD = mkOption {
       default = true;
       type = types.bool;
diff --git a/nixos/modules/programs/ryzen-monitor-ng.nix b/nixos/modules/programs/ryzen-monitor-ng.nix
new file mode 100644
index 0000000000000..cb0c391ce6b15
--- /dev/null
+++ b/nixos/modules/programs/ryzen-monitor-ng.nix
@@ -0,0 +1,35 @@
+{ pkgs
+, config
+, lib
+, ...
+}:
+let
+  inherit (lib) mkEnableOption mkPackageOption mkIf;
+  cfg = config.programs.ryzen-monitor-ng;
+in
+{
+  options = {
+    programs.ryzen-monitor-ng = {
+      enable =  mkEnableOption ''
+        ryzen_monitor_ng, a userspace application for setting and getting Ryzen SMU (System Management Unit) parameters via the ryzen_smu kernel driver.
+
+        Monitor power information of Ryzen processors via the PM table of the SMU.
+
+        SMU Set and Get for many parameters and CO counts.
+
+        https://github.com/mann1x/ryzen_monitor_ng
+
+        WARNING: Damage cause by use of your AMD processor outside of official AMD specifications or outside of factory settings are not covered under any AMD product warranty and may not be covered by your board or system manufacturer's warranty
+      '';
+
+      package = mkPackageOption pkgs "ryzen-monitor-ng" {};
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+    hardware.cpu.amd.ryzen-smu.enable = true;
+  };
+
+  meta.maintainers = with lib.maintainers; [ Cryolitia phdyellow ];
+}
diff --git a/nixos/modules/programs/sedutil.nix b/nixos/modules/programs/sedutil.nix
index d5e20a8815d44..0ef2472667cbd 100644
--- a/nixos/modules/programs/sedutil.nix
+++ b/nixos/modules/programs/sedutil.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.sedutil;
 
 in {
-  options.programs.sedutil.enable = mkEnableOption (lib.mdDoc "sedutil");
+  options.programs.sedutil.enable = mkEnableOption (lib.mdDoc "sedutil, to manage self encrypting drives that conform to the Trusted Computing Group OPAL 2.0 SSC specification");
 
   config = mkIf cfg.enable {
     boot.kernelParams = [
diff --git a/nixos/modules/programs/sniffnet.nix b/nixos/modules/programs/sniffnet.nix
index 98e9f628a9bce..5f6f2042d3fe5 100644
--- a/nixos/modules/programs/sniffnet.nix
+++ b/nixos/modules/programs/sniffnet.nix
@@ -7,7 +7,7 @@ in
 {
   options = {
     programs.sniffnet = {
-      enable = lib.mkEnableOption (lib.mdDoc "sniffnet");
+      enable = lib.mkEnableOption (lib.mdDoc "sniffnet, a network traffic monitor application");
     };
   };
 
diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix
index 34f6f0882c617..7f8d9eb3363d7 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 31803f061dce2..0fa6b90cfff2a 100644
--- a/nixos/modules/programs/steam.nix
+++ b/nixos/modules/programs/steam.nix
@@ -44,8 +44,10 @@ 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;
+        }) // (optionalAttrs cfg.extest.enable {
+          LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
+        }) // (prev.extraEnv or {});
         extraLibraries = pkgs: let
           prevLibs = if prev ? extraLibraries then prev.extraLibraries pkgs else [ ];
           additionalLibs = with config.hardware.opengl;
@@ -59,8 +61,6 @@ in {
           # use the setuid wrapped bubblewrap
           bubblewrap = "${config.security.wrapperDir}/..";
         };
-      } // optionalAttrs cfg.extest.enable {
-        extraEnv.LD_PRELOAD = "${pkgs.pkgsi686Linux.extest}/lib/libextest.so";
       });
       description = lib.mdDoc ''
         The Steam package to use. Additional libraries are added from the system
@@ -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.
       '';
     };
 
@@ -154,7 +161,7 @@ in {
     };
 
     programs.gamescope.enable = mkDefault cfg.gamescopeSession.enable;
-    services.xserver.displayManager.sessionPackages = mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
+    services.displayManager.sessionPackages = mkIf cfg.gamescopeSession.enable [ gamescopeSessionFile ];
 
     # optionally enable 32bit pulseaudio support if pulseaudio is enabled
     hardware.pulseaudio.support32Bit = config.hardware.pulseaudio.enable;
diff --git a/nixos/modules/programs/sysdig.nix b/nixos/modules/programs/sysdig.nix
index ccb1e1d4c5f18..19cd6f26bae45 100644
--- a/nixos/modules/programs/sysdig.nix
+++ b/nixos/modules/programs/sysdig.nix
@@ -5,7 +5,7 @@ with lib;
 let
   cfg = config.programs.sysdig;
 in {
-  options.programs.sysdig.enable = mkEnableOption (lib.mdDoc "sysdig");
+  options.programs.sysdig.enable = mkEnableOption (lib.mdDoc "sysdig, a tracing tool");
 
   config = mkIf cfg.enable {
     environment.systemPackages = [ pkgs.sysdig ];
diff --git a/nixos/modules/programs/thefuck.nix b/nixos/modules/programs/thefuck.nix
index e057d1ca657de..60b341638a0f8 100644
--- a/nixos/modules/programs/thefuck.nix
+++ b/nixos/modules/programs/thefuck.nix
@@ -16,7 +16,7 @@ in
   {
     options = {
       programs.thefuck = {
-        enable = mkEnableOption (lib.mdDoc "thefuck");
+        enable = mkEnableOption (lib.mdDoc "thefuck, an app which corrects your previous console command");
 
         alias = mkOption {
           default = "fuck";
diff --git a/nixos/modules/programs/trippy.nix b/nixos/modules/programs/trippy.nix
index 6e31aea43e750..8c82358acff43 100644
--- a/nixos/modules/programs/trippy.nix
+++ b/nixos/modules/programs/trippy.nix
@@ -7,7 +7,7 @@ in
 {
   options = {
     programs.trippy = {
-      enable = lib.mkEnableOption (lib.mdDoc "trippy");
+      enable = lib.mkEnableOption (lib.mdDoc "trippy, a network diagnostic tool");
     };
   };
 
diff --git a/nixos/modules/programs/udevil.nix b/nixos/modules/programs/udevil.nix
index b0f00b4b541b0..5ec84d41de841 100644
--- a/nixos/modules/programs/udevil.nix
+++ b/nixos/modules/programs/udevil.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.udevil;
 
 in {
-  options.programs.udevil.enable = mkEnableOption (lib.mdDoc "udevil");
+  options.programs.udevil.enable = mkEnableOption (lib.mdDoc "udevil, to mount filesystems without password");
 
   config = mkIf cfg.enable {
     security.wrappers.udevil =
diff --git a/nixos/modules/programs/usbtop.nix b/nixos/modules/programs/usbtop.nix
index e262ae3745bea..0b59d141bcd60 100644
--- a/nixos/modules/programs/usbtop.nix
+++ b/nixos/modules/programs/usbtop.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.usbtop;
 in {
   options = {
-    programs.usbtop.enable = mkEnableOption (lib.mdDoc "usbtop and required kernel module");
+    programs.usbtop.enable = mkEnableOption (lib.mdDoc "usbtop and required kernel module, to show estimated USB bandwidth");
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/programs/wayland/cardboard.nix b/nixos/modules/programs/wayland/cardboard.nix
index 77a094a717005..0039961927378 100644
--- a/nixos/modules/programs/wayland/cardboard.nix
+++ b/nixos/modules/programs/wayland/cardboard.nix
@@ -17,7 +17,7 @@ in
       environment.systemPackages = [ cfg.package ];
 
       # To make a cardboard session available for certain DMs like SDDM
-      services.xserver.displayManager.sessionPackages = [ cfg.package ];
+      services.displayManager.sessionPackages = [ cfg.package ];
     }
     (import ./wayland-session.nix { inherit lib pkgs; })
   ]);
diff --git a/nixos/modules/programs/wayland/hyprland.nix b/nixos/modules/programs/wayland/hyprland.nix
index 9061ce5da83a8..ccc701c547c06 100644
--- a/nixos/modules/programs/wayland/hyprland.nix
+++ b/nixos/modules/programs/wayland/hyprland.nix
@@ -13,8 +13,8 @@ in
 {
   options.programs.hyprland = {
     enable = mkEnableOption null // {
-      description = mdDoc ''
-        Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
+      description = ''
+        Whether to enable Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
 
         You can manually launch Hyprland by executing {command}`Hyprland` on a TTY.
 
@@ -33,14 +33,24 @@ in
       };
       defaultText = literalExpression
         "`programs.hyprland.package` with applied configuration";
-      description = mdDoc ''
+      description = ''
         The Hyprland package after applying configuration.
       '';
     };
 
     portalPackage = mkPackageOption pkgs "xdg-desktop-portal-hyprland" { };
 
-    xwayland.enable = mkEnableOption (mdDoc "XWayland") // { default = true; };
+    xwayland.enable = mkEnableOption ("XWayland") // { default = true; };
+
+    systemd.setPath.enable = mkEnableOption null // {
+      default = true;
+      example = false;
+      description = ''
+        Set environment path of systemd to include the current system's bin directory.
+        This is needed in Hyprland setups, where opening links in applications do not work.
+        Enabled by default.
+      '';
+    };
   };
 
   config = mkIf cfg.enable {
@@ -56,13 +66,19 @@ in
 
     security.polkit.enable = true;
 
-    services.xserver.displayManager.sessionPackages = [ cfg.finalPackage ];
+    services.displayManager.sessionPackages = [ cfg.finalPackage ];
 
     xdg.portal = {
       enable = mkDefault true;
       extraPortals = [ finalPortalPackage ];
       configPackages = mkDefault [ cfg.finalPackage ];
     };
+
+    systemd = mkIf cfg.systemd.setPath.enable {
+      user.extraConfig = ''
+        DefaultEnvironment="PATH=$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/$USER/bin:/run/wrappers/bin"
+      '';
+    };
   };
 
   imports = with lib; [
diff --git a/nixos/modules/programs/wayland/labwc.nix b/nixos/modules/programs/wayland/labwc.nix
index d0806c3aa5d0e..7f5f54f031eab 100644
--- a/nixos/modules/programs/wayland/labwc.nix
+++ b/nixos/modules/programs/wayland/labwc.nix
@@ -18,7 +18,7 @@ in
       xdg.portal.config.wlroots.default = lib.mkDefault [ "wlr" "gtk" ];
 
       # To make a labwc session available for certain DMs like SDDM
-      services.xserver.displayManager.sessionPackages = [ cfg.package ];
+      services.displayManager.sessionPackages = [ cfg.package ];
     }
     (import ./wayland-session.nix { inherit lib pkgs; })
   ]);
diff --git a/nixos/modules/programs/wayland/river.nix b/nixos/modules/programs/wayland/river.nix
index 995129b9710ae..133dda539f018 100644
--- a/nixos/modules/programs/wayland/river.nix
+++ b/nixos/modules/programs/wayland/river.nix
@@ -47,7 +47,7 @@ in {
         environment.systemPackages = optional (cfg.package != null) cfg.package ++ cfg.extraPackages;
 
         # To make a river session available if a display manager like SDDM is enabled:
-        services.xserver.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ];
+        services.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ];
 
         # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
         xdg.portal.config.river.default = mkDefault [ "wlr" "gtk" ];
diff --git a/nixos/modules/programs/wayland/sway.nix b/nixos/modules/programs/wayland/sway.nix
index 2bd297af52544..08ae162b23b48 100644
--- a/nixos/modules/programs/wayland/sway.nix
+++ b/nixos/modules/programs/wayland/sway.nix
@@ -174,7 +174,7 @@ in {
         xdg.portal.config.sway.default = mkDefault [ "wlr" "gtk" ];
 
         # To make a Sway session available if a display manager like SDDM is enabled:
-        services.xserver.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ]; }
+        services.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ]; }
       (import ./wayland-session.nix { inherit lib pkgs; })
     ]);
 
diff --git a/nixos/modules/programs/wayland/waybar.nix b/nixos/modules/programs/wayland/waybar.nix
index ec60b84f69976..f4bd865b221aa 100644
--- a/nixos/modules/programs/wayland/waybar.nix
+++ b/nixos/modules/programs/wayland/waybar.nix
@@ -7,7 +7,7 @@ let
 in
 {
   options.programs.waybar = {
-    enable = mkEnableOption (lib.mdDoc "waybar");
+    enable = mkEnableOption (lib.mdDoc "waybar, a highly customizable Wayland bar for Sway and Wlroots based compositors");
     package = mkPackageOption pkgs "waybar" { };
   };
 
diff --git a/nixos/modules/programs/wayland/wayfire.nix b/nixos/modules/programs/wayland/wayfire.nix
index 0840246e5e3ef..6f6550edc5a08 100644
--- a/nixos/modules/programs/wayland/wayfire.nix
+++ b/nixos/modules/programs/wayland/wayfire.nix
@@ -38,7 +38,7 @@ in
       finalPackage
     ];
 
-    services.xserver.displayManager.sessionPackages = [ finalPackage ];
+    services.displayManager.sessionPackages = [ finalPackage ];
 
     xdg.portal = {
       enable = lib.mkDefault true;
diff --git a/nixos/modules/programs/weylus.nix b/nixos/modules/programs/weylus.nix
index f40dfd5c96135..5c69a6658fae6 100644
--- a/nixos/modules/programs/weylus.nix
+++ b/nixos/modules/programs/weylus.nix
@@ -7,7 +7,7 @@ let
 in
 {
   options.programs.weylus = with types; {
-    enable = mkEnableOption (lib.mdDoc "weylus");
+    enable = mkEnableOption (lib.mdDoc "weylus, which turns your smart phone into a graphic tablet/touch screen for your computer");
 
     openFirewall = mkOption {
       type = bool;
diff --git a/nixos/modules/programs/xss-lock.nix b/nixos/modules/programs/xss-lock.nix
index 87b3957ab834f..2ff45de93b6da 100644
--- a/nixos/modules/programs/xss-lock.nix
+++ b/nixos/modules/programs/xss-lock.nix
@@ -40,6 +40,7 @@ in
             "--"
             cfg.lockerCommand
         ]);
+      serviceConfig.Restart = "always";
     };
   };
 }
diff --git a/nixos/modules/programs/yabar.nix b/nixos/modules/programs/yabar.nix
index 58ffe555715d1..c6223a1c4d92a 100644
--- a/nixos/modules/programs/yabar.nix
+++ b/nixos/modules/programs/yabar.nix
@@ -41,7 +41,7 @@ let
 in
   {
     options.programs.yabar = {
-      enable = mkEnableOption (lib.mdDoc "yabar");
+      enable = mkEnableOption (lib.mdDoc "yabar, a status bar for X window managers");
 
       package = mkOption {
         default = pkgs.yabar-unstable;
diff --git a/nixos/modules/programs/zmap.nix b/nixos/modules/programs/zmap.nix
index 056f788830611..2eb05b97da3d4 100644
--- a/nixos/modules/programs/zmap.nix
+++ b/nixos/modules/programs/zmap.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.zmap;
 in {
   options.programs.zmap = {
-    enable = mkEnableOption (lib.mdDoc "ZMap");
+    enable = mkEnableOption (lib.mdDoc "ZMap, a network scanner designed for Internet-wide network surveys");
   };
 
   config = mkIf cfg.enable {
diff --git a/nixos/modules/programs/zsh/oh-my-zsh.md b/nixos/modules/programs/zsh/oh-my-zsh.md
index 6a310006edbfc..7e4a41641eeaa 100644
--- a/nixos/modules/programs/zsh/oh-my-zsh.md
+++ b/nixos/modules/programs/zsh/oh-my-zsh.md
@@ -9,7 +9,7 @@ prompt themes.
 The module uses the `oh-my-zsh` package with all available
 features. The initial setup using Nix expressions is fairly similar to the
 configuration format of `oh-my-zsh`.
-```
+```nix
 {
   programs.zsh.ohMyZsh = {
     enable = true;
@@ -33,7 +33,7 @@ environment variable for this which points to a directory with additional
 scripts.
 
 The module can do this as well:
-```
+```nix
 {
   programs.zsh.ohMyZsh.custom = "~/path/to/custom/scripts";
 }
@@ -48,7 +48,7 @@ which bundles completion scripts and a plugin for `oh-my-zsh`.
 
 Rather than using a single mutable path for `ZSH_CUSTOM`,
 it's also possible to generate this path from a list of Nix packages:
-```
+```nix
 { pkgs, ... }:
 {
   programs.zsh.ohMyZsh.customPkgs = [
@@ -89,7 +89,7 @@ If third-party customizations (e.g. new themes) are supposed to be added to
     [upstream repo.](https://github.com/robbyrussell/oh-my-zsh/tree/91b771914bc7c43dd7c7a43b586c5de2c225ceb7/plugins)
 
 A derivation for `oh-my-zsh` may look like this:
-```
+```nix
 { stdenv, fetchFromGitHub }:
 
 stdenv.mkDerivation rec {
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 0a975fcd98c8c..01985995a651d 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -62,6 +62,7 @@ in
     (mkRemovedOptionModule [ "services" "fourStoreEndpoint" ] "The fourStoreEndpoint module has been removed")
     (mkRemovedOptionModule [ "services" "fprot" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "frab" ] "The frab module has been removed")
+    (mkRemovedOptionModule [ "services" "homeassistant-satellite"] "The `services.homeassistant-satellite` module has been replaced by `services.wyoming-satellite`.")
     (mkRemovedOptionModule [ "services" "ihatemoney" ] "The ihatemoney module has been removed for lack of downstream maintainer")
     (mkRemovedOptionModule [ "services" "kippo" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "mailpile" ] "The corresponding package was removed from nixpkgs.")
@@ -92,7 +93,7 @@ in
       The services.xserver.displayManager.auto module has been removed
       because it was only intended for use in internal NixOS tests, and gave the
       false impression of it being a special display manager when it's actually
-      LightDM. Please use the services.xserver.displayManager.autoLogin options
+      LightDM. Please use the services.displayManager.autoLogin options
       instead, or any other display manager in NixOS as they all support auto-login.
     '')
     (mkRemovedOptionModule [ "services" "xserver" "multitouch" ] ''
diff --git a/nixos/modules/security/acme/default.md b/nixos/modules/security/acme/default.md
index 38fbfbf0caece..a6ef2a3fdf18c 100644
--- a/nixos/modules/security/acme/default.md
+++ b/nixos/modules/security/acme/default.md
@@ -46,33 +46,35 @@ certs are overwritten when the ACME certs arrive. For
 `foo.example.com` the config would look like this:
 
 ```nix
-security.acme.acceptTerms = true;
-security.acme.defaults.email = "admin+acme@example.com";
-services.nginx = {
-  enable = true;
-  virtualHosts = {
-    "foo.example.com" = {
-      forceSSL = true;
-      enableACME = true;
-      # All serverAliases will be added as extra domain names on the certificate.
-      serverAliases = [ "bar.example.com" ];
-      locations."/" = {
-        root = "/var/www";
+{
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = "admin+acme@example.com";
+  services.nginx = {
+    enable = true;
+    virtualHosts = {
+      "foo.example.com" = {
+        forceSSL = true;
+        enableACME = true;
+        # All serverAliases will be added as extra domain names on the certificate.
+        serverAliases = [ "bar.example.com" ];
+        locations."/" = {
+          root = "/var/www";
+        };
       };
-    };
 
-    # We can also add a different vhost and reuse the same certificate
-    # but we have to append extraDomainNames manually beforehand:
-    # security.acme.certs."foo.example.com".extraDomainNames = [ "baz.example.com" ];
-    "baz.example.com" = {
-      forceSSL = true;
-      useACMEHost = "foo.example.com";
-      locations."/" = {
-        root = "/var/www";
+      # We can also add a different vhost and reuse the same certificate
+      # but we have to append extraDomainNames manually beforehand:
+      # security.acme.certs."foo.example.com".extraDomainNames = [ "baz.example.com" ];
+      "baz.example.com" = {
+        forceSSL = true;
+        useACMEHost = "foo.example.com";
+        locations."/" = {
+          root = "/var/www";
+        };
       };
     };
   };
-};
+}
 ```
 
 ## Using ACME certificates in Apache/httpd {#module-security-acme-httpd}
@@ -89,65 +91,69 @@ the intent that you will generate certs for all your vhosts and redirect
 everyone to HTTPS.
 
 ```nix
-security.acme.acceptTerms = true;
-security.acme.defaults.email = "admin+acme@example.com";
-
-# /var/lib/acme/.challenges must be writable by the ACME user
-# and readable by the Nginx user. The easiest way to achieve
-# this is to add the Nginx user to the ACME group.
-users.users.nginx.extraGroups = [ "acme" ];
-
-services.nginx = {
-  enable = true;
-  virtualHosts = {
-    "acmechallenge.example.com" = {
-      # Catchall vhost, will redirect users to HTTPS for all vhosts
-      serverAliases = [ "*.example.com" ];
-      locations."/.well-known/acme-challenge" = {
-        root = "/var/lib/acme/.challenges";
-      };
-      locations."/" = {
-        return = "301 https://$host$request_uri";
+{
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = "admin+acme@example.com";
+
+  # /var/lib/acme/.challenges must be writable by the ACME user
+  # and readable by the Nginx user. The easiest way to achieve
+  # this is to add the Nginx user to the ACME group.
+  users.users.nginx.extraGroups = [ "acme" ];
+
+  services.nginx = {
+    enable = true;
+    virtualHosts = {
+      "acmechallenge.example.com" = {
+        # Catchall vhost, will redirect users to HTTPS for all vhosts
+        serverAliases = [ "*.example.com" ];
+        locations."/.well-known/acme-challenge" = {
+          root = "/var/lib/acme/.challenges";
+        };
+        locations."/" = {
+          return = "301 https://$host$request_uri";
+        };
       };
     };
   };
-};
-# Alternative config for Apache
-users.users.wwwrun.extraGroups = [ "acme" ];
-services.httpd = {
-  enable = true;
-  virtualHosts = {
-    "acmechallenge.example.com" = {
-      # Catchall vhost, will redirect users to HTTPS for all vhosts
-      serverAliases = [ "*.example.com" ];
-      # /var/lib/acme/.challenges must be writable by the ACME user and readable by the Apache user.
-      # By default, this is the case.
-      documentRoot = "/var/lib/acme/.challenges";
-      extraConfig = ''
-        RewriteEngine On
-        RewriteCond %{HTTPS} off
-        RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge [NC]
-        RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301]
-      '';
+  # Alternative config for Apache
+  users.users.wwwrun.extraGroups = [ "acme" ];
+  services.httpd = {
+    enable = true;
+    virtualHosts = {
+      "acmechallenge.example.com" = {
+        # Catchall vhost, will redirect users to HTTPS for all vhosts
+        serverAliases = [ "*.example.com" ];
+        # /var/lib/acme/.challenges must be writable by the ACME user and readable by the Apache user.
+        # By default, this is the case.
+        documentRoot = "/var/lib/acme/.challenges";
+        extraConfig = ''
+          RewriteEngine On
+          RewriteCond %{HTTPS} off
+          RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge [NC]
+          RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301]
+        '';
+      };
     };
   };
-};
+}
 ```
 
 Now you need to configure ACME to generate a certificate.
 
 ```nix
-security.acme.certs."foo.example.com" = {
-  webroot = "/var/lib/acme/.challenges";
-  email = "foo@example.com";
-  # Ensure that the web server you use can read the generated certs
-  # Take a look at the group option for the web server you choose.
-  group = "nginx";
-  # Since we have a wildcard vhost to handle port 80,
-  # we can generate certs for anything!
-  # Just make sure your DNS resolves them.
-  extraDomainNames = [ "mail.example.com" ];
-};
+{
+  security.acme.certs."foo.example.com" = {
+    webroot = "/var/lib/acme/.challenges";
+    email = "foo@example.com";
+    # Ensure that the web server you use can read the generated certs
+    # Take a look at the group option for the web server you choose.
+    group = "nginx";
+    # Since we have a wildcard vhost to handle port 80,
+    # we can generate certs for anything!
+    # Just make sure your DNS resolves them.
+    extraDomainNames = [ "mail.example.com" ];
+  };
+}
 ```
 
 The private key {file}`key.pem` and certificate
@@ -168,31 +174,33 @@ for provider/server specific configuration values. For the sake of these
 docs, we will provide a fully self-hosted example using bind.
 
 ```nix
-services.bind = {
-  enable = true;
-  extraConfig = ''
-    include "/var/lib/secrets/dnskeys.conf";
-  '';
-  zones = [
-    rec {
-      name = "example.com";
-      file = "/var/db/bind/${name}";
-      master = true;
-      extraConfig = "allow-update { key rfc2136key.example.com.; };";
-    }
-  ];
-};
-
-# Now we can configure ACME
-security.acme.acceptTerms = true;
-security.acme.defaults.email = "admin+acme@example.com";
-security.acme.certs."example.com" = {
-  domain = "*.example.com";
-  dnsProvider = "rfc2136";
-  environmentFile = "/var/lib/secrets/certs.secret";
-  # We don't need to wait for propagation since this is a local DNS server
-  dnsPropagationCheck = false;
-};
+{
+  services.bind = {
+    enable = true;
+    extraConfig = ''
+      include "/var/lib/secrets/dnskeys.conf";
+    '';
+    zones = [
+      rec {
+        name = "example.com";
+        file = "/var/db/bind/${name}";
+        master = true;
+        extraConfig = "allow-update { key rfc2136key.example.com.; };";
+      }
+    ];
+  };
+
+  # Now we can configure ACME
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = "admin+acme@example.com";
+  security.acme.certs."example.com" = {
+    domain = "*.example.com";
+    dnsProvider = "rfc2136";
+    environmentFile = "/var/lib/secrets/certs.secret";
+    # We don't need to wait for propagation since this is a local DNS server
+    dnsPropagationCheck = false;
+  };
+}
 ```
 
 The {file}`dnskeys.conf` and {file}`certs.secret`
@@ -200,36 +208,38 @@ must be kept secure and thus you should not keep their contents in your
 Nix config. Instead, generate them one time with a systemd service:
 
 ```nix
-systemd.services.dns-rfc2136-conf = {
-  requiredBy = ["acme-example.com.service" "bind.service"];
-  before = ["acme-example.com.service" "bind.service"];
-  unitConfig = {
-    ConditionPathExists = "!/var/lib/secrets/dnskeys.conf";
-  };
-  serviceConfig = {
-    Type = "oneshot";
-    UMask = 0077;
+{
+  systemd.services.dns-rfc2136-conf = {
+    requiredBy = ["acme-example.com.service" "bind.service"];
+    before = ["acme-example.com.service" "bind.service"];
+    unitConfig = {
+      ConditionPathExists = "!/var/lib/secrets/dnskeys.conf";
+    };
+    serviceConfig = {
+      Type = "oneshot";
+      UMask = 0077;
+    };
+    path = [ pkgs.bind ];
+    script = ''
+      mkdir -p /var/lib/secrets
+      chmod 755 /var/lib/secrets
+      tsig-keygen rfc2136key.example.com > /var/lib/secrets/dnskeys.conf
+      chown named:root /var/lib/secrets/dnskeys.conf
+      chmod 400 /var/lib/secrets/dnskeys.conf
+
+      # extract secret value from the dnskeys.conf
+      while read x y; do if [ "$x" = "secret" ]; then secret="''${y:1:''${#y}-3}"; fi; done < /var/lib/secrets/dnskeys.conf
+
+      cat > /var/lib/secrets/certs.secret << EOF
+      RFC2136_NAMESERVER='127.0.0.1:53'
+      RFC2136_TSIG_ALGORITHM='hmac-sha256.'
+      RFC2136_TSIG_KEY='rfc2136key.example.com'
+      RFC2136_TSIG_SECRET='$secret'
+      EOF
+      chmod 400 /var/lib/secrets/certs.secret
+    '';
   };
-  path = [ pkgs.bind ];
-  script = ''
-    mkdir -p /var/lib/secrets
-    chmod 755 /var/lib/secrets
-    tsig-keygen rfc2136key.example.com > /var/lib/secrets/dnskeys.conf
-    chown named:root /var/lib/secrets/dnskeys.conf
-    chmod 400 /var/lib/secrets/dnskeys.conf
-
-    # extract secret value from the dnskeys.conf
-    while read x y; do if [ "$x" = "secret" ]; then secret="''${y:1:''${#y}-3}"; fi; done < /var/lib/secrets/dnskeys.conf
-
-    cat > /var/lib/secrets/certs.secret << EOF
-    RFC2136_NAMESERVER='127.0.0.1:53'
-    RFC2136_TSIG_ALGORITHM='hmac-sha256.'
-    RFC2136_TSIG_KEY='rfc2136key.example.com'
-    RFC2136_TSIG_SECRET='$secret'
-    EOF
-    chmod 400 /var/lib/secrets/certs.secret
-  '';
-};
+}
 ```
 
 Now you're all set to generate certs! You should monitor the first invocation
@@ -251,27 +261,29 @@ you will set them as defaults
 (e.g. [](#opt-security.acme.defaults.dnsProvider)).
 
 ```nix
-# Configure ACME appropriately
-security.acme.acceptTerms = true;
-security.acme.defaults.email = "admin+acme@example.com";
-security.acme.defaults = {
-  dnsProvider = "rfc2136";
-  environmentFile = "/var/lib/secrets/certs.secret";
-  # We don't need to wait for propagation since this is a local DNS server
-  dnsPropagationCheck = false;
-};
-
-# For each virtual host you would like to use DNS-01 validation with,
-# set acmeRoot = null
-services.nginx = {
-  enable = true;
-  virtualHosts = {
-    "foo.example.com" = {
-      enableACME = true;
-      acmeRoot = null;
+{
+  # Configure ACME appropriately
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = "admin+acme@example.com";
+  security.acme.defaults = {
+    dnsProvider = "rfc2136";
+    environmentFile = "/var/lib/secrets/certs.secret";
+    # We don't need to wait for propagation since this is a local DNS server
+    dnsPropagationCheck = false;
+  };
+
+  # For each virtual host you would like to use DNS-01 validation with,
+  # set acmeRoot = null
+  services.nginx = {
+    enable = true;
+    virtualHosts = {
+      "foo.example.com" = {
+        enableACME = true;
+        acmeRoot = null;
+      };
     };
   };
-};
+}
 ```
 
 And that's it! Next time your configuration is rebuilt, or when
@@ -288,39 +300,41 @@ Below is an example configuration for OpenSMTPD, but this pattern
 can be applied to any service.
 
 ```nix
-# Configure ACME however you like (DNS or HTTP validation), adding
-# the following configuration for the relevant certificate.
-# Note: You cannot use `systemctl reload` here as that would mean
-# the LoadCredential configuration below would be skipped and
-# the service would continue to use old certificates.
-security.acme.certs."mail.example.com".postRun = ''
-  systemctl restart opensmtpd
-'';
-
-# Now you must augment OpenSMTPD's systemd service to load
-# the certificate files.
-systemd.services.opensmtpd.requires = ["acme-finished-mail.example.com.target"];
-systemd.services.opensmtpd.serviceConfig.LoadCredential = let
-  certDir = config.security.acme.certs."mail.example.com".directory;
-in [
-  "cert.pem:${certDir}/cert.pem"
-  "key.pem:${certDir}/key.pem"
-];
-
-# Finally, configure OpenSMTPD to use these certs.
-services.opensmtpd = let
-  credsDir = "/run/credentials/opensmtpd.service";
-in {
-  enable = true;
-  setSendmail = false;
-  serverConfiguration = ''
-    pki mail.example.com cert "${credsDir}/cert.pem"
-    pki mail.example.com key "${credsDir}/key.pem"
-    listen on localhost tls pki mail.example.com
-    action act1 relay host smtp://127.0.0.1:10027
-    match for local action act1
+{
+  # Configure ACME however you like (DNS or HTTP validation), adding
+  # the following configuration for the relevant certificate.
+  # Note: You cannot use `systemctl reload` here as that would mean
+  # the LoadCredential configuration below would be skipped and
+  # the service would continue to use old certificates.
+  security.acme.certs."mail.example.com".postRun = ''
+    systemctl restart opensmtpd
   '';
-};
+
+  # Now you must augment OpenSMTPD's systemd service to load
+  # the certificate files.
+  systemd.services.opensmtpd.requires = ["acme-finished-mail.example.com.target"];
+  systemd.services.opensmtpd.serviceConfig.LoadCredential = let
+    certDir = config.security.acme.certs."mail.example.com".directory;
+  in [
+    "cert.pem:${certDir}/cert.pem"
+    "key.pem:${certDir}/key.pem"
+  ];
+
+  # Finally, configure OpenSMTPD to use these certs.
+  services.opensmtpd = let
+    credsDir = "/run/credentials/opensmtpd.service";
+  in {
+    enable = true;
+    setSendmail = false;
+    serverConfiguration = ''
+      pki mail.example.com cert "${credsDir}/cert.pem"
+      pki mail.example.com key "${credsDir}/key.pem"
+      listen on localhost tls pki mail.example.com
+      action act1 relay host smtp://127.0.0.1:10027
+      match for local action act1
+    '';
+  };
+}
 ```
 
 ## Regenerating certificates {#module-security-acme-regenerate}
diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
index ae188ea709dd5..fda9e35891c7a 100644
--- a/nixos/modules/security/ca.nix
+++ b/nixos/modules/security/ca.nix
@@ -19,7 +19,7 @@ in
 {
 
   options = {
-    security.pki.installCACerts = mkEnableOption "Add CA certificates to system" // {
+    security.pki.installCACerts = mkEnableOption "installing CA certificates to the system" // {
       default = true;
       internal = true;
     };
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 560e5eff5c39a..382ebf2c53b2d 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
@@ -1430,12 +1430,12 @@ in
 
     security.pam.enableEcryptfs = mkEnableOption (lib.mdDoc "eCryptfs PAM module (mounting ecryptfs home directory on login)");
     security.pam.enableFscrypt = mkEnableOption (lib.mdDoc ''
-      fscrypt to automatically unlock directories with the user's login password.
+      fscrypt, to automatically unlock directories with the user's login password.
 
       This also enables a service at security.pam.services.fscrypt which is used by
       fscrypt to verify the user's password when setting up a new protector. If you
       use something other than pam_unix to verify user passwords, please remember to
-      adjust this PAM service.
+      adjust this PAM service
     '');
 
     users.motd = mkOption {
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index 6aa9445eab65e..e2e0a981ca922 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -163,9 +163,9 @@ in
                 };
 
                 options = mkOption {
-                  type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" ]);
+                  type = with types; listOf (enum [ "NOPASSWD" "PASSWD" "NOEXEC" "EXEC" "SETENV" "NOSETENV" "LOG_INPUT" "NOLOG_INPUT" "LOG_OUTPUT" "NOLOG_OUTPUT" "MAIL" "NOMAIL" "FOLLOW" "NOFLLOW" "INTERCEPT" "NOINTERCEPT"]);
                   description = mdDoc ''
-                    Options for running the command. Refer to the [sudo manual](https://www.sudo.ws/man/1.7.10/sudoers.man.html).
+                    Options for running the command. Refer to the [sudo manual](https://www.sudo.ws/docs/man/1.9.15/sudoers.man/#Tag_Spec).
                   '';
                   default = [];
                 };
diff --git a/nixos/modules/services/admin/docuum.nix b/nixos/modules/services/admin/docuum.nix
new file mode 100644
index 0000000000000..6f6cd4e027337
--- /dev/null
+++ b/nixos/modules/services/admin/docuum.nix
@@ -0,0 +1,45 @@
+{ config, pkgs, lib, utils, ... }:
+
+let
+  cfg = config.services.docuum;
+  inherit (lib) mkIf mkEnableOption mkOption getExe types;
+in
+{
+  options.services.docuum = {
+    enable = mkEnableOption "docuum daemon";
+
+    threshold = mkOption {
+      description = "Threshold for deletion in bytes, like `10 GB`, `10 GiB`, `10GB` or percentage-based thresholds like `50%`";
+      type = types.str;
+      default = "10 GB";
+      example = "50%";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = config.virtualisation.docker.enable;
+        message = "docuum requires docker on the host";
+      }
+    ];
+
+    systemd.services.docuum = {
+      after = [ "docker.socket" ];
+      requires = [ "docker.socket" ];
+      wantedBy = [ "multi-user.target" ];
+      path = [ config.virtualisation.docker.package ];
+      environment.HOME = "/var/lib/docuum";
+
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = "docuum";
+        SupplementaryGroups = [ "docker" ];
+        ExecStart = utils.escapeSystemdExecArgs [
+          (getExe pkgs.docuum)
+          "--threshold" cfg.threshold
+        ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/admin/salt/master.nix b/nixos/modules/services/admin/salt/master.nix
index 4346022970e12..e10a10efae652 100644
--- a/nixos/modules/services/admin/salt/master.nix
+++ b/nixos/modules/services/admin/salt/master.nix
@@ -20,7 +20,7 @@ in
 {
   options = {
     services.salt.master = {
-      enable = mkEnableOption (lib.mdDoc "Salt master service");
+      enable = mkEnableOption (lib.mdDoc "Salt configuration management system master service");
       configuration = mkOption {
         type = types.attrs;
         default = {};
diff --git a/nixos/modules/services/admin/salt/minion.nix b/nixos/modules/services/admin/salt/minion.nix
index 3ae02a4cc5d5d..28b52e07c40bd 100644
--- a/nixos/modules/services/admin/salt/minion.nix
+++ b/nixos/modules/services/admin/salt/minion.nix
@@ -21,7 +21,7 @@ in
 {
   options = {
     services.salt.minion = {
-      enable = mkEnableOption (lib.mdDoc "Salt minion service");
+      enable = mkEnableOption (lib.mdDoc "Salt configuration management system minion service");
       configuration = mkOption {
         type = types.attrs;
         default = {};
diff --git a/nixos/modules/services/audio/mpdscribble.nix b/nixos/modules/services/audio/mpdscribble.nix
index 132d9ad325886..e58fcdd25f63c 100644
--- a/nixos/modules/services/audio/mpdscribble.nix
+++ b/nixos/modules/services/audio/mpdscribble.nix
@@ -77,7 +77,7 @@ in {
 
   options.services.mpdscribble = {
 
-    enable = mkEnableOption (lib.mdDoc "mpdscribble");
+    enable = mkEnableOption (lib.mdDoc "mpdscribble, an MPD client which submits info about tracks being played to Last.fm (formerly AudioScrobbler)");
 
     proxy = mkOption {
       default = null;
diff --git a/nixos/modules/services/audio/roon-server.nix b/nixos/modules/services/audio/roon-server.nix
index 8691c08b0d365..8a6cf6ec6a419 100644
--- a/nixos/modules/services/audio/roon-server.nix
+++ b/nixos/modules/services/audio/roon-server.nix
@@ -9,6 +9,7 @@ in {
   options = {
     services.roon-server = {
       enable = mkEnableOption (lib.mdDoc "Roon Server");
+      package = lib.mkPackageOption pkgs "roon-server" { };
       openFirewall = mkOption {
         type = types.bool;
         default = false;
@@ -43,7 +44,7 @@ in {
       environment.ROON_ID_DIR = "/var/lib/${name}";
 
       serviceConfig = {
-        ExecStart = "${pkgs.roon-server}/bin/RoonServer";
+        ExecStart = "${lib.getExe cfg.package}";
         LimitNOFILE = 8192;
         User = cfg.user;
         Group = cfg.group;
diff --git a/nixos/modules/services/backup/borgbackup.md b/nixos/modules/services/backup/borgbackup.md
index 39141f6ec8587..2c91174732e1f 100644
--- a/nixos/modules/services/backup/borgbackup.md
+++ b/nixos/modules/services/backup/borgbackup.md
@@ -21,22 +21,21 @@ A complete list of options for the Borgbase module may be found
 ## Basic usage for a local backup {#opt-services-backup-borgbackup-local-directory}
 
 A very basic configuration for backing up to a locally accessible directory is:
-```
+```nix
 {
     opt.services.borgbackup.jobs = {
-      { rootBackup = {
-          paths = "/";
-          exclude = [ "/nix" "/path/to/local/repo" ];
-          repo = "/path/to/local/repo";
-          doInit = true;
-          encryption = {
-            mode = "repokey";
-            passphrase = "secret";
-          };
-          compression = "auto,lzma";
-          startAt = "weekly";
+      rootBackup = {
+        paths = "/";
+        exclude = [ "/nix" "/path/to/local/repo" ];
+        repo = "/path/to/local/repo";
+        doInit = true;
+        encryption = {
+          mode = "repokey";
+          passphrase = "secret";
         };
-      }
+        compression = "auto,lzma";
+        startAt = "weekly";
+      };
     };
 }
 ```
@@ -59,7 +58,7 @@ ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID78zmOyA+5uPG4Ot0hfAy+sLDPU1L4AiIoRYEIVbbQ/
 ```
 
 Add the following snippet to your NixOS configuration:
-```
+```nix
 {
   services.borgbackup.repos = {
     my_borg_repo = {
@@ -80,7 +79,7 @@ that you have stored a secret passphrasse in the file
 {file}`/run/keys/borgbackup_passphrase`, which should be only
 accessible by root
 
-```
+```nix
 {
   services.borgbackup.jobs = {
     backupToLocalServer = {
@@ -96,7 +95,7 @@ accessible by root
       startAt = "hourly";
     };
   };
-};
+}
 ```
 
 The following few commands (run as root) let you test your backup.
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
index 6f4455d3be605..7d5714499f3a9 100644
--- a/nixos/modules/services/backup/borgbackup.nix
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -123,6 +123,7 @@ let
       };
       # if remote-backup wait for network
       after = optional (cfg.persistentTimer && !isLocalPath cfg.repo) "network-online.target";
+      wants = optional (cfg.persistentTimer && !isLocalPath cfg.repo) "network-online.target";
     };
 
   # utility function around makeWrapper
@@ -147,6 +148,9 @@ let
     let
       settings = { inherit (cfg) user group; };
     in lib.nameValuePair "borgbackup-job-${name}" ({
+      # Create parent dirs separately, to ensure correct ownership.
+      "${config.users.users."${cfg.user}".home}/.config".d = settings;
+      "${config.users.users."${cfg.user}".home}/.cache".d = settings;
       "${config.users.users."${cfg.user}".home}/.config/borg".d = settings;
       "${config.users.users."${cfg.user}".home}/.cache/borg".d = settings;
     } // optionalAttrs (isLocalPath cfg.repo && !cfg.removableDevice) {
diff --git a/nixos/modules/services/backup/restic-rest-server.nix b/nixos/modules/services/backup/restic-rest-server.nix
index 105a05caf3048..c9d5a37116a13 100644
--- a/nixos/modules/services/backup/restic-rest-server.nix
+++ b/nixos/modules/services/backup/restic-rest-server.nix
@@ -12,7 +12,7 @@ in
     enable = mkEnableOption (lib.mdDoc "Restic REST Server");
 
     listenAddress = mkOption {
-      default = ":8000";
+      default = "8000";
       example = "127.0.0.1:8080";
       type = types.str;
       description = lib.mdDoc "Listen on a specific IP address and port.";
@@ -61,14 +61,19 @@ in
   };
 
   config = mkIf cfg.enable {
+    assertions = [{
+      assertion = lib.substring 0 1 cfg.listenAddress != ":";
+      message = "The restic-rest-server now uses systemd socket activation, which expects only the Port number: services.restic.server.listenAddress = \"${lib.substring 1 6 cfg.listenAddress}\";";
+    }];
+
     systemd.services.restic-rest-server = {
       description = "Restic REST Server";
-      after = [ "network.target" ];
+      after = [ "network.target" "restic-rest-server.socket" ];
+      requires = [ "restic-rest-server.socket" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         ExecStart = ''
           ${cfg.package}/bin/rest-server \
-          --listen ${cfg.listenAddress} \
           --path ${cfg.dataDir} \
           ${optionalString cfg.appendOnly "--append-only"} \
           ${optionalString cfg.privateRepos "--private-repos"} \
@@ -80,16 +85,40 @@ in
         Group = "restic";
 
         # Security hardening
-        ReadWritePaths = [ cfg.dataDir ];
+        CapabilityBoundingSet = "";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateNetwork = true;
         PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectProc = "invisible";
         ProtectSystem = "strict";
         ProtectKernelTunables = true;
         ProtectKernelModules = true;
         ProtectControlGroups = true;
         PrivateDevices = true;
+        ReadWritePaths = [ cfg.dataDir ];
+        RemoveIPC = true;
+        RestrictAddressFamilies = "none";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = "@system-service";
+        UMask = 027;
       };
     };
 
+    systemd.sockets.restic-rest-server = {
+      listenStreams = [ cfg.listenAddress ];
+      wantedBy = [ "sockets.target" ];
+    };
+
     systemd.tmpfiles.rules = mkIf cfg.privateRepos [
         "f ${cfg.dataDir}/.htpasswd 0700 restic restic -"
     ];
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index 2ebe8ad2f69ae..f1f7433a39cdd 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -465,5 +465,5 @@ in
     };
   };
 
-  meta.maintainers = with maintainers; [ infinisil SlothOfAnarchy ];
+  meta.maintainers = with maintainers; [ SlothOfAnarchy ];
 }
diff --git a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix
index 06f0da3451a6c..fa26ab3ecd22c 100644
--- a/nixos/modules/services/continuous-integration/gitea-actions-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitea-actions-runner.nix
@@ -236,7 +236,8 @@ in
                   --instance ${escapeShellArg instance.url} \
                   --token "$TOKEN" \
                   --name ${escapeShellArg instance.name} \
-                  --labels ${escapeShellArg (concatStringsSep "," instance.labels)}
+                  --labels ${escapeShellArg (concatStringsSep "," instance.labels)} \
+                  --config ${configFile}
 
                 # and write back the configured labels
                 echo "$LABELS_WANTED" > "$LABELS_FILE"
diff --git a/nixos/modules/services/databases/foundationdb.md b/nixos/modules/services/databases/foundationdb.md
index 0815c139152f3..9f7addc9c140c 100644
--- a/nixos/modules/services/databases/foundationdb.md
+++ b/nixos/modules/services/databases/foundationdb.md
@@ -15,9 +15,11 @@ key-value store.
 
 To enable FoundationDB, add the following to your
 {file}`configuration.nix`:
-```
-services.foundationdb.enable = true;
-services.foundationdb.package = pkgs.foundationdb71; # FoundationDB 7.1.x
+```nix
+{
+  services.foundationdb.enable = true;
+  services.foundationdb.package = pkgs.foundationdb71; # FoundationDB 7.1.x
+}
 ```
 
 The {option}`services.foundationdb.package` option is required, and
@@ -109,8 +111,10 @@ default configuration. See below for more on scaling to increase this.
 FoundationDB stores all data for all server processes under
 {file}`/var/lib/foundationdb`. You can override this using
 {option}`services.foundationdb.dataDir`, e.g.
-```
-services.foundationdb.dataDir = "/data/fdb";
+```nix
+{
+  services.foundationdb.dataDir = "/data/fdb";
+}
 ```
 
 Similarly, logs are stored under {file}`/var/log/foundationdb`
@@ -265,8 +269,10 @@ directories.
 For example, to create backups in {command}`/opt/fdb-backups`, first
 set up the paths in the module options:
 
-```
-services.foundationdb.extraReadWritePaths = [ "/opt/fdb-backups" ];
+```nix
+{
+  services.foundationdb.extraReadWritePaths = [ "/opt/fdb-backups" ];
+}
 ```
 
 Restart the FoundationDB service, and it will now be able to write to this
diff --git a/nixos/modules/services/databases/hbase-standalone.nix b/nixos/modules/services/databases/hbase-standalone.nix
index 08ae7625d50ab..de295a57193f7 100644
--- a/nixos/modules/services/databases/hbase-standalone.nix
+++ b/nixos/modules/services/databases/hbase-standalone.nix
@@ -43,7 +43,7 @@ in {
 
       enable = mkEnableOption (lib.mdDoc ''
         HBase master in standalone mode with embedded regionserver and zookeper.
-        Do not use this configuration for production nor for evaluating HBase performance.
+        Do not use this configuration for production nor for evaluating HBase performance
       '');
 
       package = mkPackageOption pkgs "hbase" { };
diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix
index 033de7af886f2..75ce1ca54f87b 100644
--- a/nixos/modules/services/databases/lldap.nix
+++ b/nixos/modules/services/databases/lldap.nix
@@ -6,7 +6,7 @@ let
 in
 {
   options.services.lldap = with lib; {
-    enable = mkEnableOption (mdDoc "lldap");
+    enable = mkEnableOption (mdDoc "lldap, a lightweight authentication server that provides an opinionated, simplified LDAP interface for authentication");
 
     package = mkPackageOption pkgs "lldap" { };
 
diff --git a/nixos/modules/services/databases/postgresql.md b/nixos/modules/services/databases/postgresql.md
index 7d141f12b5dea..8a587832cd8c0 100644
--- a/nixos/modules/services/databases/postgresql.md
+++ b/nixos/modules/services/databases/postgresql.md
@@ -15,9 +15,11 @@ PostgreSQL is an advanced, free relational database.
 ## Configuring {#module-services-postgres-configuring}
 
 To enable PostgreSQL, add the following to your {file}`configuration.nix`:
-```
-services.postgresql.enable = true;
-services.postgresql.package = pkgs.postgresql_15;
+```nix
+{
+  services.postgresql.enable = true;
+  services.postgresql.package = pkgs.postgresql_15;
+}
 ```
 Note that you are required to specify the desired version of PostgreSQL (e.g. `pkgs.postgresql_15`). Since upgrading your PostgreSQL version requires a database dump and reload (see below), NixOS cannot provide a default value for [](#opt-services.postgresql.package) such as the most recent release of PostgreSQL.
 
@@ -35,8 +37,10 @@ alice=>
 -->
 
 By default, PostgreSQL stores its databases in {file}`/var/lib/postgresql/$psqlSchema`. You can override this using [](#opt-services.postgresql.dataDir), e.g.
-```
-services.postgresql.dataDir = "/data/postgresql";
+```nix
+{
+  services.postgresql.dataDir = "/data/postgresql";
+}
 ```
 
 ## Initializing {#module-services-postgres-initializing}
@@ -95,23 +99,26 @@ databases from `ensureDatabases` and `extraUser1` from `ensureUsers`
 are already created.
 
 ```nix
+  {
     systemd.services.postgresql.postStart = lib.mkAfter ''
       $PSQL service1 -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
       $PSQL service1 -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"'
       # ....
     '';
+  }
 ```
 
 ##### in intermediate oneshot service {#module-services-postgres-initializing-extra-permissions-superuser-oneshot}
 
 ```nix
+  {
     systemd.services."migrate-service1-db1" = {
       serviceConfig.Type = "oneshot";
       requiredBy = "service1.service";
       before = "service1.service";
       after = "postgresql.service";
       serviceConfig.User = "postgres";
-      environment.PSQL = "psql --port=${toString services.postgresql.port}";
+      environment.PSQL = "psql --port=${toString services.postgresql.settings.port}";
       path = [ postgresql ];
       script = ''
         $PSQL service1 -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
@@ -119,6 +126,7 @@ are already created.
         # ....
       '';
     };
+  }
 ```
 
 #### as service user {#module-services-postgres-initializing-extra-permissions-service-user}
@@ -130,25 +138,28 @@ are already created.
 ##### in service `preStart` {#module-services-postgres-initializing-extra-permissions-service-user-pre-start}
 
 ```nix
-    environment.PSQL = "psql --port=${toString services.postgresql.port}";
+  {
+    environment.PSQL = "psql --port=${toString services.postgresql.settings.port}";
     path = [ postgresql ];
     systemd.services."service1".preStart = ''
       $PSQL -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
       $PSQL -c 'GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO "extraUser1"'
       # ....
     '';
+  }
 ```
 
 ##### in intermediate oneshot service {#module-services-postgres-initializing-extra-permissions-service-user-oneshot}
 
 ```nix
+  {
     systemd.services."migrate-service1-db1" = {
       serviceConfig.Type = "oneshot";
       requiredBy = "service1.service";
       before = "service1.service";
       after = "postgresql.service";
       serviceConfig.User = "service1";
-      environment.PSQL = "psql --port=${toString services.postgresql.port}";
+      environment.PSQL = "psql --port=${toString services.postgresql.settings.port}";
       path = [ postgresql ];
       script = ''
         $PSQL -c 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "extraUser1"'
@@ -156,6 +167,7 @@ are already created.
         # ....
       '';
     };
+  }
 ```
 
 ## Upgrading {#module-services-postgres-upgrading}
@@ -174,7 +186,7 @@ $ nix-instantiate --eval -A postgresql_13.psqlSchema
 "13"
 ```
 For an upgrade, a script like this can be used to simplify the process:
-```
+```nix
 { config, pkgs, ... }:
 {
   environment.systemPackages = [
@@ -256,16 +268,18 @@ postgresql_15.pkgs.pg_partman        postgresql_15.pkgs.pgroonga
 ```
 
 To add plugins via NixOS configuration, set `services.postgresql.extraPlugins`:
-```
-services.postgresql.package = pkgs.postgresql_12;
-services.postgresql.extraPlugins = ps: with ps; [
-  pg_repack
-  postgis
-];
+```nix
+{
+  services.postgresql.package = pkgs.postgresql_12;
+  services.postgresql.extraPlugins = ps: with ps; [
+    pg_repack
+    postgis
+  ];
+}
 ```
 
 You can build custom PostgreSQL-with-plugins (to be used outside of NixOS) using function `.withPackages`. For example, creating a custom PostgreSQL package in an overlay can look like:
-```
+```nix
 self: super: {
   postgresql_custom = self.postgresql_12.withPackages (ps: [
     ps.pg_repack
@@ -275,9 +289,9 @@ self: super: {
 ```
 
 Here's a recipe on how to override a particular plugin through an overlay:
-```
+```nix
 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 c4e76c82ba5c7..d3fd6db6aea15 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
@@ -27,7 +27,7 @@ let
     else toString value;
 
   # The main PostgreSQL configuration file.
-  configFile = pkgs.writeTextDir "postgresql.conf" (concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${toStr v}") cfg.settings));
+  configFile = pkgs.writeTextDir "postgresql.conf" (concatStringsSep "\n" (mapAttrsToList (n: v: "${n} = ${toStr v}") (filterAttrs (const (x: x != null)) cfg.settings)));
 
   configFileCheck = pkgs.runCommand "postgresql-configfile-check" {} ''
     ${cfg.package}/bin/postgres -D${configFile} -C config_file >/dev/null
@@ -41,6 +41,9 @@ in
 {
   imports = [
     (mkRemovedOptionModule [ "services" "postgresql" "extraConfig" ] "Use services.postgresql.settings instead.")
+
+    (mkRenamedOptionModule [ "services" "postgresql" "logLinePrefix" ] [ "services" "postgresql" "settings" "log_line_prefix" ])
+    (mkRenamedOptionModule [ "services" "postgresql" "port" ] [ "services" "postgresql" "settings" "port" ])
   ];
 
   ###### interface
@@ -57,14 +60,6 @@ in
         example = "postgresql_15";
       };
 
-      port = mkOption {
-        type = types.port;
-        default = 5432;
-        description = lib.mdDoc ''
-          The port on which PostgreSQL listens.
-        '';
-      };
-
       checkConfig = mkOption {
         type = types.bool;
         default = true;
@@ -352,17 +347,6 @@ in
         '';
       };
 
-      logLinePrefix = mkOption {
-        type = types.str;
-        default = "[%p] ";
-        example = "%m [%p] ";
-        description = lib.mdDoc ''
-          A printf-style string that is output at the beginning of each log line.
-          Upstream default is `'%m [%p] '`, i.e. it includes the timestamp. We do
-          not include the timestamp, because journal has it anyway.
-        '';
-      };
-
       extraPlugins = mkOption {
         type = with types; coercedTo (listOf path) (path: _ignorePg: path) (functionTo (listOf path));
         default = _: [];
@@ -373,7 +357,38 @@ in
       };
 
       settings = mkOption {
-        type = with types; attrsOf (oneOf [ bool float int str ]);
+        type = with types; submodule {
+          freeformType = attrsOf (oneOf [ bool float int str ]);
+          options = {
+            shared_preload_libraries = mkOption {
+              type = nullOr (coercedTo (listOf str) (concatStringsSep ", ") str);
+              default = null;
+              example = literalExpression ''[ "auto_explain" "anon" ]'';
+              description = mdDoc ''
+                List of libraries to be preloaded.
+              '';
+            };
+
+            log_line_prefix = mkOption {
+              type = types.str;
+              default = "[%p] ";
+              example = "%m [%p] ";
+              description = lib.mdDoc ''
+                A printf-style string that is output at the beginning of each log line.
+                Upstream default is `'%m [%p] '`, i.e. it includes the timestamp. We do
+                not include the timestamp, because journal has it anyway.
+              '';
+            };
+
+            port = mkOption {
+              type = types.port;
+              default = 5432;
+              description = lib.mdDoc ''
+                The port on which PostgreSQL listens.
+              '';
+            };
+          };
+        };
         default = {};
         description = lib.mdDoc ''
           PostgreSQL configuration. Refer to
@@ -439,9 +454,7 @@ in
         hba_file = "${pkgs.writeText "pg_hba.conf" cfg.authentication}";
         ident_file = "${pkgs.writeText "pg_ident.conf" cfg.identMap}";
         log_destination = "stderr";
-        log_line_prefix = cfg.logLinePrefix;
         listen_addresses = if cfg.enableTCPIP then "*" else "localhost";
-        port = cfg.port;
         jit = mkDefault (if cfg.enableJIT then "on" else "off");
       };
 
@@ -524,7 +537,7 @@ in
         # Wait for PostgreSQL to be ready to accept connections.
         postStart =
           ''
-            PSQL="psql --port=${toString cfg.port}"
+            PSQL="psql --port=${toString cfg.settings.port}"
 
             while ! $PSQL -d postgres -c "" 2> /dev/null; do
                 if ! kill -0 "$MAINPID"; then exit 1; fi
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index 2e644895a2602..fe2d75fc53a96 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -338,7 +338,7 @@ in {
       after = [ "network.target" ];
 
       serviceConfig = {
-        ExecStart = "${cfg.package}/bin/redis-server /var/lib/${redisName name}/redis.conf ${escapeShellArgs conf.extraParams}";
+        ExecStart = "${cfg.package}/bin/${cfg.package.serverBin or "redis-server"} /var/lib/${redisName name}/redis.conf ${escapeShellArgs conf.extraParams}";
         ExecStartPre = "+"+pkgs.writeShellScript "${redisName name}-prep-conf" (let
           redisConfVar = "/var/lib/${redisName name}/redis.conf";
           redisConfRun = "/run/${redisName name}/nixos.conf";
@@ -391,7 +391,8 @@ in {
         RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
         RestrictNamespaces = true;
         LockPersonality = true;
-        MemoryDenyWriteExecute = true;
+        # we need to disable MemoryDenyWriteExecute for keydb
+        MemoryDenyWriteExecute = cfg.package.pname != "keydb";
         RestrictRealtime = true;
         RestrictSUIDSGID = true;
         PrivateMounts = true;
diff --git a/nixos/modules/services/databases/tigerbeetle.md b/nixos/modules/services/databases/tigerbeetle.md
index 47394d4430598..12d920e7bcc77 100644
--- a/nixos/modules/services/databases/tigerbeetle.md
+++ b/nixos/modules/services/databases/tigerbeetle.md
@@ -7,8 +7,10 @@
 TigerBeetle is a distributed financial accounting database designed for mission critical safety and performance.
 
 To enable TigerBeetle, add the following to your {file}`configuration.nix`:
-```
+```nix
+{
   services.tigerbeetle.enable = true;
+}
 ```
 
 When first started, the TigerBeetle service will create its data file at {file}`/var/lib/tigerbeetle` unless the file already exists, in which case it will just use the existing file.
@@ -20,13 +22,15 @@ By default, TigerBeetle will only listen on a local interface.
 To configure it to listen on a different interface (and to configure it to connect to other replicas, if you're creating more than one), you'll have to set the `addresses` option.
 Note that the TigerBeetle module won't open any firewall ports automatically, so if you configure it to listen on an external interface, you'll need to ensure that connections can reach it:
 
-```
+```nix
+{
   services.tigerbeetle = {
     enable = true;
     addresses = [ "0.0.0.0:3001" ];
   };
 
   networking.firewall.allowedTCPPorts = [ 3001 ];
+}
 ```
 
 A complete list of options for TigerBeetle can be found [here](#opt-services.tigerbeetle.enable).
diff --git a/nixos/modules/services/databases/victoriametrics.nix b/nixos/modules/services/databases/victoriametrics.nix
index 0ad2028c95b08..3d8ada394d20c 100644
--- a/nixos/modules/services/databases/victoriametrics.nix
+++ b/nixos/modules/services/databases/victoriametrics.nix
@@ -2,7 +2,7 @@
 let cfg = config.services.victoriametrics; in
 {
   options.services.victoriametrics = with lib; {
-    enable = mkEnableOption (lib.mdDoc "victoriametrics");
+    enable = mkEnableOption (lib.mdDoc "VictoriaMetrics, a time series database, long-term remote storage for Prometheus");
     package = mkPackageOption pkgs "victoriametrics" { };
     listenAddress = mkOption {
       default = ":8428";
diff --git a/nixos/modules/services/desktop-managers/plasma6.nix b/nixos/modules/services/desktop-managers/plasma6.nix
index 1710d28954d62..bc64397d5d882 100644
--- a/nixos/modules/services/desktop-managers/plasma6.nix
+++ b/nixos/modules/services/desktop-managers/plasma6.nix
@@ -170,6 +170,17 @@ in {
         breeze.qt5
         plasma-integration.qt5
         pkgs.plasma5Packages.kwayland-integration
+        (
+          # Only symlink the KIO plugins, so we don't accidentally pull any services
+          # like KCMs or kcookiejar
+          let
+            kioPluginPath = "${pkgs.plasma5Packages.qtbase.qtPluginPrefix}/kf5/kio";
+            inherit (pkgs.plasma5Packages) kio;
+          in pkgs.runCommand "kio5-plugins-only" {} ''
+            mkdir -p $out/${kioPluginPath}
+            ln -s ${kio}/${kioPluginPath}/* $out/${kioPluginPath}
+          ''
+        )
         kio-extras-kf5
       ]
       # Optional hardware support features
@@ -215,7 +226,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.
@@ -245,13 +256,14 @@ in {
     xdg.portal.configPackages = mkDefault [kdePackages.xdg-desktop-portal-kde];
     services.pipewire.enable = mkDefault true;
 
-    services.xserver.displayManager = {
+    services.displayManager = {
       sessionPackages = [kdePackages.plasma-workspace];
       defaultSession = mkDefault "plasma";
     };
-    services.xserver.displayManager.sddm = {
+    services.displayManager.sddm = {
       package = kdePackages.sddm;
       theme = mkDefault "breeze";
+      wayland.compositor = "kwin";
       extraPackages = with kdePackages; [
         breeze-icons
         kirigami
@@ -284,6 +296,7 @@ in {
     };
 
     programs.kdeconnect.package = kdePackages.kdeconnect-kde;
+    programs.partition-manager.package = kdePackages.partitionmanager;
 
     # FIXME: ugly hack. See #292632 for details.
     system.userActivationScripts.rebuildSycoca = activationScript;
diff --git a/nixos/modules/services/desktops/blueman.nix b/nixos/modules/services/desktops/blueman.nix
index fad2f21bce5bd..f09dd91c9af51 100644
--- a/nixos/modules/services/desktops/blueman.nix
+++ b/nixos/modules/services/desktops/blueman.nix
@@ -9,7 +9,7 @@ in {
   ###### interface
   options = {
     services.blueman = {
-      enable = mkEnableOption (lib.mdDoc "blueman");
+      enable = mkEnableOption (lib.mdDoc "blueman, a bluetooth manager");
     };
   };
 
diff --git a/nixos/modules/services/desktops/deepin/dde-api.nix b/nixos/modules/services/desktops/deepin/dde-api.nix
index 459876febf21f..ece1599f5cc85 100644
--- a/nixos/modules/services/desktops/deepin/dde-api.nix
+++ b/nixos/modules/services/desktops/deepin/dde-api.nix
@@ -15,7 +15,7 @@ with lib;
     services.deepin.dde-api = {
 
       enable = mkEnableOption (lib.mdDoc ''
-        some dbus interfaces that is used for screen zone detecting,
+        the DDE API, which provides some dbus interfaces that is used for screen zone detecting,
         thumbnail generating, and sound playing in Deepin Desktop Environment
       '');
 
diff --git a/nixos/modules/services/desktops/flatpak.md b/nixos/modules/services/desktops/flatpak.md
index af71d85b5a157..5299b32a03c7a 100644
--- a/nixos/modules/services/desktops/flatpak.md
+++ b/nixos/modules/services/desktops/flatpak.md
@@ -8,17 +8,21 @@ Flatpak is a system for building, distributing, and running sandboxed desktop
 applications on Linux.
 
 To enable Flatpak, add the following to your {file}`configuration.nix`:
-```
+```nix
+{
   services.flatpak.enable = true;
+}
 ```
 
 For the sandboxed apps to work correctly, desktop integration portals need to
 be installed. If you run GNOME, this will be handled automatically for you;
 in other cases, you will need to add something like the following to your
 {file}`configuration.nix`:
-```
+```nix
+{
   xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];
   xdg.portal.config.common.default = "gtk";
+}
 ```
 
 Then, you will need to add a repository, for example,
diff --git a/nixos/modules/services/desktops/flatpak.nix b/nixos/modules/services/desktops/flatpak.nix
index 4c26e6874023a..62ef38a3d5548 100644
--- a/nixos/modules/services/desktops/flatpak.nix
+++ b/nixos/modules/services/desktops/flatpak.nix
@@ -32,6 +32,8 @@ in {
 
     security.polkit.enable = true;
 
+    fonts.fontDir.enable = true;
+
     services.dbus.packages = [ pkgs.flatpak ];
 
     systemd.packages = [ pkgs.flatpak ];
diff --git a/nixos/modules/services/desktops/neard.nix b/nixos/modules/services/desktops/neard.nix
index 9130b8d3d216d..846297dfc1ad2 100644
--- a/nixos/modules/services/desktops/neard.nix
+++ b/nixos/modules/services/desktops/neard.nix
@@ -7,7 +7,7 @@ with lib;
   ###### interface
   options = {
     services.neard = {
-      enable = mkEnableOption (lib.mdDoc "neard, NFC daemon");
+      enable = mkEnableOption (lib.mdDoc "neard, an NFC daemon");
     };
   };
 
diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix
index 09448833620c6..5c6eba889ebed 100644
--- a/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -1,14 +1,21 @@
 # PipeWire service.
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
+  inherit (builtins) attrNames concatMap length;
+  inherit (lib) maintainers teams;
+  inherit (lib.attrsets) attrByPath attrsToList concatMapAttrs filterAttrs;
+  inherit (lib.lists) flatten optional optionals;
+  inherit (lib.modules) mkIf mkRemovedOptionModule;
+  inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption;
+  inherit (lib.strings) concatMapStringsSep hasPrefix optionalString;
+  inherit (lib.types) attrsOf bool listOf package;
+
   json = pkgs.formats.json {};
   mapToFiles = location: config: concatMapAttrs (name: value: { "share/pipewire/${location}.conf.d/${name}.conf" = json.generate "${name}" value; }) config;
   extraConfigPkgFromFiles = locations: filesSet: pkgs.runCommand "pipewire-extra-config" { } ''
-    mkdir -p ${lib.concatMapStringsSep " " (l: "$out/share/pipewire/${l}.conf.d") locations}
-    ${lib.concatMapStringsSep ";" ({name, value}: "ln -s ${value} $out/${name}") (lib.attrsToList filesSet)}
+    mkdir -p ${concatMapStringsSep " " (l: "$out/share/pipewire/${l}.conf.d") locations}
+    ${concatMapStringsSep ";" ({name, value}: "ln -s ${value} $out/${name}") (attrsToList filesSet)}
   '';
   cfg = config.services.pipewire;
   enable32BitAlsaPlugins = cfg.alsa.support32Bit
@@ -40,15 +47,15 @@ let
     name = "pipewire-configs";
     paths = configPackages
       ++ [ extraConfigPkg ]
-      ++ lib.optionals cfg.wireplumber.enable cfg.wireplumber.configPackages;
+      ++ optionals cfg.wireplumber.enable cfg.wireplumber.configPackages;
     pathsToLink = [ "/share/pipewire" ];
   };
 
-  requiredLv2Packages = lib.flatten
+  requiredLv2Packages = flatten
     (
-      lib.concatMap
+      concatMap
       (p:
-        lib.attrByPath ["passthru" "requiredLv2Packages"] [] p
+        attrByPath ["passthru" "requiredLv2Packages"] [] p
       )
       configPackages
     );
@@ -59,50 +66,58 @@ let
     pathsToLink = [ "/lib/lv2" ];
   };
 in {
-  meta.maintainers = teams.freedesktop.members ++ [ lib.maintainers.k900 ];
+  meta.maintainers = teams.freedesktop.members ++ [ maintainers.k900 ];
 
   ###### interface
   options = {
     services.pipewire = {
-      enable = mkEnableOption (lib.mdDoc "PipeWire service");
+      enable = mkEnableOption "PipeWire service";
 
       package = mkPackageOption pkgs "pipewire" { };
 
       socketActivation = mkOption {
         default = true;
-        type = types.bool;
-        description = lib.mdDoc ''
+        type = bool;
+        description = ''
           Automatically run PipeWire when connections are made to the PipeWire socket.
         '';
       };
 
       audio = {
-        enable = lib.mkOption {
-          type = lib.types.bool;
+        enable = mkOption {
+          type = bool;
           # this is for backwards compatibility
           default = cfg.alsa.enable || cfg.jack.enable || cfg.pulse.enable;
-          defaultText = lib.literalExpression "config.services.pipewire.alsa.enable || config.services.pipewire.jack.enable || config.services.pipewire.pulse.enable";
-          description = lib.mdDoc "Whether to use PipeWire as the primary sound server";
+          defaultText = literalExpression "config.services.pipewire.alsa.enable || config.services.pipewire.jack.enable || config.services.pipewire.pulse.enable";
+          description = "Whether to use PipeWire as the primary sound server";
         };
       };
 
       alsa = {
-        enable = mkEnableOption (lib.mdDoc "ALSA support");
-        support32Bit = mkEnableOption (lib.mdDoc "32-bit ALSA support on 64-bit systems");
+        enable = mkEnableOption "ALSA support";
+        support32Bit = mkEnableOption "32-bit ALSA support on 64-bit systems";
       };
 
       jack = {
-        enable = mkEnableOption (lib.mdDoc "JACK audio emulation");
+        enable = mkEnableOption "JACK audio emulation";
+      };
+
+      raopOpenFirewall = mkOption {
+        type = bool;
+        default = false;
+        description = ''
+          Opens UDP/6001-6002, required by RAOP/Airplay for timing and control data.
+        '';
       };
 
       pulse = {
-        enable = mkEnableOption (lib.mdDoc "PulseAudio server emulation");
+        enable = mkEnableOption "PulseAudio server emulation";
       };
 
-      systemWide = lib.mkOption {
-        type = lib.types.bool;
+      systemWide = mkOption {
+        type = bool;
         default = false;
-        description = lib.mdDoc ''
+        description = ''
           If true, a system-wide PipeWire service and socket is enabled
           allowing all users in the "pipewire" group to use it simultaneously.
           If false, then user units are used instead, restricting access to
@@ -116,7 +131,7 @@ in {
 
       extraConfig = {
         pipewire = mkOption {
-          type = lib.types.attrsOf json.type;
+          type = attrsOf json.type;
           default = {};
           example = {
             "10-clock-rate" = {
@@ -130,7 +145,7 @@ in {
               };
             };
           };
-          description = lib.mdDoc ''
+          description = ''
             Additional configuration for the PipeWire server.
 
             Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/pipewire.conf.d`.
@@ -149,7 +164,7 @@ in {
           '';
         };
         client = mkOption {
-          type = lib.types.attrsOf json.type;
+          type = attrsOf json.type;
           default = {};
           example = {
             "10-no-resample" = {
@@ -158,7 +173,7 @@ in {
               };
             };
           };
-          description = lib.mdDoc ''
+          description = ''
             Additional configuration for the PipeWire client library, used by most applications.
 
             Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/client.conf.d`.
@@ -169,7 +184,7 @@ in {
           '';
         };
         client-rt = mkOption {
-          type = lib.types.attrsOf json.type;
+          type = attrsOf json.type;
           default = {};
           example = {
             "10-alsa-linear-volume" = {
@@ -178,7 +193,7 @@ in {
               };
             };
           };
-          description = lib.mdDoc ''
+          description = ''
             Additional configuration for the PipeWire client library, used by real-time applications and legacy ALSA clients.
 
             Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/client-rt.conf.d`.
@@ -190,7 +205,7 @@ in {
           '';
         };
         jack = mkOption {
-          type = lib.types.attrsOf json.type;
+          type = attrsOf json.type;
           default = {};
           example = {
             "20-hide-midi" = {
@@ -199,7 +214,7 @@ in {
               };
             };
           };
-          description = lib.mdDoc ''
+          description = ''
             Additional configuration for the PipeWire JACK server and client library.
 
             Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/jack.conf.d`.
@@ -210,7 +225,7 @@ in {
           '';
         };
         pipewire-pulse = mkOption {
-          type = lib.types.attrsOf json.type;
+          type = attrsOf json.type;
           default = {};
           example = {
             "15-force-s16-info" = {
@@ -224,7 +239,7 @@ in {
               }];
             };
           };
-          description = lib.mdDoc ''
+          description = ''
             Additional configuration for the PipeWire PulseAudio server.
 
             Every item in this attrset becomes a separate drop-in file in `/etc/pipewire/pipewire-pulse.conf.d`.
@@ -240,10 +255,32 @@ in {
         };
       };
 
-      configPackages = lib.mkOption {
-        type = lib.types.listOf lib.types.package;
+      configPackages = mkOption {
+        type = listOf package;
         default = [];
-        description = lib.mdDoc ''
+        example = literalExpression ''[
+          (pkgs.writeTextDir "share/pipewire/pipewire.conf.d/10-loopback.conf" '''
+            context.modules = [
+            {   name = libpipewire-module-loopback
+                args = {
+                  node.description = "Scarlett Focusrite Line 1"
+                  capture.props = {
+                      audio.position = [ FL ]
+                      stream.dont-remix = true
+                      node.target = "alsa_input.usb-Focusrite_Scarlett_Solo_USB_Y7ZD17C24495BC-00.analog-stereo"
+                      node.passive = true
+                  }
+                  playback.props = {
+                      node.name = "SF_mono_in_1"
+                      media.class = "Audio/Source"
+                      audio.position = [ MONO ]
+                  }
+                }
+            }
+            ]
+          ''')
+        ]'';
+        description = ''
           List of packages that provide PipeWire configuration, in the form of
           `share/pipewire/*/*.conf` files.
 
@@ -252,11 +289,11 @@ in {
         '';
       };
 
-      extraLv2Packages = lib.mkOption {
-        type = lib.types.listOf lib.types.package;
+      extraLv2Packages = mkOption {
+        type = listOf package;
         default = [];
-        example = lib.literalExpression "[ pkgs.lsp-plugins ]";
-        description = lib.mdDoc ''
+        example = literalExpression "[ pkgs.lsp-plugins ]";
+        description = ''
           List of packages that provide LV2 plugins in `lib/lv2` that should
           be made available to PipeWire for [filter chains][wiki-filter-chain].
 
@@ -271,11 +308,11 @@ in {
   };
 
   imports = [
-    (lib.mkRemovedOptionModule ["services" "pipewire" "config"] ''
+    (mkRemovedOptionModule ["services" "pipewire" "config"] ''
       Overriding default PipeWire configuration through NixOS options never worked correctly and is no longer supported.
       Please create drop-in configuration files via `services.pipewire.extraConfig` instead.
     '')
-    (lib.mkRemovedOptionModule ["services" "pipewire" "media-session"] ''
+    (mkRemovedOptionModule ["services" "pipewire" "media-session"] ''
       pipewire-media-session is no longer supported upstream and has been removed.
       Please switch to `services.pipewire.wireplumber` instead.
     '')
@@ -298,12 +335,12 @@ in {
         message = "Using PipeWire's ALSA/PulseAudio compatibility layers requires running PipeWire as the sound server. Set `services.pipewire.audio.enable` to true.";
       }
       {
-        assertion = builtins.length
-          (builtins.attrNames
+        assertion = length
+          (attrNames
             (
-              lib.filterAttrs
+              filterAttrs
                 (name: value:
-                  lib.hasPrefix "pipewire/" name || name == "pipewire"
+                  hasPrefix "pipewire/" name || name == "pipewire"
                 )
                 config.environment.etc
             )) == 1;
@@ -312,7 +349,7 @@ in {
     ];
 
     environment.systemPackages = [ cfg.package ]
-                                 ++ lib.optional cfg.jack.enable jack-libs;
+                                 ++ optional cfg.jack.enable jack-libs;
 
     systemd.packages = [ cfg.package ];
 
@@ -328,16 +365,16 @@ in {
     systemd.user.sockets.pipewire.enable = !cfg.systemWide;
     systemd.user.services.pipewire.enable = !cfg.systemWide;
 
-    systemd.services.pipewire.environment.LV2_PATH = lib.mkIf cfg.systemWide "${lv2Plugins}/lib/lv2";
-    systemd.user.services.pipewire.environment.LV2_PATH = lib.mkIf (!cfg.systemWide) "${lv2Plugins}/lib/lv2";
+    systemd.services.pipewire.environment.LV2_PATH = mkIf cfg.systemWide "${lv2Plugins}/lib/lv2";
+    systemd.user.services.pipewire.environment.LV2_PATH = mkIf (!cfg.systemWide) "${lv2Plugins}/lib/lv2";
 
     # Mask pw-pulse if it's not wanted
     systemd.user.services.pipewire-pulse.enable = cfg.pulse.enable;
     systemd.user.sockets.pipewire-pulse.enable = cfg.pulse.enable;
 
-    systemd.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
-    systemd.user.sockets.pipewire.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
-    systemd.user.sockets.pipewire-pulse.wantedBy = lib.mkIf cfg.socketActivation [ "sockets.target" ];
+    systemd.sockets.pipewire.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
+    systemd.user.sockets.pipewire.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
+    systemd.user.sockets.pipewire-pulse.wantedBy = mkIf cfg.socketActivation [ "sockets.target" ];
 
     services.udev.packages = [ cfg.package ];
 
@@ -369,16 +406,18 @@ in {
     };
 
     environment.sessionVariables.LD_LIBRARY_PATH =
-      lib.mkIf cfg.jack.enable [ "${cfg.package.jack}/lib" ];
+      mkIf cfg.jack.enable [ "${cfg.package.jack}/lib" ];
+
+    networking.firewall.allowedUDPPorts = mkIf cfg.raopOpenFirewall [ 6001 6002 ];
 
-    users = lib.mkIf cfg.systemWide {
+    users = mkIf cfg.systemWide {
       users.pipewire = {
         uid = config.ids.uids.pipewire;
         group = "pipewire";
         extraGroups = [
           "audio"
           "video"
-        ] ++ lib.optional config.security.rtkit.enable "rtkit";
+        ] ++ optional config.security.rtkit.enable "rtkit";
         description = "PipeWire system service user";
         isSystemUser = true;
         home = "/var/lib/pipewire";
diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix
index 009d68bd4f28d..6ab62eb03c25f 100644
--- a/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -1,46 +1,65 @@
 { config, lib, pkgs, ... }:
 
 let
+  inherit (builtins) attrNames concatMap length;
+  inherit (lib) maintainers;
+  inherit (lib.attrsets) attrByPath filterAttrs;
+  inherit (lib.lists) flatten optional;
+  inherit (lib.modules) mkIf;
+  inherit (lib.options) literalExpression mkOption;
+  inherit (lib.strings) hasPrefix;
+  inherit (lib.types) bool listOf package;
+
   pwCfg = config.services.pipewire;
   cfg = pwCfg.wireplumber;
   pwUsedForAudio = pwCfg.audio.enable;
 in
 {
-  meta.maintainers = [ lib.maintainers.k900 ];
+  meta.maintainers = [ maintainers.k900 ];
 
   options = {
     services.pipewire.wireplumber = {
-      enable = lib.mkOption {
-        type = lib.types.bool;
-        default = config.services.pipewire.enable;
-        defaultText = lib.literalExpression "config.services.pipewire.enable";
-        description = lib.mdDoc "Whether to enable WirePlumber, a modular session / policy manager for PipeWire";
+      enable = mkOption {
+        type = bool;
+        default = pwCfg.enable;
+        defaultText = literalExpression "config.services.pipewire.enable";
+        description = "Whether to enable WirePlumber, a modular session / policy manager for PipeWire";
       };
 
-      package = lib.mkOption {
-        type = lib.types.package;
+      package = mkOption {
+        type = package;
         default = pkgs.wireplumber;
-        defaultText = lib.literalExpression "pkgs.wireplumber";
-        description = lib.mdDoc "The WirePlumber derivation to use.";
+        defaultText = literalExpression "pkgs.wireplumber";
+        description = "The WirePlumber derivation to use.";
       };
 
-      configPackages = lib.mkOption {
-        type = lib.types.listOf lib.types.package;
+      configPackages = mkOption {
+        type = listOf package;
         default = [ ];
-        description = lib.mdDoc ''
+        example = literalExpression ''[
+          (pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/10-bluez.conf" '''
+            monitor.bluez.properties = {
+              bluez5.roles = [ a2dp_sink a2dp_source bap_sink bap_source hsp_hs hsp_ag hfp_hf hfp_ag ]
+              bluez5.codecs = [ sbc sbc_xq aac ]
+              bluez5.enable-sbc-xq = true
+              bluez5.hfphsp-backend = "native"
+            }
+          ''')
+        ]'';
+        description = ''
           List of packages that provide WirePlumber configuration, in the form of
-          `share/wireplumber/*/*.lua` files.
+          `share/wireplumber/*/*.conf` files.
 
           LV2 dependencies will be picked up from config packages automatically
           via `passthru.requiredLv2Packages`.
         '';
       };
 
-      extraLv2Packages = lib.mkOption {
-        type = lib.types.listOf lib.types.package;
+      extraLv2Packages = mkOption {
+        type = listOf package;
         default = [];
-        example = lib.literalExpression "[ pkgs.lsp-plugins ]";
-        description = lib.mdDoc ''
+        example = literalExpression "[ pkgs.lsp-plugins ]";
+        description = ''
           List of packages that provide LV2 plugins in `lib/lv2` that should
           be made available to WirePlumber for [filter chains][wiki-filter-chain].
 
@@ -56,24 +75,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 ];
+          ++ optional (!pwUsedForAudio) pwNotForAudioConfigPkg
+          ++ optional pwCfg.systemWide systemwideConfigPkg;
 
       configs = pkgs.buildEnv {
         name = "wireplumber-configs";
@@ -81,11 +106,11 @@ in
         pathsToLink = [ "/share/wireplumber" ];
       };
 
-      requiredLv2Packages = lib.flatten
+      requiredLv2Packages = flatten
         (
-          lib.concatMap
+          concatMap
             (p:
-              lib.attrByPath ["passthru" "requiredLv2Packages"] [] p
+              attrByPath ["passthru" "requiredLv2Packages"] [] p
             )
             configPackages
         );
@@ -96,19 +121,19 @@ in
         pathsToLink = [ "/lib/lv2" ];
       };
     in
-    lib.mkIf cfg.enable {
+    mkIf cfg.enable {
       assertions = [
         {
           assertion = !config.hardware.bluetooth.hsphfpd.enable;
           message = "Using WirePlumber conflicts with hsphfpd, as it provides the same functionality. `hardware.bluetooth.hsphfpd.enable` needs be set to false";
         }
         {
-          assertion = builtins.length
-            (builtins.attrNames
+          assertion = length
+            (attrNames
               (
-                lib.filterAttrs
+                filterAttrs
                   (name: value:
-                    lib.hasPrefix "wireplumber/" name || name == "wireplumber"
+                    hasPrefix "wireplumber/" name || name == "wireplumber"
                   )
                   config.environment.etc
               )) == 1;
@@ -122,19 +147,19 @@ in
 
       systemd.packages = [ cfg.package ];
 
-      systemd.services.wireplumber.enable = config.services.pipewire.systemWide;
-      systemd.user.services.wireplumber.enable = !config.services.pipewire.systemWide;
+      systemd.services.wireplumber.enable = pwCfg.systemWide;
+      systemd.user.services.wireplumber.enable = !pwCfg.systemWide;
 
       systemd.services.wireplumber.wantedBy = [ "pipewire.service" ];
       systemd.user.services.wireplumber.wantedBy = [ "pipewire.service" ];
 
-      systemd.services.wireplumber.environment = lib.mkIf config.services.pipewire.systemWide {
+      systemd.services.wireplumber.environment = mkIf pwCfg.systemWide {
         # Force WirePlumber to use system dbus.
         DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/dbus/system_bus_socket";
         LV2_PATH = "${lv2Plugins}/lib/lv2";
       };
 
       systemd.user.services.wireplumber.environment.LV2_PATH =
-        lib.mkIf (!config.services.pipewire.systemWide) "${lv2Plugins}/lib/lv2";
+        mkIf (!pwCfg.systemWide) "${lv2Plugins}/lib/lv2";
     };
 }
diff --git a/nixos/modules/services/desktops/zeitgeist.nix b/nixos/modules/services/desktops/zeitgeist.nix
index 0eb2a4c9c371b..caa2835316b03 100644
--- a/nixos/modules/services/desktops/zeitgeist.nix
+++ b/nixos/modules/services/desktops/zeitgeist.nix
@@ -14,7 +14,7 @@ with lib;
 
   options = {
     services.zeitgeist = {
-      enable = mkEnableOption (lib.mdDoc "zeitgeist");
+      enable = mkEnableOption (lib.mdDoc "zeitgeist, a service which logs the users' activities and events");
     };
   };
 
diff --git a/nixos/modules/services/development/athens.md b/nixos/modules/services/development/athens.md
index 77663db509d59..2795930b0a029 100644
--- a/nixos/modules/services/development/athens.md
+++ b/nixos/modules/services/development/athens.md
@@ -18,7 +18,7 @@ A complete list of options for the Athens module may be found
 ## Basic usage for a caching proxy configuration {#opt-services-development-athens-caching-proxy}
 
 A very basic configuration for Athens that acts as a caching and forwarding HTTP proxy is:
-```
+```nix
 {
     services.athens = {
       enable = true;
@@ -28,7 +28,7 @@ A very basic configuration for Athens that acts as a caching and forwarding HTTP
 
 If you want to prevent Athens from writing to disk, you can instead configure it to cache modules only in memory:
 
-```
+```nix
 {
     services.athens = {
       enable = true;
@@ -39,10 +39,10 @@ If you want to prevent Athens from writing to disk, you can instead configure it
 
 To use the local proxy in Go builds, you can set the proxy as environment variable:
 
-```
+```nix
 {
   environment.variables = {
-    GOPROXY = "http://localhost:3000"
+    GOPROXY = "http://localhost:3000";
   };
 }
 ```
diff --git a/nixos/modules/services/development/blackfire.md b/nixos/modules/services/development/blackfire.md
index e2e7e4780c79c..5a7fbe68f7d28 100644
--- a/nixos/modules/services/development/blackfire.md
+++ b/nixos/modules/services/development/blackfire.md
@@ -7,7 +7,7 @@
 [Blackfire](https://blackfire.io) is a proprietary tool for profiling applications. There are several languages supported by the product but currently only PHP support is packaged in Nixpkgs. The back-end consists of a module that is loaded into the language runtime (called *probe*) and a service (*agent*) that the probe connects to and that sends the profiles to the server.
 
 To use it, you will need to enable the agent and the probe on your server. The exact method will depend on the way you use PHP but here is an example of NixOS configuration for PHP-FPM:
-```
+```nix
 let
   php = pkgs.php.withExtensions ({ enabled, all }: enabled ++ (with all; [
     blackfire
diff --git a/nixos/modules/services/development/distccd.nix b/nixos/modules/services/development/distccd.nix
index c33bf436bffb5..af59e1febd86b 100644
--- a/nixos/modules/services/development/distccd.nix
+++ b/nixos/modules/services/development/distccd.nix
@@ -8,7 +8,7 @@ in
 {
   options = {
     services.distccd = {
-      enable = mkEnableOption (lib.mdDoc "distccd");
+      enable = mkEnableOption (lib.mdDoc "distccd, a distributed C/C++ compiler");
 
       allowedClients = mkOption {
         type = types.listOf types.str;
diff --git a/nixos/modules/services/development/gemstash.nix b/nixos/modules/services/development/gemstash.nix
index eb7ccb98bde89..650c6680ee693 100644
--- a/nixos/modules/services/development/gemstash.nix
+++ b/nixos/modules/services/development/gemstash.nix
@@ -24,7 +24,7 @@ let
 in
 {
   options.services.gemstash = {
-    enable = mkEnableOption (lib.mdDoc "gemstash service");
+    enable = mkEnableOption (lib.mdDoc "gemstash, a cache for rubygems.org and a private gem server");
 
     openFirewall = mkOption {
       type = types.bool;
diff --git a/nixos/modules/services/development/livebook.md b/nixos/modules/services/development/livebook.md
index 5315f2c2755a0..aac9c58d081cd 100644
--- a/nixos/modules/services/development/livebook.md
+++ b/nixos/modules/services/development/livebook.md
@@ -9,7 +9,7 @@ Enabling the `livebook` service creates a user
 [`systemd`](https://www.freedesktop.org/wiki/Software/systemd/) unit
 which runs the server.
 
-```
+```nix
 { ... }:
 
 {
@@ -51,6 +51,8 @@ some features require additional packages.  For example, the machine
 learning Kinos require `gcc` and `gnumake`.  To add these, use
 `extraPackages`:
 
-```
-services.livebook.extraPackages = with pkgs; [ gcc gnumake ];
+```nix
+{
+  services.livebook.extraPackages = with pkgs; [ gcc gnumake ];
+}
 ```
diff --git a/nixos/modules/services/display-managers/default.nix b/nixos/modules/services/display-managers/default.nix
new file mode 100644
index 0000000000000..7e808d0d63833
--- /dev/null
+++ b/nixos/modules/services/display-managers/default.nix
@@ -0,0 +1,260 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.displayManager;
+
+  installedSessions = pkgs.runCommand "desktops"
+    { # trivial derivation
+      preferLocalBuild = true;
+      allowSubstitutes = false;
+    }
+    ''
+      mkdir -p "$out/share/"{xsessions,wayland-sessions}
+
+      ${lib.concatMapStrings (pkg: ''
+        for n in ${lib.concatStringsSep " " pkg.providedSessions}; do
+          if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \
+                    -f ${pkg}/share/xsessions/$n.desktop; then
+            echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:"
+            echo "  ${pkg}"
+            return 1
+          fi
+        done
+
+        if test -d ${pkg}/share/xsessions; then
+          ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions
+        fi
+        if test -d ${pkg}/share/wayland-sessions; then
+          ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions
+        fi
+      '') cfg.sessionPackages}
+    '';
+
+  dmDefault = config.services.xserver.desktopManager.default;
+  # fallback default for cases when only default wm is set
+  dmFallbackDefault = if dmDefault != null then dmDefault else "none";
+  wmDefault = config.services.xserver.windowManager.default;
+  defaultSessionFromLegacyOptions = dmFallbackDefault + lib.optionalString (wmDefault != null && wmDefault != "none") "+${wmDefault}";
+in
+{
+  options = {
+    services.displayManager = {
+      enable = lib.mkEnableOption "systemd's display-manager service";
+
+      preStart = lib.mkOption {
+        type = lib.types.lines;
+        default = "";
+        example = "rm -f /var/log/my-display-manager.log";
+        description = lib.mdDoc "Script executed before the display manager is started.";
+      };
+
+      execCmd = lib.mkOption {
+        type = lib.types.str;
+        example = lib.literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
+        description = lib.mdDoc "Command to start the display manager.";
+      };
+
+      environment = lib.mkOption {
+        type = with lib.types; attrsOf unspecified;
+        default = {};
+        description = lib.mdDoc "Additional environment variables needed by the display manager.";
+      };
+
+      hiddenUsers = lib.mkOption {
+        type = with lib.types; listOf str;
+        default = [ "nobody" ];
+        description = lib.mdDoc ''
+          A list of users which will not be shown in the display manager.
+        '';
+      };
+
+      logToFile = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Whether the display manager redirects the output of the
+          session script to {file}`~/.xsession-errors`.
+        '';
+      };
+
+      logToJournal = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Whether the display manager redirects the output of the
+          session script to the systemd journal.
+        '';
+      };
+
+      # Configuration for automatic login. Common for all DM.
+      autoLogin = lib.mkOption {
+        type = lib.types.submodule ({ config, options, ... }: {
+          options = {
+            enable = lib.mkOption {
+              type = lib.types.bool;
+              default = config.user != null;
+              defaultText = lib.literalExpression "config.${options.user} != null";
+              description = lib.mdDoc ''
+                Automatically log in as {option}`autoLogin.user`.
+              '';
+            };
+
+            user = lib.mkOption {
+              type = with lib.types; nullOr str;
+              default = null;
+              description = lib.mdDoc ''
+                User to be used for the automatic login.
+              '';
+            };
+          };
+        });
+
+        default = {};
+        description = lib.mdDoc ''
+          Auto login configuration attrset.
+        '';
+      };
+
+      defaultSession = lib.mkOption {
+        type = lib.types.nullOr lib.types.str // {
+          description = "session name";
+          check = d:
+            lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d config.services.displayManager.sessionData.sessionNames)) ''
+                Default graphical session, '${d}', not found.
+                Valid names for 'services.displayManager.defaultSession' are:
+                  ${lib.concatStringsSep "\n  " cfg.displayManager.sessionData.sessionNames}
+              '';
+        };
+        default =
+          if dmDefault != null || wmDefault != null then
+            defaultSessionFromLegacyOptions
+          else
+            null;
+        defaultText = lib.literalMD ''
+          Taken from display manager settings or window manager settings, if either is set.
+        '';
+        example = "gnome";
+        description = lib.mdDoc ''
+          Graphical session to pre-select in the session chooser (only effective for GDM, LightDM and SDDM).
+
+          On GDM, LightDM and SDDM, it will also be used as a session for auto-login.
+        '';
+      };
+
+      sessionData = lib.mkOption {
+        description = lib.mdDoc "Data exported for display managers’ convenience";
+        internal = true;
+        default = {};
+      };
+
+      sessionPackages = lib.mkOption {
+        type = lib.types.listOf (lib.types.package // {
+          description = "package with provided sessions";
+          check = p: lib.assertMsg
+            (lib.types.package.check p && p ? providedSessions
+            && p.providedSessions != [] && lib.all lib.isString p.providedSessions)
+            ''
+              Package, '${p.name}', did not specify any session names, as strings, in
+              'passthru.providedSessions'. This is required when used as a session package.
+
+              The session names can be looked up in:
+                ${p}/share/xsessions
+                ${p}/share/wayland-sessions
+           '';
+        });
+        default = [];
+        description = lib.mdDoc ''
+          A list of packages containing x11 or wayland session files to be passed to the display manager.
+        '';
+      };
+    };
+  };
+
+  imports = [
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "autoLogin" ] [ "services" "displayManager" "autoLogin" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "defaultSession" ] [ "services" "displayManager" "defaultSession" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "hiddenUsers" ] [ "services" "displayManager" "hiddenUsers" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "environment" ] [ "services" "displayManager" "environment" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "execCmd" ] [ "services" "displayManager" "execCmd" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logToFile" ] [ "services" "displayManager" "logToFile" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logToJournal" ] [ "services" "displayManager" "logToJournal" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "preStart" ] [ "services" "displayManager" "preStart" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "sessionData" ] [ "services" "displayManager" "sessionData" ])
+    (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "sessionPackages" ] [ "services" "displayManager" "sessionPackages" ])
+  ];
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
+        message = ''
+          services.displayManager.autoLogin.enable requires services.displayManager.autoLogin.user to be set
+        '';
+      }
+    ];
+
+    warnings =
+      lib.mkIf (dmDefault != null || wmDefault != null) [
+        ''
+          The following options are deprecated:
+            ${lib.concatStringsSep "\n  " (map ({c, t}: t) (lib.filter ({c, t}: c != null) [
+            { c = dmDefault; t = "- services.xserver.desktopManager.default"; }
+            { c = wmDefault; t = "- services.xserver.windowManager.default"; }
+            ]))}
+          Please use
+            services.displayManager.defaultSession = "${defaultSessionFromLegacyOptions}";
+          instead.
+        ''
+      ];
+
+    # Make xsessions and wayland sessions available in XDG_DATA_DIRS
+    # as some programs have behavior that depends on them being present
+    environment.sessionVariables.XDG_DATA_DIRS = lib.mkIf (cfg.sessionPackages != [ ]) [
+      "${cfg.sessionData.desktops}/share"
+    ];
+
+    services.displayManager.sessionData = {
+      desktops = installedSessions;
+      sessionNames = lib.concatMap (p: p.providedSessions) config.services.displayManager.sessionPackages;
+      # We do not want to force users to set defaultSession when they have only single DE.
+      autologinSession =
+        if cfg.defaultSession != null then
+          cfg.defaultSession
+        else if cfg.sessionData.sessionNames != [] then
+          lib.head cfg.sessionData.sessionNames
+        else
+          null;
+    };
+
+    # so that the service won't be enabled when only startx is used
+    systemd.services.display-manager.enable =
+      let dmConf = config.services.xserver.displayManager;
+          noDmUsed = !(dmConf.gdm.enable
+                    || cfg.sddm.enable
+                    || dmConf.xpra.enable
+                    || dmConf.lightdm.enable);
+      in lib.mkIf noDmUsed (lib.mkDefault false);
+
+    systemd.services.display-manager = {
+      description = "Display Manager";
+      after = [ "acpid.service" "systemd-logind.service" "systemd-user-sessions.service" ];
+      restartIfChanged = false;
+
+      environment = lib.optionalAttrs config.hardware.opengl.setLdLibraryPath {
+        LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ];
+      } // cfg.environment;
+
+      preStart = cfg.preStart;
+      script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd;
+
+      # Stop restarting if the display manager stops (crashes) 2 times
+      # in one minute. Starting X typically takes 3-4s.
+      startLimitIntervalSec = 30;
+      startLimitBurst = 3;
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = "200ms";
+        SyslogIdentifier = "display-manager";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/display-managers/greetd.nix b/nixos/modules/services/display-managers/greetd.nix
index c2d345152de93..f858a7fc8be19 100644
--- a/nixos/modules/services/display-managers/greetd.nix
+++ b/nixos/modules/services/display-managers/greetd.nix
@@ -8,7 +8,7 @@ let
 in
 {
   options.services.greetd = {
-    enable = mkEnableOption (lib.mdDoc "greetd");
+    enable = mkEnableOption (lib.mdDoc "greetd, a minimal and flexible login manager daemon");
 
     package = mkPackageOption pkgs [ "greetd" "greetd" ] { };
 
@@ -60,7 +60,12 @@ in
     # This prevents nixos-rebuild from killing greetd by activating getty again
     systemd.services."autovt@${tty}".enable = false;
 
+    # Enable desktop session data
+    services.displayManager.enable = lib.mkDefault true;
+
     systemd.services.greetd = {
+      aliases = [ "display-manager.service" ];
+
       unitConfig = {
         Wants = [
           "systemd-user-sessions.service"
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/display-managers/sddm.nix
index 5b7f4bc58d80c..16efec04e6194 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/display-managers/sddm.nix
@@ -1,27 +1,32 @@
 { config, lib, pkgs, ... }:
 
-with lib;
 let
   xcfg = config.services.xserver;
-  dmcfg = xcfg.displayManager;
-  cfg = dmcfg.sddm;
+  dmcfg = config.services.displayManager;
+  cfg = config.services.displayManager.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} "$@"
+    exec systemd-cat -t xserver-wrapper ${xcfg.displayManager.xserverBin} ${toString xcfg.displayManager.xserverArgs} "$@"
   '';
 
   Xsetup = pkgs.writeShellScript "Xsetup" ''
     ${cfg.setupScript}
-    ${dmcfg.setupCommands}
+    ${xcfg.displayManager.setupCommands}
   '';
 
   Xstop = pkgs.writeShellScript "Xstop" ''
@@ -35,15 +40,24 @@ let
       Numlock = if cfg.autoNumlock then "on" else "none"; # on, off none
 
       # Implementation is done via pkgs/applications/display-managers/sddm/sddm-default-session.patch
-      DefaultSession = optionalString (dmcfg.defaultSession != null) "${dmcfg.defaultSession}.desktop";
+      DefaultSession = optionalString (config.services.displayManager.defaultSession != null) "${config.services.displayManager.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 = {
@@ -52,7 +66,7 @@ let
       HideShells = "/run/current-system/sw/bin/nologin";
     };
 
-    X11 = {
+    X11 = optionalAttrs xcfg.enable {
       MinimumVT = if xcfg.tty != null then xcfg.tty else 7;
       ServerPath = toString xserverWrapper;
       XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr";
@@ -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,26 +97,67 @@ 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 = [
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoLogin" "minimumUid" ] [ "services" "displayManager" "sddm" "autoLogin" "minimumUid" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoLogin" "relogin" ] [ "services" "displayManager" "sddm" "autoLogin" "relogin" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "autoNumlock" ] [ "services" "displayManager" "sddm" "autoNumlock" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "enable" ]      [ "services" "displayManager" "sddm" "enable" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "enableHidpi" ] [ "services" "displayManager" "sddm" "enableHidpi" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "extraPackages" ] [ "services" "displayManager" "sddm" "extraPackages" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "package" ]     [ "services" "displayManager" "sddm" "package" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "settings" ]    [ "services" "displayManager" "sddm" "settings" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "setupScript" ] [ "services" "displayManager" "sddm" "setupScript" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "stopScript" ]  [ "services" "displayManager" "sddm" "stopScript" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "theme" ]       [ "services" "displayManager" "sddm" "theme" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "sddm" "wayland" "enable" ] [ "services" "displayManager" "sddm" "wayland" "enable" ])
+
     (mkRemovedOptionModule
-      [ "services" "xserver" "displayManager" "sddm" "themes" ]
-      "Set the option `services.xserver.displayManager.sddm.package' instead.")
+      [ "services" "displayManager" "sddm" "themes" ]
+      "Set the option `services.displayManager.sddm.package' instead.")
     (mkRenamedOptionModule
-      [ "services" "xserver" "displayManager" "sddm" "autoLogin" "enable" ]
-      [ "services" "xserver" "displayManager" "autoLogin" "enable" ])
+      [ "services" "displayManager" "sddm" "autoLogin" "enable" ]
+      [ "services" "displayManager" "autoLogin" "enable" ])
     (mkRenamedOptionModule
-      [ "services" "xserver" "displayManager" "sddm" "autoLogin" "user" ]
-      [ "services" "xserver" "displayManager" "autoLogin" "user" ])
+      [ "services" "displayManager" "sddm" "autoLogin" "user" ]
+      [ "services" "displayManager" "autoLogin" "user" ])
     (mkRemovedOptionModule
-      [ "services" "xserver" "displayManager" "sddm" "extraConfig" ]
-      "Set the option `services.xserver.displayManager.sddm.settings' instead.")
+      [ "services" "displayManager" "sddm" "extraConfig" ]
+      "Set the option `services.displayManager.sddm.settings' instead.")
   ];
 
   options = {
 
-    services.xserver.displayManager.sddm = {
+    services.displayManager.sddm = {
       enable = mkOption {
         type = types.bool;
         default = false;
@@ -111,7 +166,7 @@ in
         '';
       };
 
-      package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] {};
+      package = mkPackageOption pkgs [ "plasma5Packages" "sddm" ] { };
 
       enableHidpi = mkOption {
         type = types.bool;
@@ -145,7 +200,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 +261,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";
         };
       };
@@ -234,20 +281,23 @@ in
 
     assertions = [
       {
-        assertion = xcfg.enable;
+        assertion = xcfg.enable || cfg.wayland.enable;
         message = ''
-          SDDM requires services.xserver.enable to be true
+          SDDM requires either services.xserver.enable or services.displayManager.sddm.wayland.enable to be true
         '';
       }
       {
-        assertion = dmcfg.autoLogin.enable -> autoLoginSessionName != null;
+        assertion = config.services.displayManager.autoLogin.enable -> autoLoginSessionName != null;
         message = ''
-          SDDM auto-login requires that services.xserver.displayManager.defaultSession is set.
+          SDDM auto-login requires that services.displayManager.defaultSession is set.
         '';
       }
     ];
 
-    services.xserver.displayManager.job.execCmd = "exec /run/current-system/sw/bin/sddm";
+    services.displayManager = {
+      enable = true;
+      execCmd = "exec /run/current-system/sw/bin/sddm";
+    };
 
     security.pam.services = {
       sddm.text = ''
@@ -293,30 +343,40 @@ 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 = {
+        # 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/editors/emacs.md b/nixos/modules/services/editors/emacs.md
index 02f47b098d86c..885f927422bdc 100644
--- a/nixos/modules/services/editors/emacs.md
+++ b/nixos/modules/services/editors/emacs.md
@@ -178,7 +178,7 @@ file {file}`configuration.nix` to make it contain:
 ::: {.example #module-services-emacs-configuration-nix}
 ### Custom Emacs in `configuration.nix`
 
-```
+```nix
 {
  environment.systemPackages = [
    # [...]
@@ -203,7 +203,7 @@ adding it to your {file}`~/.config/nixpkgs/config.nix` (see
 ::: {.example #module-services-emacs-config-nix}
 ### Custom Emacs in `~/.config/nixpkgs/config.nix`
 
-```
+```nix
 {
   packageOverrides = super: let self = super.pkgs; in {
     myemacs = import ./emacs.nix { pkgs = self; };
@@ -228,7 +228,7 @@ only use {command}`emacsclient`), you can change your file
 ::: {.example #ex-emacsGtk3Nix}
 ### Custom Emacs build
 
-```
+```nix
 { pkgs ? import <nixpkgs> {} }:
 let
   myEmacs = (pkgs.emacs.override {
@@ -242,7 +242,7 @@ let
       rm $out/share/applications/emacs.desktop
     '';
   });
-in [...]
+in [ /* ... */ ]
 ```
 :::
 
@@ -262,8 +262,10 @@ with the user's login session.
 
 To install and enable the {command}`systemd` user service for Emacs
 daemon, add the following to your {file}`configuration.nix`:
-```
-services.emacs.enable = true;
+```nix
+{
+  services.emacs.enable = true;
+}
 ```
 
 The {var}`services.emacs.package` option allows a custom
@@ -323,9 +325,11 @@ In general, {command}`systemd` user services are globally enabled
 by symlinks in {file}`/etc/systemd/user`. In the case where
 Emacs daemon is not wanted for all users, it is possible to install the
 service but not globally enable it:
-```
-services.emacs.enable = false;
-services.emacs.install = true;
+```nix
+{
+  services.emacs.enable = false;
+  services.emacs.install = true;
+}
 ```
 
 To enable the {command}`systemd` user service for just the
diff --git a/nixos/modules/services/finance/odoo.nix b/nixos/modules/services/finance/odoo.nix
index aa9bd0014d985..3a3b3c2fa6eec 100644
--- a/nixos/modules/services/finance/odoo.nix
+++ b/nixos/modules/services/finance/odoo.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.odoo = {
-      enable = mkEnableOption (lib.mdDoc "odoo");
+      enable = mkEnableOption (lib.mdDoc "odoo, an open source ERP and CRM system");
 
       package = mkPackageOption pkgs "odoo" { };
 
diff --git a/nixos/modules/services/games/archisteamfarm.nix b/nixos/modules/services/games/archisteamfarm.nix
index 4bb7234f430f2..be3f72829239d 100644
--- a/nixos/modules/services/games/archisteamfarm.nix
+++ b/nixos/modules/services/games/archisteamfarm.nix
@@ -255,7 +255,7 @@ in
               ln -fs ${ipc-config} config/IPC.config
             ''}
 
-            ${lib.optionalString (cfg.ipcSettings != {}) ''
+            ${lib.optionalString (cfg.bots != {}) ''
               ln -fs ${createBotsScript}/* config/
             ''}
 
diff --git a/nixos/modules/services/games/mchprs.nix b/nixos/modules/services/games/mchprs.nix
index 71e546049c58b..36f7ea71d6351 100644
--- a/nixos/modules/services/games/mchprs.nix
+++ b/nixos/modules/services/games/mchprs.nix
@@ -22,7 +22,7 @@ in
 {
   options = {
     services.mchprs = {
-      enable = mkEnableOption "MCHPRS";
+      enable = mkEnableOption "MCHPRS, a Minecraft server";
 
       declarativeSettings = mkOption {
         type = types.bool;
diff --git a/nixos/modules/services/games/openarena.nix b/nixos/modules/services/games/openarena.nix
index 14e485b06a0df..592cec9a552f2 100644
--- a/nixos/modules/services/games/openarena.nix
+++ b/nixos/modules/services/games/openarena.nix
@@ -7,7 +7,7 @@ in
 {
   options = {
     services.openarena = {
-      enable = mkEnableOption (lib.mdDoc "OpenArena");
+      enable = mkEnableOption (lib.mdDoc "OpenArena game server");
       package = lib.mkPackageOption pkgs "openarena" { };
 
       openPorts = mkOption {
diff --git a/nixos/modules/services/games/terraria.nix b/nixos/modules/services/games/terraria.nix
index ccdd779165b88..0b85f14aaf435 100644
--- a/nixos/modules/services/games/terraria.nix
+++ b/nixos/modules/services/games/terraria.nix
@@ -148,16 +148,13 @@ in
 
       serviceConfig = {
         User    = "terraria";
+        Group = "terraria";
         Type = "forking";
         GuessMainPID = true;
+        UMask = 007;
         ExecStart = "${getBin pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/terraria.sock new -d ${pkgs.terraria-server}/bin/TerrariaServer ${concatStringsSep " " flags}";
         ExecStop = "${stopScript} $MAINPID";
       };
-
-      postStart = ''
-        ${pkgs.coreutils}/bin/chmod 660 ${cfg.dataDir}/terraria.sock
-        ${pkgs.coreutils}/bin/chgrp terraria ${cfg.dataDir}/terraria.sock
-      '';
     };
 
     networking.firewall = mkIf cfg.openFirewall {
diff --git a/nixos/modules/services/hardware/kanata.nix b/nixos/modules/services/hardware/kanata.nix
index 05e76d8432154..fa589f685a028 100644
--- a/nixos/modules/services/hardware/kanata.nix
+++ b/nixos/modules/services/hardware/kanata.nix
@@ -151,7 +151,7 @@ let
 in
 {
   options.services.kanata = {
-    enable = mkEnableOption (mdDoc "kanata");
+    enable = mkEnableOption (mdDoc "kanata, a tool to improve keyboard comfort and usability with advanced customization");
     package = mkPackageOption pkgs "kanata" {
       example = "kanata-with-cmd";
       extraDescription = ''
diff --git a/nixos/modules/services/hardware/lirc.nix b/nixos/modules/services/hardware/lirc.nix
index 5b1a8d10c7299..c182c3895fb87 100644
--- a/nixos/modules/services/hardware/lirc.nix
+++ b/nixos/modules/services/hardware/lirc.nix
@@ -11,7 +11,7 @@ in {
   options = {
     services.lirc = {
 
-      enable = mkEnableOption (lib.mdDoc "LIRC daemon");
+      enable = mkEnableOption (lib.mdDoc "the LIRC daemon, to receive and send infrared signals");
 
       options = mkOption {
         type = types.lines;
diff --git a/nixos/modules/services/hardware/openrgb.nix b/nixos/modules/services/hardware/openrgb.nix
index 81b199e50778e..b400f77049f75 100644
--- a/nixos/modules/services/hardware/openrgb.nix
+++ b/nixos/modules/services/hardware/openrgb.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.hardware.openrgb;
 in {
   options.services.hardware.openrgb = {
-    enable = mkEnableOption (lib.mdDoc "OpenRGB server");
+    enable = mkEnableOption (lib.mdDoc "OpenRGB server, for RGB lighting control");
 
     package = mkPackageOption pkgs "openrgb" { };
 
diff --git a/nixos/modules/services/hardware/pcscd.nix b/nixos/modules/services/hardware/pcscd.nix
index 77c2d9b53f03d..34df1f3e91ba3 100644
--- a/nixos/modules/services/hardware/pcscd.nix
+++ b/nixos/modules/services/hardware/pcscd.nix
@@ -18,7 +18,7 @@ let
 in
 {
   options.services.pcscd = {
-    enable = mkEnableOption (lib.mdDoc "PCSC-Lite daemon");
+    enable = mkEnableOption (lib.mdDoc "PCSC-Lite daemon, to access smart cards using SCard API (PC/SC)");
 
     plugins = mkOption {
       type = types.listOf types.package;
diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan5.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan5.nix
index d29e0f542f55d..a4f93221475da 100644
--- a/nixos/modules/services/hardware/sane_extra_backends/brscan5.nix
+++ b/nixos/modules/services/hardware/sane_extra_backends/brscan5.nix
@@ -94,7 +94,7 @@ in
       { source = "${etcFiles}/etc/opt/brother/scanner/brscan5"; };
     environment.etc."opt/brother/scanner/models" =
       { source = "${etcFiles}/etc/opt/brother/scanner/brscan5/models"; };
-    environment.etc."sane.d/dll.d/brother5.conf".source = "${pkgs.brscan5}/etc/sane.d/dll.d/brother.conf";
+    environment.etc."sane.d/dll.d/brother5.conf".source = "${pkgs.brscan5}/etc/sane.d/dll.d/brother5.conf";
 
     assertions = [
       { assertion = all (x: !(null != x.ip && null != x.nodename)) netDeviceList;
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 670b9087f1107..2bf86a535fc10 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -200,7 +200,7 @@ in
     };
 
     services.udev = {
-      enable = mkEnableOption (lib.mdDoc "udev") // {
+      enable = mkEnableOption (lib.mdDoc "udev, a device manager for the Linux kernel") // {
         default = true;
       };
 
diff --git a/nixos/modules/services/hardware/undervolt.nix b/nixos/modules/services/hardware/undervolt.nix
index c4d4c6791a21b..ea4d60eac7c2c 100644
--- a/nixos/modules/services/hardware/undervolt.nix
+++ b/nixos/modules/services/hardware/undervolt.nix
@@ -37,7 +37,7 @@ in
     enable = mkEnableOption (lib.mdDoc ''
        Undervolting service for Intel CPUs.
 
-       Warning: This service is not endorsed by Intel and may permanently damage your hardware. Use at your own risk!
+       Warning: This service is not endorsed by Intel and may permanently damage your hardware. Use at your own risk
     '');
 
     verbose = mkOption {
diff --git a/nixos/modules/services/hardware/vdr.nix b/nixos/modules/services/hardware/vdr.nix
index 689d83f7eedcd..0c9cc0abf798d 100644
--- a/nixos/modules/services/hardware/vdr.nix
+++ b/nixos/modules/services/hardware/vdr.nix
@@ -9,7 +9,7 @@ in
   options = {
 
     services.vdr = {
-      enable = mkEnableOption (mdDoc "Start VDR");
+      enable = mkEnableOption (mdDoc "VDR, a video disk recorder");
 
       package = mkPackageOption pkgs "vdr" {
         example = "wrapVdr.override { plugins = with pkgs.vdrPlugins; [ hello ]; }";
diff --git a/nixos/modules/services/home-automation/ebusd.nix b/nixos/modules/services/home-automation/ebusd.nix
index f68a8bdb6bfa2..e1dcfb338421f 100644
--- a/nixos/modules/services/home-automation/ebusd.nix
+++ b/nixos/modules/services/home-automation/ebusd.nix
@@ -44,7 +44,7 @@ in
   meta.maintainers = with maintainers; [ nathan-gs ];
 
   options.services.ebusd = {
-    enable = mkEnableOption (lib.mdDoc "ebusd service");
+    enable = mkEnableOption (lib.mdDoc "ebusd, a daemon for communication with eBUS heating systems");
 
     device = mkOption {
       type = types.str;
diff --git a/nixos/modules/services/home-automation/esphome.nix b/nixos/modules/services/home-automation/esphome.nix
index 3c0fd8aed08a4..ac623cf36b3a9 100644
--- a/nixos/modules/services/home-automation/esphome.nix
+++ b/nixos/modules/services/home-automation/esphome.nix
@@ -24,7 +24,7 @@ in
   meta.maintainers = with maintainers; [ oddlama ];
 
   options.services.esphome = {
-    enable = mkEnableOption (mdDoc "esphome");
+    enable = mkEnableOption (mdDoc "esphome, for making custom firmwares for ESP32/ESP8266");
 
     package = lib.mkPackageOption pkgs "esphome" { };
 
diff --git a/nixos/modules/services/home-automation/homeassistant-satellite.nix b/nixos/modules/services/home-automation/homeassistant-satellite.nix
deleted file mode 100644
index 6ca428f2af818..0000000000000
--- a/nixos/modules/services/home-automation/homeassistant-satellite.nix
+++ /dev/null
@@ -1,225 +0,0 @@
-{ config
-, lib
-, pkgs
-, ...
-}:
-
-let
-  cfg = config.services.homeassistant-satellite;
-
-  inherit (lib)
-    escapeShellArg
-    escapeShellArgs
-    mkOption
-    mdDoc
-    mkEnableOption
-    mkIf
-    mkPackageOption
-    types
-    ;
-
-  inherit (builtins)
-    toString
-    ;
-
-  # override the package with the relevant vad dependencies
-  package = cfg.package.overridePythonAttrs (oldAttrs: {
-    propagatedBuildInputs = oldAttrs.propagatedBuildInputs
-      ++ lib.optional (cfg.vad == "webrtcvad") cfg.package.optional-dependencies.webrtc
-      ++ lib.optional (cfg.vad == "silero") cfg.package.optional-dependencies.silerovad
-      ++ lib.optional (cfg.pulseaudio.enable) cfg.package.optional-dependencies.pulseaudio;
-  });
-
-in
-
-{
-  meta.buildDocsInSandbox = false;
-
-  options.services.homeassistant-satellite = with types; {
-    enable = mkEnableOption (mdDoc "Home Assistant Satellite");
-
-    package = mkPackageOption pkgs "homeassistant-satellite" { };
-
-    user = mkOption {
-      type = str;
-      example = "alice";
-      description = mdDoc ''
-        User to run homeassistant-satellite under.
-      '';
-    };
-
-    group = mkOption {
-      type = str;
-      default = "users";
-      description = mdDoc ''
-        Group to run homeassistant-satellite under.
-      '';
-    };
-
-    host = mkOption {
-      type = str;
-      example = "home-assistant.local";
-      description = mdDoc ''
-        Hostname on which your Home Assistant instance can be reached.
-      '';
-    };
-
-    port = mkOption {
-      type = port;
-      example = 8123;
-      description = mdDoc ''
-        Port on which your Home Assistance can be reached.
-      '';
-      apply = toString;
-    };
-
-    protocol = mkOption {
-      type = enum [ "http" "https" ];
-      default = "http";
-      example = "https";
-      description = mdDoc ''
-        The transport protocol used to connect to Home Assistant.
-      '';
-    };
-
-    tokenFile = mkOption {
-      type = path;
-      example = "/run/keys/hass-token";
-      description = mdDoc ''
-        Path to a file containing a long-lived access token for your Home Assistant instance.
-      '';
-      apply = escapeShellArg;
-    };
-
-    sounds = {
-      awake = mkOption {
-        type = nullOr str;
-        default = null;
-        description = mdDoc ''
-          Audio file to play when the wake word is detected.
-        '';
-      };
-
-      done = mkOption {
-        type = nullOr str;
-        default = null;
-        description = mdDoc ''
-          Audio file to play when the voice command is done.
-        '';
-      };
-    };
-
-    vad = mkOption {
-      type = enum [ "disabled" "webrtcvad" "silero" ];
-      default = "disabled";
-      example = "silero";
-      description = mdDoc ''
-        Voice activity detection model. With `disabled` sound will be transmitted continously.
-      '';
-    };
-
-    pulseaudio = {
-      enable = mkEnableOption "recording/playback via PulseAudio or PipeWire";
-
-      socket = mkOption {
-        type = nullOr str;
-        default = null;
-        example = "/run/user/1000/pulse/native";
-        description = mdDoc ''
-          Path or hostname to connect with the PulseAudio server.
-        '';
-      };
-
-      duckingVolume = mkOption {
-        type = nullOr float;
-        default = null;
-        example = 0.4;
-        description = mdDoc ''
-          Reduce output volume (between 0 and 1) to this percentage value while recording.
-        '';
-      };
-
-      echoCancellation = mkEnableOption "acoustic echo cancellation";
-    };
-
-    extraArgs = mkOption {
-      type = listOf str;
-      default = [ ];
-      description = mdDoc ''
-        Extra arguments to pass to the commandline.
-      '';
-      apply = escapeShellArgs;
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services."homeassistant-satellite" = {
-      description = "Home Assistant Satellite";
-      after = [
-        "network-online.target"
-      ];
-      wants = [
-        "network-online.target"
-      ];
-      wantedBy = [
-        "multi-user.target"
-      ];
-      path = with pkgs; [
-        ffmpeg-headless
-      ] ++ lib.optionals (!cfg.pulseaudio.enable) [
-        alsa-utils
-      ];
-      serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
-        # https://github.com/rhasspy/hassio-addons/blob/master/assist_microphone/rootfs/etc/s6-overlay/s6-rc.d/assist_microphone/run
-        ExecStart = ''
-          ${package}/bin/homeassistant-satellite \
-            --host ${cfg.host} \
-            --port ${cfg.port} \
-            --protocol ${cfg.protocol} \
-            --token-file ${cfg.tokenFile} \
-            --vad ${cfg.vad} \
-            ${lib.optionalString cfg.pulseaudio.enable "--pulseaudio"}${lib.optionalString (cfg.pulseaudio.socket != null) "=${cfg.pulseaudio.socket}"} \
-            ${lib.optionalString (cfg.pulseaudio.enable && cfg.pulseaudio.duckingVolume != null) "--ducking-volume=${toString cfg.pulseaudio.duckingVolume}"} \
-            ${lib.optionalString (cfg.pulseaudio.enable && cfg.pulseaudio.echoCancellation) "--echo-cancel"} \
-            ${lib.optionalString (cfg.sounds.awake != null) "--awake-sound=${toString cfg.sounds.awake}"} \
-            ${lib.optionalString (cfg.sounds.done != null) "--done-sound=${toString cfg.sounds.done}"} \
-            ${cfg.extraArgs}
-        '';
-        CapabilityBoundingSet = "";
-        DeviceAllow = "";
-        DevicePolicy = "closed";
-        LockPersonality = true;
-        MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Operation not permitted
-        PrivateDevices = true;
-        PrivateUsers = true;
-        ProtectHome = false; # Would deny access to local pulse/pipewire server
-        ProtectHostname = true;
-        ProtectKernelLogs = true;
-        ProtectKernelModules = true;
-        ProtectKernelTunables = true;
-        ProtectControlGroups = true;
-        ProtectProc = "invisible";
-        ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo
-        Restart = "always";
-        RestrictAddressFamilies = [
-          "AF_INET"
-          "AF_INET6"
-          "AF_UNIX"
-        ];
-        RestrictNamespaces = true;
-        RestrictRealtime = true;
-        SupplementaryGroups = [
-          "audio"
-        ];
-        SystemCallArchitectures = "native";
-        SystemCallFilter = [
-          "@system-service"
-          "~@privileged"
-        ];
-        UMask = "0077";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/audio/wyoming/faster-whisper.nix b/nixos/modules/services/home-automation/wyoming/faster-whisper.nix
index dd7f62744cd02..0c36e8c9ab059 100644
--- a/nixos/modules/services/audio/wyoming/faster-whisper.nix
+++ b/nixos/modules/services/home-automation/wyoming/faster-whisper.nix
@@ -37,22 +37,13 @@ in
             enable = mkEnableOption (mdDoc "Wyoming faster-whisper server");
 
             model = mkOption {
-              # Intersection between available and referenced models here:
-              # https://github.com/rhasspy/models/releases/tag/v1.0
-              # https://github.com/rhasspy/rhasspy3/blob/wyoming-v1/programs/asr/faster-whisper/server/wyoming_faster_whisper/download.py#L17-L27
-              type = enum [
-                "tiny"
-                "tiny-int8"
-                "base"
-                "base-int8"
-                "small"
-                "small-int8"
-                "medium-int8"
-              ];
+              type = str;
               default = "tiny-int8";
-              example = "medium-int8";
+              example = "Systran/faster-distil-whisper-small.en";
               description = mdDoc ''
                 Name of the voice model to use.
+
+                Check the [2.0.0 release notes](https://github.com/rhasspy/wyoming-faster-whisper/releases/tag/v2.0.0) for possible values.
               '';
             };
 
diff --git a/nixos/modules/services/audio/wyoming/openwakeword.nix b/nixos/modules/services/home-automation/wyoming/openwakeword.nix
index 252f70be2baa4..252f70be2baa4 100644
--- a/nixos/modules/services/audio/wyoming/openwakeword.nix
+++ b/nixos/modules/services/home-automation/wyoming/openwakeword.nix
diff --git a/nixos/modules/services/audio/wyoming/piper.nix b/nixos/modules/services/home-automation/wyoming/piper.nix
index 2828fdf078921..2828fdf078921 100644
--- a/nixos/modules/services/audio/wyoming/piper.nix
+++ b/nixos/modules/services/home-automation/wyoming/piper.nix
diff --git a/nixos/modules/services/home-automation/wyoming/satellite.nix b/nixos/modules/services/home-automation/wyoming/satellite.nix
new file mode 100644
index 0000000000000..531d375e703a3
--- /dev/null
+++ b/nixos/modules/services/home-automation/wyoming/satellite.nix
@@ -0,0 +1,244 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+let
+  cfg = config.services.wyoming.satellite;
+
+  inherit (lib)
+    elem
+    escapeShellArgs
+    getExe
+    literalExpression
+    mkOption
+    mkEnableOption
+    mkIf
+    mkPackageOption
+    optional
+    optionals
+    types
+  ;
+
+  finalPackage = cfg.package.overridePythonAttrs (oldAttrs: {
+    propagatedBuildInputs = oldAttrs.propagatedBuildInputs
+      # for audio enhancements like auto-gain, noise suppression
+      ++ cfg.package.optional-dependencies.webrtc
+      # vad is currently optional, because it is broken on aarch64-linux
+      ++ optionals cfg.vad.enable cfg.package.optional-dependencies.silerovad;
+    });
+in
+
+{
+  meta.buildDocsInSandbox = false;
+
+  options.services.wyoming.satellite = with types; {
+    enable = mkEnableOption "Wyoming Satellite";
+
+    package = mkPackageOption pkgs "wyoming-satellite" { };
+
+    user = mkOption {
+      type = str;
+      example = "alice";
+      description = ''
+        User to run wyoming-satellite under.
+      '';
+    };
+
+    group = mkOption {
+      type = str;
+      default = "users";
+      description = ''
+        Group to run wyoming-satellite under.
+      '';
+    };
+
+    uri = mkOption {
+      type = str;
+      default = "tcp://0.0.0.0:10700";
+      description = ''
+        URI where wyoming-satellite will bind its socket.
+      '';
+    };
+
+    name = mkOption {
+      type = str;
+      default = config.networking.hostName;
+      defaultText = literalExpression ''
+        config.networking.hostName
+      '';
+      description = ''
+        Name of the satellite.
+      '';
+    };
+
+    area = mkOption {
+      type = nullOr str;
+      default = null;
+      example = "Kitchen";
+      description = ''
+        Area to the satellite.
+      '';
+    };
+
+    microphone = {
+      command = mkOption {
+        type = str;
+        default = "arecord -r 16000 -c 1 -f S16_LE -t raw";
+        description = ''
+          Program to run for audio input.
+        '';
+      };
+
+      autoGain = mkOption {
+        type = ints.between 0 31;
+        default = 5;
+        example = 15;
+        description = ''
+          Automatic gain control in dbFS, with 31 being the loudest value. Set to 0 to disable.
+        '';
+      };
+
+      noiseSuppression = mkOption {
+        type = ints.between 0 4;
+        default = 2;
+        example = 3;
+        description = ''
+          Noise suppression level with 4 being the maximum suppression,
+          which may cause audio distortion. Set to 0 to disable.
+        '';
+      };
+    };
+
+    sound = {
+      command = mkOption {
+        type = nullOr str;
+        default = "aplay -r 22050 -c 1 -f S16_LE -t raw";
+        description = ''
+          Program to run for sound output.
+        '';
+      };
+    };
+
+    sounds = {
+      awake = mkOption {
+        type = nullOr path;
+        default = null;
+        description = ''
+          Path to audio file in WAV format to play when wake word is detected.
+        '';
+      };
+
+      done = mkOption {
+        type = nullOr path;
+        default = null;
+        description = ''
+          Path to audio file in WAV format to play when voice command recording has ended.
+        '';
+      };
+    };
+
+    vad = {
+      enable = mkOption {
+        type = bool;
+        default = true;
+        description = ''
+          Whether to enable voice activity detection.
+
+          Enabling will result in only streaming audio, when speech gets
+          detected.
+        '';
+      };
+    };
+
+    extraArgs = mkOption {
+      type = listOf str;
+      default = [ ];
+      description = ''
+        Extra arguments to pass to the executable.
+
+        Check `wyoming-satellite --help` for possible options.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services."wyoming-satellite" = {
+      description = "Wyoming Satellite";
+      after = [
+        "network-online.target"
+        "sound.target"
+      ];
+      wants = [
+        "network-online.target"
+        "sound.target"
+      ];
+      wantedBy = [
+        "multi-user.target"
+      ];
+      path = with pkgs; [
+        alsa-utils
+      ];
+      script = let
+        optionalParam = param: argument: optionals (!elem argument [ null 0 false ]) [
+          param argument
+        ];
+      in ''
+        export XDG_RUNTIME_DIR=/run/user/$UID
+        ${escapeShellArgs ([
+          (getExe finalPackage)
+          "--uri" cfg.uri
+          "--name" cfg.name
+          "--mic-command" cfg.microphone.command
+        ]
+        ++ optionalParam "--mic-auto-gain" cfg.microphone.autoGain
+        ++ optionalParam "--mic-noise-suppression" cfg.microphone.noiseSuppression
+        ++ optionalParam "--area" cfg.area
+        ++ optionalParam "--snd-command" cfg.sound.command
+        ++ optionalParam "--awake-wav" cfg.sounds.awake
+        ++ optionalParam "--done-wav" cfg.sounds.done
+        ++ optional cfg.vad.enable "--vad"
+        ++ cfg.extraArgs)}
+      '';
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        # https://github.com/rhasspy/hassio-addons/blob/master/assist_microphone/rootfs/etc/s6-overlay/s6-rc.d/assist_microphone/run
+        CapabilityBoundingSet = "";
+        DeviceAllow = "";
+        DevicePolicy = "closed";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = false; # onnxruntime/capi/onnxruntime_pybind11_state.so: cannot enable executable stack as shared object requires: Operation not permitted
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProtectHome = false; # Would deny access to local pulse/pipewire server
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectControlGroups = true;
+        ProtectProc = "invisible";
+        ProcSubset = "all"; # Error in cpuinfo: failed to parse processor information from /proc/cpuinfo
+        Restart = "always";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+          "AF_UNIX"
+          "AF_NETLINK"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SupplementaryGroups = [
+          "audio"
+        ];
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ];
+        UMask = "0077";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/logging/awstats.nix b/nixos/modules/services/logging/awstats.nix
index 708775bfcf03a..642118650c65e 100644
--- a/nixos/modules/services/logging/awstats.nix
+++ b/nixos/modules/services/logging/awstats.nix
@@ -95,7 +95,7 @@ in
   ];
 
   options.services.awstats = {
-    enable = mkEnableOption (lib.mdDoc "awstats");
+    enable = mkEnableOption (lib.mdDoc "awstats, a real-time logfile analyzer");
 
     dataDir = mkOption {
       type = types.path;
diff --git a/nixos/modules/services/logging/fluentd.nix b/nixos/modules/services/logging/fluentd.nix
index c8718f26db383..cfc584309835c 100644
--- a/nixos/modules/services/logging/fluentd.nix
+++ b/nixos/modules/services/logging/fluentd.nix
@@ -12,7 +12,7 @@ in {
   options = {
 
     services.fluentd = {
-      enable = mkEnableOption (lib.mdDoc "fluentd");
+      enable = mkEnableOption (lib.mdDoc "fluentd, a data/log collector");
 
       config = mkOption {
         type = types.lines;
diff --git a/nixos/modules/services/logging/graylog.nix b/nixos/modules/services/logging/graylog.nix
index 673930c4cb5c6..853539d4d63bf 100644
--- a/nixos/modules/services/logging/graylog.nix
+++ b/nixos/modules/services/logging/graylog.nix
@@ -33,7 +33,7 @@ in
 
     services.graylog = {
 
-      enable = mkEnableOption (lib.mdDoc "Graylog");
+      enable = mkEnableOption (lib.mdDoc "Graylog, a log management solution");
 
       package = mkOption {
         type = types.package;
diff --git a/nixos/modules/services/logging/heartbeat.nix b/nixos/modules/services/logging/heartbeat.nix
index 768ffe5315fe0..84c15b1caeb28 100644
--- a/nixos/modules/services/logging/heartbeat.nix
+++ b/nixos/modules/services/logging/heartbeat.nix
@@ -18,7 +18,7 @@ in
 
     services.heartbeat = {
 
-      enable = mkEnableOption (lib.mdDoc "heartbeat");
+      enable = mkEnableOption (lib.mdDoc "heartbeat, uptime monitoring");
 
       package = mkPackageOption pkgs "heartbeat" {
         example = "heartbeat7";
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 5d87fc87d4161..44763afa9d238 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -109,7 +109,7 @@ in
 {
   options = {
     services.logcheck = {
-      enable = mkEnableOption (lib.mdDoc "logcheck cron job");
+      enable = mkEnableOption (lib.mdDoc "logcheck cron job, to mail anomalies in the system logfiles to the administrator");
 
       user = mkOption {
         default = "logcheck";
diff --git a/nixos/modules/services/logging/logrotate.nix b/nixos/modules/services/logging/logrotate.nix
index ba1445f083975..49cca0cb8112a 100644
--- a/nixos/modules/services/logging/logrotate.nix
+++ b/nixos/modules/services/logging/logrotate.nix
@@ -104,9 +104,12 @@ in
         default = { };
         description = lib.mdDoc ''
           logrotate freeform settings: each attribute here will define its own section,
-          ordered by priority, which can either define files to rotate with their settings
+          ordered by {option}`services.logrotate.settings.<name>.priority`,
+          which can either define files to rotate with their settings
           or settings common to all further files settings.
-          Refer to <https://linux.die.net/man/8/logrotate> for details.
+          All attribute names not explicitly defined as sub-options here are passed through
+          as logrotate config directives,
+          refer to <https://linux.die.net/man/8/logrotate> for details.
         '';
         example = literalExpression ''
           {
@@ -125,6 +128,14 @@ in
                 "/var/log/second.log"
               ];
             };
+            # specify custom order of sections
+            "/var/log/myservice/*.log" = {
+              # ensure lower priority
+              priority = 110;
+              postrotate = '''
+                systemctl reload myservice
+              ''';
+            };
           };
           '';
         type = types.attrsOf (types.submodule ({ name, ... }: {
diff --git a/nixos/modules/services/logging/ulogd.nix b/nixos/modules/services/logging/ulogd.nix
index 05c9797bb28bc..9beb0859cef52 100644
--- a/nixos/modules/services/logging/ulogd.nix
+++ b/nixos/modules/services/logging/ulogd.nix
@@ -8,7 +8,7 @@ let
 in {
   options = {
     services.ulogd = {
-      enable = mkEnableOption (lib.mdDoc "ulogd");
+      enable = mkEnableOption (lib.mdDoc "ulogd, a userspace logging daemon for netfilter/iptables related logging");
 
       settings = mkOption {
         example = {
diff --git a/nixos/modules/services/logging/vector.nix b/nixos/modules/services/logging/vector.nix
index 9ccf8a4fa0610..92605c4030873 100644
--- a/nixos/modules/services/logging/vector.nix
+++ b/nixos/modules/services/logging/vector.nix
@@ -6,7 +6,7 @@ let cfg = config.services.vector;
 in
 {
   options.services.vector = {
-    enable = mkEnableOption (lib.mdDoc "Vector");
+    enable = mkEnableOption (lib.mdDoc "Vector, a high-performance observability data pipeline");
 
     package = mkPackageOption pkgs "vector" { };
 
diff --git a/nixos/modules/services/mail/goeland.nix b/nixos/modules/services/mail/goeland.nix
index 13092a65ed90e..a39d0d27d537b 100644
--- a/nixos/modules/services/mail/goeland.nix
+++ b/nixos/modules/services/mail/goeland.nix
@@ -8,7 +8,7 @@ let
 in
 {
   options.services.goeland = {
-    enable = mkEnableOption (mdDoc "goeland");
+    enable = mkEnableOption (mdDoc "goeland, an alternative to rss2email");
 
     settings = mkOption {
       description = mdDoc ''
diff --git a/nixos/modules/services/mail/mailcatcher.nix b/nixos/modules/services/mail/mailcatcher.nix
index d0f4550c19264..60abf3f2c5a02 100644
--- a/nixos/modules/services/mail/mailcatcher.nix
+++ b/nixos/modules/services/mail/mailcatcher.nix
@@ -11,7 +11,7 @@ in
   options = {
 
     services.mailcatcher = {
-      enable = mkEnableOption (lib.mdDoc "MailCatcher");
+      enable = mkEnableOption (lib.mdDoc "MailCatcher, an SMTP server and web interface to locally test outbound emails");
 
       http.ip = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/mail/mailhog.nix b/nixos/modules/services/mail/mailhog.nix
index 7ae62de291bab..14df891fbb1a4 100644
--- a/nixos/modules/services/mail/mailhog.nix
+++ b/nixos/modules/services/mail/mailhog.nix
@@ -27,7 +27,7 @@ in
   options = {
 
     services.mailhog = {
-      enable = mkEnableOption (lib.mdDoc "MailHog");
+      enable = mkEnableOption (lib.mdDoc "MailHog, web and API based SMTP testing");
 
       storage = mkOption {
         type = types.enum [ "maildir" "memory" ];
diff --git a/nixos/modules/services/mail/mailman.md b/nixos/modules/services/mail/mailman.md
index 55b61f8a25828..446aa1f921b64 100644
--- a/nixos/modules/services/mail/mailman.md
+++ b/nixos/modules/services/mail/mailman.md
@@ -9,7 +9,7 @@ an existing, securely configured Postfix setup, as it does not automatically con
 ## Basic usage with Postfix {#module-services-mailman-basic-usage}
 
 For a basic configuration with Postfix as the MTA, the following settings are suggested:
-```
+```nix
 { config, ... }: {
   services.postfix = {
     enable = true;
@@ -50,7 +50,7 @@ necessary, but outside the scope of the Mailman module.
 ## Using with other MTAs {#module-services-mailman-other-mtas}
 
 Mailman also supports other MTA, though with a little bit more configuration. For example, to use Mailman with Exim, you can use the following settings:
-```
+```nix
 { config, ... }: {
   services = {
     mailman = {
diff --git a/nixos/modules/services/matrix/maubot.md b/nixos/modules/services/matrix/maubot.md
index f6a05db56cafd..d49066057a237 100644
--- a/nixos/modules/services/matrix/maubot.md
+++ b/nixos/modules/services/matrix/maubot.md
@@ -10,7 +10,9 @@ framework for Matrix.
 2. If you want to use PostgreSQL instead of SQLite, do this:
 
    ```nix
-   services.maubot.settings.database = "postgresql://maubot@localhost/maubot";
+   {
+     services.maubot.settings.database = "postgresql://maubot@localhost/maubot";
+   }
    ```
 
    If the PostgreSQL connection requires a password, you will have to
@@ -18,54 +20,58 @@ framework for Matrix.
 3. If you plan to expose your Maubot interface to the web, do something
    like this:
    ```nix
-   services.nginx.virtualHosts."matrix.example.org".locations = {
-     "/_matrix/maubot/" = {
-       proxyPass = "http://127.0.0.1:${toString config.services.maubot.settings.server.port}";
-       proxyWebsockets = true;
+   {
+     services.nginx.virtualHosts."matrix.example.org".locations = {
+       "/_matrix/maubot/" = {
+         proxyPass = "http://127.0.0.1:${toString config.services.maubot.settings.server.port}";
+         proxyWebsockets = true;
+       };
      };
-   };
-   services.maubot.settings.server.public_url = "matrix.example.org";
-   # do the following only if you want to use something other than /_matrix/maubot...
-   services.maubot.settings.server.ui_base_path = "/another/base/path";
+     services.maubot.settings.server.public_url = "matrix.example.org";
+     # do the following only if you want to use something other than /_matrix/maubot...
+     services.maubot.settings.server.ui_base_path = "/another/base/path";
+   }
    ```
 4. Optionally, set `services.maubot.pythonPackages` to a list of python3
    packages to make available for Maubot plugins.
 5. Optionally, set `services.maubot.plugins` to a list of Maubot
    plugins (full list available at https://plugins.maubot.xyz/):
    ```nix
-   services.maubot.plugins = with config.services.maubot.package.plugins; [
-     reactbot
-     # This will only change the default config! After you create a
-     # plugin instance, the default config will be copied into that
-     # instance's config in Maubot's database, and further base config
-     # changes won't affect the running plugin.
-     (rss.override {
-       base_config = {
-         update_interval = 60;
-         max_backoff = 7200;
-         spam_sleep = 2;
-         command_prefix = "rss";
-         admins = [ "@chayleaf:pavluk.org" ];
-       };
-     })
-   ];
-   # ...or...
-   services.maubot.plugins = config.services.maubot.package.plugins.allOfficialPlugins;
-   # ...or...
-   services.maubot.plugins = config.services.maubot.package.plugins.allPlugins;
-   # ...or...
-   services.maubot.plugins = with config.services.maubot.package.plugins; [
-     (weather.override {
-       # you can pass base_config as a string
-       base_config = ''
-         default_location: New York
-         default_units: M
-         default_language:
-         show_link: true
-         show_image: false
-       '';
-     })
-   ];
+   {
+     services.maubot.plugins = with config.services.maubot.package.plugins; [
+       reactbot
+       # This will only change the default config! After you create a
+       # plugin instance, the default config will be copied into that
+       # instance's config in Maubot's database, and further base config
+       # changes won't affect the running plugin.
+       (rss.override {
+         base_config = {
+           update_interval = 60;
+           max_backoff = 7200;
+           spam_sleep = 2;
+           command_prefix = "rss";
+           admins = [ "@chayleaf:pavluk.org" ];
+         };
+       })
+     ];
+     # ...or...
+     services.maubot.plugins = config.services.maubot.package.plugins.allOfficialPlugins;
+     # ...or...
+     services.maubot.plugins = config.services.maubot.package.plugins.allPlugins;
+     # ...or...
+     services.maubot.plugins = with config.services.maubot.package.plugins; [
+       (weather.override {
+         # you can pass base_config as a string
+         base_config = ''
+           default_location: New York
+           default_units: M
+           default_language:
+           show_link: true
+           show_image: false
+         '';
+       })
+     ];
+   }
    ```
 6. Start Maubot at least once before doing the following steps (it's
    necessary to generate the initial config).
diff --git a/nixos/modules/services/matrix/mautrix-meta.nix b/nixos/modules/services/matrix/mautrix-meta.nix
new file mode 100644
index 0000000000000..f0905c3af1297
--- /dev/null
+++ b/nixos/modules/services/matrix/mautrix-meta.nix
@@ -0,0 +1,562 @@
+{ config, pkgs, lib, ... }:
+
+let
+  settingsFormat = pkgs.formats.yaml {};
+
+  upperConfig = config;
+  cfg = config.services.mautrix-meta;
+  upperCfg = cfg;
+
+  fullDataDir = cfg: "/var/lib/${cfg.dataDir}";
+
+  settingsFile = cfg: "${fullDataDir cfg}/config.yaml";
+  settingsFileUnsubstituted = cfg: settingsFormat.generate "mautrix-meta-config.yaml" cfg.settings;
+
+  metaName = name: "mautrix-meta-${name}";
+
+  enabledInstances = lib.filterAttrs (name: config: config.enable) config.services.mautrix-meta.instances;
+  registerToSynapseInstances = lib.filterAttrs (name: config: config.enable && config.registerToSynapse) config.services.mautrix-meta.instances;
+in {
+  options = {
+    services.mautrix-meta = {
+
+      package = lib.mkPackageOption pkgs "mautrix-meta" { };
+
+      instances = lib.mkOption {
+        type = lib.types.attrsOf (lib.types.submodule ({ config, name, ... }: {
+
+          options = {
+
+            enable = lib.mkEnableOption "Mautrix-Meta, a Matrix <-> Facebook and Matrix <-> Instagram hybrid puppeting/relaybot bridge";
+
+            dataDir = lib.mkOption {
+              type = lib.types.str;
+              default = metaName name;
+              description = ''
+                Path to the directory with database, registration, and other data for the bridge service.
+                This path is relative to `/var/lib`, it cannot start with `../` (it cannot be outside of `/var/lib`).
+              '';
+            };
+
+            registrationFile = lib.mkOption {
+              type = lib.types.path;
+              readOnly = true;
+              description = ''
+                Path to the yaml registration file of the appservice.
+              '';
+            };
+
+            registerToSynapse = lib.mkOption {
+              type = lib.types.bool;
+              default = true;
+              description = ''
+                Whether to add registration file to `services.matrix-synapse.settings.app_service_config_files` and
+                make Synapse wait for registration service.
+              '';
+            };
+
+            settings = lib.mkOption rec {
+              apply = lib.recursiveUpdate default;
+              inherit (settingsFormat) type;
+              default = {
+                homeserver = {
+                  software = "standard";
+
+                  domain = "";
+                  address = "";
+                };
+
+                appservice = {
+                  id = "";
+
+                  database = {
+                    type = "sqlite3-fk-wal";
+                    uri = "file:${fullDataDir config}/mautrix-meta.db?_txlock=immediate";
+                  };
+
+                  bot = {
+                    username = "";
+                  };
+
+                  hostname = "localhost";
+                  port = 29319;
+                  address = "http://${config.settings.appservice.hostname}:${toString config.settings.appservice.port}";
+                };
+
+                meta = {
+                  mode = "";
+                };
+
+                bridge = {
+                  # Enable encryption by default to make the bridge more secure
+                  encryption = {
+                    allow = true;
+                    default = true;
+                    require = true;
+
+                    # Recommended options from mautrix documentation
+                    # for additional security.
+                    delete_keys = {
+                      dont_store_outbound = true;
+                      ratchet_on_decrypt = true;
+                      delete_fully_used_on_decrypt = true;
+                      delete_prev_on_new_session = true;
+                      delete_on_device_delete = true;
+                      periodically_delete_expired = true;
+                      delete_outdated_inbound = true;
+                    };
+
+                    verification_levels = {
+                      receive = "cross-signed-tofu";
+                      send = "cross-signed-tofu";
+                      share = "cross-signed-tofu";
+                    };
+                  };
+
+                  permissions = {};
+                };
+
+                logging = {
+                  min_level = "info";
+                  writers = lib.singleton {
+                    type = "stdout";
+                    format = "pretty-colored";
+                    time_format = " ";
+                  };
+                };
+              };
+              defaultText = ''
+              {
+                homeserver = {
+                  software = "standard";
+                  address = "https://''${config.settings.homeserver.domain}";
+                };
+
+                appservice = {
+                  database = {
+                    type = "sqlite3-fk-wal";
+                    uri = "file:''${fullDataDir config}/mautrix-meta.db?_txlock=immediate";
+                  };
+
+                  hostname = "localhost";
+                  port = 29319;
+                  address = "http://''${config.settings.appservice.hostname}:''${toString config.settings.appservice.port}";
+                };
+
+                bridge = {
+                  # Require encryption by default to make the bridge more secure
+                  encryption = {
+                    allow = true;
+                    default = true;
+                    require = true;
+
+                    # Recommended options from mautrix documentation
+                    # for optimal security.
+                    delete_keys = {
+                      dont_store_outbound = true;
+                      ratchet_on_decrypt = true;
+                      delete_fully_used_on_decrypt = true;
+                      delete_prev_on_new_session = true;
+                      delete_on_device_delete = true;
+                      periodically_delete_expired = true;
+                      delete_outdated_inbound = true;
+                    };
+
+                    verification_levels = {
+                      receive = "cross-signed-tofu";
+                      send = "cross-signed-tofu";
+                      share = "cross-signed-tofu";
+                    };
+                  };
+                };
+
+                logging = {
+                  min_level = "info";
+                  writers = lib.singleton {
+                    type = "stdout";
+                    format = "pretty-colored";
+                    time_format = " ";
+                  };
+                };
+              };
+              '';
+              description = ''
+                {file}`config.yaml` configuration as a Nix attribute set.
+                Configuration options should match those described in
+                [example-config.yaml](https://github.com/mautrix/meta/blob/main/example-config.yaml).
+
+                Secret tokens should be specified using {option}`environmentFile`
+                instead
+              '';
+            };
+
+            environmentFile = lib.mkOption {
+              type = lib.types.nullOr lib.types.path;
+              default = null;
+              description = ''
+                File containing environment variables to substitute when copying the configuration
+                out of Nix store to the `services.mautrix-meta.dataDir`.
+
+                Can be used for storing the secrets without making them available in the Nix store.
+
+                For example, you can set `services.mautrix-meta.settings.appservice.as_token = "$MAUTRIX_META_APPSERVICE_AS_TOKEN"`
+                and then specify `MAUTRIX_META_APPSERVICE_AS_TOKEN="{token}"` in the environment file.
+                This value will get substituted into the configuration file as as token.
+              '';
+            };
+
+            serviceDependencies = lib.mkOption {
+              type = lib.types.listOf lib.types.str;
+              default =
+                [ config.registrationServiceUnit ] ++
+                (lib.lists.optional upperConfig.services.matrix-synapse.enable upperConfig.services.matrix-synapse.serviceUnit) ++
+                (lib.lists.optional upperConfig.services.matrix-conduit.enable "matrix-conduit.service") ++
+                (lib.lists.optional upperConfig.services.dendrite.enable "dendrite.service");
+
+              defaultText = ''
+                [ config.registrationServiceUnit ] ++
+                (lib.lists.optional upperConfig.services.matrix-synapse.enable upperConfig.services.matrix-synapse.serviceUnit) ++
+                (lib.lists.optional upperConfig.services.matrix-conduit.enable "matrix-conduit.service") ++
+                (lib.lists.optional upperConfig.services.dendrite.enable "dendrite.service");
+              '';
+              description = ''
+                List of Systemd services to require and wait for when starting the application service.
+              '';
+            };
+
+            serviceUnit = lib.mkOption {
+              type = lib.types.str;
+              readOnly = true;
+              description = ''
+                The systemd unit (a service or a target) for other services to depend on if they
+                need to be started after matrix-synapse.
+
+                This option is useful as the actual parent unit for all matrix-synapse processes
+                changes when configuring workers.
+              '';
+            };
+
+            registrationServiceUnit = lib.mkOption {
+              type = lib.types.str;
+              readOnly = true;
+              description = ''
+                The registration service that generates the registration file.
+
+                Systemd unit (a service or a target) for other services to depend on if they
+                need to be started after mautrix-meta registration service.
+
+                This option is useful as the actual parent unit for all matrix-synapse processes
+                changes when configuring workers.
+              '';
+            };
+          };
+
+          config = {
+            serviceUnit = (metaName name) + ".service";
+            registrationServiceUnit = (metaName name) + "-registration.service";
+            registrationFile = (fullDataDir config) + "/meta-registration.yaml";
+          };
+        }));
+
+        description = ''
+          Configuration of multiple `mautrix-meta` instances.
+          `services.mautrix-meta.instances.facebook` and `services.mautrix-meta.instances.instagram`
+          come preconfigured with meta.mode, appservice.id, bot username, display name and avatar.
+        '';
+
+        example = ''
+          {
+            facebook = {
+              enable = true;
+              settings = {
+                homeserver.domain = "example.com";
+              };
+            };
+
+            instagram = {
+              enable = true;
+              settings = {
+                homeserver.domain = "example.com";
+              };
+            };
+
+            messenger = {
+              enable = true;
+              settings = {
+                meta.mode = "messenger";
+                homeserver.domain = "example.com";
+                appservice = {
+                  id = "messenger";
+                  bot = {
+                    username = "messengerbot";
+                    displayname = "Messenger bridge bot";
+                    avatar = "mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak";
+                  };
+                };
+              };
+            };
+          }
+        '';
+      };
+    };
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf (enabledInstances != {}) {
+      assertions = lib.mkMerge (lib.attrValues (lib.mapAttrs (name: cfg: [
+        {
+          assertion = cfg.settings.homeserver.domain != "" && cfg.settings.homeserver.address != "";
+          message = ''
+            The options with information about the homeserver:
+            `services.mautrix-meta.instances.${name}.settings.homeserver.domain` and
+            `services.mautrix-meta.instances.${name}.settings.homeserver.address` have to be set.
+          '';
+        }
+        {
+          assertion = builtins.elem cfg.settings.meta.mode [ "facebook" "facebook-tor" "messenger" "instagram" ];
+          message = ''
+            The option `services.mautrix-meta.instances.${name}.settings.meta.mode` has to be set
+            to one of: facebook, facebook-tor, messenger, instagram.
+            This configures the mode of the bridge.
+          '';
+        }
+        {
+          assertion = cfg.settings.bridge.permissions != {};
+          message = ''
+            The option `services.mautrix-meta.instances.${name}.settings.bridge.permissions` has to be set.
+          '';
+        }
+        {
+          assertion = cfg.settings.appservice.id != "";
+          message = ''
+            The option `services.mautrix-meta.instances.${name}.settings.appservice.id` has to be set.
+          '';
+        }
+        {
+          assertion = cfg.settings.appservice.bot.username != "";
+          message = ''
+            The option `services.mautrix-meta.instances.${name}.settings.appservice.bot.username` has to be set.
+          '';
+        }
+      ]) enabledInstances));
+
+      users.users = lib.mapAttrs' (name: cfg: lib.nameValuePair "mautrix-meta-${name}" {
+        isSystemUser = true;
+        group = "mautrix-meta";
+        extraGroups = [ "mautrix-meta-registration" ];
+        description = "Mautrix-Meta-${name} bridge user";
+      }) enabledInstances;
+
+      users.groups.mautrix-meta = {};
+      users.groups.mautrix-meta-registration = {
+        members = lib.lists.optional config.services.matrix-synapse.enable "matrix-synapse";
+      };
+
+      services.matrix-synapse = lib.mkIf (config.services.matrix-synapse.enable) (let
+        registrationFiles = lib.attrValues
+          (lib.mapAttrs (name: cfg: cfg.registrationFile) registerToSynapseInstances);
+      in {
+        settings.app_service_config_files = registrationFiles;
+      });
+
+      systemd.services = lib.mkMerge [
+        {
+          matrix-synapse = lib.mkIf (config.services.matrix-synapse.enable) (let
+            registrationServices = lib.attrValues
+              (lib.mapAttrs (name: cfg: cfg.registrationServiceUnit) registerToSynapseInstances);
+          in {
+            wants = registrationServices;
+            after = registrationServices;
+          });
+        }
+
+        (lib.mapAttrs' (name: cfg: lib.nameValuePair "${metaName name}-registration" {
+          description = "Mautrix-Meta registration generation service - ${metaName name}";
+
+          path = [
+            pkgs.yq
+            pkgs.envsubst
+            upperCfg.package
+          ];
+
+          script = ''
+            # substitute the settings file by environment variables
+            # in this case read from EnvironmentFile
+            rm -f '${settingsFile cfg}'
+            old_umask=$(umask)
+            umask 0177
+            envsubst \
+              -o '${settingsFile cfg}' \
+              -i '${settingsFileUnsubstituted cfg}'
+
+            config_has_tokens=$(yq '.appservice | has("as_token") and has("hs_token")' '${settingsFile cfg}')
+            registration_already_exists=$([[ -f '${cfg.registrationFile}' ]] && echo "true" || echo "false")
+
+            echo "There are tokens in the config: $config_has_tokens"
+            echo "Registration already existed: $registration_already_exists"
+
+            # tokens not configured from config/environment file, and registration file
+            # is already generated, override tokens in config to make sure they are not lost
+            if [[ $config_has_tokens == "false" && $registration_already_exists == "true" ]]; then
+              echo "Copying as_token, hs_token from registration into configuration"
+              yq -sY '.[0].appservice.as_token = .[1].as_token
+                | .[0].appservice.hs_token = .[1].hs_token
+                | .[0]' '${settingsFile cfg}' '${cfg.registrationFile}' \
+                > '${settingsFile cfg}.tmp'
+              mv '${settingsFile cfg}.tmp' '${settingsFile cfg}'
+            fi
+
+            # make sure --generate-registration does not affect config.yaml
+            cp '${settingsFile cfg}' '${settingsFile cfg}.tmp'
+
+            echo "Generating registration file"
+            mautrix-meta \
+              --generate-registration \
+              --config='${settingsFile cfg}.tmp' \
+              --registration='${cfg.registrationFile}'
+
+            rm '${settingsFile cfg}.tmp'
+
+            # no tokens configured, and new were just generated by generate registration for first time
+            if [[ $config_has_tokens == "false" && $registration_already_exists == "false" ]]; then
+              echo "Copying newly generated as_token, hs_token from registration into configuration"
+              yq -sY '.[0].appservice.as_token = .[1].as_token
+                | .[0].appservice.hs_token = .[1].hs_token
+                | .[0]' '${settingsFile cfg}' '${cfg.registrationFile}' \
+                > '${settingsFile cfg}.tmp'
+              mv '${settingsFile cfg}.tmp' '${settingsFile cfg}'
+            fi
+
+            # Make sure correct tokens are in the registration file
+            if [[ $config_has_tokens == "true" || $registration_already_exists == "true" ]]; then
+              echo "Copying as_token, hs_token from configuration to the registration file"
+              yq -sY '.[1].as_token = .[0].appservice.as_token
+                | .[1].hs_token = .[0].appservice.hs_token
+                | .[1]' '${settingsFile cfg}' '${cfg.registrationFile}' \
+                > '${cfg.registrationFile}.tmp'
+              mv '${cfg.registrationFile}.tmp' '${cfg.registrationFile}'
+            fi
+
+            umask $old_umask
+
+            chown :mautrix-meta-registration '${cfg.registrationFile}'
+            chmod 640 '${cfg.registrationFile}'
+          '';
+
+          serviceConfig = {
+            Type = "oneshot";
+            UMask = 0027;
+
+            User = "mautrix-meta-${name}";
+            Group = "mautrix-meta";
+
+            SystemCallFilter = [ "@system-service" ];
+
+            ProtectSystem = "strict";
+            ProtectHome = true;
+
+            ReadWritePaths = fullDataDir cfg;
+            StateDirectory = cfg.dataDir;
+            EnvironmentFile = cfg.environmentFile;
+          };
+
+          restartTriggers = [ (settingsFileUnsubstituted cfg) ];
+        }) enabledInstances)
+
+        (lib.mapAttrs' (name: cfg: lib.nameValuePair "${metaName name}" {
+          description = "Mautrix-Meta bridge - ${metaName name}";
+          wantedBy = [ "multi-user.target" ];
+          wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+          after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+
+          serviceConfig = {
+            Type = "simple";
+
+            User = "mautrix-meta-${name}";
+            Group = "mautrix-meta";
+            PrivateUsers = true;
+
+            LockPersonality = true;
+            MemoryDenyWriteExecute = true;
+            NoNewPrivileges = true;
+            PrivateDevices = true;
+            PrivateTmp = true;
+            ProtectClock = true;
+            ProtectControlGroups = true;
+            ProtectHome = true;
+            ProtectHostname = true;
+            ProtectKernelLogs = true;
+            ProtectKernelModules = true;
+            ProtectKernelTunables = true;
+            ProtectSystem = "strict";
+            Restart = "on-failure";
+            RestartSec = "30s";
+            RestrictRealtime = true;
+            RestrictSUIDSGID = true;
+            SystemCallArchitectures = "native";
+            SystemCallErrorNumber = "EPERM";
+            SystemCallFilter = ["@system-service"];
+            UMask = 0027;
+
+            WorkingDirectory = fullDataDir cfg;
+            ReadWritePaths = fullDataDir cfg;
+            StateDirectory = cfg.dataDir;
+            EnvironmentFile = cfg.environmentFile;
+
+            ExecStart = lib.escapeShellArgs [
+              (lib.getExe upperCfg.package)
+              "--config=${settingsFile cfg}"
+            ];
+          };
+          restartTriggers = [ (settingsFileUnsubstituted cfg) ];
+        }) enabledInstances)
+      ];
+    })
+    {
+      services.mautrix-meta.instances = let
+        inherit (lib.modules) mkDefault;
+      in {
+        instagram = {
+          settings = {
+            meta.mode = mkDefault "instagram";
+
+            bridge = {
+              username_template = mkDefault "instagram_{{.}}";
+            };
+
+            appservice = {
+              id = mkDefault "instagram";
+              port = mkDefault 29320;
+              bot = {
+                username = mkDefault "instagrambot";
+                displayname = mkDefault "Instagram bridge bot";
+                avatar = mkDefault "mxc://maunium.net/JxjlbZUlCPULEeHZSwleUXQv";
+              };
+            };
+          };
+        };
+        facebook = {
+          settings = {
+            meta.mode = mkDefault "facebook";
+
+            bridge = {
+              username_template = mkDefault "facebook_{{.}}";
+            };
+
+            appservice = {
+              id = mkDefault "facebook";
+              port = mkDefault 29321;
+              bot = {
+                username = mkDefault "facebookbot";
+                displayname = mkDefault "Facebook bridge bot";
+                avatar = mkDefault "mxc://maunium.net/ygtkteZsXnGJLJHRchUwYWak";
+              };
+            };
+          };
+        };
+      };
+    }
+  ];
+
+  meta.maintainers = with lib.maintainers; [ rutherther ];
+}
diff --git a/nixos/modules/services/matrix/mjolnir.md b/nixos/modules/services/matrix/mjolnir.md
index f6994eeb8fa5b..2594f05ce27bb 100644
--- a/nixos/modules/services/matrix/mjolnir.md
+++ b/nixos/modules/services/matrix/mjolnir.md
@@ -46,7 +46,7 @@ autoconfigure a new Pantalaimon instance, which will connect to the homeserver
 set in [services.mjolnir.homeserverUrl](#opt-services.mjolnir.homeserverUrl) and Mjolnir itself
 will be configured to connect to the new Pantalaimon instance.
 
-```
+```nix
 {
   services.mjolnir = {
     enable = true;
@@ -78,7 +78,7 @@ uses across an entire homeserver.
 To use the Antispam Module, add `matrix-synapse-plugins.matrix-synapse-mjolnir-antispam`
 to the Synapse plugin list and enable the `mjolnir.Module` module.
 
-```
+```nix
 {
   services.matrix-synapse = {
     plugins = with pkgs; [
diff --git a/nixos/modules/services/matrix/synapse.md b/nixos/modules/services/matrix/synapse.md
index 9c9c025fc5f54..7f6587ce09df8 100644
--- a/nixos/modules/services/matrix/synapse.md
+++ b/nixos/modules/services/matrix/synapse.md
@@ -23,7 +23,7 @@ synapse server for the `example.org` domain, served from
 the host `myhostname.example.org`. For more information,
 please refer to the
 [installation instructions of Synapse](https://element-hq.github.io/synapse/latest/setup/installation.html) .
-```
+```nix
 { pkgs, lib, config, ... }:
 let
   fqdn = "${config.networking.hostName}.${config.networking.domain}";
@@ -158,7 +158,7 @@ in an additional file like this:
     by `matrix-synapse`.
   - Include the file like this in your configuration:
 
-    ```
+    ```nix
     {
       services.matrix-synapse.extraConfigFiles = [
         "/run/secrets/matrix-shared-secret"
@@ -190,7 +190,7 @@ fill in the required connection details automatically when you enter your
 Matrix Identifier. See
 [Try Matrix Now!](https://matrix.org/docs/projects/try-matrix-now.html)
 for a list of existing clients and their supported featureset.
-```
+```nix
 {
   services.nginx.virtualHosts."element.${fqdn}" = {
     enableACME = true;
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index e3f9c7742cc7d..e6b8462642dda 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -367,7 +367,7 @@ in {
     });
   in {
     services.matrix-synapse = {
-      enable = mkEnableOption (lib.mdDoc "matrix.org synapse");
+      enable = mkEnableOption (lib.mdDoc "matrix.org synapse, the reference homeserver");
 
       enableRegistrationScript = mkOption {
         type = types.bool;
@@ -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/anki-sync-server.md b/nixos/modules/services/misc/anki-sync-server.md
index 5d2b4da4d2fc2..f58d3d8ad0dab 100644
--- a/nixos/modules/services/misc/anki-sync-server.md
+++ b/nixos/modules/services/misc/anki-sync-server.md
@@ -16,7 +16,7 @@ unit which runs the sync server with an isolated user using the systemd
 `DynamicUser` option.
 
 This can be done by enabling the `anki-sync-server` service:
-```
+```nix
 { ... }:
 
 {
@@ -27,7 +27,7 @@ This can be done by enabling the `anki-sync-server` service:
 It is necessary to set at least one username-password pair under
 {option}`services.anki-sync-server.users`. For example
 
-```
+```nix
 {
   services.anki-sync-server.users = [
     {
@@ -50,7 +50,7 @@ you want to expose the sync server directly to other computers (not recommended
 in most circumstances, because the sync server doesn't use HTTPS), then set the
 following options:
 
-```
+```nix
 {
   services.anki-sync-server.host = "0.0.0.0";
   services.anki-sync-server.openFirewall = true;
diff --git a/nixos/modules/services/misc/ankisyncd.nix b/nixos/modules/services/misc/ankisyncd.nix
index f5acfbb0ee969..cb8671ebc5e67 100644
--- a/nixos/modules/services/misc/ankisyncd.nix
+++ b/nixos/modules/services/misc/ankisyncd.nix
@@ -22,7 +22,7 @@ let
 in
   {
     options.services.ankisyncd = {
-      enable = mkEnableOption (lib.mdDoc "ankisyncd");
+      enable = mkEnableOption (lib.mdDoc "ankisyncd, a standalone unofficial anky sync server");
 
       package = mkPackageOption pkgs "ankisyncd" { };
 
diff --git a/nixos/modules/services/misc/autorandr.nix b/nixos/modules/services/misc/autorandr.nix
index aa96acb613067..22d1fb727477c 100644
--- a/nixos/modules/services/misc/autorandr.nix
+++ b/nixos/modules/services/misc/autorandr.nix
@@ -260,6 +260,12 @@ in {
         description = lib.mdDoc "Treat outputs as connected even if their lids are closed";
       };
 
+      matchEdid = mkOption {
+        default = false;
+        type = types.bool;
+        description = lib.mdDoc "Match displays based on edid instead of name";
+      };
+
       hooks = mkOption {
         type = hooksModule;
         description = lib.mdDoc "Global hook scripts";
@@ -351,7 +357,8 @@ in {
             --batch \
             --change \
             --default ${cfg.defaultTarget} \
-            ${optionalString cfg.ignoreLid "--ignore-lid"}
+            ${optionalString cfg.ignoreLid "--ignore-lid"} \
+            ${optionalString cfg.matchEdid "--match-edid"}
         '';
         Type = "oneshot";
         RemainAfterExit = false;
diff --git a/nixos/modules/services/misc/bepasty.nix b/nixos/modules/services/misc/bepasty.nix
index 70d07629493b6..1061a78983784 100644
--- a/nixos/modules/services/misc/bepasty.nix
+++ b/nixos/modules/services/misc/bepasty.nix
@@ -13,7 +13,7 @@ let
 in
 {
   options.services.bepasty = {
-    enable = mkEnableOption (lib.mdDoc "Bepasty servers");
+    enable = mkEnableOption (lib.mdDoc "bepasty, a binary pastebin server");
 
     servers = mkOption {
       default = {};
diff --git a/nixos/modules/services/misc/calibre-server.nix b/nixos/modules/services/misc/calibre-server.nix
index 66ae5fa91bb68..efc4b17c88747 100644
--- a/nixos/modules/services/misc/calibre-server.nix
+++ b/nixos/modules/services/misc/calibre-server.nix
@@ -32,7 +32,7 @@ in
   options = {
     services.calibre-server = {
 
-      enable = mkEnableOption (lib.mdDoc "calibre-server");
+      enable = mkEnableOption (lib.mdDoc "calibre-server (e-book software)");
       package = lib.mkPackageOption pkgs "calibre" { };
 
       libraries = mkOption {
diff --git a/nixos/modules/services/misc/confd.nix b/nixos/modules/services/misc/confd.nix
index 93731547ede8b..98776f6e839f7 100644
--- a/nixos/modules/services/misc/confd.nix
+++ b/nixos/modules/services/misc/confd.nix
@@ -17,7 +17,7 @@ let
 
 in {
   options.services.confd = {
-    enable = mkEnableOption (lib.mdDoc "confd service");
+    enable = mkEnableOption (lib.mdDoc "confd, a service to manage local application configuration files using templates and data from etcd/consul/redis/zookeeper");
 
     backend = mkOption {
       description = lib.mdDoc "Confd config storage backend to use.";
diff --git a/nixos/modules/services/misc/etebase-server.nix b/nixos/modules/services/misc/etebase-server.nix
index f5a5e8a780d48..6ec3807f0fb20 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/etesync-dav.nix b/nixos/modules/services/misc/etesync-dav.nix
index ae2b5ad043433..ef2ba9086af46 100644
--- a/nixos/modules/services/misc/etesync-dav.nix
+++ b/nixos/modules/services/misc/etesync-dav.nix
@@ -7,7 +7,7 @@ let
 in
   {
     options.services.etesync-dav = {
-      enable = mkEnableOption (lib.mdDoc "etesync-dav");
+      enable = mkEnableOption (lib.mdDoc "etesync-dav, end-to-end encrypted sync for contacts, calendars and tasks");
 
       host = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/misc/evdevremapkeys.nix b/nixos/modules/services/misc/evdevremapkeys.nix
index 11ea6a5f03f23..d623c038b174f 100644
--- a/nixos/modules/services/misc/evdevremapkeys.nix
+++ b/nixos/modules/services/misc/evdevremapkeys.nix
@@ -8,7 +8,7 @@ let
 in
 {
   options.services.evdevremapkeys = {
-    enable = mkEnableOption (lib.mdDoc ''evdevremapkeys'');
+    enable = mkEnableOption (lib.mdDoc ''evdevremapkeys, a daemon to remap events on linux input devices'');
 
     settings = mkOption {
       type = format.type;
diff --git a/nixos/modules/services/misc/forgejo.md b/nixos/modules/services/misc/forgejo.md
index 14b21933e6b09..f234ebf44aefb 100644
--- a/nixos/modules/services/misc/forgejo.md
+++ b/nixos/modules/services/misc/forgejo.md
@@ -57,23 +57,25 @@ locations and database, instead of having to copy or rename them.
 Make sure to disable `services.gitea`, when doing this.
 
 ```nix
-services.gitea.enable = false;
-
-services.forgejo = {
-  enable = true;
-  user = "gitea";
-  group = "gitea";
-  stateDir = "/var/lib/gitea";
-  database.name = "gitea";
-  database.user = "gitea";
-};
-
-users.users.gitea = {
-  home = "/var/lib/gitea";
-  useDefaultShell = true;
-  group = "gitea";
-  isSystemUser = true;
-};
-
-users.groups.gitea = {};
+{
+  services.gitea.enable = false;
+
+  services.forgejo = {
+    enable = true;
+    user = "gitea";
+    group = "gitea";
+    stateDir = "/var/lib/gitea";
+    database.name = "gitea";
+    database.user = "gitea";
+  };
+
+  users.users.gitea = {
+    home = "/var/lib/gitea";
+    useDefaultShell = true;
+    group = "gitea";
+    isSystemUser = true;
+  };
+
+  users.groups.gitea = {};
+}
 ```
diff --git a/nixos/modules/services/misc/forgejo.nix b/nixos/modules/services/misc/forgejo.nix
index 08cddc3a07105..d9fd33ddedb1a 100644
--- a/nixos/modules/services/misc/forgejo.nix
+++ b/nixos/modules/services/misc/forgejo.nix
@@ -55,7 +55,7 @@ in
 
   options = {
     services.forgejo = {
-      enable = mkEnableOption (mdDoc "Forgejo");
+      enable = mkEnableOption (mdDoc "Forgejo, a software forge");
 
       package = mkPackageOption pkgs "forgejo" { };
 
@@ -114,11 +114,11 @@ in
 
         port = mkOption {
           type = types.port;
-          default = if !usePostgresql then 3306 else pg.port;
+          default = if usePostgresql then pg.settings.port else 3306;
           defaultText = literalExpression ''
             if config.${opt.database.type} != "postgresql"
             then 3306
-            else config.${options.services.postgresql.port}
+            else 5432
           '';
           description = mdDoc "Database host port.";
         };
diff --git a/nixos/modules/services/misc/geoipupdate.nix b/nixos/modules/services/misc/geoipupdate.nix
index 27c1157e9a8c7..f18645ffb5895 100644
--- a/nixos/modules/services/misc/geoipupdate.nix
+++ b/nixos/modules/services/misc/geoipupdate.nix
@@ -12,7 +12,7 @@ in
   options = {
     services.geoipupdate = {
       enable = lib.mkEnableOption (lib.mdDoc ''
-        periodic downloading of GeoIP databases using geoipupdate.
+        periodic downloading of GeoIP databases using geoipupdate
       '');
 
       interval = lib.mkOption {
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index 08feea853e470..13617931c23e4 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -100,11 +100,11 @@ in
 
         port = mkOption {
           type = types.port;
-          default = if !usePostgresql then 3306 else pg.port;
+          default = if usePostgresql then pg.settings.port else 3306;
           defaultText = literalExpression ''
             if config.${opt.database.type} != "postgresql"
             then 3306
-            else config.${options.services.postgresql.port}
+            else 5432
           '';
           description = lib.mdDoc "Database host port.";
         };
diff --git a/nixos/modules/services/misc/gitlab.md b/nixos/modules/services/misc/gitlab.md
index 916b23584ed0c..f7a5a80274890 100644
--- a/nixos/modules/services/misc/gitlab.md
+++ b/nixos/modules/services/misc/gitlab.md
@@ -10,19 +10,21 @@ configure a webserver to proxy HTTP requests to the socket.
 
 For instance, the following configuration could be used to use nginx as
 frontend proxy:
-```
-services.nginx = {
-  enable = true;
-  recommendedGzipSettings = true;
-  recommendedOptimisation = true;
-  recommendedProxySettings = true;
-  recommendedTlsSettings = true;
-  virtualHosts."git.example.com" = {
-    enableACME = true;
-    forceSSL = true;
-    locations."/".proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket";
+```nix
+{
+  services.nginx = {
+    enable = true;
+    recommendedGzipSettings = true;
+    recommendedOptimisation = true;
+    recommendedProxySettings = true;
+    recommendedTlsSettings = true;
+    virtualHosts."git.example.com" = {
+      enableACME = true;
+      forceSSL = true;
+      locations."/".proxyPass = "http://unix:/run/gitlab/gitlab-workhorse.socket";
+    };
   };
-};
+}
 ```
 
 ## Configuring {#module-services-gitlab-configuring}
@@ -35,36 +37,38 @@ The default state dir is `/var/gitlab/state`. This is where
 all data like the repositories and uploads will be stored.
 
 A basic configuration with some custom settings could look like this:
-```
-services.gitlab = {
-  enable = true;
-  databasePasswordFile = "/var/keys/gitlab/db_password";
-  initialRootPasswordFile = "/var/keys/gitlab/root_password";
-  https = true;
-  host = "git.example.com";
-  port = 443;
-  user = "git";
-  group = "git";
-  smtp = {
+```nix
+{
+  services.gitlab = {
     enable = true;
-    address = "localhost";
-    port = 25;
-  };
-  secrets = {
-    dbFile = "/var/keys/gitlab/db";
-    secretFile = "/var/keys/gitlab/secret";
-    otpFile = "/var/keys/gitlab/otp";
-    jwsFile = "/var/keys/gitlab/jws";
-  };
-  extraConfig = {
-    gitlab = {
-      email_from = "gitlab-no-reply@example.com";
-      email_display_name = "Example GitLab";
-      email_reply_to = "gitlab-no-reply@example.com";
-      default_projects_features = { builds = false; };
+    databasePasswordFile = "/var/keys/gitlab/db_password";
+    initialRootPasswordFile = "/var/keys/gitlab/root_password";
+    https = true;
+    host = "git.example.com";
+    port = 443;
+    user = "git";
+    group = "git";
+    smtp = {
+      enable = true;
+      address = "localhost";
+      port = 25;
+    };
+    secrets = {
+      dbFile = "/var/keys/gitlab/db";
+      secretFile = "/var/keys/gitlab/secret";
+      otpFile = "/var/keys/gitlab/otp";
+      jwsFile = "/var/keys/gitlab/jws";
+    };
+    extraConfig = {
+      gitlab = {
+        email_from = "gitlab-no-reply@example.com";
+        email_display_name = "Example GitLab";
+        email_reply_to = "gitlab-no-reply@example.com";
+        default_projects_features = { builds = false; };
+      };
     };
   };
-};
+}
 ```
 
 If you're setting up a new GitLab instance, generate new
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index ec347a75f063e..6619949f5540f 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -901,6 +901,16 @@ in {
         '';
       };
 
+      sidekiq.concurrency = mkOption {
+        type = with types; nullOr int;
+        default = null;
+        description = lib.mdDoc ''
+          How many processor threads to use for processing sidekiq background job queues. When null, the GitLab default is used.
+
+          See <https://docs.gitlab.com/ee/administration/sidekiq/extra_sidekiq_processes.html#manage-thread-counts-explicitly> for details.
+        '';
+      };
+
       sidekiq.memoryKiller.enable = mkOption {
         type = types.bool;
         default = true;
@@ -1439,6 +1449,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 +1464,17 @@ 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"
+            "*" # all queue groups
+          ] ++ lib.optionals (cfg.sidekiq.concurrency != null) [
+            "--concurrency" (toString cfg.sidekiq.concurrency)
+          ] ++ [
+            "--environment" "production"
+            "--require" "."
+          ]
+        );
       };
     };
 
@@ -1550,7 +1572,7 @@ in {
         gnutar
         gzip
         openssh
-        gitlab-workhorse
+        cfg.packages.gitlab-workhorse
       ];
       serviceConfig = {
         Type = "simple";
@@ -1571,7 +1593,9 @@ in {
           rm "${cfg.statePath}/config/gitlab-workhorse.json"
         '';
         ExecStart =
-          "${cfg.packages.gitlab-workhorse}/bin/workhorse "
+          "${cfg.packages.gitlab-workhorse}/bin/${
+              optionalString (lib.versionAtLeast (lib.getVersion cfg.packages.gitlab-workhorse) "16.10") "gitlab-"
+            }workhorse "
           + "-listenUmask 0 "
           + "-listenNetwork unix "
           + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
diff --git a/nixos/modules/services/misc/gogs.nix b/nixos/modules/services/misc/gogs.nix
index 9bf7e4aab8148..cea3d4806944e 100644
--- a/nixos/modules/services/misc/gogs.nix
+++ b/nixos/modules/services/misc/gogs.nix
@@ -217,7 +217,6 @@ in
           sed -e "s,#secretkey#,$KEY,g" \
               -e "s,#dbpass#,$DBPASS,g" \
               -i ${runConfig}
-          chmod 440 ${runConfig} ${secretKey}
         ''}
 
         mkdir -p ${cfg.repositoryRoot}
@@ -239,6 +238,7 @@ in
         WorkingDirectory = cfg.stateDir;
         ExecStart = "${pkgs.gogs}/bin/gogs web";
         Restart = "always";
+        UMask = "0027";
       };
 
       environment = {
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index e31eeaf8a30a8..04ac89970e3f6 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -8,7 +8,7 @@ in
 
 {
   options.services.gollum = {
-    enable = mkEnableOption (lib.mdDoc "Gollum service");
+    enable = mkEnableOption (lib.mdDoc "Gollum, a git-powered wiki service");
 
     address = mkOption {
       type = types.str;
diff --git a/nixos/modules/services/misc/graphical-desktop.nix b/nixos/modules/services/misc/graphical-desktop.nix
new file mode 100644
index 0000000000000..a88c02e610bf4
--- /dev/null
+++ b/nixos/modules/services/misc/graphical-desktop.nix
@@ -0,0 +1,54 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  xcfg = config.services.xserver;
+  dmcfg = config.services.displayManager;
+in
+{
+  config = lib.mkIf (xcfg.enable || dmcfg.enable) {
+    # The default max inotify watches is 8192.
+    # Nowadays most apps require a good number of inotify watches,
+    # the value below is used by default on several other distros.
+    boot.kernel.sysctl = {
+      "fs.inotify.max_user_instances" = lib.mkDefault 524288;
+      "fs.inotify.max_user_watches" = lib.mkDefault 524288;
+    };
+
+    environment = {
+      # localectl looks into 00-keyboard.conf
+      etc."X11/xorg.conf.d/00-keyboard.conf".text = ''
+        Section "InputClass"
+          Identifier "Keyboard catchall"
+          MatchIsKeyboard "on"
+          Option "XkbModel" "${xcfg.xkb.model}"
+          Option "XkbLayout" "${xcfg.xkb.layout}"
+          Option "XkbOptions" "${xcfg.xkb.options}"
+          Option "XkbVariant" "${xcfg.xkb.variant}"
+        EndSection
+      '';
+      systemPackages = with pkgs; [
+        nixos-icons # needed for gnome and pantheon about dialog, nixos-manual and maybe more
+        xdg-utils
+      ];
+    };
+
+    fonts.enableDefaultPackages = lib.mkDefault true;
+
+    hardware.opengl.enable = lib.mkDefault true;
+
+    programs.gnupg.agent.pinentryPackage = lib.mkOverride 1100 pkgs.pinentry-gnome3;
+
+    systemd.defaultUnit = lib.mkIf (xcfg.autorun || dmcfg.enable) "graphical.target";
+
+    xdg = {
+      autostart.enable = true;
+      menus.enable = true;
+      mime.enable = true;
+      icons.enable = true;
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/greenclip.nix b/nixos/modules/services/misc/greenclip.nix
index ecfb864ab2b78..554769b63331f 100644
--- a/nixos/modules/services/misc/greenclip.nix
+++ b/nixos/modules/services/misc/greenclip.nix
@@ -7,7 +7,7 @@ let
 in {
 
   options.services.greenclip = {
-    enable = mkEnableOption (lib.mdDoc "Greenclip daemon");
+    enable = mkEnableOption (lib.mdDoc "Greenclip, a clipboard manager");
 
     package = mkPackageOption pkgs [ "haskellPackages" "greenclip" ] { };
   };
diff --git a/nixos/modules/services/misc/homepage-dashboard.nix b/nixos/modules/services/misc/homepage-dashboard.nix
index 02f1378cb0d59..e1b0264230ba5 100644
--- a/nixos/modules/services/misc/homepage-dashboard.nix
+++ b/nixos/modules/services/misc/homepage-dashboard.nix
@@ -12,7 +12,7 @@ in
 {
   options = {
     services.homepage-dashboard = {
-      enable = lib.mkEnableOption (lib.mdDoc "Homepage Dashboard");
+      enable = lib.mkEnableOption (lib.mdDoc "Homepage Dashboard, a highly customizable application dashboard");
 
       package = lib.mkPackageOption pkgs "homepage-dashboard" { };
 
diff --git a/nixos/modules/services/misc/invidious-router.nix b/nixos/modules/services/misc/invidious-router.nix
new file mode 100644
index 0000000000000..01ef18dad5356
--- /dev/null
+++ b/nixos/modules/services/misc/invidious-router.nix
@@ -0,0 +1,121 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}: let
+  cfg = config.services.invidious-router;
+  settingsFormat = pkgs.formats.yaml {};
+  configFile = settingsFormat.generate "config.yaml" cfg.settings;
+in {
+  meta.maintainers = [lib.maintainers.s1ls];
+
+  options.services.invidious-router = {
+    enable = lib.mkEnableOption "Enables the invidious-router service";
+    port = lib.mkOption {
+      type = lib.types.port;
+      default = 8050;
+      description = lib.mdDoc ''
+        Port to bind to.
+      '';
+    };
+    address = lib.mkOption {
+      type = lib.types.str;
+      default = "127.0.0.1";
+      description = lib.mdDoc ''
+        Address on which invidious-router should listen on.
+      '';
+    };
+    settings = lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+      };
+      default = {
+        app = {
+          listen = "127.0.0.1:8050";
+          enable_youtube_fallback = false;
+          reload_instance_list_interval = "60s";
+        };
+        api = {
+          enabled = true;
+          url = "https://api.invidious.io/instances.json";
+          filter_regions = true;
+          allowed_regions = [
+            "AT"
+            "DE"
+            "CH"
+          ];
+        };
+        healthcheck = {
+          path = "/";
+          allowed_status_codes = [
+            200
+          ];
+          timeout = "1s";
+          interval = "10s";
+          filter_by_response_time = {
+            enabled = true;
+            qty_of_top_results = 3;
+          };
+          minimum_ratio = 0.2;
+          remove_no_ratio = true;
+          text_not_present = "YouTube is currently trying to block Invidious instances";
+        };
+      };
+      description = lib.mdDoc ''
+        Configuration for invidious-router.
+        Check https://gitlab.com/gaincoder/invidious-router#configuration
+        for configuration options.
+      '';
+    };
+    package = lib.mkOption {
+      type = lib.types.package;
+      default = pkgs.invidious-router;
+      defaultText = lib.literalExpression "pkgs.invidious-router";
+      description = lib.mdDoc ''
+        The invidious-router package to use.
+      '';
+    };
+    nginx = {
+      enable = lib.mkEnableOption (lib.mdDoc ''
+        Automatic nginx proxy configuration
+      '');
+      domain = lib.mkOption {
+        type = lib.types.str;
+        example = "invidious-router.example.com";
+        description = lib.mdDoc ''
+          The domain on which invidious-router should be served.
+        '';
+      };
+      extraDomains = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
+        default = [];
+        description = lib.mdDoc ''
+          Additional domains to serve invidious-router on.
+        '';
+      };
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    systemd.services.invidious-router = {
+      wantedBy = ["multi-user.target"];
+      serviceConfig = {
+        Restart = "on-failure";
+        ExecStart = "${lib.getExe cfg.package} --configfile ${configFile}";
+        DynamicUser = "yes";
+      };
+    };
+
+    services.nginx.virtualHosts = lib.mkIf cfg.nginx.enable {
+      ${cfg.nginx.domain} = {
+        locations."/" = {
+          recommendedProxySettings = true;
+          proxyPass = "http://${cfg.address}:${toString cfg.port}";
+        };
+        enableACME = true;
+        forceSSL = true;
+        serverAliases = cfg.nginx.extraDomains;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/misc/jackett.nix b/nixos/modules/services/misc/jackett.nix
index c0bb0a575f01d..ad9288e45dd29 100644
--- a/nixos/modules/services/misc/jackett.nix
+++ b/nixos/modules/services/misc/jackett.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.jackett = {
-      enable = mkEnableOption (lib.mdDoc "Jackett");
+      enable = mkEnableOption (lib.mdDoc "Jackett, API support for your favorite torrent trackers");
 
       dataDir = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/misc/languagetool.nix b/nixos/modules/services/misc/languagetool.nix
index 9adf792373b5a..902e55320b46d 100644
--- a/nixos/modules/services/misc/languagetool.nix
+++ b/nixos/modules/services/misc/languagetool.nix
@@ -7,7 +7,7 @@ let
   settingsFormat = pkgs.formats.javaProperties {};
 in {
   options.services.languagetool = {
-    enable = mkEnableOption (mdDoc "the LanguageTool server");
+    enable = mkEnableOption (mdDoc "the LanguageTool server, a multilingual spelling, style, and grammar checker that helps correct or paraphrase texts");
 
     port = mkOption {
       type = types.port;
diff --git a/nixos/modules/services/misc/leaps.nix b/nixos/modules/services/misc/leaps.nix
index 5522223ecc97c..b021937f8715c 100644
--- a/nixos/modules/services/misc/leaps.nix
+++ b/nixos/modules/services/misc/leaps.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.leaps = {
-      enable = mkEnableOption (lib.mdDoc "leaps");
+      enable = mkEnableOption (lib.mdDoc "leaps, a pair programming service");
       port = mkOption {
         type = types.port;
         default = 8080;
diff --git a/nixos/modules/services/misc/libreddit.nix b/nixos/modules/services/misc/libreddit.nix
index 02d71c198e783..e125d975d7e2a 100644
--- a/nixos/modules/services/misc/libreddit.nix
+++ b/nixos/modules/services/misc/libreddit.nix
@@ -47,7 +47,7 @@ in
         after = [ "network.target" ];
         serviceConfig = {
           DynamicUser = true;
-          ExecStart = "${cfg.package}/bin/libreddit ${args}";
+          ExecStart = "${lib.getExe cfg.package} ${args}";
           AmbientCapabilities = lib.mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
           Restart = "on-failure";
           RestartSec = "2s";
diff --git a/nixos/modules/services/misc/lidarr.nix b/nixos/modules/services/misc/lidarr.nix
index 8ceb567e88010..42627344dcd95 100644
--- a/nixos/modules/services/misc/lidarr.nix
+++ b/nixos/modules/services/misc/lidarr.nix
@@ -8,7 +8,7 @@ in
 {
   options = {
     services.lidarr = {
-      enable = mkEnableOption (lib.mdDoc "Lidarr");
+      enable = mkEnableOption (lib.mdDoc "Lidarr, a Usenet/BitTorrent music downloader");
 
       dataDir = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/misc/lifecycled.nix b/nixos/modules/services/misc/lifecycled.nix
index fb5cabb4f038b..fa36ec693a844 100644
--- a/nixos/modules/services/misc/lifecycled.nix
+++ b/nixos/modules/services/misc/lifecycled.nix
@@ -25,7 +25,7 @@ in
 
   options = {
     services.lifecycled = {
-      enable = mkEnableOption (lib.mdDoc "lifecycled");
+      enable = mkEnableOption (lib.mdDoc "lifecycled, a daemon for responding to AWS AutoScaling Lifecycle Hooks");
 
       queueCleaner = {
         enable = mkEnableOption (lib.mdDoc "lifecycled-queue-cleaner");
diff --git a/nixos/modules/services/misc/llama-cpp.nix b/nixos/modules/services/misc/llama-cpp.nix
index 4d76456fb2fd5..c73cff027e224 100644
--- a/nixos/modules/services/misc/llama-cpp.nix
+++ b/nixos/modules/services/misc/llama-cpp.nix
@@ -20,7 +20,7 @@ in {
       extraFlags = lib.mkOption {
         type = lib.types.listOf lib.types.str;
         description = "Extra flags passed to llama-cpp-server.";
-        example = ["-c" "4096" "-ngl" "32" "--numa"];
+        example = ["-c" "4096" "-ngl" "32" "--numa" "numactl"];
         default = [];
       };
 
@@ -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/logkeys.nix b/nixos/modules/services/misc/logkeys.nix
index 75d073a0c94bf..85f453b0d9da1 100644
--- a/nixos/modules/services/misc/logkeys.nix
+++ b/nixos/modules/services/misc/logkeys.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.logkeys;
 in {
   options.services.logkeys = {
-    enable = mkEnableOption (lib.mdDoc "logkeys service");
+    enable = mkEnableOption (lib.mdDoc "logkeys, a keylogger service");
 
     device = mkOption {
       description = lib.mdDoc "Use the given device as keyboard input event device instead of /dev/input/eventX default.";
diff --git a/nixos/modules/services/misc/mbpfan.nix b/nixos/modules/services/misc/mbpfan.nix
index ef56ea49d1a95..69f6331169f7d 100644
--- a/nixos/modules/services/misc/mbpfan.nix
+++ b/nixos/modules/services/misc/mbpfan.nix
@@ -4,14 +4,13 @@ with lib;
 let
   cfg = config.services.mbpfan;
   verbose = optionalString cfg.verbose "v";
-  settingsFormat = pkgs.formats.ini {};
-  settingsFile = settingsFormat.generate "mbpfan.ini" cfg.settings;
+  format = pkgs.formats.ini {};
+  cfgfile = format.generate "mbpfan.ini" cfg.settings;
 
 in {
   options.services.mbpfan = {
     enable = mkEnableOption (lib.mdDoc "mbpfan, fan controller daemon for Apple Macs and MacBooks");
-
-    package = mkPackageOption pkgs "mbpfan" { };
+    package = mkPackageOption pkgs "mbpfan" {};
 
     verbose = mkOption {
       type = types.bool;
@@ -29,7 +28,7 @@ in {
       default = {};
       description = lib.mdDoc "INI configuration for Mbpfan.";
       type = types.submodule {
-        freeformType = settingsFormat.type;
+        freeformType = format.type;
 
         options.general.low_temp = mkOption {
           type = types.int;
@@ -70,12 +69,12 @@ in {
   config = mkIf cfg.enable {
     boot.kernelModules = [ "coretemp" "applesmc" ];
     environment.systemPackages = [ cfg.package ];
-    environment.etc."mbpfan.conf".source = settingsFile;
+    environment.etc."mbpfan.conf".source = cfgfile;
 
     systemd.services.mbpfan = {
       description = "A fan manager daemon for MacBook Pro";
       wantedBy = [ "sysinit.target" ];
-      after = [ "syslog.target" "sysinit.target" ];
+      after = [ "sysinit.target" ];
       restartTriggers = [ config.environment.etc."mbpfan.conf".source ];
 
       serviceConfig = {
diff --git a/nixos/modules/services/misc/mollysocket.nix b/nixos/modules/services/misc/mollysocket.nix
new file mode 100644
index 0000000000000..f40caa4a782ef
--- /dev/null
+++ b/nixos/modules/services/misc/mollysocket.nix
@@ -0,0 +1,133 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) getExe mkIf mkOption mkEnableOption optionals types;
+
+  cfg = config.services.mollysocket;
+  configuration = format.generate "mollysocket.conf" cfg.settings;
+  format = pkgs.formats.toml { };
+  package = pkgs.writeShellScriptBin "mollysocket" ''
+    MOLLY_CONF=${configuration} exec ${getExe pkgs.mollysocket} "$@"
+  '';
+in {
+  options.services.mollysocket = {
+    enable = mkEnableOption ''
+      [MollySocket](https://github.com/mollyim/mollysocket) for getting Signal
+      notifications via UnifiedPush
+    '';
+
+    settings = mkOption {
+      default = { };
+      description = ''
+        Configuration for MollySocket. Available options are listed
+        [here](https://github.com/mollyim/mollysocket#configuration).
+      '';
+      type = types.submodule {
+        freeformType = format.type;
+        options = {
+          host = mkOption {
+            default = "127.0.0.1";
+            description = "Listening address of the web server";
+            type = types.str;
+          };
+
+          port = mkOption {
+            default = 8020;
+            description = "Listening port of the web server";
+            type = types.port;
+          };
+
+          allowed_endpoints = mkOption {
+            default = [ "*" ];
+            description = "List of UnifiedPush servers";
+            example = [ "https://ntfy.sh" ];
+            type = with types; listOf str;
+          };
+
+          allowed_uuids = mkOption {
+            default = [ "*" ];
+            description = "UUIDs of Signal accounts that may use this server";
+            example = [ "abcdef-12345-tuxyz-67890" ];
+            type = with types; listOf str;
+          };
+        };
+      };
+    };
+
+    environmentFile = mkOption {
+      default = null;
+      description = ''
+        Environment file (see {manpage}`systemd.exec(5)` "EnvironmentFile="
+        section for the syntax) passed to the service. This option can be
+        used to safely include secrets in the configuration.
+      '';
+      example = "/run/secrets/mollysocket";
+      type = with types; nullOr path;
+    };
+
+    logLevel = mkOption {
+      default = "info";
+      description = "Set the {env}`RUST_LOG` environment variable";
+      example = "debug";
+      type = types.str;
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      package
+    ];
+
+    # see https://github.com/mollyim/mollysocket/blob/main/mollysocket.service
+    systemd.services.mollysocket = {
+      description = "MollySocket";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      environment.RUST_LOG = cfg.logLevel;
+      serviceConfig = let
+        capabilities = [ "" ] ++ optionals (cfg.settings.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
+      in {
+        EnvironmentFile = cfg.environmentFile;
+        ExecStart = "${getExe package} server";
+        KillSignal = "SIGINT";
+        Restart = "on-failure";
+        StateDirectory = "mollysocket";
+        TimeoutStopSec = 5;
+        WorkingDirectory = "/var/lib/mollysocket";
+
+        # hardening
+        AmbientCapabilities = capabilities;
+        CapabilityBoundingSet = capabilities;
+        DevicePolicy = "closed";
+        DynamicUser = true;
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@resources" "~@privileged" ];
+        UMask = "0077";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ dotlambda ];
+}
diff --git a/nixos/modules/services/misc/nitter.nix b/nixos/modules/services/misc/nitter.nix
index d2cf7c0de2b77..0f2f62153b00e 100644
--- a/nixos/modules/services/misc/nitter.nix
+++ b/nixos/modules/services/misc/nitter.nix
@@ -52,7 +52,7 @@ in
 
   options = {
     services.nitter = {
-      enable = mkEnableOption (lib.mdDoc "Nitter");
+      enable = mkEnableOption (lib.mdDoc "Nitter, an alternative Twitter front-end");
 
       package = mkPackageOption pkgs "nitter" { };
 
diff --git a/nixos/modules/services/misc/nix-optimise.nix b/nixos/modules/services/misc/nix-optimise.nix
index 0398229a13da9..514855abaee46 100644
--- a/nixos/modules/services/misc/nix-optimise.nix
+++ b/nixos/modules/services/misc/nix-optimise.nix
@@ -42,9 +42,11 @@ in
         startAt = lib.optionals cfg.automatic cfg.dates;
       };
 
-      timers.nix-optimise.timerConfig = {
-        Persistent = true;
-        RandomizedDelaySec = 1800;
+      timers.nix-optimise = lib.mkIf cfg.automatic {
+        timerConfig = {
+          Persistent = true;
+          RandomizedDelaySec = 1800;
+        };
       };
     };
   };
diff --git a/nixos/modules/services/misc/nzbget.nix b/nixos/modules/services/misc/nzbget.nix
index d02fda62fa4fa..122b3dd249af4 100644
--- a/nixos/modules/services/misc/nzbget.nix
+++ b/nixos/modules/services/misc/nzbget.nix
@@ -25,7 +25,7 @@ in
 
   options = {
     services.nzbget = {
-      enable = mkEnableOption (lib.mdDoc "NZBGet");
+      enable = mkEnableOption (lib.mdDoc "NZBGet, for downloading files from news servers");
 
       user = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/misc/nzbhydra2.nix b/nixos/modules/services/misc/nzbhydra2.nix
index 536a4e4b00758..2c1a5eae076d2 100644
--- a/nixos/modules/services/misc/nzbhydra2.nix
+++ b/nixos/modules/services/misc/nzbhydra2.nix
@@ -7,7 +7,7 @@ let cfg = config.services.nzbhydra2;
 in {
   options = {
     services.nzbhydra2 = {
-      enable = mkEnableOption (lib.mdDoc "NZBHydra2");
+      enable = mkEnableOption (lib.mdDoc "NZBHydra2, Usenet meta search");
 
       dataDir = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/misc/ollama.nix b/nixos/modules/services/misc/ollama.nix
index 3ac3beb4de078..30c2b26d8322e 100644
--- a/nixos/modules/services/misc/ollama.nix
+++ b/nixos/modules/services/misc/ollama.nix
@@ -13,48 +13,76 @@ 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" { };
+      home = lib.mkOption {
+        type = types.str;
+        default = "%S/ollama";
+        example = "/home/foo";
+        description = ''
+          The home directory that the ollama service is started in.
+        '';
+      };
+      models = lib.mkOption {
+        type = types.str;
+        default = "%S/ollama/models";
+        example = "/path/to/ollama/models";
+        description = ''
+          The directory that the ollama service will read models from and download new models to.
+        '';
+      };
       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 = cfg.home;
+        OLLAMA_MODELS = cfg.models;
+        OLLAMA_HOST = cfg.listenAddress;
+      };
+      serviceConfig = {
+        ExecStart = "${lib.getExe ollamaPackage} serve";
+        WorkingDirectory = "%S/ollama";
+        StateDirectory = [ "ollama" ];
+        DynamicUser = true;
       };
     };
 
diff --git a/nixos/modules/services/misc/ombi.nix b/nixos/modules/services/misc/ombi.nix
index 8bf6a9b116ec2..2e452f7d88eac 100644
--- a/nixos/modules/services/misc/ombi.nix
+++ b/nixos/modules/services/misc/ombi.nix
@@ -8,7 +8,9 @@ in {
   options = {
     services.ombi = {
       enable = mkEnableOption (lib.mdDoc ''
-        Ombi.
+        Ombi, a web application that automatically gives your shared Plex or
+        Emby users the ability to request content by themselves!
+
         Optionally see <https://docs.ombi.app/info/reverse-proxy>
         on how to set up a reverse proxy
       '');
diff --git a/nixos/modules/services/misc/owncast.nix b/nixos/modules/services/misc/owncast.nix
index 01fe34cf50fec..cbb6ba29e3227 100644
--- a/nixos/modules/services/misc/owncast.nix
+++ b/nixos/modules/services/misc/owncast.nix
@@ -5,7 +5,7 @@ in {
 
   options.services.owncast = {
 
-    enable = mkEnableOption (lib.mdDoc "owncast");
+    enable = mkEnableOption (lib.mdDoc "owncast, a video live streaming solution");
 
     dataDir = mkOption {
       type = types.str;
diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix
index 9314c4f3848d8..9a81fdde62af8 100644
--- a/nixos/modules/services/misc/paperless.nix
+++ b/nixos/modules/services/misc/paperless.nix
@@ -27,6 +27,8 @@ let
       name = "paperless_ngx_nltk_data";
       paths = pkg.nltkData;
     };
+  } // optionalAttrs (cfg.openMPThreadingWorkaround) {
+    OMP_NUM_THREADS = "1";
   } // (lib.mapAttrs (_: s:
     if (lib.isAttrs s || lib.isList s) then builtins.toJSON s
     else if lib.isBool s then lib.boolToString s
@@ -199,20 +201,35 @@ in
     };
 
     package = mkPackageOption pkgs "paperless-ngx" { };
+
+    openMPThreadingWorkaround = mkEnableOption ''
+      a workaround for document classifier timeouts.
+
+      Paperless uses OpenBLAS via scikit-learn for document classification.
+
+      The default is to use threading for OpenMP but this would cause the
+      document classifier to spin on one core seemingly indefinitely if there
+      are large amounts of classes per classification; causing it to
+      effectively never complete due to running into timeouts.
+
+      This sets `OMP_NUM_THREADS` to `1` in order to mitigate the issue. See
+      https://github.com/NixOS/nixpkgs/issues/240591 for more information.
+    '' // mkOption { default = true; };
   };
 
   config = mkIf cfg.enable {
     services.redis.servers.paperless.enable = mkIf enableRedis true;
 
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
-      "d '${cfg.mediaDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
-      (if cfg.consumptionDirIsPublic then
-        "d '${cfg.consumptionDir}' 777 - - - -"
-      else
-        "d '${cfg.consumptionDir}' - ${cfg.user} ${config.users.users.${cfg.user}.group} - -"
-      )
-    ];
+    systemd.tmpfiles.settings."10-paperless" = let
+      defaultRule = {
+        inherit (cfg) user;
+        inherit (config.users.users.${cfg.user}) group;
+      };
+    in {
+      "${cfg.dataDir}".d = defaultRule;
+      "${cfg.mediaDir}".d = defaultRule;
+      "${cfg.consumptionDir}".d = if cfg.consumptionDirIsPublic then { mode = "777"; } else defaultRule;
+    };
 
     systemd.services.paperless-scheduler = {
       description = "Paperless Celery Beat";
@@ -222,6 +239,7 @@ in
         User = cfg.user;
         ExecStart = "${pkg}/bin/celery --app paperless beat --loglevel INFO";
         Restart = "on-failure";
+        LoadCredential = lib.optionalString (cfg.passwordFile != null) "PAPERLESS_ADMIN_PASSWORD:${cfg.passwordFile}";
       };
       environment = env;
 
@@ -254,7 +272,7 @@ in
       ''
       + optionalString (cfg.passwordFile != null) ''
         export PAPERLESS_ADMIN_USER="''${PAPERLESS_ADMIN_USER:-admin}"
-        export PAPERLESS_ADMIN_PASSWORD=$(cat "${cfg.dataDir}/superuser-password")
+        export PAPERLESS_ADMIN_PASSWORD=$(cat $CREDENTIALS_DIRECTORY/PAPERLESS_ADMIN_PASSWORD)
         superuserState="$PAPERLESS_ADMIN_USER:$PAPERLESS_ADMIN_PASSWORD"
         superuserStateFile="${cfg.dataDir}/superuser-state"
 
@@ -282,19 +300,6 @@ in
       environment = env;
     };
 
-    # Reading the user-provided password file requires root access
-    systemd.services.paperless-copy-password = mkIf (cfg.passwordFile != null) {
-      requiredBy = [ "paperless-scheduler.service" ];
-      before = [ "paperless-scheduler.service" ];
-      serviceConfig = {
-        ExecStart = ''
-          ${pkgs.coreutils}/bin/install --mode 600 --owner '${cfg.user}' --compare \
-            '${cfg.passwordFile}' '${cfg.dataDir}/superuser-password'
-        '';
-        Type = "oneshot";
-      };
-    };
-
     systemd.services.paperless-consumer = {
       description = "Paperless document consumer";
       # Bind to `paperless-scheduler` so that the consumer never runs
diff --git a/nixos/modules/services/misc/pinnwand.nix b/nixos/modules/services/misc/pinnwand.nix
index 5fca9f4125a83..466d045a7b16b 100644
--- a/nixos/modules/services/misc/pinnwand.nix
+++ b/nixos/modules/services/misc/pinnwand.nix
@@ -10,7 +10,7 @@ let
 in
 {
   options.services.pinnwand = {
-    enable = mkEnableOption (lib.mdDoc "Pinnwand");
+    enable = mkEnableOption (lib.mdDoc "Pinnwand, a pastebin");
 
     port = mkOption {
       type = types.port;
diff --git a/nixos/modules/services/misc/plikd.nix b/nixos/modules/services/misc/plikd.nix
index 9b0825bf40c98..bb5d3c1a36ca5 100644
--- a/nixos/modules/services/misc/plikd.nix
+++ b/nixos/modules/services/misc/plikd.nix
@@ -11,7 +11,7 @@ in
 {
   options = {
     services.plikd = {
-      enable = mkEnableOption (lib.mdDoc "the plikd server");
+      enable = mkEnableOption (lib.mdDoc "plikd, a temporary file upload system");
 
       openFirewall = mkOption {
         type = types.bool;
diff --git a/nixos/modules/services/misc/prowlarr.nix b/nixos/modules/services/misc/prowlarr.nix
index 84d365003992c..952f95485bd08 100644
--- a/nixos/modules/services/misc/prowlarr.nix
+++ b/nixos/modules/services/misc/prowlarr.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.prowlarr = {
-      enable = mkEnableOption (lib.mdDoc "Prowlarr");
+      enable = mkEnableOption (lib.mdDoc "Prowlarr, an indexer manager/proxy for Torrent trackers and Usenet indexers");
 
       package = mkPackageOption pkgs "prowlarr" { };
 
diff --git a/nixos/modules/services/misc/radarr.nix b/nixos/modules/services/misc/radarr.nix
index a5f264331ed36..eaf49673d3c7e 100644
--- a/nixos/modules/services/misc/radarr.nix
+++ b/nixos/modules/services/misc/radarr.nix
@@ -9,7 +9,7 @@ in
 {
   options = {
     services.radarr = {
-      enable = mkEnableOption (lib.mdDoc "Radarr");
+      enable = mkEnableOption (lib.mdDoc "Radarr, a UsetNet/BitTorrent movie downloader");
 
       package = mkPackageOption pkgs "radarr" { };
 
diff --git a/nixos/modules/services/misc/readarr.nix b/nixos/modules/services/misc/readarr.nix
index 73868b4baa953..486e2b4155b56 100644
--- a/nixos/modules/services/misc/readarr.nix
+++ b/nixos/modules/services/misc/readarr.nix
@@ -8,7 +8,7 @@ in
 {
   options = {
     services.readarr = {
-      enable = mkEnableOption (lib.mdDoc "Readarr");
+      enable = mkEnableOption (lib.mdDoc "Readarr, a Usenet/BitTorrent ebook downloader");
 
       dataDir = mkOption {
         type = types.str;
diff --git a/nixos/modules/services/misc/redmine.nix b/nixos/modules/services/misc/redmine.nix
index c1209e34a92b5..f99fe7a845be4 100644
--- a/nixos/modules/services/misc/redmine.nix
+++ b/nixos/modules/services/misc/redmine.nix
@@ -10,16 +10,22 @@ let
   format = pkgs.formats.yaml {};
   bundle = "${cfg.package}/share/redmine/bin/bundle";
 
-  databaseYml = pkgs.writeText "database.yml" ''
-    production:
-      adapter: ${cfg.database.type}
-      database: ${cfg.database.name}
-      host: ${if (cfg.database.type == "postgresql" && cfg.database.socket != null) then cfg.database.socket else cfg.database.host}
-      port: ${toString cfg.database.port}
-      username: ${cfg.database.user}
-      password: #dbpass#
-      ${optionalString (cfg.database.type == "mysql2" && cfg.database.socket != null) "socket: ${cfg.database.socket}"}
-  '';
+  databaseSettings = {
+    production = {
+      adapter = cfg.database.type;
+      database = if cfg.database.type == "sqlite3" then "${cfg.stateDir}/database.sqlite3" else cfg.database.name;
+    } // optionalAttrs (cfg.database.type != "sqlite3") {
+      host = if (cfg.database.type == "postgresql" && cfg.database.socket != null) then cfg.database.socket else cfg.database.host;
+      port = cfg.database.port;
+      username = cfg.database.user;
+    } // optionalAttrs (cfg.database.type != "sqlite3" && cfg.database.passwordFile != null) {
+      password = "#dbpass#";
+    } // optionalAttrs (cfg.database.type == "mysql2" && cfg.database.socket != null) {
+      socket = cfg.database.socket;
+    };
+  };
+
+  databaseYml = format.generate "database.yml" databaseSettings;
 
   configurationYml = format.generate "configuration.yml" cfg.settings;
   additionalEnvironment = pkgs.writeText "additional_environment.rb" cfg.extraEnv;
@@ -50,7 +56,7 @@ in
   # interface
   options = {
     services.redmine = {
-      enable = mkEnableOption (lib.mdDoc "Redmine");
+      enable = mkEnableOption (lib.mdDoc "Redmine, a project management web application");
 
       package = mkPackageOption pkgs "redmine" {
         example = "redmine.override { ruby = pkgs.ruby_3_2; }";
@@ -145,7 +151,7 @@ in
 
       database = {
         type = mkOption {
-          type = types.enum [ "mysql2" "postgresql" ];
+          type = types.enum [ "mysql2" "postgresql" "sqlite3" ];
           example = "postgresql";
           default = "mysql2";
           description = lib.mdDoc "Database engine to use.";
@@ -261,7 +267,7 @@ in
   config = mkIf cfg.enable {
 
     assertions = [
-      { assertion = cfg.database.passwordFile != null || cfg.database.socket != null;
+      { assertion = cfg.database.type != "sqlite3" -> cfg.database.passwordFile != null || cfg.database.socket != null;
         message = "one of services.redmine.database.socket or services.redmine.database.passwordFile must be set";
       }
       { assertion = cfg.database.createLocally -> cfg.database.user == cfg.user;
@@ -270,8 +276,8 @@ in
       { assertion = pgsqlLocal -> cfg.database.user == cfg.database.name;
         message = "services.redmine.database.user and services.redmine.database.name must be the same when using a local postgresql database";
       }
-      { assertion = cfg.database.createLocally -> cfg.database.socket != null;
-        message = "services.redmine.database.socket must be set if services.redmine.database.createLocally is set to true";
+      { assertion = (cfg.database.createLocally && cfg.database.type != "sqlite3") -> cfg.database.socket != null;
+        message = "services.redmine.database.socket must be set if services.redmine.database.createLocally is set to true and no sqlite database is used";
       }
       { assertion = cfg.database.createLocally -> cfg.database.host == "localhost";
         message = "services.redmine.database.host must be set to localhost if services.redmine.database.createLocally is set to true";
@@ -395,9 +401,13 @@ in
 
 
         # handle database.passwordFile & permissions
-        DBPASS=${optionalString (cfg.database.passwordFile != null) "$(head -n1 ${cfg.database.passwordFile})"}
         cp -f ${databaseYml} "${cfg.stateDir}/config/database.yml"
-        sed -e "s,#dbpass#,$DBPASS,g" -i "${cfg.stateDir}/config/database.yml"
+
+        ${optionalString ((cfg.database.type != "sqlite3") && (cfg.database.passwordFile != null)) ''
+          DBPASS="$(head -n1 ${cfg.database.passwordFile})"
+          sed -e "s,#dbpass#,$DBPASS,g" -i "${cfg.stateDir}/config/database.yml"
+        ''}
+
         chmod 440 "${cfg.stateDir}/config/database.yml"
 
 
diff --git a/nixos/modules/services/misc/rippled.nix b/nixos/modules/services/misc/rippled.nix
index 68a8318942506..08833e4135419 100644
--- a/nixos/modules/services/misc/rippled.nix
+++ b/nixos/modules/services/misc/rippled.nix
@@ -207,7 +207,7 @@ in
 
   options = {
     services.rippled = {
-      enable = mkEnableOption (lib.mdDoc "rippled");
+      enable = mkEnableOption (lib.mdDoc "rippled, a decentralized cryptocurrency blockchain daemon implementing the XRP Ledger protocol in C++");
 
       package = mkPackageOption pkgs "rippled" { };
 
diff --git a/nixos/modules/services/misc/signald.nix b/nixos/modules/services/misc/signald.nix
index 32ba154506ce3..44b82a931cd84 100644
--- a/nixos/modules/services/misc/signald.nix
+++ b/nixos/modules/services/misc/signald.nix
@@ -8,7 +8,7 @@ let
 in
 {
   options.services.signald = {
-    enable = mkEnableOption (lib.mdDoc "the signald service");
+    enable = mkEnableOption (lib.mdDoc "signald, the unofficial daemon for interacting with Signal");
 
     user = mkOption {
       type = types.str;
diff --git a/nixos/modules/services/misc/sourcehut/default.md b/nixos/modules/services/misc/sourcehut/default.md
index 44d58aa0bef3e..f965c395038a3 100644
--- a/nixos/modules/services/misc/sourcehut/default.md
+++ b/nixos/modules/services/misc/sourcehut/default.md
@@ -12,7 +12,7 @@ This NixOS module also provides basic configuration integrating Sourcehut into l
 and `services.postgresql` services.
 
 A very basic configuration may look like this:
-```
+```nix
 { pkgs, ... }:
 let
   fqdn =
@@ -66,9 +66,9 @@ in {
     # Settings to setup what certificates are used for which endpoint.
     virtualHosts = {
       "${fqdn}".enableACME = true;
-      "meta.${fqdn}".useACMEHost = fqdn:
-      "man.${fqdn}".useACMEHost = fqdn:
-      "git.${fqdn}".useACMEHost = fqdn:
+      "meta.${fqdn}".useACMEHost = fqdn;
+      "man.${fqdn}".useACMEHost = fqdn;
+      "git.${fqdn}".useACMEHost = fqdn;
     };
   };
 }
diff --git a/nixos/modules/services/misc/tandoor-recipes.nix b/nixos/modules/services/misc/tandoor-recipes.nix
index a8300ecd52337..1b1fde78ad0a5 100644
--- a/nixos/modules/services/misc/tandoor-recipes.nix
+++ b/nixos/modules/services/misc/tandoor-recipes.nix
@@ -20,7 +20,10 @@ let
   manage = pkgs.writeShellScript "manage" ''
     set -o allexport # Export the following env vars
     ${lib.toShellVars env}
-    exec ${pkg}/bin/tandoor-recipes "$@"
+    eval "$(${config.systemd.package}/bin/systemctl show -pUID,GID,MainPID tandoor-recipes.service)"
+    exec ${pkgs.util-linux}/bin/nsenter \
+      -t $MainPID -m -S $UID -G $GID \
+      ${pkg}/bin/tandoor-recipes "$@"
   '';
 in
 {
@@ -82,6 +85,7 @@ in
         Restart = "on-failure";
 
         User = "tandoor_recipes";
+        Group = "tandoor_recipes";
         DynamicUser = true;
         StateDirectory = "tandoor-recipes";
         WorkingDirectory = "/var/lib/tandoor-recipes";
diff --git a/nixos/modules/services/misc/wastebin.nix b/nixos/modules/services/misc/wastebin.nix
new file mode 100644
index 0000000000000..3d0af2862683d
--- /dev/null
+++ b/nixos/modules/services/misc/wastebin.nix
@@ -0,0 +1,158 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.wastebin;
+  inherit (lib)
+    mkEnableOption mkPackageOption mkIf mkOption
+    types mapAttrs isBool getExe boolToString optionalAttrs;
+in
+{
+
+  options.services.wastebin = {
+
+    enable = mkEnableOption "Wastenbin pastebin service";
+
+    package = mkPackageOption pkgs "wastebin" { };
+
+    stateDir = mkOption {
+      type = types.path;
+      default = "/var/lib/wastebin";
+      description = "State directory of the daemon.";
+    };
+
+    secretFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      example = "/run/secrets/wastebin.env";
+      description = ''
+        Path to file containing sensitive environment variables.
+        Some variables that can be considered secrets are:
+
+        - WASTEBIN_PASSWORD_SALT:
+          salt used to hash user passwords used for encrypting pastes.
+
+        - WASTEBIN_SIGNING_KEY:
+          sets the key to sign cookies. If not set, a random key will be
+          generated which means cookies will become invalid after restarts and
+          paste creators will not be able to delete their pastes anymore.
+      '';
+    };
+
+    settings = mkOption {
+
+      description = ''
+        Additional configuration for wastebin, see
+        <https://github.com/matze/wastebin#usage> for supported values.
+        For secrets use secretFile option instead.
+      '';
+
+      type = types.submodule {
+
+        freeformType = with types; attrsOf (oneOf [ bool int str ]);
+
+        options = {
+
+          WASTEBIN_ADDRESS_PORT = mkOption {
+            type = types.str;
+            default = "0.0.0.0:8088";
+            description = "Address and port to bind to";
+          };
+
+          WASTEBIN_BASE_URL = mkOption {
+            default = "http://localhost";
+            example = "https://myhost.tld";
+            type = types.str;
+            description = ''
+              Base URL for the QR code display. If not set, the user agent's Host
+              header field is used as an approximation.
+            '';
+          };
+
+          WASTEBIN_CACHE_SIZE = mkOption {
+            default = 128;
+            type = types.int;
+            description = "Number of rendered syntax highlight items to cache. Can be disabled by setting to 0.";
+          };
+
+          WASTEBIN_DATABASE_PATH = mkOption {
+            default = "/var/lib/wastebin/sqlite3.db"; # TODO make this default to stateDir/sqlite3.db
+            type = types.str;
+            description = "Path to the sqlite3 database file. If not set, an in-memory database is used.";
+          };
+
+          WASTEBIN_HTTP_TIMEOUT = mkOption {
+            default = 5;
+            type = types.int;
+            description = "Maximum number of seconds a request can be processed until wastebin responds with 408";
+          };
+
+          WASTEBIN_MAX_BODY_SIZE = mkOption {
+            default = 1024;
+            type = types.int;
+            description = "Number of bytes to accept for POST requests";
+          };
+
+          WASTEBIN_TITLE = mkOption {
+            default = "wastebin";
+            type = types.str;
+            description = "Overrides the HTML page title";
+          };
+
+          RUST_LOG = mkOption {
+            default = "info";
+            type = types.str;
+            description =
+              ''
+                Influences logging. Besides the typical trace, debug, info etc.
+                keys, you can also set the tower_http key to some log level to get
+                additional information request and response logs.
+              '';
+          };
+        };
+      };
+
+      default = { };
+
+      example = {
+        WASTEBIN_TITLE = "My awesome pastebin";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable
+    {
+      systemd.services.wastebin = {
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        environment = mapAttrs (_: v: if isBool v then boolToString v else toString v) cfg.settings;
+        serviceConfig = {
+          CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+          DevicePolicy = "closed";
+          DynamicUser = true;
+          ExecStart = "${getExe cfg.package}";
+          LockPersonality = true;
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          PrivateUsers = true;
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          SystemCallArchitectures = [ "native" ];
+          SystemCallFilter = [ "@system-service" ];
+          StateDirectory = baseNameOf cfg.stateDir;
+          ReadWritePaths = cfg.stateDir;
+        } // optionalAttrs (cfg.secretFile != null) {
+          EnvironmentFile = cfg.secretFile;
+        };
+      };
+    };
+
+  meta.maintainers = with lib.maintainers; [ pinpox ];
+}
diff --git a/nixos/modules/services/misc/weechat.md b/nixos/modules/services/misc/weechat.md
index 21f41be5b4a0e..fb20ebe1e4db2 100644
--- a/nixos/modules/services/misc/weechat.md
+++ b/nixos/modules/services/misc/weechat.md
@@ -12,7 +12,7 @@ unit which runs the chat client in a detached
 session.
 
 This can be done by enabling the `weechat` service:
-```
+```nix
 { ... }:
 
 {
@@ -30,7 +30,7 @@ allow your another user to attach to this session, the
 `screenrc` needs to be tweaked by adding
 [multiuser](https://www.gnu.org/software/screen/manual/html_node/Multiuser.html#Multiuser)
 support:
-```
+```nix
 {
   programs.screen.screenrc = ''
     multiuser on
diff --git a/nixos/modules/services/misc/workout-tracker.nix b/nixos/modules/services/misc/workout-tracker.nix
new file mode 100644
index 0000000000000..13555504be302
--- /dev/null
+++ b/nixos/modules/services/misc/workout-tracker.nix
@@ -0,0 +1,83 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+  cfg = config.services.workout-tracker;
+  stateDir = "workout-tracker";
+in
+
+{
+  options = {
+    services.workout-tracker = {
+      enable = lib.mkEnableOption "workout tracking web application for personal use (or family, friends), geared towards running and other GPX-based activities";
+
+      package = lib.mkPackageOption pkgs "workout-tracker" { };
+
+      address = lib.mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "Web interface address.";
+      };
+
+      port = lib.mkOption {
+        type = types.port;
+        default = 8080;
+        description = "Web interface port.";
+      };
+
+      environmentFile = lib.mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/keys/workout-tracker.env";
+        description = ''
+          An environment file as defined in {manpage}`systemd.exec(5)`.
+
+          Secrets like `WT_JWT_ENCRYPTION_KEY` may be passed to the service without adding them
+          to the world-readable Nix store.
+        '';
+      };
+
+      settings = lib.mkOption {
+        type = types.attrsOf types.str;
+
+        default = { };
+        description = ''
+          Extra config options.
+        '';
+        example = {
+          WT_LOGGING = "true";
+          WT_DEBUG = "false";
+          WT_DATABASE_DRIVER = "sqlite";
+          WT_DSN = "./database.db";
+        };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.workout-tracker = {
+      description = "A workout tracking web application for personal use (or family, friends), geared towards running and other GPX-based activities";
+      wantedBy = [ "multi-user.target" ];
+      environment = {
+        WT_BIND = "${cfg.address}:${toString cfg.port}";
+        WT_DATABASE_DRIVER = "sqlite";
+        WT_DSN = "./database.db";
+      } // cfg.settings;
+      serviceConfig = {
+        ExecStart = lib.getExe cfg.package;
+        DynamicUser = true;
+        StateDirectory = stateDir;
+        WorkingDirectory = "%S/${stateDir}";
+        Restart = "always";
+        EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ bhankas ];
+}
diff --git a/nixos/modules/services/monitoring/certspotter.md b/nixos/modules/services/monitoring/certspotter.md
index 9bf6e1d946a04..e999bfe65ec3e 100644
--- a/nixos/modules/services/monitoring/certspotter.md
+++ b/nixos/modules/services/monitoring/certspotter.md
@@ -9,17 +9,19 @@ A basic config that notifies you of all certificate changes for your
 domain would look as follows:
 
 ```nix
-services.certspotter = {
-  enable = true;
-  # replace example.org with your domain name
-  watchlist = [ ".example.org" ];
-  emailRecipients = [ "webmaster@example.org" ];
-};
+{
+  services.certspotter = {
+    enable = true;
+    # replace example.org with your domain name
+    watchlist = [ ".example.org" ];
+    emailRecipients = [ "webmaster@example.org" ];
+  };
 
-# Configure an SMTP client
-programs.msmtp.enable = true;
-# Or you can use any other module that provides sendmail, like
-# services.nullmailer, services.opensmtpd, services.postfix
+  # Configure an SMTP client
+  programs.msmtp.enable = true;
+  # Or you can use any other module that provides sendmail, like
+  # services.nullmailer, services.opensmtpd, services.postfix
+}
 ```
 
 In this case, the leading dot in `".example.org"` means that Cert
@@ -59,16 +61,18 @@ For example, you can remove `emailRecipients` and send email
 notifications manually using the following hook:
 
 ```nix
-services.certspotter.hooks = [
-  (pkgs.writeShellScript "certspotter-hook" ''
-    function print_email() {
-      echo "Subject: [certspotter] $SUMMARY"
-      echo "Mime-Version: 1.0"
-      echo "Content-Type: text/plain; charset=US-ASCII"
-      echo
-      cat "$TEXT_FILENAME"
-    }
-    print_email | ${config.services.certspotter.sendmailPath} -i webmaster@example.org
-  '')
-];
+{
+  services.certspotter.hooks = [
+    (pkgs.writeShellScript "certspotter-hook" ''
+      function print_email() {
+        echo "Subject: [certspotter] $SUMMARY"
+        echo "Mime-Version: 1.0"
+        echo "Content-Type: text/plain; charset=US-ASCII"
+        echo
+        cat "$TEXT_FILENAME"
+      }
+      print_email | ${config.services.certspotter.sendmailPath} -i webmaster@example.org
+    '')
+  ];
+}
 ```
diff --git a/nixos/modules/services/monitoring/goss.md b/nixos/modules/services/monitoring/goss.md
index 1e636aa3bdf33..bf91d42011fa7 100644
--- a/nixos/modules/services/monitoring/goss.md
+++ b/nixos/modules/services/monitoring/goss.md
@@ -7,7 +7,7 @@ for validating a server's configuration.
 
 A minimal configuration looks like this:
 
-```
+```nix
 {
   services.goss = {
     enable = true;
diff --git a/nixos/modules/services/monitoring/nezha-agent.nix b/nixos/modules/services/monitoring/nezha-agent.nix
new file mode 100644
index 0000000000000..ef6878798f377
--- /dev/null
+++ b/nixos/modules/services/monitoring/nezha-agent.nix
@@ -0,0 +1,103 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  cfg = config.services.nezha-agent;
+in
+{
+  meta = {
+    maintainers = with lib.maintainers; [ moraxyc ];
+  };
+  options = {
+    services.nezha-agent = {
+      enable = lib.mkEnableOption (lib.mdDoc "Agent of Nezha Monitoring");
+
+      package = lib.mkPackageOption pkgs "nezha-agent" { };
+      debug = lib.mkEnableOption (lib.mdDoc "verbose log");
+      tls = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Enable SSL/TLS encryption.
+        '';
+      };
+      disableCommandExecute = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc ''
+          Disable executing the command from dashboard.
+        '';
+      };
+      skipConnection = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Do not monitor the number of connections.
+        '';
+      };
+      skipProcess = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Do not monitor the number of processes.
+        '';
+      };
+      reportDelay = lib.mkOption {
+        type = lib.types.enum [ 1 2 3 4 ];
+        default = 1;
+        description = lib.mdDoc ''
+          The interval between system status reportings.
+          The value must be an integer from 1 to 4
+        '';
+      };
+      passwordFile = lib.mkOption {
+        type = with lib.types; nullOr str;
+        default = null;
+        description = lib.mdDoc ''
+          Path to the file contained the password from dashboard.
+        '';
+      };
+      server = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc ''
+          Address to the dashboard
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.packages = [ cfg.package ];
+
+    systemd.services.nezha-agent = {
+      serviceConfig = {
+        ProtectSystem = "full";
+        PrivateDevices = "yes";
+        PrivateTmp = "yes";
+        NoNewPrivileges = true;
+      };
+      path = [ cfg.package ];
+      startLimitIntervalSec = 10;
+      startLimitBurst = 3;
+      script = lib.concatStringsSep " " (
+        [
+          "${cfg.package}/bin/agent"
+          "--disable-auto-update"
+          "--disable-force-update"
+          "--password $(cat ${cfg.passwordFile})"
+        ]
+        ++ lib.optional cfg.debug "--debug"
+        ++ lib.optional cfg.disableCommandExecute "--disable-command-execute"
+        ++ lib.optional (cfg.reportDelay != null) "--report-delay ${toString cfg.reportDelay}"
+        ++ lib.optional (cfg.server != null) "--server ${cfg.server}"
+        ++ lib.optional cfg.skipConnection "--skip-conn"
+        ++ lib.optional cfg.skipProcess "--skip-procs"
+        ++ lib.optional cfg.tls "--tls"
+      );
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/parsedmarc.md b/nixos/modules/services/monitoring/parsedmarc.md
index eac07e0cc9fec..765846bbbaf36 100644
--- a/nixos/modules/services/monitoring/parsedmarc.md
+++ b/nixos/modules/services/monitoring/parsedmarc.md
@@ -11,15 +11,17 @@ email address and saves them to a local Elasticsearch instance looks
 like this:
 
 ```nix
-services.parsedmarc = {
-  enable = true;
-  settings.imap = {
-    host = "imap.example.com";
-    user = "alice@example.com";
-    password = "/path/to/imap_password_file";
+{
+  services.parsedmarc = {
+    enable = true;
+    settings.imap = {
+      host = "imap.example.com";
+      user = "alice@example.com";
+      password = "/path/to/imap_password_file";
+    };
+    provision.geoIp = false; # Not recommended!
   };
-  provision.geoIp = false; # Not recommended!
-};
+}
 ```
 
 Note that GeoIP provisioning is disabled in the example for
@@ -37,16 +39,18 @@ configured in the domain's dmarc policy is
 `dmarc@monitoring.example.com`.
 
 ```nix
-services.parsedmarc = {
-  enable = true;
-  provision = {
-    localMail = {
-      enable = true;
-      hostname = monitoring.example.com;
+{
+  services.parsedmarc = {
+    enable = true;
+    provision = {
+      localMail = {
+        enable = true;
+        hostname = monitoring.example.com;
+      };
+      geoIp = false; # Not recommended!
     };
-    geoIp = false; # Not recommended!
   };
-};
+}
 ```
 
 ## Grafana and GeoIP {#module-services-parsedmarc-grafana-geoip}
@@ -58,55 +62,57 @@ is automatically added as a Grafana datasource, and the dashboard is
 added to Grafana as well.
 
 ```nix
-services.parsedmarc = {
-  enable = true;
-  provision = {
-    localMail = {
-      enable = true;
-      hostname = url;
-    };
-    grafana = {
-      datasource = true;
-      dashboard = true;
+{
+  services.parsedmarc = {
+    enable = true;
+    provision = {
+      localMail = {
+        enable = true;
+        hostname = url;
+      };
+      grafana = {
+        datasource = true;
+        dashboard = true;
+      };
     };
   };
-};
 
-# Not required, but recommended for full functionality
-services.geoipupdate = {
-  settings = {
-    AccountID = 000000;
-    LicenseKey = "/path/to/license_key_file";
+  # Not required, but recommended for full functionality
+  services.geoipupdate = {
+    settings = {
+      AccountID = 000000;
+      LicenseKey = "/path/to/license_key_file";
+    };
   };
-};
 
-services.grafana = {
-  enable = true;
-  addr = "0.0.0.0";
-  domain = url;
-  rootUrl = "https://" + url;
-  protocol = "socket";
-  security = {
-    adminUser = "admin";
-    adminPasswordFile = "/path/to/admin_password_file";
-    secretKeyFile = "/path/to/secret_key_file";
+  services.grafana = {
+    enable = true;
+    addr = "0.0.0.0";
+    domain = url;
+    rootUrl = "https://" + url;
+    protocol = "socket";
+    security = {
+      adminUser = "admin";
+      adminPasswordFile = "/path/to/admin_password_file";
+      secretKeyFile = "/path/to/secret_key_file";
+    };
   };
-};
 
-services.nginx = {
-  enable = true;
-  recommendedTlsSettings = true;
-  recommendedOptimisation = true;
-  recommendedGzipSettings = true;
-  recommendedProxySettings = true;
-  upstreams.grafana.servers."unix:/${config.services.grafana.socket}" = {};
-  virtualHosts.${url} = {
-    root = config.services.grafana.staticRootPath;
-    enableACME = true;
-    forceSSL = true;
-    locations."/".tryFiles = "$uri @grafana";
-    locations."@grafana".proxyPass = "http://grafana";
+  services.nginx = {
+    enable = true;
+    recommendedTlsSettings = true;
+    recommendedOptimisation = true;
+    recommendedGzipSettings = true;
+    recommendedProxySettings = true;
+    upstreams.grafana.servers."unix:/${config.services.grafana.socket}" = {};
+    virtualHosts.${url} = {
+      root = config.services.grafana.staticRootPath;
+      enableACME = true;
+      forceSSL = true;
+      locations."/".tryFiles = "$uri @grafana";
+      locations."@grafana".proxyPass = "http://grafana";
+    };
   };
-};
-users.users.nginx.extraGroups = [ "grafana" ];
+  users.users.nginx.extraGroups = [ "grafana" ];
+}
 ```
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.md b/nixos/modules/services/monitoring/prometheus/exporters.md
index 34fadecadc749..d291020d36733 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.md
+++ b/nixos/modules/services/monitoring/prometheus/exporters.md
@@ -9,7 +9,8 @@ One of the most common exporters is the
 [node exporter](https://github.com/prometheus/node_exporter),
 it provides hardware and OS metrics from the host it's
 running on. The exporter could be configured as follows:
-```
+```nix
+{
   services.prometheus.exporters.node = {
     enable = true;
     port = 9100;
@@ -23,6 +24,7 @@ running on. The exporter could be configured as follows:
     openFirewall = true;
     firewallFilter = "-i br0 -p tcp -m tcp --dport 9100";
   };
+}
 ```
 It should now serve all metrics from the collectors that are explicitly
 enabled and the ones that are
@@ -35,7 +37,8 @@ configuration see `man configuration.nix` or search through
 the [available options](https://nixos.org/nixos/options.html#prometheus.exporters).
 
 Prometheus can now be configured to consume the metrics produced by the exporter:
-```
+```nix
+{
     services.prometheus = {
       # ...
 
@@ -49,7 +52,8 @@ Prometheus can now be configured to consume the metrics produced by the exporter
       ];
 
       # ...
-    }
+    };
+}
 ```
 
 ## Adding a new exporter {#module-services-prometheus-exporters-new-exporter}
@@ -68,6 +72,7 @@ example:
       - `extraFlags`
       - `openFirewall`
       - `firewallFilter`
+      - `firewallRules`
       - `user`
       - `group`
   - As there is already a package available, the module can now be added. This
@@ -75,7 +80,7 @@ example:
     `nixos/modules/services/monitoring/prometheus/exporters/`
     directory, which will be called postfix.nix and contains all exporter
     specific options and configuration:
-    ```
+    ```nix
     # nixpkgs/nixos/modules/services/prometheus/exporters/postfix.nix
     { config, lib, pkgs, options }:
 
@@ -148,7 +153,7 @@ example:
 Should an exporter option change at some point, it is possible to add
 information about the change to the exporter definition similar to
 `nixpkgs/nixos/modules/rename.nix`:
-```
+```nix
 { config, lib, pkgs, options }:
 
 with lib;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 6be6ba7edf721..0331a07b5109d 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"
@@ -54,6 +55,7 @@ let
     "modemmanager"
     "mongodb"
     "mysqld"
+    "nats"
     "nextcloud"
     "nginx"
     "nginxlog"
@@ -94,10 +96,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";
@@ -168,6 +170,17 @@ let
         is true. It is used as `ip46tables -I nixos-fw firewallFilter -j nixos-fw-accept`.
       '';
     };
+    firewallRules = mkOption {
+      type = types.nullOr types.lines;
+      default = null;
+      example = literalExpression ''
+        iifname "eth0" tcp dport ${toString port} counter accept
+      '';
+      description = lib.mdDoc ''
+        Specify rules for nftables to add to the input chain
+        when {option}`services.prometheus.exporters.${name}.openFirewall` is true.
+      '';
+    };
     user = mkOption {
       type = types.str;
       default = "${name}-exporter";
@@ -193,6 +206,7 @@ let
         } // extraOpts);
       } ({ config, ... }: mkIf config.openFirewall {
         firewallFilter = mkDefault "-p tcp -m tcp --dport ${toString config.port}";
+        firewallRules = mkDefault ''tcp dport ${toString config.port} accept comment "${name}-exporter"'';
       })];
       internal = true;
       default = {};
@@ -211,6 +225,7 @@ let
   mkExporterConf = { name, conf, serviceOpts }:
     let
       enableDynamicUser = serviceOpts.serviceConfig.DynamicUser or true;
+      nftables = config.networking.nftables.enable;
     in
     mkIf conf.enable {
       warnings = conf.warnings or [];
@@ -222,10 +237,11 @@ let
       users.groups = (mkIf (conf.group == "${name}-exporter" && !enableDynamicUser) {
         "${name}-exporter" = {};
       });
-      networking.firewall.extraCommands = mkIf conf.openFirewall (concatStrings [
+      networking.firewall.extraCommands = mkIf (conf.openFirewall && !nftables) (concatStrings [
         "ip46tables -A nixos-fw ${conf.firewallFilter} "
         "-m comment --comment ${name}-exporter -j nixos-fw-accept"
       ]);
+      networking.firewall.extraInputRules = mkIf (conf.openFirewall && nftables) conf.firewallRules;
       systemd.services."prometheus-${name}-exporter" = mkMerge ([{
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix b/nixos/modules/services/monitoring/prometheus/exporters/apcupsd.nix
index a8a9f84ea8eaf..de6cda18bc374 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 bc67fe59b3b83..b3afdb5966861 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 bd2003f06504e..100446c1a4ebb 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 5f6c36f4c5671..fc52135e3b45e 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 330d541264488..45f00a04a86c5 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 ce2c391de5232..e8399e1bec800 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 0515b72b13f9f..6bfadc3b76320 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 f67596f05a3a1..3b2b123bbd078 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 437cece588a78..a4a917b473ce0 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 ece42a34cb06b..4cfee7c54a41d 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 61e2fc80afde3..b2c8e6664c0f6 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 6fb438353a4c4..df6b1ef3200cb 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 8511abbee1bd0..c632b02902627 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 2a8b7fc0818d5..097ea39594788 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 81099aaf17042..42292abeada2e 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 0000000000000..c3a962b576a5b
--- /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 dc53d21406ff2..7b881a8e2693c 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 34a8871042126..07c06afe14094 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 f5604bc00ee04..78ae4826215ce 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 c5024a258e719..68fc63e40fcd1 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 61c0c08d2250e..d0d7f16bdadf3 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 9adbe31d84d69..fe9734d33c7c7 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 0246027186023..bc670ba9cc0e6 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 473f3a7e47e39..7f78985d80cd4 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 15e0c9ecb1778..72119d17fcb71 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 3abb6ff6bdf8b..ccfdd98b8db93 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 dfa56343b8717..afdb664a0de5e 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 7758487508033..0352aff8b0135 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 9f914b1dc1464..66d9c02f904b7 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 15079f5841f43..8c88f47ab86a0 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 54dab4b5581ae..a8dba75251d83 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 82cc3fc314f2f..e24d4f766e30e 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 222ea3e5384f6..0eb193c0021f2 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 b36a09c609206..1ed6bbf0325d8 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 849c514de6816..c6da052ccdf30 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/nats.nix b/nixos/modules/services/monitoring/prometheus/exporters/nats.nix
new file mode 100644
index 0000000000000..83e60426f5ed2
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nats.nix
@@ -0,0 +1,34 @@
+{ config, lib, pkgs, options, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.prometheus.exporters.nats;
+
+in
+{
+  port = 7777;
+
+  extraOpts = {
+    url = mkOption {
+      type = types.str;
+      default = "http://127.0.0.1:8222";
+      description = ''
+        NATS monitor endpoint to query.
+      '';
+    };
+  };
+
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-nats-exporter}/bin/prometheus-nats-exporter \
+          -addr ${cfg.listenAddress} \
+          -port ${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags} \
+          ${cfg.url}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix b/nixos/modules/services/monitoring/prometheus/exporters/nextcloud.nix
index 28a3eb6a134c0..82deea6864e8f 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 88dc79fc2503f..339749226aa45 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 674dc9dd41581..b79a034e1384e 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 dd8602e2c63db..9b8a0d2c6bc27 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 e58a394456a38..a14e379079b07 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 9e55cadae5237..9587403c78023 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 8238f1ac1856e..4ea5f64012c08 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 6f403b3e58c81..4b7eca7493a69 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 af78b6bef6258..bda5038a0c64a 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 9f402b123110a..ead8e806f85a8 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 755d771ecdff4..514b2d0c8f2d9 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 278d6cd780748..86c71a88e28b0 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 83e740320df2d..96db49d9591f7 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 f03b3c4df9165..60243e0ed0694 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 befbcb21f7664..71f94a700efd9 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 977bd42e9812e..12962af5f111f 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 f9dcfad07d300..8169d4075a9f4 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 1f7235cb78303..42b659501161c 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 b9ab305f7c082..0d937ac6673f8 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 3b6ebf65b0903..d4c929d88b9c8 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 eab0e1d8a6b51..f37fa456d27c5 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 b9cfd1b1e84a9..1d2329dfbae18 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 50e1321a1e9ce..1040e9ecadbd5 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 459f5842f546f..2bacc9cd7cac8 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 452cb154bcf61..207446e39f49b 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 678bc348679db..dbfa69678a0c9 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 d9d732d8c125a..94df86167e8ce 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 b1d6760b40b34..337ebd4ed66fe 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 7a9167110a279..b91f69aded3d5 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 f2336429d42fe..2f4444a96c694 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 70f26d9783be5..b7addcd568270 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 3b7f978528cd1..aff1197a8775e 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 a019157c664be..7b21e5fc7cb7b 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 a7e5b41dffc66..98fbba82c8e93 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 9b7590314936e..127c8021a9f0f 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 ff12a52d49a92..21f6354cc4a20 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 2862cdd801281..031f5a30cada6 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/monitoring/thanos.nix b/nixos/modules/services/monitoring/thanos.nix
index 02502816ef5d7..f9ec271c6ef55 100644
--- a/nixos/modules/services/monitoring/thanos.nix
+++ b/nixos/modules/services/monitoring/thanos.nix
@@ -353,6 +353,10 @@ let
         See <https://tools.ietf.org/html/rfc4366#section-3.1>
       '';
 
+      grpc-compression = mkParam types.str ''
+        Compression algorithm to use for gRPC requests to other clients.
+      '';
+
       web.route-prefix = mkParam types.str ''
         Prefix for API and UI endpoints.
 
@@ -642,6 +646,10 @@ let
 
     receive = params.common cfg.receive // params.objstore cfg.receive // {
 
+      receive.grpc-compression = mkParam types.str ''
+        Compression algorithm to use for gRPC requests to other receivers.
+      '';
+
       remote-write.address = mkParamDef types.str "0.0.0.0:19291" ''
         Address to listen on for remote write requests.
       '';
diff --git a/nixos/modules/services/monitoring/zabbix-proxy.nix b/nixos/modules/services/monitoring/zabbix-proxy.nix
index fea5704af6f66..7a0fc77d5b256 100644
--- a/nixos/modules/services/monitoring/zabbix-proxy.nix
+++ b/nixos/modules/services/monitoring/zabbix-proxy.nix
@@ -103,11 +103,11 @@ in
 
         port = mkOption {
           type = types.port;
-          default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.services.port;
           defaultText = literalExpression ''
             if config.${opt.database.type} == "mysql"
             then config.${options.services.mysql.port}
-            else config.${options.services.postgresql.port}
+            else config.services.postgresql.settings.port
           '';
           description = lib.mdDoc "Database host port.";
         };
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index f2fb5fbe7ac66..fc9295d294d19 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -95,11 +95,11 @@ in
 
         port = mkOption {
           type = types.port;
-          default = if cfg.database.type == "mysql" then mysql.port else pgsql.port;
+          default = if cfg.database.type == "mysql" then mysql.port else pgsql.settings.port;
           defaultText = literalExpression ''
             if config.${opt.database.type} == "mysql"
             then config.${options.services.mysql.port}
-            else config.${options.services.postgresql.port}
+            else config.services.postgresql.settings.port
           '';
           description = lib.mdDoc "Database host port.";
         };
diff --git a/nixos/modules/services/network-filesystems/davfs2.nix b/nixos/modules/services/network-filesystems/davfs2.nix
index 8024cfba08be5..90bb5ee5e66cd 100644
--- a/nixos/modules/services/network-filesystems/davfs2.nix
+++ b/nixos/modules/services/network-filesystems/davfs2.nix
@@ -3,14 +3,22 @@
 with lib;
 
 let
+  toStr = value:
+    if true == value then "yes"
+    else if false == value then "no"
+    else toString value;
+
   cfg = config.services.davfs2;
-  cfgFile = pkgs.writeText "davfs2.conf" ''
-    dav_user ${cfg.davUser}
-    dav_group ${cfg.davGroup}
+  format = pkgs.formats.toml { };
+  configFile = let
+    settings = mapAttrsToList (n: v: "${n} = ${toStr v}") cfg.settings;
+  in pkgs.writeText "davfs2.conf" ''
+    ${concatStringsSep "\n" settings}
     ${cfg.extraConfig}
   '';
 in
 {
+
   options.services.davfs2 = {
     enable = mkOption {
       type = types.bool;
@@ -49,13 +57,46 @@ in
       '';
       description = lib.mdDoc ''
         Extra lines appended to the configuration of davfs2.
+        See {manpage}`davfs2.conf(5)` for available settings.
+
+        **Note**: Please pass structured settings via
+        {option}`settings` instead, this option
+        will get deprecated in the future.
+      ''  ;
+    };
+
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = format.type;
+      };
+      default = {};
+      example = literalExpression ''
+        {
+          kernel_fs = "coda";
+          proxy = "foo.bar:8080";
+          use_locks = 0;
+        }
+      '';
+      description = lib.mdDoc ''
+        Extra settings appended to the configuration of davfs2.
+        See {manpage}`davfs2.conf(5)` for available settings.
       ''  ;
     };
   };
 
   config = mkIf cfg.enable {
+
+    warnings = lib.optional (cfg.extraConfig != null) ''
+      services.davfs2.extraConfig will be deprecated in future releases, please use the settings option now.
+    '';
+
     environment.systemPackages = [ pkgs.davfs2 ];
-    environment.etc."davfs2/davfs2.conf".source = cfgFile;
+    environment.etc."davfs2/davfs2.conf".source = configFile;
+
+    services.davfs2.settings = {
+      dav_user = cfg.davUser;
+      dav_group = cfg.davGroup;
+    };
 
     users.groups = optionalAttrs (cfg.davGroup == "davfs2") {
       davfs2.gid = config.ids.gids.davfs2;
diff --git a/nixos/modules/services/network-filesystems/litestream/default.md b/nixos/modules/services/network-filesystems/litestream/default.md
index 8d8486507b77e..626d69df84a50 100644
--- a/nixos/modules/services/network-filesystems/litestream/default.md
+++ b/nixos/modules/services/network-filesystems/litestream/default.md
@@ -8,7 +8,7 @@ replication tool for SQLite.
 Litestream service is managed by a dedicated user named `litestream`
 which needs permission to the database file. Here's an example config which gives
 required permissions to access [grafana database](#opt-services.grafana.settings.database.path):
-```
+```nix
 { pkgs, ... }:
 {
   users.users.litestream.extraGroups = [ "grafana" ];
diff --git a/nixos/modules/services/network-filesystems/nfsd.nix b/nixos/modules/services/network-filesystems/nfsd.nix
index c9e1cbcbbda48..656f1aa5a4881 100644
--- a/nixos/modules/services/network-filesystems/nfsd.nix
+++ b/nixos/modules/services/network-filesystems/nfsd.nix
@@ -113,25 +113,6 @@ in
 
   config = mkIf cfg.enable {
 
-    services.nfs.extraConfig = ''
-      [nfsd]
-      threads=${toString cfg.nproc}
-      ${optionalString (cfg.hostName != null) "host=${cfg.hostName}"}
-      ${cfg.extraNfsdConfig}
-
-      [mountd]
-      ${optionalString (cfg.mountdPort != null) "port=${toString cfg.mountdPort}"}
-
-      [statd]
-      ${optionalString (cfg.statdPort != null) "port=${toString cfg.statdPort}"}
-
-      [lockd]
-      ${optionalString (cfg.lockdPort != null) ''
-        port=${toString cfg.lockdPort}
-        udp-port=${toString cfg.lockdPort}
-      ''}
-    '';
-
     services.rpcbind.enable = true;
 
     boot.supportedFilesystems = [ "nfs" ]; # needed for statd and idmapd
diff --git a/nixos/modules/services/networking/bind.nix b/nixos/modules/services/networking/bind.nix
index da8633d5066f7..0a4fa4c524a6c 100644
--- a/nixos/modules/services/networking/bind.nix
+++ b/nixos/modules/services/networking/bind.nix
@@ -121,7 +121,7 @@ in
       package = mkPackageOption pkgs "bind" { };
 
       cacheNetworks = mkOption {
-        default = [ "127.0.0.0/24" ];
+        default = [ "127.0.0.0/24" "::1/128" ];
         type = types.listOf types.str;
         description = lib.mdDoc ''
           What networks are allowed to use us as a resolver.  Note
diff --git a/nixos/modules/services/networking/cjdns.nix b/nixos/modules/services/networking/cjdns.nix
index 80085da92702c..7eb31cfd4edec 100644
--- a/nixos/modules/services/networking/cjdns.nix
+++ b/nixos/modules/services/networking/cjdns.nix
@@ -246,12 +246,8 @@ in
             shopt -s lastpipe
             ${pkg}/bin/makekeys | { read private ipv6 public; }
 
-            umask 0077
-            echo "CJDNS_PRIVATE_KEY=$private" >> /etc/cjdns.keys
-            echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public" > /etc/cjdns.public
-
-            chmod 600 /etc/cjdns.keys
-            chmod 444 /etc/cjdns.public
+            install -m 600 <(echo "CJDNS_PRIVATE_KEY=$private") /etc/cjdns.keys
+            install -m 444 <(echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public") /etc/cjdns.public
         fi
 
         if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then
diff --git a/nixos/modules/services/networking/cloudflared.nix b/nixos/modules/services/networking/cloudflared.nix
index b9556bfa60d06..76db339a1831c 100644
--- a/nixos/modules/services/networking/cloudflared.nix
+++ b/nixos/modules/services/networking/cloudflared.nix
@@ -11,7 +11,7 @@ let
       default = null;
       example = "30s";
       description = lib.mdDoc ''
-        Timeout for establishing a new TCP connection to your origin server. This excludes the time taken to establish TLS, which is controlled by [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#tlstimeout](tlsTimeout).
+        Timeout for establishing a new TCP connection to your origin server. This excludes the time taken to establish TLS, which is controlled by [tlsTimeout](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#tlstimeout).
       '';
     };
 
diff --git a/nixos/modules/services/networking/dnscache.nix b/nixos/modules/services/networking/dnscache.nix
index eff13f69f470a..4f5b77a5b6851 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/dnsproxy.nix b/nixos/modules/services/networking/dnsproxy.nix
new file mode 100644
index 0000000000000..f0be74d7591f4
--- /dev/null
+++ b/nixos/modules/services/networking/dnsproxy.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib)
+    escapeShellArgs
+    getExe
+    lists
+    literalExpression
+    maintainers
+    mdDoc
+    mkEnableOption
+    mkIf
+    mkOption
+    mkPackageOption
+    types;
+
+  cfg = config.services.dnsproxy;
+
+  yaml = pkgs.formats.yaml { };
+  configFile = yaml.generate "config.yaml" cfg.settings;
+
+  finalFlags = (lists.optional (cfg.settings != { }) "--config-path=${configFile}") ++ cfg.flags;
+in
+{
+
+  options.services.dnsproxy = {
+
+    enable = mkEnableOption (lib.mdDoc "dnsproxy");
+
+    package = mkPackageOption pkgs "dnsproxy" { };
+
+    settings = mkOption {
+      type = yaml.type;
+      default = { };
+      example = literalExpression ''
+        {
+          bootstrap = [
+            "8.8.8.8:53"
+          ];
+          listen-addrs = [
+            "0.0.0.0"
+          ];
+          listen-ports = [
+            53
+          ];
+          upstream = [
+            "1.1.1.1:53"
+          ];
+        }
+      '';
+      description = mdDoc ''
+        Contents of the `config.yaml` config file.
+        The `--config-path` argument will only be passed if this set is not empty.
+
+        See <https://github.com/AdguardTeam/dnsproxy/blob/master/config.yaml.dist>.
+      '';
+    };
+
+    flags = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = [ "--upstream=1.1.1.1:53" ];
+      description = lib.mdDoc ''
+        A list of extra command-line flags to pass to dnsproxy. For details on the
+        available options, see <https://github.com/AdguardTeam/dnsproxy#usage>.
+        Keep in mind that options passed through command-line flags override
+        config options.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.dnsproxy = {
+      description = "Simple DNS proxy with DoH, DoT, DoQ and DNSCrypt support";
+      after = [ "network.target" "nss-lookup.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${getExe cfg.package} ${escapeShellArgs finalFlags}";
+        Restart = "always";
+        RestartSec = 10;
+        DynamicUser = true;
+
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        ProtectClock = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        RemoveIPC = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" "~@privileged @resources" ];
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ diogotcorreia ];
+
+}
diff --git a/nixos/modules/services/networking/firefox-syncserver.md b/nixos/modules/services/networking/firefox-syncserver.md
index 4d8777d204bb2..f6b515e67f159 100644
--- a/nixos/modules/services/networking/firefox-syncserver.md
+++ b/nixos/modules/services/networking/firefox-syncserver.md
@@ -7,19 +7,21 @@ A storage server for Firefox Sync that you can easily host yourself.
 The absolute minimal configuration for the sync server looks like this:
 
 ```nix
-services.mysql.package = pkgs.mariadb;
-
-services.firefox-syncserver = {
-  enable = true;
-  secrets = builtins.toFile "sync-secrets" ''
-    SYNC_MASTER_SECRET=this-secret-is-actually-leaked-to-/nix/store
-  '';
-  singleNode = {
+{
+  services.mysql.package = pkgs.mariadb;
+
+  services.firefox-syncserver = {
     enable = true;
-    hostname = "localhost";
-    url = "http://localhost:5000";
+    secrets = builtins.toFile "sync-secrets" ''
+      SYNC_MASTER_SECRET=this-secret-is-actually-leaked-to-/nix/store
+    '';
+    singleNode = {
+      enable = true;
+      hostname = "localhost";
+      url = "http://localhost:5000";
+    };
   };
-};
+}
 ```
 
 This will start a sync server that is only accessible locally. Once the services is
diff --git a/nixos/modules/services/networking/i2p.nix b/nixos/modules/services/networking/i2p.nix
index c5c7a955cbd4f..5c6c08831a43d 100644
--- a/nixos/modules/services/networking/i2p.nix
+++ b/nixos/modules/services/networking/i2p.nix
@@ -5,7 +5,8 @@ with lib;
 let
   cfg = config.services.i2p;
   homeDir = "/var/lib/i2p";
-in {
+in
+{
   ###### interface
   options.services.i2p.enable = mkEnableOption (lib.mdDoc "I2P router");
 
@@ -27,7 +28,7 @@ in {
         User = "i2p";
         WorkingDirectory = homeDir;
         Restart = "on-abort";
-        ExecStart = "${pkgs.i2p}/bin/i2prouter-plain";
+        ExecStart = "${pkgs.i2p}/bin/i2prouter";
       };
     };
   };
diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix
index 656ddd41fd12b..5fd6427c90f86 100644
--- a/nixos/modules/services/networking/kea.nix
+++ b/nixos/modules/services/networking/kea.nix
@@ -9,7 +9,6 @@ with lib;
 let
   cfg = config.services.kea;
 
-  xor = x: y: (!x && y) || (x && !y);
   format = pkgs.formats.json {};
 
   chooseNotNull = x: y: if x != null then x else y;
diff --git a/nixos/modules/services/networking/microsocks.nix b/nixos/modules/services/networking/microsocks.nix
new file mode 100644
index 0000000000000..be79a8495636f
--- /dev/null
+++ b/nixos/modules/services/networking/microsocks.nix
@@ -0,0 +1,146 @@
+{ config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.services.microsocks;
+
+  cmd =
+    if cfg.execWrapper != null
+    then "${cfg.execWrapper} ${cfg.package}/bin/microsocks"
+    else "${cfg.package}/bin/microsocks";
+  args =
+    [ "-i" cfg.ip "-p" (toString cfg.port) ]
+    ++ lib.optionals (cfg.authOnce) [ "-1" ]
+    ++ lib.optionals (cfg.disableLogging) [ "-q" ]
+    ++ lib.optionals (cfg.outgoingBindIp != null) [ "-b" cfg.outgoingBindIp ]
+    ++ lib.optionals (cfg.authUsername != null) [ "-u" cfg.authUsername ];
+in {
+  options.services.microsocks = {
+    enable = lib.mkEnableOption (lib.mdDoc "Tiny, portable SOCKS5 server with very moderate resource usage");
+    user = lib.mkOption {
+      default = "microsocks";
+      description = lib.mdDoc "User microsocks runs as.";
+      type = lib.types.str;
+    };
+    group = lib.mkOption {
+      default = "microsocks";
+      description = lib.mdDoc "Group microsocks runs as.";
+      type = lib.types.str;
+    };
+    package = lib.mkPackageOption pkgs "microsocks" {};
+    ip = lib.mkOption {
+      type = lib.types.str;
+      default = "127.0.0.1";
+      description = lib.mdDoc ''
+        IP on which microsocks should listen. Defaults to 127.0.0.1 for
+        security reasons.
+      '';
+    };
+    port = lib.mkOption {
+      type = lib.types.port;
+      default = 1080;
+      description = lib.mdDoc "Port on which microsocks should listen.";
+    };
+    disableLogging = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc "If true, microsocks will not log any messages to stdout/stderr.";
+    };
+    authOnce = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        If true, once a specific ip address authed successfully with user/pass,
+        it is added to a whitelist and may use the proxy without auth.
+      '';
+    };
+    outgoingBindIp = lib.mkOption {
+      type = lib.types.nullOr lib.types.str;
+      default = null;
+      description = lib.mdDoc "Specifies which ip outgoing connections are bound to";
+    };
+    authUsername = lib.mkOption {
+      type = lib.types.nullOr lib.types.str;
+      default = null;
+      example = "alice";
+      description = lib.mdDoc "Optional username to use for authentication.";
+    };
+    authPasswordFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      example = "/run/secrets/microsocks-password";
+      description = lib.mdDoc "Path to a file containing the password for authentication.";
+    };
+    execWrapper = lib.mkOption {
+      type = lib.types.nullOr lib.types.str;
+      default = null;
+      example = ''
+        ''${pkgs.mullvad-vpn}/bin/mullvad-exclude
+      '';
+      description = lib.mdDoc ''
+        An optional command to prepend to the microsocks command (such as proxychains, or a VPN exclude command).
+      '';
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.authUsername != null) == (cfg.authPasswordFile != null);
+        message = "Need to set both authUsername and authPasswordFile for microsocks";
+      }
+    ];
+    users = {
+      users = lib.mkIf (cfg.user == "microsocks") {
+        microsocks = {
+          group = cfg.group;
+          isSystemUser = true;
+        };
+      };
+      groups = lib.mkIf (cfg.group == "microsocks") {
+        microsocks = {};
+      };
+    };
+    systemd.services.microsocks = {
+      enable = true;
+      description = "a tiny socks server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        User = cfg.user;
+        Group = cfg.group;
+        Restart = "on-failure";
+        RestartSec = 10;
+        LoadCredential = lib.optionalString (cfg.authPasswordFile != null) "MICROSOCKS_PASSWORD_FILE:${cfg.authPasswordFile}";
+        MemoryDenyWriteExecute = true;
+        SystemCallArchitectures = "native";
+        PrivateTmp = true;
+        NoNewPrivileges = true;
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        PrivateDevices = true;
+        RestrictSUIDSGID = true;
+        RestrictNamespaces = [
+          "cgroup"
+          "ipc"
+          "pid"
+          "user"
+          "uts"
+        ];
+      };
+      script =
+        if cfg.authPasswordFile != null
+        then ''
+          PASSWORD=$(cat "$CREDENTIALS_DIRECTORY/MICROSOCKS_PASSWORD_FILE")
+          ${cmd} ${lib.escapeShellArgs args} -P "$PASSWORD"
+        ''
+        else ''
+          ${cmd} ${lib.escapeShellArgs args}
+        '';
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/mihomo.nix b/nixos/modules/services/networking/mihomo.nix
new file mode 100644
index 0000000000000..312530caeaade
--- /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:
+        - metacubexd:
+          - https://d.metacubex.one
+          - https://metacubex.github.io/metacubexd
+          - https://metacubexd.pages.dev
+        - yacd:
+          - https://yacd.haishan.me
+        - clash-dashboard:
+          - 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/mosquitto.md b/nixos/modules/services/networking/mosquitto.md
index 5cdb598151e51..66b3ad6cfa8f1 100644
--- a/nixos/modules/services/networking/mosquitto.md
+++ b/nixos/modules/services/networking/mosquitto.md
@@ -7,14 +7,16 @@ Mosquitto is a MQTT broker often used for IoT or home automation data transport.
 A minimal configuration for Mosquitto is
 
 ```nix
-services.mosquitto = {
-  enable = true;
-  listeners = [ {
-    acl = [ "pattern readwrite #" ];
-    omitPasswordAuth = true;
-    settings.allow_anonymous = true;
-  } ];
-};
+{
+  services.mosquitto = {
+    enable = true;
+    listeners = [ {
+      acl = [ "pattern readwrite #" ];
+      omitPasswordAuth = true;
+      settings.allow_anonymous = true;
+    } ];
+  };
+}
 ```
 
 This will start a broker on port 1883, listening on all interfaces of the machine, allowing
@@ -25,37 +27,42 @@ full read access to a user `monitor` and restricted write access to a user `serv
 like
 
 ```nix
-services.mosquitto = {
-  enable = true;
-  listeners = [ {
-    users = {
-      monitor = {
-        acl = [ "read #" ];
-        password = "monitor";
+{
+  services.mosquitto = {
+    enable = true;
+    listeners = [ {
+      users = {
+        monitor = {
+          acl = [ "read #" ];
+          password = "monitor";
+        };
+        service = {
+          acl = [ "write service/#" ];
+          password = "service";
+        };
       };
-      service = {
-        acl = [ "write service/#" ];
-        password = "service";
-      };
-    };
-  } ];
-};
+    } ];
+  };
+}
 ```
 
 TLS authentication is configured by setting TLS-related options of the listener:
 
 ```nix
-services.mosquitto = {
-  enable = true;
-  listeners = [ {
-    port = 8883; # port change is not required, but helpful to avoid mistakes
-    # ...
-    settings = {
-      cafile = "/path/to/mqtt.ca.pem";
-      certfile = "/path/to/mqtt.pem";
-      keyfile = "/path/to/mqtt.key";
-    };
-  } ];
+{
+  services.mosquitto = {
+    enable = true;
+    listeners = [ {
+      port = 8883; # port change is not required, but helpful to avoid mistakes
+      # ...
+      settings = {
+        cafile = "/path/to/mqtt.ca.pem";
+        certfile = "/path/to/mqtt.pem";
+        keyfile = "/path/to/mqtt.key";
+      };
+    } ];
+  };
+}
 ```
 
 ## Configuration {#module-services-mosquitto-config}
diff --git a/nixos/modules/services/networking/mycelium.nix b/nixos/modules/services/networking/mycelium.nix
new file mode 100644
index 0000000000000..9487a5daafee0
--- /dev/null
+++ b/nixos/modules/services/networking/mycelium.nix
@@ -0,0 +1,142 @@
+{ config, pkgs, lib, utils, ... }:
+
+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.
+      '';
+    };
+    extraArgs = lib.mkOption {
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      description = ''
+        Extra command-line arguments to pass to mycelium.
+
+        See `mycelium --help` for all available options.
+      '';
+    };
+  };
+  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"
+          "${utils.escapeSystemdExecArgs cfg.extraArgs}"
+        ] ++
+        (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/netbird.md b/nixos/modules/services/networking/netbird.md
index a326207becc8e..e1f6753cbd30c 100644
--- a/nixos/modules/services/networking/netbird.md
+++ b/nixos/modules/services/networking/netbird.md
@@ -5,7 +5,9 @@
 The absolute minimal configuration for the netbird daemon looks like this:
 
 ```nix
-services.netbird.enable = true;
+{
+  services.netbird.enable = true;
+}
 ```
 
 This will set up a netbird service listening on the port `51820` associated to the
@@ -14,7 +16,9 @@ This will set up a netbird service listening on the port `51820` associated to t
 It is strictly equivalent to setting:
 
 ```nix
-services.netbird.tunnels.wt0.stateDir = "netbird";
+{
+  services.netbird.tunnels.wt0.stateDir = "netbird";
+}
 ```
 
 The `enable` option is mainly kept for backward compatibility, as defining netbird
@@ -29,11 +33,13 @@ The following configuration will start a netbird daemon using the interface `wt1
 the port 51830. Its configuration file will then be located at `/var/lib/netbird-wt1/config.json`.
 
 ```nix
-services.netbird.tunnels = {
-  wt1 = {
-    port = 51830;
+{
+  services.netbird.tunnels = {
+    wt1 = {
+      port = 51830;
+    };
   };
-};
+}
 ```
 
 To interact with it, you will need to specify the correct daemon address:
@@ -48,9 +54,11 @@ It is also possible to overwrite default options passed to the service, for
 example:
 
 ```nix
-services.netbird.tunnels.wt1.environment = {
-  NB_DAEMON_ADDR = "unix:///var/run/toto.sock"
-};
+{
+  services.netbird.tunnels.wt1.environment = {
+    NB_DAEMON_ADDR = "unix:///var/run/toto.sock";
+  };
+}
 ```
 
 This will set the socket to interact with the netbird service to `/var/run/toto.sock`.
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 63804a3b1c543..1eaf065972d21 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" ];
@@ -374,7 +390,7 @@ in
           };
         });
         default = [ ];
-        example = literalExpression ''[{ name = "03f0:4e1d"; script = "''${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/03f0:4e1d"; }]'';
+        example = literalExpression ''[{ id = "03f0:4e1d"; path = "''${pkgs.modemmanager}/share/ModemManager/fcc-unlock.available.d/03f0:4e1d"; }]'';
         description = lib.mdDoc ''
           List of FCC unlock scripts to enable on the system, behaving as described in
           https://modemmanager.org/docs/modemmanager/fcc-unlock/#integration-with-third-party-fcc-unlock-tools.
@@ -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/nsd.nix b/nixos/modules/services/networking/nsd.nix
index 6db728e7aa5ae..23bb92f09ab9a 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -81,7 +81,6 @@ let
       zonesdir: "${stateDir}"
 
       # the list of dynamically added zones.
-      database:     "${stateDir}/var/nsd.db"
       pidfile:      "${pidFile}"
       xfrdfile:     "${stateDir}/var/xfrd.state"
       xfrdir:       "${stateDir}/tmp"
@@ -112,6 +111,7 @@ let
       ${maybeString "version: " cfg.version}
       xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout}
       zonefiles-check:     ${yesOrNo  cfg.zonefilesCheck}
+      zonefiles-write:     ${toString cfg.zonefilesWrite}
 
       ${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength}
       ${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength}
@@ -173,6 +173,7 @@ let
       ${maybeToString "min-retry-time:   " zone.minRetrySecs}
 
       allow-axfr-fallback: ${yesOrNo       zone.allowAXFRFallback}
+      multi-master-check: ${yesOrNo        zone.multiMasterCheck}
     ${forEach     "  allow-notify: "       zone.allowNotify}
     ${forEach     "  request-xfr: "        zone.requestXFR}
 
@@ -201,7 +202,7 @@ let
       allowAXFRFallback = mkOption {
         type = types.bool;
         default = true;
-        description = lib.mdDoc ''
+        description = ''
           If NSD as secondary server should be allowed to AXFR if the primary
           server does not allow IXFR.
         '';
@@ -213,7 +214,7 @@ let
         example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name"
                     "10.0.3.4&255.255.0.0 BLOCKED"
                   ];
-        description = lib.mdDoc ''
+        description = ''
           Listed primary servers are allowed to notify this secondary server.
 
           Format: `<ip> <key-name | NOKEY | BLOCKED>`
@@ -243,7 +244,7 @@ let
         # to default values, breaking the parent inheriting function.
         type = types.attrsOf types.anything;
         default = {};
-        description = lib.mdDoc ''
+        description = ''
           Children zones inherit all options of their parents. Attributes
           defined in a child will overwrite the ones of its parent. Only
           leaf zones will be actually served. This way it's possible to
@@ -256,29 +257,29 @@ let
       data = mkOption {
         type = types.lines;
         default = "";
-        description = lib.mdDoc ''
+        description = ''
           The actual zone data. This is the content of your zone file.
           Use imports or pkgs.lib.readFile if you don't want this data in your config file.
         '';
       };
 
-      dnssec = mkEnableOption (lib.mdDoc "DNSSEC");
+      dnssec = mkEnableOption "DNSSEC";
 
       dnssecPolicy = {
         algorithm = mkOption {
           type = types.str;
           default = "RSASHA256";
-          description = lib.mdDoc "Which algorithm to use for DNSSEC";
+          description = "Which algorithm to use for DNSSEC";
         };
         keyttl = mkOption {
           type = types.str;
           default = "1h";
-          description = lib.mdDoc "TTL for dnssec records";
+          description = "TTL for dnssec records";
         };
         coverage = mkOption {
           type = types.str;
           default = "1y";
-          description = lib.mdDoc ''
+          description = ''
             The length of time to ensure that keys will be correct; no action will be taken to create new keys to be activated after this time.
           '';
         };
@@ -289,7 +290,7 @@ let
                       postPublish = "1w";
                       rollPeriod = "1mo";
                     };
-          description = lib.mdDoc "Key policy for zone signing keys";
+          description = "Key policy for zone signing keys";
         };
         ksk = mkOption {
           type = keyPolicy;
@@ -298,14 +299,14 @@ let
                       postPublish = "1mo";
                       rollPeriod = "0";
                     };
-          description = lib.mdDoc "Key policy for key signing keys";
+          description = "Key policy for key signing keys";
         };
       };
 
       maxRefreshSecs = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Limit refresh time for secondary zones. This is the timer which
           checks to see if the zone has to be refetched when it expires.
           Normally the value from the SOA record is used, but this  option
@@ -316,7 +317,7 @@ let
       minRefreshSecs = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Limit refresh time for secondary zones.
         '';
       };
@@ -324,7 +325,7 @@ let
       maxRetrySecs = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Limit retry time for secondary zones. This is the timeout after
           a failed fetch attempt for the zone. Normally the value from
           the SOA record is used, but this option restricts that value.
@@ -334,17 +335,26 @@ let
       minRetrySecs = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Limit retry time for secondary zones.
         '';
       };
 
+      multiMasterCheck = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, checks all masters for the last zone version.
+          It uses the higher version from all configured masters.
+          Useful if you have multiple masters that have different version numbers served.
+        '';
+      };
 
       notify = mkOption {
         type = types.listOf types.str;
         default = [];
         example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ];
-        description = lib.mdDoc ''
+        description = ''
           This primary server will notify all given secondary servers about
           zone changes.
 
@@ -361,7 +371,7 @@ let
       notifyRetry = mkOption {
         type = types.int;
         default = 5;
-        description = lib.mdDoc ''
+        description = ''
           Specifies the number of retries for failed notifies. Set this along with notify.
         '';
       };
@@ -370,7 +380,7 @@ let
         type = types.nullOr types.str;
         default = null;
         example = "2000::1@1234";
-        description = lib.mdDoc ''
+        description = ''
           This address will be used for zone-transfer requests if configured
           as a secondary server or notifications in case of a primary server.
           Supply either a plain IPv4 or IPv6 address with an optional port
@@ -382,7 +392,7 @@ let
         type = types.listOf types.str;
         default = [];
         example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ];
-        description = lib.mdDoc ''
+        description = ''
           Allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED
           address range 192.0.2.0/24, 1.2.3.4&255.255.0.0, 3.0.2.20-3.0.2.40
         '';
@@ -391,7 +401,7 @@ let
       requestXFR = mkOption {
         type = types.listOf types.str;
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           Format: `[AXFR|UDP] <ip-address> <key-name | NOKEY>`
         '';
       };
@@ -399,7 +409,7 @@ let
       rrlWhitelist = mkOption {
         type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]);
         default = [];
-        description = lib.mdDoc ''
+        description = ''
           Whitelists the given rrl-types.
         '';
       };
@@ -408,7 +418,7 @@ let
         type = types.nullOr types.str;
         default = null;
         example = "%s";
-        description = lib.mdDoc ''
+        description = ''
           When set to something distinct to null NSD is able to collect
           statistics per zone. All statistics of this zone(s) will be added
           to the group specified by this given name. Use "%s" to use the zones
@@ -423,19 +433,19 @@ let
     options = {
       keySize = mkOption {
         type = types.int;
-        description = lib.mdDoc "Key size in bits";
+        description = "Key size in bits";
       };
       prePublish = mkOption {
         type = types.str;
-        description = lib.mdDoc "How long in advance to publish new keys";
+        description = "How long in advance to publish new keys";
       };
       postPublish = mkOption {
         type = types.str;
-        description = lib.mdDoc "How long after deactivation to keep a key in the zone";
+        description = "How long after deactivation to keep a key in the zone";
       };
       rollPeriod = mkOption {
         type = types.str;
-        description = lib.mdDoc "How frequently to change keys";
+        description = "How frequently to change keys";
       };
     };
   };
@@ -478,14 +488,14 @@ in
   # options are ordered alphanumerically
   options.services.nsd = {
 
-    enable = mkEnableOption (lib.mdDoc "NSD authoritative DNS server");
+    enable = mkEnableOption "NSD authoritative DNS server";
 
-    bind8Stats = mkEnableOption (lib.mdDoc "BIND8 like statistics");
+    bind8Stats = mkEnableOption "BIND8 like statistics";
 
     dnssecInterval = mkOption {
       type = types.str;
       default = "1h";
-      description = lib.mdDoc ''
+      description = ''
         How often to check whether dnssec key rollover is required
       '';
     };
@@ -493,7 +503,7 @@ in
     extraConfig = mkOption {
       type = types.lines;
       default = "";
-      description = lib.mdDoc ''
+      description = ''
         Extra nsd config.
       '';
     };
@@ -501,7 +511,7 @@ in
     hideVersion = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries.
       '';
     };
@@ -509,7 +519,7 @@ in
     identity = mkOption {
       type = types.str;
       default = "unidentified server";
-      description = lib.mdDoc ''
+      description = ''
         Identify the server (CH TXT ID.SERVER entry).
       '';
     };
@@ -517,7 +527,7 @@ in
     interfaces = mkOption {
       type = types.listOf types.str;
       default = [ "127.0.0.0" "::1" ];
-      description = lib.mdDoc ''
+      description = ''
         What addresses the server should listen to.
       '';
     };
@@ -525,7 +535,7 @@ in
     ipFreebind = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether to bind to nonlocal addresses and interfaces that are down.
         Similar to ip-transparent.
       '';
@@ -534,7 +544,7 @@ in
     ipTransparent = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Allow binding to non local addresses.
       '';
     };
@@ -542,7 +552,7 @@ in
     ipv4 = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether to listen on IPv4 connections.
       '';
     };
@@ -550,7 +560,7 @@ in
     ipv4EDNSSize = mkOption {
       type = types.int;
       default = 4096;
-      description = lib.mdDoc ''
+      description = ''
         Preferred EDNS buffer size for IPv4.
       '';
     };
@@ -558,7 +568,7 @@ in
     ipv6 = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether to listen on IPv6 connections.
       '';
     };
@@ -566,7 +576,7 @@ in
     ipv6EDNSSize = mkOption {
       type = types.int;
       default = 4096;
-      description = lib.mdDoc ''
+      description = ''
         Preferred EDNS buffer size for IPv6.
       '';
     };
@@ -574,7 +584,7 @@ in
     logTimeAscii = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Log time in ascii, if false then in unix epoch seconds.
       '';
     };
@@ -582,7 +592,7 @@ in
     nsid = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         NSID identity (hex string, or "ascii_somestring").
       '';
     };
@@ -590,7 +600,7 @@ in
     port = mkOption {
       type = types.port;
       default = 53;
-      description = lib.mdDoc ''
+      description = ''
         Port the service should bind do.
       '';
     };
@@ -599,7 +609,7 @@ in
       type = types.bool;
       default = pkgs.stdenv.isLinux;
       defaultText = literalExpression "pkgs.stdenv.isLinux";
-      description = lib.mdDoc ''
+      description = ''
         Whether to enable SO_REUSEPORT on all used sockets. This lets multiple
         processes bind to the same port. This speeds up operation especially
         if the server count is greater than one and makes fast restarts less
@@ -610,18 +620,18 @@ in
     rootServer = mkOption {
       type = types.bool;
       default = false;
-      description = lib.mdDoc ''
+      description = ''
         Whether this server will be a root server (a DNS root server, you
         usually don't want that).
       '';
     };
 
-    roundRobin = mkEnableOption (lib.mdDoc "round robin rotation of records");
+    roundRobin = mkEnableOption "round robin rotation of records";
 
     serverCount = mkOption {
       type = types.int;
       default = 1;
-      description = lib.mdDoc ''
+      description = ''
         Number of NSD servers to fork. Put the number of CPUs to use here.
       '';
     };
@@ -629,7 +639,7 @@ in
     statistics = mkOption {
       type = types.nullOr types.int;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         Statistics are produced every number of seconds. Prints to log.
         If null no statistics are logged.
       '';
@@ -638,7 +648,7 @@ in
     tcpCount = mkOption {
       type = types.int;
       default = 100;
-      description = lib.mdDoc ''
+      description = ''
         Maximum number of concurrent TCP connections per server.
       '';
     };
@@ -646,7 +656,7 @@ in
     tcpQueryCount = mkOption {
       type = types.int;
       default = 0;
-      description = lib.mdDoc ''
+      description = ''
         Maximum number of queries served on a single TCP connection.
         0 means no maximum.
       '';
@@ -655,7 +665,7 @@ in
     tcpTimeout = mkOption {
       type = types.int;
       default = 120;
-      description = lib.mdDoc ''
+      description = ''
         TCP timeout in seconds.
       '';
     };
@@ -663,7 +673,7 @@ in
     verbosity = mkOption {
       type = types.int;
       default = 0;
-      description = lib.mdDoc ''
+      description = ''
         Verbosity level.
       '';
     };
@@ -671,7 +681,7 @@ in
     version = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The version string replied for CH TXT version.server and version.bind
         queries. Will use the compiled package version on null.
         See hideVersion for enabling/disabling this responses.
@@ -681,7 +691,7 @@ in
     xfrdReloadTimeout = mkOption {
       type = types.int;
       default = 1;
-      description = lib.mdDoc ''
+      description = ''
         Number of seconds between reloads triggered by xfrd.
       '';
     };
@@ -689,11 +699,22 @@ in
     zonefilesCheck = mkOption {
       type = types.bool;
       default = true;
-      description = lib.mdDoc ''
+      description = ''
         Whether to check mtime of all zone files on start and sighup.
       '';
     };
 
+    zonefilesWrite = mkOption {
+      type = types.int;
+      default = 0;
+      description = ''
+        Write changed secondary zones to their zonefile every N seconds.
+        If the zone (pattern) configuration has "" zonefile, it is not written.
+        Zones that have received zone transfer updates are written to their zonefile.
+        0 disables writing to zone files.
+      '';
+    };
+
 
     keys = mkOption {
       type = types.attrsOf (types.submodule {
@@ -702,14 +723,14 @@ in
           algorithm = mkOption {
             type = types.str;
             default = "hmac-sha256";
-            description = lib.mdDoc ''
+            description = ''
               Authentication algorithm for this key.
             '';
           };
 
           keyFile = mkOption {
             type = types.path;
-            description = lib.mdDoc ''
+            description = ''
               Path to the file which contains the actual base64 encoded
               key. The key will be copied into "${stateDir}/private" before
               NSD starts. The copied file is only accessibly by the NSD
@@ -727,7 +748,7 @@ in
           };
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Define your TSIG keys here.
       '';
     };
@@ -735,12 +756,12 @@ in
 
     ratelimit = {
 
-      enable = mkEnableOption (lib.mdDoc "ratelimit capabilities");
+      enable = mkEnableOption "ratelimit capabilities";
 
       ipv4PrefixLength = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           IPv4 prefix length. Addresses are grouped by netblock.
         '';
       };
@@ -748,7 +769,7 @@ in
       ipv6PrefixLength = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           IPv6 prefix length. Addresses are grouped by netblock.
         '';
       };
@@ -756,7 +777,7 @@ in
       ratelimit = mkOption {
         type = types.int;
         default = 200;
-        description = lib.mdDoc ''
+        description = ''
           Max qps allowed from any query source.
           0 means unlimited. With an verbosity of 2 blocked and
           unblocked subnets will be logged.
@@ -766,7 +787,7 @@ in
       slip = mkOption {
         type = types.nullOr types.int;
         default = null;
-        description = lib.mdDoc ''
+        description = ''
           Number of packets that get discarded before replying a SLIP response.
           0 disables SLIP responses. 1 will make every response a SLIP response.
         '';
@@ -775,7 +796,7 @@ in
       size = mkOption {
         type = types.int;
         default = 1000000;
-        description = lib.mdDoc ''
+        description = ''
           Size of the hashtable. More buckets use more memory but lower
           the chance of hash hash collisions.
         '';
@@ -784,7 +805,7 @@ in
       whitelistRatelimit = mkOption {
         type = types.int;
         default = 2000;
-        description = lib.mdDoc ''
+        description = ''
           Max qps allowed from whitelisted sources.
           0 means unlimited. Set the rrl-whitelist option for specific
           queries to apply this limit instead of the default to them.
@@ -796,12 +817,12 @@ in
 
     remoteControl = {
 
-      enable = mkEnableOption (lib.mdDoc "remote control via nsd-control");
+      enable = mkEnableOption "remote control via nsd-control";
 
       controlCertFile = mkOption {
         type = types.path;
         default = "/etc/nsd/nsd_control.pem";
-        description = lib.mdDoc ''
+        description = ''
           Path to the client certificate signed with the server certificate.
           This file is used by nsd-control and generated by nsd-control-setup.
         '';
@@ -810,7 +831,7 @@ in
       controlKeyFile = mkOption {
         type = types.path;
         default = "/etc/nsd/nsd_control.key";
-        description = lib.mdDoc ''
+        description = ''
           Path to the client private key, which is used by nsd-control
           but not by the server. This file is generated by nsd-control-setup.
         '';
@@ -819,7 +840,7 @@ in
       interfaces = mkOption {
         type = types.listOf types.str;
         default = [ "127.0.0.1" "::1" ];
-        description = lib.mdDoc ''
+        description = ''
           Which interfaces NSD should bind to for remote control.
         '';
       };
@@ -827,7 +848,7 @@ in
       port = mkOption {
         type = types.port;
         default = 8952;
-        description = lib.mdDoc ''
+        description = ''
           Port number for remote control operations (uses TLS over TCP).
         '';
       };
@@ -835,7 +856,7 @@ in
       serverCertFile = mkOption {
         type = types.path;
         default = "/etc/nsd/nsd_server.pem";
-        description = lib.mdDoc ''
+        description = ''
           Path to the server self signed certificate, which is used by the server
           but and by nsd-control. This file is generated by nsd-control-setup.
         '';
@@ -844,7 +865,7 @@ in
       serverKeyFile = mkOption {
         type = types.path;
         default = "/etc/nsd/nsd_server.key";
-        description = lib.mdDoc ''
+        description = ''
           Path to the server private key, which is used by the server
           but not by nsd-control. This file is generated by nsd-control-setup.
         '';
@@ -886,7 +907,7 @@ in
           };
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         Define your zones here. Zones can cascade other zones and therefore
         inherit settings from parent zones. Look at the definition of
         children to learn about inheritance and child zones.
diff --git a/nixos/modules/services/networking/pleroma.md b/nixos/modules/services/networking/pleroma.md
index 7c499e1c616c2..c2313fd63e6a5 100644
--- a/nixos/modules/services/networking/pleroma.md
+++ b/nixos/modules/services/networking/pleroma.md
@@ -17,11 +17,13 @@ The `config.exs` file can be further customized following the instructions on th
 ## Initializing the database {#module-services-pleroma-initialize-db}
 
 First, the Postgresql service must be enabled in the NixOS configuration
-```
-services.postgresql = {
-  enable = true;
-  package = pkgs.postgresql_13;
-};
+```nix
+{
+  services.postgresql = {
+    enable = true;
+    package = pkgs.postgresql_13;
+  };
+}
 ```
 and activated with the usual
 ```ShellSession
@@ -38,43 +40,45 @@ $ sudo -u postgres psql -f setup.psql
 In this section we will enable the Pleroma service only locally, so its configurations can be improved incrementally.
 
 This is an example of configuration, where [](#opt-services.pleroma.configs) option contains the content of the file `config.exs`, generated [in the first section](#module-services-pleroma-generate-config), but with the secrets (database password, endpoint secret key, salts, etc.) removed. Removing secrets is important, because otherwise they will be stored publicly in the Nix store.
-```
-services.pleroma = {
-  enable = true;
-  secretConfigFile = "/var/lib/pleroma/secrets.exs";
-  configs = [
-    ''
-    import Config
-
-    config :pleroma, Pleroma.Web.Endpoint,
-      url: [host: "pleroma.example.net", scheme: "https", port: 443],
-      http: [ip: {127, 0, 0, 1}, port: 4000]
-
-    config :pleroma, :instance,
-      name: "Test",
-      email: "admin@example.net",
-      notify_email: "admin@example.net",
-      limit: 5000,
-      registrations_open: true
-
-    config :pleroma, :media_proxy,
-      enabled: false,
-      redirect_on_failure: true
-
-    config :pleroma, Pleroma.Repo,
-      adapter: Ecto.Adapters.Postgres,
-      username: "pleroma",
-      database: "pleroma",
-      hostname: "localhost"
-
-    # Configure web push notifications
-    config :web_push_encryption, :vapid_details,
-      subject: "mailto:admin@example.net"
-
-    # ... TO CONTINUE ...
-    ''
-  ];
-};
+```nix
+{
+  services.pleroma = {
+    enable = true;
+    secretConfigFile = "/var/lib/pleroma/secrets.exs";
+    configs = [
+      ''
+      import Config
+
+      config :pleroma, Pleroma.Web.Endpoint,
+        url: [host: "pleroma.example.net", scheme: "https", port: 443],
+        http: [ip: {127, 0, 0, 1}, port: 4000]
+
+      config :pleroma, :instance,
+        name: "Test",
+        email: "admin@example.net",
+        notify_email: "admin@example.net",
+        limit: 5000,
+        registrations_open: true
+
+      config :pleroma, :media_proxy,
+        enabled: false,
+        redirect_on_failure: true
+
+      config :pleroma, Pleroma.Repo,
+        adapter: Ecto.Adapters.Postgres,
+        username: "pleroma",
+        database: "pleroma",
+        hostname: "localhost"
+
+      # Configure web push notifications
+      config :web_push_encryption, :vapid_details,
+        subject: "mailto:admin@example.net"
+
+      # ... TO CONTINUE ...
+      ''
+    ];
+  };
+}
 ```
 
 Secrets must be moved into a file pointed by [](#opt-services.pleroma.secretConfigFile), in our case `/var/lib/pleroma/secrets.exs`. This file can be created copying the previously generated `config.exs` file and then removing all the settings, except the secrets. This is an example
@@ -121,60 +125,62 @@ $ pleroma_ctl user new <nickname> <email>  --admin --moderator --password <passw
 
 In this configuration, Pleroma is listening only on the local port 4000. Nginx can be configured as a Reverse Proxy, for forwarding requests from public ports to the Pleroma service. This is an example of configuration, using
 [Let's Encrypt](https://letsencrypt.org/) for the TLS certificates
-```
-security.acme = {
-  email = "root@example.net";
-  acceptTerms = true;
-};
-
-services.nginx = {
-  enable = true;
-  addSSL = true;
-
-  recommendedTlsSettings = true;
-  recommendedOptimisation = true;
-  recommendedGzipSettings = true;
-
-  recommendedProxySettings = false;
-  # NOTE: if enabled, the NixOS proxy optimizations will override the Pleroma
-  # specific settings, and they will enter in conflict.
-
-  virtualHosts = {
-    "pleroma.example.net" = {
-      http2 = true;
-      enableACME = true;
-      forceSSL = true;
-
-      locations."/" = {
-        proxyPass = "http://127.0.0.1:4000";
-
-        extraConfig = ''
-          etag on;
-          gzip on;
-
-          add_header 'Access-Control-Allow-Origin' '*' always;
-          add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
-          add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
-          add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
-          if ($request_method = OPTIONS) {
-            return 204;
-          }
-          add_header X-XSS-Protection "1; mode=block";
-          add_header X-Permitted-Cross-Domain-Policies none;
-          add_header X-Frame-Options DENY;
-          add_header X-Content-Type-Options nosniff;
-          add_header Referrer-Policy same-origin;
-          add_header X-Download-Options noopen;
-          proxy_http_version 1.1;
-          proxy_set_header Upgrade $http_upgrade;
-          proxy_set_header Connection "upgrade";
-          proxy_set_header Host $host;
-
-          client_max_body_size 16m;
-          # NOTE: increase if users need to upload very big files
-        '';
+```nix
+{
+  security.acme = {
+    email = "root@example.net";
+    acceptTerms = true;
+  };
+
+  services.nginx = {
+    enable = true;
+    addSSL = true;
+
+    recommendedTlsSettings = true;
+    recommendedOptimisation = true;
+    recommendedGzipSettings = true;
+
+    recommendedProxySettings = false;
+    # NOTE: if enabled, the NixOS proxy optimizations will override the Pleroma
+    # specific settings, and they will enter in conflict.
+
+    virtualHosts = {
+      "pleroma.example.net" = {
+        http2 = true;
+        enableACME = true;
+        forceSSL = true;
+
+        locations."/" = {
+          proxyPass = "http://127.0.0.1:4000";
+
+          extraConfig = ''
+            etag on;
+            gzip on;
+
+            add_header 'Access-Control-Allow-Origin' '*' always;
+            add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
+            add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
+            add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
+            if ($request_method = OPTIONS) {
+              return 204;
+            }
+            add_header X-XSS-Protection "1; mode=block";
+            add_header X-Permitted-Cross-Domain-Policies none;
+            add_header X-Frame-Options DENY;
+            add_header X-Content-Type-Options nosniff;
+            add_header Referrer-Policy same-origin;
+            add_header X-Download-Options noopen;
+            proxy_http_version 1.1;
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection "upgrade";
+            proxy_set_header Host $host;
+
+            client_max_body_size 16m;
+            # NOTE: increase if users need to upload very big files
+          '';
+        };
       };
     };
   };
-};
+}
 ```
diff --git a/nixos/modules/services/networking/prosody.md b/nixos/modules/services/networking/prosody.md
index 2da2c242a98b9..d6eee4e29f0a2 100644
--- a/nixos/modules/services/networking/prosody.md
+++ b/nixos/modules/services/networking/prosody.md
@@ -25,25 +25,27 @@ A good configuration to start with, including a
 [Multi User Chat (MUC)](https://xmpp.org/extensions/xep-0045.html)
 endpoint as well as a [HTTP File Upload](https://xmpp.org/extensions/xep-0363.html)
 endpoint will look like this:
-```
-services.prosody = {
-  enable = true;
-  admins = [ "root@example.org" ];
-  ssl.cert = "/var/lib/acme/example.org/fullchain.pem";
-  ssl.key = "/var/lib/acme/example.org/key.pem";
-  virtualHosts."example.org" = {
-      enabled = true;
-      domain = "example.org";
-      ssl.cert = "/var/lib/acme/example.org/fullchain.pem";
-      ssl.key = "/var/lib/acme/example.org/key.pem";
-  };
-  muc = [ {
-      domain = "conference.example.org";
-  } ];
-  uploadHttp = {
-      domain = "upload.example.org";
+```nix
+{
+  services.prosody = {
+    enable = true;
+    admins = [ "root@example.org" ];
+    ssl.cert = "/var/lib/acme/example.org/fullchain.pem";
+    ssl.key = "/var/lib/acme/example.org/key.pem";
+    virtualHosts."example.org" = {
+        enabled = true;
+        domain = "example.org";
+        ssl.cert = "/var/lib/acme/example.org/fullchain.pem";
+        ssl.key = "/var/lib/acme/example.org/key.pem";
+    };
+    muc = [ {
+        domain = "conference.example.org";
+    } ];
+    uploadHttp = {
+        domain = "upload.example.org";
+    };
   };
-};
+}
 ```
 
 ## Let's Encrypt Configuration {#module-services-prosody-letsencrypt}
@@ -57,16 +59,18 @@ certificate by leveraging the ACME
 
 Provided the setup detailed in the previous section, you'll need the following acme configuration to generate
 a TLS certificate for the three endponits:
-```
-security.acme = {
-  email = "root@example.org";
-  acceptTerms = true;
-  certs = {
-    "example.org" = {
-      webroot = "/var/www/example.org";
-      email = "root@example.org";
-      extraDomainNames = [ "conference.example.org" "upload.example.org" ];
+```nix
+{
+  security.acme = {
+    email = "root@example.org";
+    acceptTerms = true;
+    certs = {
+      "example.org" = {
+        webroot = "/var/www/example.org";
+        email = "root@example.org";
+        extraDomainNames = [ "conference.example.org" "upload.example.org" ];
+      };
     };
   };
-};
+}
 ```
diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix
index 00dbd6bbe386d..d6b2f652c8887 100644
--- a/nixos/modules/services/networking/radicale.nix
+++ b/nixos/modules/services/networking/radicale.nix
@@ -200,5 +200,5 @@ in {
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ infinisil dotlambda ];
+  meta.maintainers = with lib.maintainers; [ dotlambda ];
 }
diff --git a/nixos/modules/services/networking/scion/scion-control.nix b/nixos/modules/services/networking/scion/scion-control.nix
new file mode 100644
index 0000000000000..fdf3a9ba3cc15
--- /dev/null
+++ b/nixos/modules/services/networking/scion/scion-control.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.scion.scion-control;
+  toml = pkgs.formats.toml { };
+  defaultConfig = {
+    general = {
+      id = "cs";
+      config_dir = "/etc/scion";
+      reconnect_to_dispatcher = true;
+    };
+    beacon_db = {
+      connection = "/var/lib/scion-control/control.beacon.db";
+    };
+    path_db = {
+      connection = "/var/lib/scion-control/control.path.db";
+    };
+    trust_db = {
+      connection = "/var/lib/scion-control/control.trust.db";
+    };
+    log.console = {
+      level = "info";
+    };
+  };
+  configFile = toml.generate "scion-control.toml" (defaultConfig // cfg.settings);
+in
+{
+  options.services.scion.scion-control = {
+    enable = mkEnableOption (lib.mdDoc "the scion-control service");
+    settings = mkOption {
+      default = { };
+      type = toml.type;
+      example = literalExpression ''
+        {
+          path_db = {
+            connection = "/var/lib/scion-control/control.path.db";
+          };
+          log.console = {
+            level = "info";
+          };
+        }
+      '';
+      description = lib.mdDoc ''
+        scion-control configuration. Refer to
+        <https://docs.scion.org/en/latest/manuals/common.html>
+        for details on supported values.
+      '';
+    };
+  };
+  config = mkIf cfg.enable {
+    systemd.services.scion-control = {
+      description = "SCION Control Service";
+      after = [ "network-online.target" "scion-dispatcher.service" ];
+      wants = [ "network-online.target" "scion-dispatcher.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "simple";
+        Group = if (config.services.scion.scion-dispatcher.enable == true) then "scion" else null;
+        ExecStart = "${pkgs.scion}/bin/scion-control --config ${configFile}";
+        DynamicUser = true;
+        Restart = "on-failure";
+        BindPaths = [ "/dev/shm:/run/shm" ];
+        StateDirectory = "scion-control";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/scion/scion-daemon.nix b/nixos/modules/services/networking/scion/scion-daemon.nix
new file mode 100644
index 0000000000000..0bcc18771fc3c
--- /dev/null
+++ b/nixos/modules/services/networking/scion/scion-daemon.nix
@@ -0,0 +1,64 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.scion.scion-daemon;
+  toml = pkgs.formats.toml { };
+  defaultConfig = {
+    general = {
+      id = "sd";
+      config_dir = "/etc/scion";
+      reconnect_to_dispatcher = true;
+    };
+    path_db = {
+      connection = "/var/lib/scion-daemon/sd.path.db";
+    };
+    trust_db = {
+      connection = "/var/lib/scion-daemon/sd.trust.db";
+    };
+    log.console = {
+      level = "info";
+    };
+  };
+  configFile = toml.generate "scion-daemon.toml" (defaultConfig // cfg.settings);
+in
+{
+  options.services.scion.scion-daemon = {
+    enable = mkEnableOption (lib.mdDoc "the scion-daemon service");
+    settings = mkOption {
+      default = { };
+      type = toml.type;
+      example = literalExpression ''
+        {
+          path_db = {
+            connection = "/var/lib/scion-daemon/sd.path.db";
+          };
+          log.console = {
+            level = "info";
+          };
+        }
+      '';
+      description = lib.mdDoc ''
+        scion-daemon configuration. Refer to
+        <https://docs.scion.org/en/latest/manuals/common.html>
+        for details on supported values.
+      '';
+    };
+  };
+  config = mkIf cfg.enable {
+    systemd.services.scion-daemon = {
+      description = "SCION Daemon";
+      after = [ "network-online.target" "scion-dispatcher.service" ];
+      wants = [ "network-online.target" "scion-dispatcher.service" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.scion}/bin/scion-daemon --config ${configFile}";
+        Restart = "on-failure";
+        DynamicUser = true;
+        StateDirectory = "scion-daemon";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/scion/scion-dispatcher.nix b/nixos/modules/services/networking/scion/scion-dispatcher.nix
new file mode 100644
index 0000000000000..bab1ec0a989b5
--- /dev/null
+++ b/nixos/modules/services/networking/scion/scion-dispatcher.nix
@@ -0,0 +1,74 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.scion.scion-dispatcher;
+  toml = pkgs.formats.toml { };
+  defaultConfig = {
+    dispatcher = {
+      id = "dispatcher";
+      socket_file_mode = "0770";
+      application_socket = "/dev/shm/dispatcher/default.sock";
+    };
+    log.console = {
+      level = "info";
+    };
+  };
+  configFile = toml.generate "scion-dispatcher.toml" (defaultConfig // cfg.settings);
+in
+{
+  options.services.scion.scion-dispatcher = {
+    enable = mkEnableOption (lib.mdDoc "the scion-dispatcher service");
+    settings = mkOption {
+      default = { };
+      type = toml.type;
+      example = literalExpression ''
+        {
+          dispatcher = {
+            id = "dispatcher";
+            socket_file_mode = "0770";
+            application_socket = "/dev/shm/dispatcher/default.sock";
+          };
+          log.console = {
+            level = "info";
+          };
+        }
+      '';
+      description = lib.mdDoc ''
+        scion-dispatcher configuration. Refer to
+        <https://docs.scion.org/en/latest/manuals/common.html>
+        for details on supported values.
+      '';
+    };
+  };
+  config = mkIf cfg.enable {
+    # Needed for group ownership of the dispatcher socket
+    users.groups.scion = {};
+
+    # scion programs hardcode path to dispatcher in /run/shm, and is not
+    # configurable at runtime upstream plans to obsolete the dispatcher in
+    # favor of an SCMP daemon, at which point this can be removed.
+    system.activationScripts.scion-dispatcher = ''
+      ln -sf /dev/shm /run/shm
+    '';
+
+    systemd.services.scion-dispatcher = {
+      description = "SCION Dispatcher";
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "simple";
+        Group = "scion";
+        DynamicUser = true;
+        BindPaths = [ "/dev/shm:/run/shm" ];
+        ExecStartPre = "${pkgs.coreutils}/bin/rm -rf /run/shm/dispatcher";
+        ExecStart = "${pkgs.scion}/bin/scion-dispatcher --config ${configFile}";
+        Restart = "on-failure";
+        StateDirectory = "scion-dispatcher";
+      };
+    };
+  };
+}
+
diff --git a/nixos/modules/services/networking/scion/scion-router.nix b/nixos/modules/services/networking/scion/scion-router.nix
new file mode 100644
index 0000000000000..cbe83c6dbf8d1
--- /dev/null
+++ b/nixos/modules/services/networking/scion/scion-router.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.scion.scion-router;
+  toml = pkgs.formats.toml { };
+  defaultConfig = {
+    general = {
+      id = "br";
+      config_dir = "/etc/scion";
+    };
+  };
+  configFile = toml.generate "scion-router.toml" (defaultConfig // cfg.settings);
+in
+{
+  options.services.scion.scion-router = {
+    enable = mkEnableOption (lib.mdDoc "the scion-router service");
+    settings = mkOption {
+      default = { };
+      type = toml.type;
+      example = literalExpression ''
+        {
+          general.id = "br";
+        }
+      '';
+      description = lib.mdDoc ''
+        scion-router configuration. Refer to
+        <https://docs.scion.org/en/latest/manuals/common.html>
+        for details on supported values.
+      '';
+    };
+  };
+  config = mkIf cfg.enable {
+    systemd.services.scion-router = {
+      description = "SCION Router";
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${pkgs.scion}/bin/scion-router --config ${configFile}";
+        Restart = "on-failure";
+        DynamicUser = true;
+        StateDirectory = "scion-router";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/scion/scion.nix b/nixos/modules/services/networking/scion/scion.nix
new file mode 100644
index 0000000000000..704f942b5d9e3
--- /dev/null
+++ b/nixos/modules/services/networking/scion/scion.nix
@@ -0,0 +1,39 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.scion;
+in
+{
+  options.services.scion = {
+    enable = mkEnableOption (lib.mdDoc "all of the scion components and services");
+    bypassBootstrapWarning = mkOption {
+      type = types.bool;
+      default = false;
+      description = lib.mdDoc ''
+        bypass Nix warning about SCION PKI bootstrapping
+      '';
+    };
+  };
+  config = mkIf cfg.enable {
+    services.scion = {
+      scion-dispatcher.enable = true;
+      scion-daemon.enable = true;
+      scion-router.enable = true;
+      scion-control.enable = true;
+    };
+    assertions = [
+      { assertion = cfg.bypassBootstrapWarning == true;
+        message = ''
+          SCION is a routing protocol and requires bootstrapping with a manual, imperative key signing ceremony. You may want to join an existing Isolation Domain (ISD) such as scionlab.org, or bootstrap your own. If you have completed and configured the public key infrastructure for SCION and are sure this process is complete, then add the following to your configuration:
+
+          services.scion.bypassBootstrapWarning = true;
+
+          refer to docs.scion.org for more information
+        '';
+      }
+    ];
+  };
+}
+
diff --git a/nixos/modules/services/networking/soju.nix b/nixos/modules/services/networking/soju.nix
index d69ec08ca13a0..810957be65f57 100644
--- a/nixos/modules/services/networking/soju.nix
+++ b/nixos/modules/services/networking/soju.nix
@@ -5,7 +5,10 @@ with lib;
 let
   cfg = config.services.soju;
   stateDir = "/var/lib/soju";
-  listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") cfg.listen;
+  runtimeDir = "/run/soju";
+  listen = cfg.listen
+    ++ optional cfg.adminSocket.enable "unix+admin://${runtimeDir}/admin";
+  listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") listen;
   tlsCfg = optionalString (cfg.tlsCertificate != null)
     "tls ${cfg.tlsCertificate} ${cfg.tlsCertificateKey}";
   logCfg = optionalString cfg.enableMessageLogging
@@ -22,6 +25,10 @@ let
 
     ${cfg.extraConfig}
   '';
+
+  sojuctl = pkgs.writeShellScriptBin "sojuctl" ''
+    exec ${cfg.package}/bin/sojuctl --config ${configFile} "$@"
+  '';
 in
 {
   ###### interface
@@ -29,6 +36,8 @@ in
   options.services.soju = {
     enable = mkEnableOption (lib.mdDoc "soju");
 
+    package = mkPackageOption pkgs "soju" { };
+
     listen = mkOption {
       type = types.listOf types.str;
       default = [ ":6697" ];
@@ -66,6 +75,14 @@ in
       description = lib.mdDoc "Whether to enable message logging.";
     };
 
+    adminSocket.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Listen for admin connections from sojuctl at /run/soju/admin.
+      '';
+    };
+
     httpOrigins = mkOption {
       type = types.listOf types.str;
       default = [];
@@ -107,6 +124,8 @@ in
       }
     ];
 
+    environment.systemPackages = [ sojuctl ];
+
     systemd.services.soju = {
       description = "soju IRC bouncer";
       wantedBy = [ "multi-user.target" ];
@@ -115,8 +134,9 @@ in
       serviceConfig = {
         DynamicUser = true;
         Restart = "always";
-        ExecStart = "${pkgs.soju}/bin/soju -config ${configFile}";
+        ExecStart = "${cfg.package}/bin/soju -config ${configFile}";
         StateDirectory = "soju";
+        RuntimeDirectory = "soju";
       };
     };
   };
diff --git a/nixos/modules/services/networking/technitium-dns-server.nix b/nixos/modules/services/networking/technitium-dns-server.nix
new file mode 100644
index 0000000000000..0c8499e072d4f
--- /dev/null
+++ b/nixos/modules/services/networking/technitium-dns-server.nix
@@ -0,0 +1,109 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.services.technitium-dns-server;
+  stateDir = "/var/lib/technitium-dns-server";
+  inherit (lib)
+    mkEnableOption
+    mkPackageOption
+    mkOption
+    mkIf
+    types
+    ;
+in
+{
+  options.services.technitium-dns-server = {
+    enable = mkEnableOption "Technitium DNS Server";
+
+    package = mkPackageOption pkgs "technitium-dns-server" { };
+
+    openFirewall = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Whether to open ports in the firewall.
+        Standard ports are 53 (UDP and TCP, for DNS), 5380 and 53443 (TCP, HTTP and HTTPS for web interface).
+        Specify different or additional ports in options firewallUDPPorts and firewallTCPPorts if necessary.
+      '';
+    };
+
+    firewallUDPPorts = mkOption {
+      type = with types; listOf int;
+      default = [ 53 ];
+      description = ''
+        List of UDP ports to open in firewall.
+      '';
+    };
+
+    firewallTCPPorts = mkOption {
+      type = with types; listOf int;
+      default = [
+        53
+        5380 # web interface HTTP
+        53443 # web interface HTTPS
+      ];
+      description = ''
+        List of TCP ports to open in firewall.
+        You might want to open ports 443 and 853 if you intend to use DNS over HTTPS or DNS over TLS.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.technitium-dns-server = {
+      description = "Technitium DNS Server";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/technitium-dns-server ${stateDir}";
+
+        DynamicUser = true;
+
+        StateDirectory = "technitium-dns-server";
+        WorkingDirectory = stateDir;
+        BindPaths = stateDir;
+
+        Restart = "always";
+        RestartSec = 10;
+        TimeoutStopSec = 10;
+        KillSignal = "SIGINT";
+
+        # Harden the service
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateMounts = true;
+        PrivateTmp = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_NETLINK";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+
+        AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+        CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+      };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedUDPPorts = cfg.firewallUDPPorts;
+      allowedTCPPorts = cfg.firewallTCPPorts;
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ fabianrig ];
+}
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index eb769f53901cf..5e20c83652988 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -348,7 +348,7 @@ in
         (flip mapAttrsToList cfg.networks (network: data:
           flip mapAttrs' data.hosts (host: text: nameValuePair
             ("tinc/${network}/hosts/${host}")
-            ({ mode = "0644"; user = "tinc.${network}"; inherit text; })
+            ({ mode = "0644"; user = "tinc-${network}"; inherit text; })
           ) // {
             "tinc/${network}/tinc.conf" = {
               mode = "0444";
@@ -375,13 +375,13 @@ in
             Restart = "always";
             RestartSec = "3";
             ExecReload = mkIf (versionAtLeast version "1.1pre") "${data.package}/bin/tinc -n ${network} reload";
-            ExecStart = "${data.package}/bin/tincd -D -U tinc.${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel}";
+            ExecStart = "${data.package}/bin/tincd -D -U tinc-${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel}";
           };
           preStart = ''
             mkdir -p /etc/tinc/${network}/hosts
-            chown tinc.${network} /etc/tinc/${network}/hosts
+            chown tinc-${network} /etc/tinc/${network}/hosts
             mkdir -p /etc/tinc/${network}/invitations
-            chown tinc.${network} /etc/tinc/${network}/invitations
+            chown tinc-${network} /etc/tinc/${network}/invitations
 
             # Determine how we should generate our keys
             if type tinc >/dev/null 2>&1; then
@@ -420,14 +420,14 @@ in
       in [ cli-wrappers ];
 
       users.users = flip mapAttrs' cfg.networks (network: _:
-        nameValuePair ("tinc.${network}") ({
+        nameValuePair ("tinc-${network}") ({
           description = "Tinc daemon user for ${network}";
           isSystemUser = true;
-          group = "tinc.${network}";
+          group = "tinc-${network}";
         })
       );
       users.groups = flip mapAttrs' cfg.networks (network: _:
-        nameValuePair "tinc.${network}" {}
+        nameValuePair "tinc-${network}" {}
       );
     });
 
diff --git a/nixos/modules/services/networking/tinyproxy.nix b/nixos/modules/services/networking/tinyproxy.nix
index 8ff12b52f10ca..2b7509e99ca4d 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/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix
index 8eb29f2bcdb6f..569f39969450c 100644
--- a/nixos/modules/services/networking/unifi.nix
+++ b/nixos/modules/services/networking/unifi.nix
@@ -39,10 +39,10 @@ in
     services.unifi.unifiPackage = lib.mkPackageOption pkgs "unifi5" { };
 
     services.unifi.mongodbPackage = lib.mkPackageOption pkgs "mongodb" {
-      default = "mongodb-4_4";
+      default = "mongodb-5_0";
       extraDescription = ''
         ::: {.note}
-        unifi7 officially only supports mongodb up until 3.6 but works with 4.4.
+        unifi7 officially only supports mongodb up until 4.4 but works with 5.0.
         :::
       '';
     };
diff --git a/nixos/modules/services/networking/v2raya.nix b/nixos/modules/services/networking/v2raya.nix
index 0bea73798daf1..58ce89ae00a48 100644
--- a/nixos/modules/services/networking/v2raya.nix
+++ b/nixos/modules/services/networking/v2raya.nix
@@ -42,7 +42,7 @@ with lib;
         };
 
         wantedBy = [ "multi-user.target" ];
-        path = with pkgs; [ iptables bash iproute2 ]; # required by v2rayA TProxy functionality
+        path = with pkgs; [ iptables bash iproute2 ] ++ lib.optionals nftablesEnabled [ nftables ]; # required by v2rayA TProxy functionality
       };
   };
 
diff --git a/nixos/modules/services/networking/yggdrasil.md b/nixos/modules/services/networking/yggdrasil.md
index bbaea5bc74aaf..7b899f9d6ddb6 100644
--- a/nixos/modules/services/networking/yggdrasil.md
+++ b/nixos/modules/services/networking/yggdrasil.md
@@ -12,7 +12,7 @@ self-arranging IPv6 network.
 ### Simple ephemeral node {#module-services-networking-yggdrasil-configuration-simple}
 
 An annotated example of a simple configuration:
-```
+```nix
 {
   services.yggdrasil = {
     enable = true;
@@ -39,7 +39,7 @@ An annotated example of a simple configuration:
 ### Persistent node with prefix {#module-services-networking-yggdrasil-configuration-prefix}
 
 A node with a fixed address that announces a prefix:
-```
+```nix
 let
   address = "210:5217:69c0:9afc:1b95:b9f:8718:c3d2";
   prefix = "310:5217:69c0:9afc";
@@ -90,7 +90,7 @@ in {
 
 A NixOS container attached to the Yggdrasil network via a node running on the
 host:
-```
+```nix
 let
   yggPrefix64 = "310:5217:69c0:9afc";
     # Again, taken from the output of "yggdrasilctl getself".
diff --git a/nixos/modules/services/search/meilisearch.md b/nixos/modules/services/search/meilisearch.md
index 299f56bf82932..b9f65861b1d1c 100644
--- a/nixos/modules/services/search/meilisearch.md
+++ b/nixos/modules/services/search/meilisearch.md
@@ -7,7 +7,9 @@ Meilisearch is a lightweight, fast and powerful search engine. Think elastic sea
 the minimum to start meilisearch is
 
 ```nix
-services.meilisearch.enable = true;
+{
+  services.meilisearch.enable = true;
+}
 ```
 
 this will start the http server included with meilisearch on port 7700.
diff --git a/nixos/modules/services/security/oauth2_proxy_nginx.nix b/nixos/modules/services/security/oauth2_proxy_nginx.nix
index b8e45f67cf783..dd3ded6259c4a 100644
--- a/nixos/modules/services/security/oauth2_proxy_nginx.nix
+++ b/nixos/modules/services/security/oauth2_proxy_nginx.nix
@@ -13,6 +13,17 @@ in
         The address of the reverse proxy endpoint for oauth2_proxy
       '';
     };
+
+    domain = mkOption {
+      type = types.str;
+      description = lib.mdDoc ''
+        The domain under which the oauth2_proxy will be accesible and the path of cookies are set to.
+        This setting must be set to ensure back-redirects are working properly
+        if oauth2-proxy is configured with {option}`services.oauth2_proxy.cookie.domain`
+        or multiple {option}`services.oauth2_proxy.nginx.virtualHosts` that are not on the same domain.
+      '';
+    };
+
     virtualHosts = mkOption {
       type = types.listOf types.str;
       default = [];
@@ -21,22 +32,26 @@ in
       '';
     };
   };
+
   config.services.oauth2_proxy = mkIf (cfg.virtualHosts != [] && (hasPrefix "127.0.0.1:" cfg.proxy)) {
     enable = true;
   };
-  config.services.nginx = mkIf config.services.oauth2_proxy.enable (mkMerge
-  ((optional (cfg.virtualHosts != []) {
-    recommendedProxySettings = true; # needed because duplicate headers
-  }) ++ (map (vhost: {
-    virtualHosts.${vhost} = {
-      locations."/oauth2/" = {
+
+  config.services.nginx = mkIf (cfg.virtualHosts != [] && config.services.oauth2_proxy.enable) (mkMerge ([
+    {
+      virtualHosts.${cfg.domain}.locations."/oauth2/" = {
         proxyPass = cfg.proxy;
         extraConfig = ''
           proxy_set_header X-Scheme                $scheme;
           proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
         '';
       };
-      locations."/oauth2/auth" = {
+    }
+  ] ++ optional (cfg.virtualHosts != []) {
+    recommendedProxySettings = true; # needed because duplicate headers
+  } ++ (map (vhost: {
+    virtualHosts.${vhost}.locations = {
+      "/oauth2/auth" = {
         proxyPass = cfg.proxy;
         extraConfig = ''
           proxy_set_header X-Scheme         $scheme;
@@ -45,9 +60,10 @@ in
           proxy_pass_request_body           off;
         '';
       };
-      locations."/".extraConfig = ''
+      "@redirectToAuth2ProxyLogin".return = "307 https://${cfg.domain}/oauth2/start?rd=$scheme://$host$request_uri";
+      "/".extraConfig = ''
         auth_request /oauth2/auth;
-        error_page 401 = /oauth2/sign_in;
+        error_page 401 = @redirectToAuth2ProxyLogin;
 
         # pass information via X-User and X-Email headers to backend,
         # requires running with --set-xauthrequest flag
@@ -60,7 +76,6 @@ in
         auth_request_set $auth_cookie $upstream_http_set_cookie;
         add_header Set-Cookie $auth_cookie;
       '';
-
     };
   }) cfg.virtualHosts)));
 }
diff --git a/nixos/modules/services/security/usbguard.nix b/nixos/modules/services/security/usbguard.nix
index f167fbb2eca82..ff54176e13d3a 100644
--- a/nixos/modules/services/security/usbguard.nix
+++ b/nixos/modules/services/security/usbguard.nix
@@ -80,7 +80,7 @@ in
       };
 
       implicitPolicyTarget = mkOption {
-        type = policy;
+        type = types.enum [ "allow" "block" "reject" ];
         default = "block";
         description = lib.mdDoc ''
           How to treat USB devices that don't match any rule in the policy.
@@ -110,7 +110,7 @@ in
       };
 
       insertedDevicePolicy = mkOption {
-        type = policy;
+        type = types.enum [ "block" "reject" "apply-policy" ];
         default = "apply-policy";
         description = lib.mdDoc ''
           How to treat USB devices that are already connected after the daemon
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 60d8015d0ceeb..b2920931f9a9e 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -1,7 +1,5 @@
 { config, lib, pkgs, ... }:
 
-with lib;
-
 let
   cfg = config.services.vaultwarden;
   user = config.users.users.vaultwarden.name;
@@ -11,60 +9,60 @@ let
   nameToEnvVar = name:
     let
       parts = builtins.split "([A-Z0-9]+)" name;
-      partsToEnvVar = parts: foldl' (key: x: let last = stringLength key - 1; in
-        if isList x then key + optionalString (key != "" && substring last 1 key != "_") "_" + head x
-        else if key != "" && elem (substring 0 1 x) lowerChars then # to handle e.g. [ "disable" [ "2FAR" ] "emember" ]
-          substring 0 last key + optionalString (substring (last - 1) 1 key != "_") "_" + substring last 1 key + toUpper x
-        else key + toUpper x) "" parts;
+      partsToEnvVar = parts: lib.foldl' (key: x: let last = lib.stringLength key - 1; in
+        if lib.isList x then key + lib.optionalString (key != "" && lib.substring last 1 key != "_") "_" + lib.head x
+        else if key != "" && lib.elem (lib.substring 0 1 x) lib.lowerChars then # to handle e.g. [ "disable" [ "2FAR" ] "emember" ]
+          lib.substring 0 last key + lib.optionalString (lib.substring (last - 1) 1 key != "_") "_" + lib.substring last 1 key + lib.toUpper x
+        else key + lib.toUpper x) "" parts;
     in if builtins.match "[A-Z0-9_]+" name != null then name else partsToEnvVar parts;
 
   # Due to the different naming schemes allowed for config keys,
   # we can only check for values consistently after converting them to their corresponding environment variable name.
   configEnv =
     let
-      configEnv = concatMapAttrs (name: value: optionalAttrs (value != null) {
-        ${nameToEnvVar name} = if isBool value then boolToString value else toString value;
+      configEnv = lib.concatMapAttrs (name: value: lib.optionalAttrs (value != null) {
+        ${nameToEnvVar name} = if lib.isBool value then lib.boolToString value else toString value;
       }) cfg.config;
-    in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") {
+    in { DATA_FOLDER = "/var/lib/bitwarden_rs"; } // lib.optionalAttrs (!(configEnv ? WEB_VAULT_ENABLED) || configEnv.WEB_VAULT_ENABLED == "true") {
       WEB_VAULT_FOLDER = "${cfg.webVaultPackage}/share/vaultwarden/vault";
     } // configEnv;
 
-  configFile = pkgs.writeText "vaultwarden.env" (concatStrings (mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
+  configFile = pkgs.writeText "vaultwarden.env" (lib.concatStrings (lib.mapAttrsToList (name: value: "${name}=${value}\n") configEnv));
 
   vaultwarden = cfg.package.override { inherit (cfg) dbBackend; };
 
 in {
   imports = [
-    (mkRenamedOptionModule [ "services" "bitwarden_rs" ] [ "services" "vaultwarden" ])
+    (lib.mkRenamedOptionModule [ "services" "bitwarden_rs" ] [ "services" "vaultwarden" ])
   ];
 
-  options.services.vaultwarden = with types; {
-    enable = mkEnableOption (lib.mdDoc "vaultwarden");
+  options.services.vaultwarden = {
+    enable = lib.mkEnableOption "vaultwarden";
 
-    dbBackend = mkOption {
-      type = enum [ "sqlite" "mysql" "postgresql" ];
+    dbBackend = lib.mkOption {
+      type = lib.types.enum [ "sqlite" "mysql" "postgresql" ];
       default = "sqlite";
-      description = lib.mdDoc ''
+      description = ''
         Which database backend vaultwarden will be using.
       '';
     };
 
-    backupDir = mkOption {
-      type = nullOr str;
+    backupDir = lib.mkOption {
+      type = with lib.types; nullOr str;
       default = null;
-      description = lib.mdDoc ''
+      description = ''
         The directory under which vaultwarden will backup its persistent data.
       '';
       example = "/var/backup/vaultwarden";
     };
 
-    config = mkOption {
-      type = attrsOf (nullOr (oneOf [ bool int str ]));
+    config = lib.mkOption {
+      type = with lib.types; attrsOf (nullOr (oneOf [ bool int str ]));
       default = {
         ROCKET_ADDRESS = "::1"; # default to localhost
         ROCKET_PORT = 8222;
       };
-      example = literalExpression ''
+      example = lib.literalExpression ''
         {
           DOMAIN = "https://bitwarden.example.com";
           SIGNUPS_ALLOWED = false;
@@ -101,7 +99,7 @@ in {
           SMTP_FROM_NAME = "example.com Bitwarden server";
         }
       '';
-      description = lib.mdDoc ''
+      description = ''
         The configuration of vaultwarden is done through environment variables,
         therefore it is recommended to use upper snake case (e.g. {env}`DISABLE_2FA_REMEMBER`).
 
@@ -125,11 +123,11 @@ in {
       '';
     };
 
-    environmentFile = mkOption {
-      type = with types; nullOr path;
+    environmentFile = lib.mkOption {
+      type = with lib.types; nullOr path;
       default = null;
       example = "/var/lib/vaultwarden.env";
-      description = lib.mdDoc ''
+      description = ''
         Additional environment file as defined in {manpage}`systemd.exec(5)`.
 
         Secrets like {env}`ADMIN_TOKEN` and {env}`SMTP_PASSWORD`
@@ -157,17 +155,17 @@ in {
       '';
     };
 
-    package = mkPackageOption pkgs "vaultwarden" { };
+    package = lib.mkPackageOption pkgs "vaultwarden" { };
 
-    webVaultPackage = mkOption {
-      type = package;
+    webVaultPackage = lib.mkOption {
+      type = lib.types.package;
       default = pkgs.vaultwarden.webvault;
-      defaultText = literalExpression "pkgs.vaultwarden.webvault";
-      description = lib.mdDoc "Web vault package to use.";
+      defaultText = lib.literalExpression "pkgs.vaultwarden.webvault";
+      description = "Web vault package to use.";
     };
   };
 
-  config = mkIf cfg.enable {
+  config = lib.mkIf cfg.enable {
     assertions = [ {
       assertion = cfg.backupDir != null -> cfg.dbBackend == "sqlite";
       message = "Backups for database backends other than sqlite will need customization";
@@ -185,7 +183,7 @@ in {
       serviceConfig = {
         User = user;
         Group = group;
-        EnvironmentFile = [ configFile ] ++ optional (cfg.environmentFile != null) cfg.environmentFile;
+        EnvironmentFile = [ configFile ] ++ lib.optional (cfg.environmentFile != null) cfg.environmentFile;
         ExecStart = "${vaultwarden}/bin/vaultwarden";
         LimitNOFILE = "1048576";
         PrivateTmp = "true";
@@ -200,7 +198,7 @@ in {
       wantedBy = [ "multi-user.target" ];
     };
 
-    systemd.services.backup-vaultwarden = mkIf (cfg.backupDir != null) {
+    systemd.services.backup-vaultwarden = lib.mkIf (cfg.backupDir != null) {
       description = "Backup vaultwarden";
       environment = {
         DATA_FOLDER = "/var/lib/bitwarden_rs";
@@ -212,24 +210,24 @@ in {
       serviceConfig = {
         SyslogIdentifier = "backup-vaultwarden";
         Type = "oneshot";
-        User = mkDefault user;
-        Group = mkDefault group;
+        User = lib.mkDefault user;
+        Group = lib.mkDefault group;
         ExecStart = "${pkgs.bash}/bin/bash ${./backup.sh}";
       };
       wantedBy = [ "multi-user.target" ];
     };
 
-    systemd.timers.backup-vaultwarden = mkIf (cfg.backupDir != null) {
+    systemd.timers.backup-vaultwarden = lib.mkIf (cfg.backupDir != null) {
       description = "Backup vaultwarden on time";
       timerConfig = {
-        OnCalendar = mkDefault "23:00";
+        OnCalendar = lib.mkDefault "23:00";
         Persistent = "true";
         Unit = "backup-vaultwarden.service";
       };
       wantedBy = [ "multi-user.target" ];
     };
 
-    systemd.tmpfiles.settings = mkIf (cfg.backupDir != null) {
+    systemd.tmpfiles.settings = lib.mkIf (cfg.backupDir != null) {
       "10-vaultwarden".${cfg.backupDir}.d = {
         inherit user group;
         mode = "0770";
diff --git a/nixos/modules/services/security/yubikey-agent.nix b/nixos/modules/services/security/yubikey-agent.nix
index 3d5f84af2cf48..f7ee952e7f61e 100644
--- a/nixos/modules/services/security/yubikey-agent.nix
+++ b/nixos/modules/services/security/yubikey-agent.nix
@@ -23,7 +23,7 @@ in
           SSH_AUTH_SOCK to point at yubikey-agent.
 
           Note that yubikey-agent will use whatever pinentry is
-          specified in programs.gnupg.agent.pinentryFlavor.
+          specified in programs.gnupg.agent.pinentryPackage.
         '';
       };
 
diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix
index 00ae77be4271c..689e0000fdd54 100644
--- a/nixos/modules/services/system/cloud-init.nix
+++ b/nixos/modules/services/system/cloud-init.nix
@@ -17,6 +17,7 @@ let
   ++ optional cfg.ext4.enable e2fsprogs
   ++ optional cfg.xfs.enable xfsprogs
   ;
+  hasFs = fsName: lib.any (fs: fs.fsType == fsName) (lib.attrValues config.fileSystems);
   settingsFormat = pkgs.formats.yaml { };
   cfgfile = settingsFormat.generate "cloud.cfg" cfg.settings;
 in
@@ -44,7 +45,8 @@ in
 
       btrfs.enable = mkOption {
         type = types.bool;
-        default = false;
+        default = hasFs "btrfs";
+        defaultText = literalExpression ''hasFs "btrfs"'';
         description = mdDoc ''
           Allow the cloud-init service to operate `btrfs` filesystem.
         '';
@@ -52,7 +54,8 @@ in
 
       ext4.enable = mkOption {
         type = types.bool;
-        default = true;
+        default = hasFs "ext4";
+        defaultText = literalExpression ''hasFs "ext4"'';
         description = mdDoc ''
           Allow the cloud-init service to operate `ext4` filesystem.
         '';
@@ -60,7 +63,8 @@ in
 
       xfs.enable = mkOption {
         type = types.bool;
-        default = false;
+        default = hasFs "xfs";
+        defaultText = literalExpression ''hasFs "xfs"'';
         description = mdDoc ''
           Allow the cloud-init service to operate `xfs` filesystem.
         '';
@@ -204,7 +208,7 @@ in
       description = "Apply the settings specified in cloud-config";
       wantedBy = [ "multi-user.target" ];
       wants = [ "network-online.target" ];
-      after = [ "network-online.target" "syslog.target" "cloud-config.target" ];
+      after = [ "network-online.target" "cloud-config.target" ];
 
       path = path;
       serviceConfig = {
@@ -220,7 +224,7 @@ in
       description = "Execute cloud user/final scripts";
       wantedBy = [ "multi-user.target" ];
       wants = [ "network-online.target" ];
-      after = [ "network-online.target" "syslog.target" "cloud-config.service" "rc-local.service" ];
+      after = [ "network-online.target" "cloud-config.service" "rc-local.service" ];
       requires = [ "cloud-config.target" ];
       path = path;
       serviceConfig = {
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index e8f8b48d0337f..e57a13e8de96b 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -39,7 +39,7 @@ in
 
       implementation = mkOption {
         type = types.enum [ "dbus" "broker" ];
-        default = "dbus";
+        default = "broker";
         description = lib.mdDoc ''
           The implementation to use for the message bus defined by the D-Bus specification.
           Can be either the classic dbus daemon or dbus-broker, which aims to provide high
@@ -101,6 +101,11 @@ in
 
       users.groups.messagebus.gid = config.ids.gids.messagebus;
 
+      # Install dbus for dbus tools even when using dbus-broker
+      environment.systemPackages = [
+        pkgs.dbus
+      ];
+
       # You still need the dbus reference implementation installed to use dbus-broker
       systemd.packages = [
         pkgs.dbus
@@ -132,10 +137,6 @@ in
     })
 
     (mkIf (cfg.implementation == "dbus") {
-      environment.systemPackages = [
-        pkgs.dbus
-      ];
-
       security.wrappers.dbus-daemon-launch-helper = {
         source = "${pkgs.dbus}/libexec/dbus-daemon-launch-helper";
         owner = "root";
diff --git a/nixos/modules/services/system/nix-daemon.nix b/nixos/modules/services/system/nix-daemon.nix
index ce255cd8d0a46..550ef6b1e18c2 100644
--- a/nixos/modules/services/system/nix-daemon.nix
+++ b/nixos/modules/services/system/nix-daemon.nix
@@ -247,7 +247,7 @@ in
 
     users.users = nixbldUsers;
 
-    services.xserver.displayManager.hiddenUsers = attrNames nixbldUsers;
+    services.displayManager.hiddenUsers = attrNames nixbldUsers;
 
     # Legacy configuration conversion.
     nix.settings = mkMerge [
diff --git a/nixos/modules/services/video/photonvision.nix b/nixos/modules/services/video/photonvision.nix
new file mode 100644
index 0000000000000..fdbe9da3999da
--- /dev/null
+++ b/nixos/modules/services/video/photonvision.nix
@@ -0,0 +1,64 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.photonvision;
+in
+{
+  options = {
+    services.photonvision = {
+      enable = lib.mkEnableOption (lib.mdDoc "Enable PhotonVision");
+
+      package = lib.mkPackageOption pkgs "photonvision" {};
+
+      openFirewall = lib.mkOption {
+        description = lib.mdDoc ''
+          Whether to open the required ports in the firewall.
+        '';
+        default = false;
+        type = lib.types.bool;
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.photonvision = {
+      description = "PhotonVision, the free, fast, and easy-to-use computer vision solution for the FIRST Robotics Competition";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" ];
+
+      serviceConfig = {
+        ExecStart = lib.getExe cfg.package;
+
+        # ephemeral root directory
+        RuntimeDirectory = "photonvision";
+        RootDirectory = "/run/photonvision";
+
+        # setup persistent state and logs directories
+        StateDirectory = "photonvision";
+        LogsDirectory = "photonvision";
+
+        BindReadOnlyPaths = [
+          # mount the nix store read-only
+          "/nix/store"
+
+          # the JRE reads the user.home property from /etc/passwd
+          "/etc/passwd"
+        ];
+        BindPaths = [
+          # mount the configuration and logs directories to the host
+          "/var/lib/photonvision:/photonvision_config"
+          "/var/log/photonvision:/photonvision_config/logs"
+        ];
+
+        # for PhotonVision's dynamic libraries, which it writes to /tmp
+        PrivateTmp = true;
+      };
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall {
+      allowedTCPPorts = [ 5800 ];
+      allowedTCPPortRanges = [{ from = 1180; to = 1190; }];
+    };
+  };
+}
diff --git a/nixos/modules/services/video/unifi-video.nix b/nixos/modules/services/video/unifi-video.nix
index 518977e49baef..1eb29fb0fd84a 100644
--- a/nixos/modules/services/video/unifi-video.nix
+++ b/nixos/modules/services/video/unifi-video.nix
@@ -108,7 +108,7 @@ in
     unifiVideoPackage = mkPackageOption pkgs "unifi-video" { };
 
     mongodbPackage = mkPackageOption pkgs "mongodb" {
-      default = "mongodb-4_4";
+      default = "mongodb-5_0";
     };
 
     logDir = mkOption {
diff --git a/nixos/modules/services/web-apps/akkoma.md b/nixos/modules/services/web-apps/akkoma.md
index 83dd1a8b35f23..13b074b228a41 100644
--- a/nixos/modules/services/web-apps/akkoma.md
+++ b/nixos/modules/services/web-apps/akkoma.md
@@ -19,21 +19,23 @@ be run behind a HTTP proxy on `fediverse.example.com`.
 
 
 ```nix
-services.akkoma.enable = true;
-services.akkoma.config = {
-  ":pleroma" = {
-    ":instance" = {
-      name = "My Akkoma instance";
-      description = "More detailed description";
-      email = "admin@example.com";
-      registration_open = false;
-    };
-
-    "Pleroma.Web.Endpoint" = {
-      url.host = "fediverse.example.com";
+{
+  services.akkoma.enable = true;
+  services.akkoma.config = {
+    ":pleroma" = {
+      ":instance" = {
+        name = "My Akkoma instance";
+        description = "More detailed description";
+        email = "admin@example.com";
+        registration_open = false;
+      };
+
+      "Pleroma.Web.Endpoint" = {
+        url.host = "fediverse.example.com";
+      };
     };
   };
-};
+}
 ```
 
 Please refer to the [configuration cheat sheet](https://docs.akkoma.dev/stable/configuration/cheatsheet/)
@@ -55,19 +57,21 @@ Although it is possible to expose Akkoma directly, it is common practice to oper
 HTTP reverse proxy such as nginx.
 
 ```nix
-services.akkoma.nginx = {
-  enableACME = true;
-  forceSSL = true;
-};
-
-services.nginx = {
-  enable = true;
-
-  clientMaxBodySize = "16m";
-  recommendedTlsSettings = true;
-  recommendedOptimisation = true;
-  recommendedGzipSettings = true;
-};
+{
+  services.akkoma.nginx = {
+    enableACME = true;
+    forceSSL = true;
+  };
+
+  services.nginx = {
+    enable = true;
+
+    clientMaxBodySize = "16m";
+    recommendedTlsSettings = true;
+    recommendedOptimisation = true;
+    recommendedGzipSettings = true;
+  };
+}
 ```
 
 Please refer to [](#module-security-acme) for details on how to provision an SSL/TLS certificate.
@@ -78,51 +82,53 @@ Without the media proxy function, Akkoma does not store any remote media like pi
 locally, and clients have to fetch them directly from the source server.
 
 ```nix
-# Enable nginx slice module distributed with Tengine
-services.nginx.package = pkgs.tengine;
-
-# Enable media proxy
-services.akkoma.config.":pleroma".":media_proxy" = {
-  enabled = true;
-  proxy_opts.redirect_on_failure = true;
-};
-
-# Adjust the persistent cache size as needed:
-#  Assuming an average object size of 128 KiB, around 1 MiB
-#  of memory is required for the key zone per GiB of cache.
-# Ensure that the cache directory exists and is writable by nginx.
-services.nginx.commonHttpConfig = ''
-  proxy_cache_path /var/cache/nginx/cache/akkoma-media-cache
-    levels= keys_zone=akkoma_media_cache:16m max_size=16g
-    inactive=1y use_temp_path=off;
-'';
-
-services.akkoma.nginx = {
-  locations."/proxy" = {
-    proxyPass = "http://unix:/run/akkoma/socket";
-
-    extraConfig = ''
-      proxy_cache akkoma_media_cache;
-
-      # Cache objects in slices of 1 MiB
-      slice 1m;
-      proxy_cache_key $host$uri$is_args$args$slice_range;
-      proxy_set_header Range $slice_range;
-
-      # Decouple proxy and upstream responses
-      proxy_buffering on;
-      proxy_cache_lock on;
-      proxy_ignore_client_abort on;
-
-      # Default cache times for various responses
-      proxy_cache_valid 200 1y;
-      proxy_cache_valid 206 301 304 1h;
-
-      # Allow serving of stale items
-      proxy_cache_use_stale error timeout invalid_header updating;
-    '';
+{
+  # Enable nginx slice module distributed with Tengine
+  services.nginx.package = pkgs.tengine;
+
+  # Enable media proxy
+  services.akkoma.config.":pleroma".":media_proxy" = {
+    enabled = true;
+    proxy_opts.redirect_on_failure = true;
   };
-};
+
+  # Adjust the persistent cache size as needed:
+  #  Assuming an average object size of 128 KiB, around 1 MiB
+  #  of memory is required for the key zone per GiB of cache.
+  # Ensure that the cache directory exists and is writable by nginx.
+  services.nginx.commonHttpConfig = ''
+    proxy_cache_path /var/cache/nginx/cache/akkoma-media-cache
+      levels= keys_zone=akkoma_media_cache:16m max_size=16g
+      inactive=1y use_temp_path=off;
+  '';
+
+  services.akkoma.nginx = {
+    locations."/proxy" = {
+      proxyPass = "http://unix:/run/akkoma/socket";
+
+      extraConfig = ''
+        proxy_cache akkoma_media_cache;
+
+        # Cache objects in slices of 1 MiB
+        slice 1m;
+        proxy_cache_key $host$uri$is_args$args$slice_range;
+        proxy_set_header Range $slice_range;
+
+        # Decouple proxy and upstream responses
+        proxy_buffering on;
+        proxy_cache_lock on;
+        proxy_ignore_client_abort on;
+
+        # Default cache times for various responses
+        proxy_cache_valid 200 1y;
+        proxy_cache_valid 206 301 304 1h;
+
+        # Allow serving of stale items
+        proxy_cache_use_stale error timeout invalid_header updating;
+      '';
+    };
+  };
+}
 ```
 
 #### Prefetch remote media {#modules-services-akkoma-prefetch-remote-media}
@@ -132,10 +138,12 @@ fetches all media associated with a post through the media proxy, as soon as the
 received by the instance.
 
 ```nix
-services.akkoma.config.":pleroma".":mrf".policies =
-  map (pkgs.formats.elixirConf { }).lib.mkRaw [
-    "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy"
-];
+{
+  services.akkoma.config.":pleroma".":mrf".policies =
+    map (pkgs.formats.elixirConf { }).lib.mkRaw [
+      "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy"
+  ];
+}
 ```
 
 #### Media previews {#modules-services-akkoma-media-previews}
@@ -143,11 +151,13 @@ services.akkoma.config.":pleroma".":mrf".policies =
 Akkoma can generate previews for media.
 
 ```nix
-services.akkoma.config.":pleroma".":media_preview_proxy" = {
-  enabled = true;
-  thumbnail_max_width = 1920;
-  thumbnail_max_height = 1080;
-};
+{
+  services.akkoma.config.":pleroma".":media_preview_proxy" = {
+    enabled = true;
+    thumbnail_max_width = 1920;
+    thumbnail_max_height = 1080;
+  };
+}
 ```
 
 ## Frontend management {#modules-services-akkoma-frontend-management}
@@ -160,29 +170,31 @@ The following example overrides the primary frontend’s default configuration u
 derivation.
 
 ```nix
-services.akkoma.frontends.primary.package = pkgs.runCommand "akkoma-fe" {
-  config = builtins.toJSON {
-    expertLevel = 1;
-    collapseMessageWithSubject = false;
-    stopGifs = false;
-    replyVisibility = "following";
-    webPushHideIfCW = true;
-    hideScopeNotice = true;
-    renderMisskeyMarkdown = false;
-    hideSiteFavicon = true;
-    postContentType = "text/markdown";
-    showNavShortcuts = false;
-  };
-  nativeBuildInputs = with pkgs; [ jq xorg.lndir ];
-  passAsFile = [ "config" ];
-} ''
-  mkdir $out
-  lndir ${pkgs.akkoma-frontends.akkoma-fe} $out
-
-  rm $out/static/config.json
-  jq -s add ${pkgs.akkoma-frontends.akkoma-fe}/static/config.json ${config} \
-    >$out/static/config.json
-'';
+{
+  services.akkoma.frontends.primary.package = pkgs.runCommand "akkoma-fe" {
+    config = builtins.toJSON {
+      expertLevel = 1;
+      collapseMessageWithSubject = false;
+      stopGifs = false;
+      replyVisibility = "following";
+      webPushHideIfCW = true;
+      hideScopeNotice = true;
+      renderMisskeyMarkdown = false;
+      hideSiteFavicon = true;
+      postContentType = "text/markdown";
+      showNavShortcuts = false;
+    };
+    nativeBuildInputs = with pkgs; [ jq xorg.lndir ];
+    passAsFile = [ "config" ];
+  } ''
+    mkdir $out
+    lndir ${pkgs.akkoma-frontends.akkoma-fe} $out
+
+    rm $out/static/config.json
+    jq -s add ${pkgs.akkoma-frontends.akkoma-fe}/static/config.json ${config} \
+      >$out/static/config.json
+  '';
+}
 ```
 
 ## Federation policies {#modules-services-akkoma-federation-policies}
@@ -198,28 +210,30 @@ of the fediverse and providing a pleasant experience to the users of an instance
 
 
 ```nix
-services.akkoma.config.":pleroma" = with (pkgs.formats.elixirConf { }).lib; {
-  ":mrf".policies = map mkRaw [
-    "Pleroma.Web.ActivityPub.MRF.SimplePolicy"
-  ];
-
-  ":mrf_simple" = {
-    # Tag all media as sensitive
-    media_nsfw = mkMap {
-      "nsfw.weird.kinky" = "Untagged NSFW content";
-    };
-
-    # Reject all activities except deletes
-    reject = mkMap {
-      "kiwifarms.cc" = "Persistent harassment of users, no moderation";
-    };
-
-    # Force posts to be visible by followers only
-    followers_only = mkMap {
-      "beta.birdsite.live" = "Avoid polluting timelines with Twitter posts";
+{
+  services.akkoma.config.":pleroma" = with (pkgs.formats.elixirConf { }).lib; {
+    ":mrf".policies = map mkRaw [
+      "Pleroma.Web.ActivityPub.MRF.SimplePolicy"
+    ];
+
+    ":mrf_simple" = {
+      # Tag all media as sensitive
+      media_nsfw = mkMap {
+        "nsfw.weird.kinky" = "Untagged NSFW content";
+      };
+
+      # Reject all activities except deletes
+      reject = mkMap {
+        "kiwifarms.cc" = "Persistent harassment of users, no moderation";
+      };
+
+      # Force posts to be visible by followers only
+      followers_only = mkMap {
+        "beta.birdsite.live" = "Avoid polluting timelines with Twitter posts";
+      };
     };
   };
-};
+}
 ```
 
 ## Upload filters {#modules-services-akkoma-upload-filters}
@@ -228,12 +242,14 @@ This example strips GPS and location metadata from uploads, deduplicates them an
 the file name.
 
 ```nix
-services.akkoma.config.":pleroma"."Pleroma.Upload".filters =
-  map (pkgs.formats.elixirConf { }).lib.mkRaw [
-    "Pleroma.Upload.Filter.Exiftool"
-    "Pleroma.Upload.Filter.Dedupe"
-    "Pleroma.Upload.Filter.AnonymizeFilename"
-  ];
+{
+  services.akkoma.config.":pleroma"."Pleroma.Upload".filters =
+    map (pkgs.formats.elixirConf { }).lib.mkRaw [
+      "Pleroma.Upload.Filter.Exiftool"
+      "Pleroma.Upload.Filter.Dedupe"
+      "Pleroma.Upload.Filter.AnonymizeFilename"
+    ];
+}
 ```
 
 ## Migration from Pleroma {#modules-services-akkoma-migration-pleroma}
@@ -286,9 +302,11 @@ To reâ€use the Pleroma data in place, disable Pleroma and enable Akkoma, pointi
 Pleroma database and upload directory.
 
 ```nix
-# Adjust these settings according to the database name and upload directory path used by Pleroma
-services.akkoma.config.":pleroma"."Pleroma.Repo".database = "pleroma";
-services.akkoma.config.":pleroma".":instance".upload_dir = "/var/lib/pleroma/uploads";
+{
+  # Adjust these settings according to the database name and upload directory path used by Pleroma
+  services.akkoma.config.":pleroma"."Pleroma.Repo".database = "pleroma";
+  services.akkoma.config.":pleroma".":instance".upload_dir = "/var/lib/pleroma/uploads";
+}
 ```
 
 Please keep in mind that after the Akkoma service has been started, any migrations applied by
@@ -304,7 +322,9 @@ details.
 The Akkoma systemd service may be confined to a chroot with
 
 ```nix
-services.systemd.akkoma.confinement.enable = true;
+{
+  services.systemd.akkoma.confinement.enable = true;
+}
 ```
 
 Confinement of services is not generally supported in NixOS and therefore disabled by default.
diff --git a/nixos/modules/services/web-apps/c2fmzq-server.md b/nixos/modules/services/web-apps/c2fmzq-server.md
index 236953bd4ff7a..d8e59b3ad2103 100644
--- a/nixos/modules/services/web-apps/c2fmzq-server.md
+++ b/nixos/modules/services/web-apps/c2fmzq-server.md
@@ -4,7 +4,7 @@ c2FmZQ is an application that can securely encrypt, store, and share files,
 including but not limited to pictures and videos.
 
 The service `c2fmzq-server` can be enabled by setting
-```
+```nix
 {
   services.c2fmzq-server.enable = true;
 }
@@ -17,7 +17,7 @@ In principle the server can be exposed directly on a public interface and there
 are command line options to manage HTTPS certificates directly, but the module
 is designed to be served behind a reverse proxy or only accessed via localhost.
 
-```
+```nix
 {
   services.c2fmzq-server = {
     enable = true;
diff --git a/nixos/modules/services/audio/castopod.md b/nixos/modules/services/web-apps/castopod.md
index ee8590737a7c7..5ecd807686fdb 100644
--- a/nixos/modules/services/audio/castopod.md
+++ b/nixos/modules/services/web-apps/castopod.md
@@ -4,19 +4,22 @@ Castopod is an open-source hosting platform made for podcasters who want to enga
 
 ## Quickstart {#module-services-castopod-quickstart}
 
+Configure ACME (https://nixos.org/manual/nixos/unstable/#module-security-acme).
 Use the following configuration to start a public instance of Castopod on `castopod.example.com` domain:
 
 ```nix
-networking.firewall.allowedTCPPorts = [ 80 443 ];
-services.castopod = {
-  enable = true;
-  database.createLocally = true;
-  nginx.virtualHost = {
-    serverName = "castopod.example.com";
-    enableACME = true;
-    forceSSL = true;
+{
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  services.castopod = {
+    enable = true;
+    database.createLocally = true;
+    nginx.virtualHost = {
+      serverName = "castopod.example.com";
+      enableACME = true;
+      forceSSL = true;
+    };
   };
-};
+}
 ```
 
 Go to `https://castopod.example.com/cp-install` to create superadmin account after applying the above configuration.
diff --git a/nixos/modules/services/audio/castopod.nix b/nixos/modules/services/web-apps/castopod.nix
index b782b54891479..0b905bb50761e 100644
--- a/nixos/modules/services/audio/castopod.nix
+++ b/nixos/modules/services/web-apps/castopod.nix
@@ -4,7 +4,6 @@ let
   fpm = config.services.phpfpm.pools.castopod;
 
   user = "castopod";
-  stateDirectory = "/var/lib/castopod";
 
   # https://docs.castopod.org/getting-started/install.html#requirements
   phpPackage = pkgs.php.withExtensions ({ enabled, all }: with all; [
@@ -22,13 +21,22 @@ in
 
   options.services = {
     castopod = {
-      enable = lib.mkEnableOption (lib.mdDoc "Castopod");
+      enable = lib.mkEnableOption (lib.mdDoc "Castopod, a hosting platform for podcasters");
       package = lib.mkOption {
         type = lib.types.package;
         default = pkgs.castopod;
         defaultText = lib.literalMD "pkgs.castopod";
         description = lib.mdDoc "Which Castopod package to use.";
       };
+      dataDir = lib.mkOption {
+        type = lib.types.path;
+        default = "/var/lib/castopod";
+        description = lib.mdDoc ''
+          The path where castopod stores all data. This path must be in sync
+          with the castopod package (where it is hardcoded during the build in
+          accordance with its own `dataDir` argument).
+        '';
+      };
       database = {
         createLocally = lib.mkOption {
           type = lib.types.bool;
@@ -59,6 +67,8 @@ in
           description = lib.mdDoc ''
             A file containing the password corresponding to
             [](#opt-services.castopod.database.user).
+
+            This file is loaded using systemd LoadCredentials.
           '';
         };
       };
@@ -85,6 +95,8 @@ in
           Environment file to inject e.g. secrets into the configuration.
           See [](https://code.castopod.org/adaures/castopod/-/blob/main/.env.example)
           for available environment variables.
+
+          This file is loaded using systemd LoadCredentials.
         '';
       };
       configureNginx = lib.mkOption {
@@ -111,6 +123,19 @@ in
           Options for Castopod's PHP pool. See the documentation on `php-fpm.conf` for details on configuration directives.
         '';
       };
+      maxUploadSize = lib.mkOption {
+        type = lib.types.str;
+        default = "512M";
+        description = lib.mdDoc ''
+          Maximum supported size for a file upload in. Maximum HTTP body
+          size is set to this value for nginx and PHP (because castopod doesn't
+          support chunked uploads yet:
+          https://code.castopod.org/adaures/castopod/-/issues/330).
+
+          Note, that practical upload size limit is smaller. For example, with
+          512 MiB setting - around 500 MiB is possible.
+        '';
+      };
     };
   };
 
@@ -120,13 +145,13 @@ in
         sslEnabled = with config.services.nginx.virtualHosts.${cfg.localDomain}; addSSL || forceSSL || onlySSL || enableACME || useACMEHost != null;
         baseURL = "http${lib.optionalString sslEnabled "s"}://${cfg.localDomain}";
       in
-      lib.mapAttrs (name: lib.mkDefault) {
+      lib.mapAttrs (_: lib.mkDefault) {
         "app.forceGlobalSecureRequests" = sslEnabled;
         "app.baseURL" = baseURL;
 
-        "media.baseURL" = "/";
+        "media.baseURL" = baseURL;
         "media.root" = "media";
-        "media.storage" = stateDirectory;
+        "media.storage" = cfg.dataDir;
 
         "admin.gateway" = "admin";
         "auth.gateway" = "auth";
@@ -142,13 +167,13 @@ in
     services.phpfpm.pools.castopod = {
       inherit user;
       group = config.services.nginx.group;
-      phpPackage = phpPackage;
+      inherit phpPackage;
       phpOptions = ''
-        # https://code.castopod.org/adaures/castopod/-/blob/main/docker/production/app/uploads.ini
+        # https://code.castopod.org/adaures/castopod/-/blob/develop/docker/production/common/uploads.template.ini
         file_uploads = On
         memory_limit = 512M
-        upload_max_filesize = 500M
-        post_max_size = 512M
+        upload_max_filesize = ${cfg.maxUploadSize}
+        post_max_size = ${cfg.maxUploadSize}
         max_execution_time = 300
         max_input_time = 300
       '';
@@ -165,45 +190,50 @@ in
       path = [ pkgs.openssl phpPackage ];
       script =
         let
-          envFile = "${stateDirectory}/.env";
+          envFile = "${cfg.dataDir}/.env";
           media = "${cfg.settings."media.storage"}/${cfg.settings."media.root"}";
         in
         ''
-          mkdir -p ${stateDirectory}/writable/{cache,logs,session,temp,uploads}
+          mkdir -p ${cfg.dataDir}/writable/{cache,logs,session,temp,uploads}
 
           if [ ! -d ${lib.escapeShellArg media} ]; then
             cp --no-preserve=mode,ownership -r ${cfg.package}/share/castopod/public/media ${lib.escapeShellArg media}
           fi
 
-          if [ ! -f ${stateDirectory}/salt ]; then
-            openssl rand -base64 33 > ${stateDirectory}/salt
+          if [ ! -f ${cfg.dataDir}/salt ]; then
+            openssl rand -base64 33 > ${cfg.dataDir}/salt
           fi
 
           cat <<'EOF' > ${envFile}
           ${lib.generators.toKeyValue { } cfg.settings}
           EOF
 
-          echo "analytics.salt=$(cat ${stateDirectory}/salt)" >> ${envFile}
+          echo "analytics.salt=$(cat ${cfg.dataDir}/salt)" >> ${envFile}
 
           ${if (cfg.database.passwordFile != null) then ''
-            echo "database.default.password=$(cat ${lib.escapeShellArg cfg.database.passwordFile})" >> ${envFile}
+            echo "database.default.password=$(cat "$CREDENTIALS_DIRECTORY/dbpasswordfile)" >> ${envFile}
           '' else ''
             echo "database.default.password=" >> ${envFile}
           ''}
 
           ${lib.optionalString (cfg.environmentFile != null) ''
-            cat ${lib.escapeShellArg cfg.environmentFile}) >> ${envFile}
+            cat "$CREDENTIALS_DIRECTORY/envfile" >> ${envFile}
           ''}
 
-          php spark castopod:database-update
+          php ${cfg.package}/share/castopod/spark castopod:database-update
         '';
       serviceConfig = {
         StateDirectory = "castopod";
+        LoadCredential = lib.optional (cfg.environmentFile != null)
+          "envfile:${cfg.environmentFile}"
+        ++ (lib.optional (cfg.database.passwordFile != null)
+          "dbpasswordfile:${cfg.database.passwordFile}");
         WorkingDirectory = "${cfg.package}/share/castopod";
         Type = "oneshot";
         RemainAfterExit = true;
         User = user;
         Group = config.services.nginx.group;
+        ReadWritePaths = cfg.dataDir;
       };
     };
 
@@ -212,9 +242,7 @@ in
       wantedBy = [ "multi-user.target" ];
       path = [ phpPackage ];
       script = ''
-        php public/index.php scheduled-activities
-        php public/index.php scheduled-websub-publish
-        php public/index.php scheduled-video-clips
+        php ${cfg.package}/share/castopod/spark tasks:run
       '';
       serviceConfig = {
         StateDirectory = "castopod";
@@ -222,6 +250,8 @@ in
         Type = "oneshot";
         User = user;
         Group = config.services.nginx.group;
+        ReadWritePaths = cfg.dataDir;
+        LogLevelMax = "notice"; # otherwise periodic tasks flood the journal
       };
     };
 
@@ -251,6 +281,7 @@ in
         extraConfig = ''
           try_files $uri $uri/ /index.php?$args;
           index index.php index.html;
+          client_max_body_size ${cfg.maxUploadSize};
         '';
 
         locations."^~ /${cfg.settings."media.root"}/" = {
@@ -278,7 +309,7 @@ in
       };
     };
 
-    users.users.${user} = lib.mapAttrs (name: lib.mkDefault) {
+    users.users.${user} = lib.mapAttrs (_: lib.mkDefault) {
       description = "Castopod user";
       isSystemUser = true;
       group = config.services.nginx.group;
diff --git a/nixos/modules/services/web-apps/davis.md b/nixos/modules/services/web-apps/davis.md
new file mode 100644
index 0000000000000..9775d8221b5bf
--- /dev/null
+++ b/nixos/modules/services/web-apps/davis.md
@@ -0,0 +1,32 @@
+# Davis {#module-services-davis}
+
+[Davis](https://github.com/tchapi/davis/) is a caldav and carrddav server. It
+has a simple, fully translatable admin interface for sabre/dav based on Symfony
+5 and Bootstrap 5, initially inspired by Baïkal.
+
+## Basic Usage {#module-services-davis-basic-usage}
+
+At first, an application secret is needed, this can be generated with:
+```ShellSession
+$ cat /dev/urandom | tr -dc a-zA-Z0-9 | fold -w 48 | head -n 1
+```
+
+After that, `davis` can be deployed like this:
+```
+{
+  services.davis = {
+    enable = true;
+    hostname = "davis.example.com";
+    mail = {
+      dsn = "smtp://username@example.com:25";
+      inviteFromAddress = "davis@example.com";
+    };
+    adminLogin = "admin";
+    adminPasswordFile = "/run/secrets/davis-admin-password";
+    appSecretFile = "/run/secrets/davis-app-secret";
+    nginx = {};
+  };
+}
+```
+
+This deploys Davis using a sqlite database running out of `/var/lib/davis`.
diff --git a/nixos/modules/services/web-apps/davis.nix b/nixos/modules/services/web-apps/davis.nix
new file mode 100644
index 0000000000000..7e7f6b5379749
--- /dev/null
+++ b/nixos/modules/services/web-apps/davis.nix
@@ -0,0 +1,554 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.services.davis;
+  db = cfg.database;
+  mail = cfg.mail;
+
+  mysqlLocal = db.createLocally && db.driver == "mysql";
+  pgsqlLocal = db.createLocally && db.driver == "postgresql";
+
+  user = cfg.user;
+  group = cfg.group;
+
+  isSecret = v: lib.isAttrs v && v ? _secret && (lib.isString v._secret || builtins.isPath v._secret);
+  davisEnvVars = lib.generators.toKeyValue {
+    mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
+      mkValueString =
+        v:
+        if builtins.isInt v then
+          toString v
+        else if lib.isString v then
+          "\"${v}\""
+        else if true == v then
+          "true"
+        else if false == v then
+          "false"
+        else if null == v then
+          ""
+        else if isSecret v then
+          if (lib.isString v._secret) then
+            builtins.hashString "sha256" v._secret
+          else
+            builtins.hashString "sha256" (builtins.readFile v._secret)
+        else
+          throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty { }) v}";
+    };
+  };
+  secretPaths = lib.mapAttrsToList (_: v: v._secret) (lib.filterAttrs (_: isSecret) cfg.config);
+  mkSecretReplacement = file: ''
+    replace-secret ${
+      lib.escapeShellArgs [
+        (
+          if (lib.isString file) then
+            builtins.hashString "sha256" file
+          else
+            builtins.hashString "sha256" (builtins.readFile file)
+        )
+        file
+        "${cfg.dataDir}/.env.local"
+      ]
+    }
+  '';
+  secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
+  filteredConfig = lib.converge (lib.filterAttrsRecursive (
+    _: v:
+    !lib.elem v [
+      { }
+      null
+    ]
+  )) cfg.config;
+  davisEnv = pkgs.writeText "davis.env" (davisEnvVars filteredConfig);
+in
+{
+  options.services.davis = {
+    enable = lib.mkEnableOption (lib.mdDoc "Davis is a caldav and carddav server");
+
+    user = lib.mkOption {
+      default = "davis";
+      description = lib.mdDoc "User davis runs as.";
+      type = lib.types.str;
+    };
+
+    group = lib.mkOption {
+      default = "davis";
+      description = lib.mdDoc "Group davis runs as.";
+      type = lib.types.str;
+    };
+
+    package = lib.mkPackageOption pkgs "davis" { };
+
+    dataDir = lib.mkOption {
+      type = lib.types.path;
+      default = "/var/lib/davis";
+      description = lib.mdDoc ''
+        Davis data directory.
+      '';
+    };
+
+    hostname = lib.mkOption {
+      type = lib.types.str;
+      example = "davis.yourdomain.org";
+      description = lib.mdDoc ''
+        Domain of the host to serve davis under. You may want to change it if you
+        run Davis on a different URL than davis.yourdomain.
+      '';
+    };
+
+    config = lib.mkOption {
+      type = lib.types.attrsOf (
+        lib.types.nullOr (
+          lib.types.either
+            (lib.types.oneOf [
+              lib.types.bool
+              lib.types.int
+              lib.types.port
+              lib.types.path
+              lib.types.str
+            ])
+            (
+              lib.types.submodule {
+                options = {
+                  _secret = lib.mkOption {
+                    type = lib.types.nullOr (
+                      lib.types.oneOf [
+                        lib.types.str
+                        lib.types.path
+                      ]
+                    );
+                    description = lib.mdDoc ''
+                      The path to a file containing the value the
+                      option should be set to in the final
+                      configuration file.
+                    '';
+                  };
+                };
+              }
+            )
+        )
+      );
+      default = { };
+
+      example = '''';
+      description = lib.mdDoc '''';
+    };
+
+    adminLogin = lib.mkOption {
+      type = lib.types.str;
+      default = "root";
+      description = lib.mdDoc ''
+        Username for the admin account.
+      '';
+    };
+    adminPasswordFile = lib.mkOption {
+      type = lib.types.path;
+      description = lib.mdDoc ''
+        The full path to a file that contains the admin's password. Must be
+        readable by the user.
+      '';
+      example = "/run/secrets/davis-admin-pass";
+    };
+
+    appSecretFile = lib.mkOption {
+      type = lib.types.path;
+      description = lib.mdDoc ''
+        A file containing the Symfony APP_SECRET - Its value should be a series
+        of characters, numbers and symbols chosen randomly and the recommended
+        length is around 32 characters. Can be generated with <code>cat
+        /dev/urandom | tr -dc a-zA-Z0-9 | fold -w 48 | head -n 1</code>.
+      '';
+      example = "/run/secrets/davis-appsecret";
+    };
+
+    database = {
+      driver = lib.mkOption {
+        type = lib.types.enum [
+          "sqlite"
+          "postgresql"
+          "mysql"
+        ];
+        default = "sqlite";
+        description = lib.mdDoc "Database type, required in all circumstances.";
+      };
+      urlFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/run/secrets/davis-db-url";
+        description = lib.mdDoc ''
+          A file containing the database connection url. If set then it
+          overrides all other database settings (except driver). This is
+          mandatory if you want to use an external database, that is when
+          `services.davis.database.createLocally` is `false`.
+        '';
+      };
+      name = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = "davis";
+        description = lib.mdDoc "Database name, only used when the databse is created locally.";
+      };
+      createLocally = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = lib.mdDoc "Create the database and database user locally.";
+      };
+    };
+
+    mail = {
+      dsn = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        description = lib.mdDoc "Mail DSN for sending emails. Mutually exclusive with `services.davis.mail.dsnFile`.";
+        example = "smtp://username:password@example.com:25";
+      };
+      dsnFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        example = "/run/secrets/davis-mail-dsn";
+        description = lib.mdDoc "A file containing the mail DSN for sending emails.  Mutually exclusive with `servies.davis.mail.dsn`.";
+      };
+      inviteFromAddress = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = null;
+        description = lib.mdDoc "Email address to send invitations from.";
+        example = "no-reply@dav.example.com";
+      };
+    };
+
+    nginx = lib.mkOption {
+      type = lib.types.submodule (
+        lib.recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) { }
+      );
+      default = null;
+      example = ''
+        {
+          serverAliases = [
+            "dav.''${config.networking.domain}"
+          ];
+          # To enable encryption and let let's encrypt take care of certificate
+          forceSSL = true;
+          enableACME = true;
+        }
+      '';
+      description = lib.mdDoc ''
+        With this option, you can customize the nginx virtualHost settings.
+      '';
+    };
+
+    poolConfig = lib.mkOption {
+      type = lib.types.attrsOf (
+        lib.types.oneOf [
+          lib.types.str
+          lib.types.int
+          lib.types.bool
+        ]
+      );
+      default = {
+        "pm" = "dynamic";
+        "pm.max_children" = 32;
+        "pm.start_servers" = 2;
+        "pm.min_spare_servers" = 2;
+        "pm.max_spare_servers" = 4;
+        "pm.max_requests" = 500;
+      };
+      description = lib.mdDoc ''
+        Options for the davis PHP pool. See the documentation on <literal>php-fpm.conf</literal>
+        for details on configuration directives.
+      '';
+    };
+  };
+
+  config =
+    let
+      defaultServiceConfig = {
+        ReadWritePaths = "${cfg.dataDir}";
+        User = user;
+        UMask = 77;
+        DeviceAllow = "";
+        LockPersonality = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [
+          "@system-service"
+          "~@resources"
+          "~@privileged"
+        ];
+        WorkingDirectory = "${cfg.package}/";
+      };
+    in
+    lib.mkIf cfg.enable {
+      assertions = [
+        {
+          assertion = db.createLocally -> db.urlFile == null;
+          message = "services.davis.database.urlFile must be unset if services.davis.database.createLocally is set true.";
+        }
+        {
+          assertion = db.createLocally || db.urlFile != null;
+          message = "One of services.davis.database.urlFile or services.davis.database.createLocally must be set.";
+        }
+        {
+          assertion = (mail.dsn != null) != (mail.dsnFile != null);
+          message = "One of (and only one of) services.davis.mail.dsn or services.davis.mail.dsnFile must be set.";
+        }
+      ];
+      services.davis.config =
+        {
+          APP_ENV = "prod";
+          APP_CACHE_DIR = "${cfg.dataDir}/var/cache";
+          # note: we do not need the log dir (we log to stdout/journald), by davis/symfony will try to create it, and the default value is one in the nix-store
+          #       so we set it to a path under dataDir to avoid something like: Unable to create the "logs" directory (/nix/store/5cfskz0ybbx37s1161gjn5klwb5si1zg-davis-4.4.1/var/log).
+          APP_LOG_DIR = "${cfg.dataDir}/var/log";
+          LOG_FILE_PATH = "/dev/stdout";
+          DATABASE_DRIVER = db.driver;
+          INVITE_FROM_ADDRESS = mail.inviteFromAddress;
+          APP_SECRET._secret = cfg.appSecretFile;
+          ADMIN_LOGIN = cfg.adminLogin;
+          ADMIN_PASSWORD._secret = cfg.adminPasswordFile;
+          APP_TIMEZONE = config.time.timeZone;
+          WEBDAV_ENABLED = false;
+          CALDAV_ENABLED = true;
+          CARDDAV_ENABLED = true;
+        }
+        // (if mail.dsn != null then { MAILER_DSN = mail.dsn; } else { MAILER_DSN._secret = mail.dsnFile; })
+        // (
+          if db.createLocally then
+            {
+              DATABASE_URL =
+                if db.driver == "sqlite" then
+                  "sqlite:///${cfg.dataDir}/davis.db" # note: sqlite needs 4 slashes for an absolute path
+                else if
+                  pgsqlLocal
+                # note: davis expects a non-standard postgres uri (due to the underlying doctrine library)
+                # specifically the dummy hostname which is overriden by the host query parameter
+                then
+                  "postgres://${user}@localhost/${db.name}?host=/run/postgresql"
+                else if mysqlLocal then
+                  "mysql://${user}@localhost/${db.name}?socket=/run/mysqld/mysqld.sock"
+                else
+                  null;
+            }
+          else
+            { DATABASE_URL._secret = db.urlFile; }
+        );
+
+      users = {
+        users = lib.mkIf (user == "davis") {
+          davis = {
+            description = "Davis service user";
+            group = cfg.group;
+            isSystemUser = true;
+            home = cfg.dataDir;
+          };
+        };
+        groups = lib.mkIf (group == "davis") { davis = { }; };
+      };
+
+      systemd.tmpfiles.rules = [
+        "d ${cfg.dataDir}                            0710 ${user} ${group} - -"
+        "d ${cfg.dataDir}/var                        0700 ${user} ${group} - -"
+        "d ${cfg.dataDir}/var/log                    0700 ${user} ${group} - -"
+        "d ${cfg.dataDir}/var/cache                  0700 ${user} ${group} - -"
+      ];
+
+      services.phpfpm.pools.davis = {
+        inherit user group;
+        phpOptions = ''
+          log_errors = on
+        '';
+        phpEnv = {
+          ENV_DIR = "${cfg.dataDir}";
+          APP_CACHE_DIR = "${cfg.dataDir}/var/cache";
+          APP_LOG_DIR = "${cfg.dataDir}/var/log";
+        };
+        settings =
+          {
+            "listen.mode" = "0660";
+            "pm" = "dynamic";
+            "pm.max_children" = 256;
+            "pm.start_servers" = 10;
+            "pm.min_spare_servers" = 5;
+            "pm.max_spare_servers" = 20;
+          }
+          // (
+            if cfg.nginx != null then
+              {
+                "listen.owner" = config.services.nginx.user;
+                "listen.group" = config.services.nginx.group;
+              }
+            else
+              { }
+          )
+          // cfg.poolConfig;
+      };
+
+      # Reading the user-provided secret files requires root access
+      systemd.services.davis-env-setup = {
+        description = "Setup davis environment";
+        before = [
+          "phpfpm-davis.service"
+          "davis-db-migrate.service"
+        ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          Type = "oneshot";
+          RemainAfterExit = true;
+        };
+        path = [ pkgs.replace-secret ];
+        restartTriggers = [
+          cfg.package
+          davisEnv
+        ];
+        script = ''
+          # error handling
+          set -euo pipefail
+          # create .env file with the upstream values
+          install -T -m 0600 -o ${user} ${cfg.package}/env-upstream "${cfg.dataDir}/.env"
+          # create .env.local file with the user-provided values
+          install -T -m 0600 -o ${user} ${davisEnv} "${cfg.dataDir}/.env.local"
+          ${secretReplacements}
+        '';
+      };
+
+      systemd.services.davis-db-migrate = {
+        description = "Migrate davis database";
+        before = [ "phpfpm-davis.service" ];
+        after =
+          lib.optional mysqlLocal "mysql.service"
+          ++ lib.optional pgsqlLocal "postgresql.service"
+          ++ [ "davis-env-setup.service" ];
+        requires =
+          lib.optional mysqlLocal "mysql.service"
+          ++ lib.optional pgsqlLocal "postgresql.service"
+          ++ [ "davis-env-setup.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = defaultServiceConfig // {
+          Type = "oneshot";
+          RemainAfterExit = true;
+          Environment = [
+            "ENV_DIR=${cfg.dataDir}"
+            "APP_CACHE_DIR=${cfg.dataDir}/var/cache"
+            "APP_LOG_DIR=${cfg.dataDir}/var/log"
+          ];
+          EnvironmentFile = "${cfg.dataDir}/.env.local";
+        };
+        restartTriggers = [
+          cfg.package
+          davisEnv
+        ];
+        script = ''
+          set -euo pipefail
+          ${cfg.package}/bin/console cache:clear --no-debug
+          ${cfg.package}/bin/console cache:warmup --no-debug
+          ${cfg.package}/bin/console doctrine:migrations:migrate
+        '';
+      };
+
+      systemd.services.phpfpm-davis.after = [
+        "davis-env-setup.service"
+        "davis-db-migrate.service"
+      ];
+      systemd.services.phpfpm-davis.requires = [
+        "davis-env-setup.service"
+        "davis-db-migrate.service"
+      ] ++ lib.optional mysqlLocal "mysql.service" ++ lib.optional pgsqlLocal "postgresql.service";
+      systemd.services.phpfpm-davis.serviceConfig.ReadWritePaths = [ cfg.dataDir ];
+
+      services.nginx = lib.mkIf (cfg.nginx != null) {
+        enable = lib.mkDefault true;
+        virtualHosts = {
+          "${cfg.hostname}" = lib.mkMerge [
+            cfg.nginx
+            {
+              root = lib.mkForce "${cfg.package}/public";
+              extraConfig = ''
+                charset utf-8;
+                index index.php;
+              '';
+              locations = {
+                "/" = {
+                  extraConfig = ''
+                    try_files $uri $uri/ /index.php$is_args$args;
+                  '';
+                };
+                "~* ^/.well-known/(caldav|carddav)$" = {
+                  extraConfig = ''
+                    return 302 $http_x_forwarded_proto://$host/dav/;
+                  '';
+                };
+                "~ ^(.+\.php)(.*)$" = {
+                  extraConfig = ''
+                    try_files                $fastcgi_script_name =404;
+                    include                  ${config.services.nginx.package}/conf/fastcgi_params;
+                    include                  ${config.services.nginx.package}/conf/fastcgi.conf;
+                    fastcgi_pass             unix:${config.services.phpfpm.pools.davis.socket};
+                    fastcgi_param            SCRIPT_FILENAME  $document_root$fastcgi_script_name;
+                    fastcgi_param            PATH_INFO        $fastcgi_path_info;
+                    fastcgi_split_path_info  ^(.+\.php)(.*)$;
+                    fastcgi_param            X-Forwarded-Proto $http_x_forwarded_proto;
+                    fastcgi_param            X-Forwarded-Port $http_x_forwarded_port;
+                  '';
+                };
+                "~ /(\\.ht)" = {
+                  extraConfig = ''
+                    deny all;
+                    return 404;
+                  '';
+                };
+              };
+            }
+          ];
+        };
+      };
+
+      services.mysql = lib.mkIf mysqlLocal {
+        enable = true;
+        package = lib.mkDefault pkgs.mariadb;
+        ensureDatabases = [ db.name ];
+        ensureUsers = [
+          {
+            name = user;
+            ensurePermissions = {
+              "${db.name}.*" = "ALL PRIVILEGES";
+            };
+          }
+        ];
+      };
+
+      services.postgresql = lib.mkIf pgsqlLocal {
+        enable = true;
+        ensureDatabases = [ db.name ];
+        ensureUsers = [
+          {
+            name = user;
+            ensureDBOwnership = true;
+          }
+        ];
+      };
+    };
+
+  meta = {
+    doc = ./davis.md;
+    maintainers = pkgs.davis.meta.maintainers;
+  };
+}
diff --git a/nixos/modules/services/web-apps/discourse.md b/nixos/modules/services/web-apps/discourse.md
index 35180bea87d90..d4b9c93c4ead0 100644
--- a/nixos/modules/services/web-apps/discourse.md
+++ b/nixos/modules/services/web-apps/discourse.md
@@ -6,20 +6,22 @@ modern and open source discussion platform.
 ## Basic usage {#module-services-discourse-basic-usage}
 
 A minimal configuration using Let's Encrypt for TLS certificates looks like this:
-```
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
+```nix
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
-security.acme.email = "me@example.com";
-security.acme.acceptTerms = true;
+  security.acme.email = "me@example.com";
+  security.acme.acceptTerms = true;
+}
 ```
 
 Provided a proper DNS setup, you'll be able to connect to the
@@ -34,20 +36,22 @@ the [](#opt-services.discourse.sslCertificate)
 and [](#opt-services.discourse.sslCertificateKey)
 options:
 
-```
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
+```nix
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
 
 ## Database access {#module-services-discourse-database}
@@ -80,27 +84,29 @@ A basic setup which assumes you want to use your configured
 [hostname](#opt-services.discourse.hostname) as
 email domain can be done like this:
 
-```
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
-  };
-  mail.outgoing = {
-    serverAddress = "smtp.emailprovider.com";
-    port = 587;
-    username = "user@emailprovider.com";
-    passwordFile = "/path/to/smtp_password_file";
+```nix
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    mail.outgoing = {
+      serverAddress = "smtp.emailprovider.com";
+      port = 587;
+      username = "user@emailprovider.com";
+      passwordFile = "/path/to/smtp_password_file";
+    };
+    mail.incoming.enable = true;
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  mail.incoming.enable = true;
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
 
 This assumes you have set up an MX record for the address you've
@@ -162,44 +168,46 @@ The following example sets the title and description of the
 Discourse instance and enables
 GitHub login in the site settings,
 and changes a few request limits in the backend settings:
-```
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
-  };
-  mail.outgoing = {
-    serverAddress = "smtp.emailprovider.com";
-    port = 587;
-    username = "user@emailprovider.com";
-    passwordFile = "/path/to/smtp_password_file";
-  };
-  mail.incoming.enable = true;
-  siteSettings = {
-    required = {
-      title = "My Cats";
-      site_description = "Discuss My Cats (and be nice plz)";
+```nix
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
     };
-    login = {
-      enable_github_logins = true;
-      github_client_id = "a2f6dfe838cb3206ce20";
-      github_client_secret._secret = /run/keys/discourse_github_client_secret;
+    mail.outgoing = {
+      serverAddress = "smtp.emailprovider.com";
+      port = 587;
+      username = "user@emailprovider.com";
+      passwordFile = "/path/to/smtp_password_file";
     };
+    mail.incoming.enable = true;
+    siteSettings = {
+      required = {
+        title = "My Cats";
+        site_description = "Discuss My Cats (and be nice plz)";
+      };
+      login = {
+        enable_github_logins = true;
+        github_client_id = "a2f6dfe838cb3206ce20";
+        github_client_secret._secret = /run/keys/discourse_github_client_secret;
+      };
+    };
+    backendSettings = {
+      max_reqs_per_ip_per_minute = 300;
+      max_reqs_per_ip_per_10_seconds = 60;
+      max_asset_reqs_per_ip_per_10_seconds = 250;
+      max_reqs_per_ip_mode = "warn+block";
+    };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  backendSettings = {
-    max_reqs_per_ip_per_minute = 300;
-    max_reqs_per_ip_per_10_seconds = 60;
-    max_asset_reqs_per_ip_per_10_seconds = 250;
-    max_reqs_per_ip_mode = "warn+block";
-  };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
 
 In the resulting site settings file, the
@@ -253,34 +261,36 @@ and [discourse-solved](https://github.com/discourse/discourse-solved)
 plugins, and disable `discourse-spoiler-alert`
 by default:
 
-```
-services.discourse = {
-  enable = true;
-  hostname = "discourse.example.com";
-  sslCertificate = "/path/to/ssl_certificate";
-  sslCertificateKey = "/path/to/ssl_certificate_key";
-  admin = {
-    email = "admin@example.com";
-    username = "admin";
-    fullName = "Administrator";
-    passwordFile = "/path/to/password_file";
-  };
-  mail.outgoing = {
-    serverAddress = "smtp.emailprovider.com";
-    port = 587;
-    username = "user@emailprovider.com";
-    passwordFile = "/path/to/smtp_password_file";
-  };
-  mail.incoming.enable = true;
-  plugins = with config.services.discourse.package.plugins; [
-    discourse-spoiler-alert
-    discourse-solved
-  ];
-  siteSettings = {
-    plugins = {
-      spoiler_enabled = false;
+```nix
+{
+  services.discourse = {
+    enable = true;
+    hostname = "discourse.example.com";
+    sslCertificate = "/path/to/ssl_certificate";
+    sslCertificateKey = "/path/to/ssl_certificate_key";
+    admin = {
+      email = "admin@example.com";
+      username = "admin";
+      fullName = "Administrator";
+      passwordFile = "/path/to/password_file";
+    };
+    mail.outgoing = {
+      serverAddress = "smtp.emailprovider.com";
+      port = 587;
+      username = "user@emailprovider.com";
+      passwordFile = "/path/to/smtp_password_file";
+    };
+    mail.incoming.enable = true;
+    plugins = with config.services.discourse.package.plugins; [
+      discourse-spoiler-alert
+      discourse-solved
+    ];
+    siteSettings = {
+      plugins = {
+        spoiler_enabled = false;
+      };
     };
+    secretKeyBaseFile = "/path/to/secret_key_base_file";
   };
-  secretKeyBaseFile = "/path/to/secret_key_base_file";
-};
+}
 ```
diff --git a/nixos/modules/services/web-apps/engelsystem.nix b/nixos/modules/services/web-apps/engelsystem.nix
index 669620debce55..ae7b2b9e7d0c7 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/gotosocial.md b/nixos/modules/services/web-apps/gotosocial.md
index a290d7d1893a1..b3540f0d5811f 100644
--- a/nixos/modules/services/web-apps/gotosocial.md
+++ b/nixos/modules/services/web-apps/gotosocial.md
@@ -8,17 +8,19 @@ The following configuration sets up the PostgreSQL as database backend and binds
 GoToSocial to `127.0.0.1:8080`, expecting to be run behind a HTTP proxy on `gotosocial.example.com`.
 
 ```nix
-services.gotosocial = {
-  enable = true;
-  setupPostgresqlDB = true;
-  settings = {
-    application-name = "My GoToSocial";
-    host = "gotosocial.example.com";
-    protocol = "https";
-    bind-address = "127.0.0.1";
-    port = 8080;
+{
+  services.gotosocial = {
+    enable = true;
+    setupPostgresqlDB = true;
+    settings = {
+      application-name = "My GoToSocial";
+      host = "gotosocial.example.com";
+      protocol = "https";
+      bind-address = "127.0.0.1";
+      port = 8080;
+    };
   };
-};
+}
 ```
 
 Please refer to the [GoToSocial Documentation](https://docs.gotosocial.org/en/latest/configuration/general/)
@@ -30,24 +32,26 @@ Although it is possible to expose GoToSocial directly, it is common practice to
 HTTP reverse proxy such as nginx.
 
 ```nix
-networking.firewall.allowedTCPPorts = [ 80 443 ];
-services.nginx = {
-  enable = true;
-  clientMaxBodySize = "40M";
-  virtualHosts = with config.services.gotosocial.settings; {
-    "${host}" = {
-      enableACME = true;
-      forceSSL = true;
-      locations = {
-        "/" = {
-          recommendedProxySettings = true;
-          proxyWebsockets = true;
-          proxyPass = "http://${bind-address}:${toString port}";
+{
+  networking.firewall.allowedTCPPorts = [ 80 443 ];
+  services.nginx = {
+    enable = true;
+    clientMaxBodySize = "40M";
+    virtualHosts = with config.services.gotosocial.settings; {
+      "${host}" = {
+        enableACME = true;
+        forceSSL = true;
+        locations = {
+          "/" = {
+            recommendedProxySettings = true;
+            proxyWebsockets = true;
+            proxyPass = "http://${bind-address}:${toString port}";
+          };
         };
       };
     };
   };
-};
+}
 ```
 
 Please refer to [](#module-security-acme) for details on how to provision an SSL/TLS certificate.
diff --git a/nixos/modules/services/web-apps/grocy.md b/nixos/modules/services/web-apps/grocy.md
index 62aad4b103df1..f4b5769c2479c 100644
--- a/nixos/modules/services/web-apps/grocy.md
+++ b/nixos/modules/services/web-apps/grocy.md
@@ -6,7 +6,7 @@
 ## Basic usage {#module-services-grocy-basic-usage}
 
 A very basic configuration may look like this:
-```
+```nix
 { pkgs, ... }:
 {
   services.grocy = {
@@ -29,7 +29,7 @@ of the application.
 
 The configuration for `grocy` is located at `/etc/grocy/config.php`.
 By default, the following settings can be defined in the NixOS-configuration:
-```
+```nix
 { pkgs, ... }:
 {
   services.grocy.settings = {
@@ -56,7 +56,7 @@ By default, the following settings can be defined in the NixOS-configuration:
 
 If you want to alter the configuration file on your own, you can do this manually with
 an expression like this:
-```
+```nix
 { lib, ... }:
 {
   environment.etc."grocy/config.php".text = lib.mkAfter ''
diff --git a/nixos/modules/services/web-apps/healthchecks.nix b/nixos/modules/services/web-apps/healthchecks.nix
index 1d439f162313b..04b40e6eb8b08 100644
--- a/nixos/modules/services/web-apps/healthchecks.nix
+++ b/nixos/modules/services/web-apps/healthchecks.nix
@@ -213,8 +213,7 @@ in
           preStart = ''
             ${pkg}/opt/healthchecks/manage.py collectstatic --no-input
             ${pkg}/opt/healthchecks/manage.py remove_stale_contenttypes --no-input
-            ${pkg}/opt/healthchecks/manage.py compress
-          '';
+          '' + lib.optionalString (cfg.settings.DEBUG != "True") "${pkg}/opt/healthchecks/manage.py compress";
 
           serviceConfig = commonConfig // {
             Restart = "always";
diff --git a/nixos/modules/services/web-apps/hedgedoc.nix b/nixos/modules/services/web-apps/hedgedoc.nix
index adcfe80a7332b..8b17c6cbc3be4 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/hledger-web.nix b/nixos/modules/services/web-apps/hledger-web.nix
index be8ecc645e59c..2e888d081a9f9 100644
--- a/nixos/modules/services/web-apps/hledger-web.nix
+++ b/nixos/modules/services/web-apps/hledger-web.nix
@@ -26,28 +26,17 @@ in {
       '';
     };
 
-    capabilities = {
-      view = mkOption {
-        type = types.bool;
-        default = true;
-        description = lib.mdDoc ''
-          Enable the view capability.
-        '';
-      };
-      add = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable the add capability.
-        '';
-      };
-      manage = mkOption {
-        type = types.bool;
-        default = false;
-        description = lib.mdDoc ''
-          Enable the manage capability.
-        '';
-      };
+    allow = mkOption {
+      type = types.enum [ "view" "add" "edit" "sandstorm" ];
+      default = "view";
+      description = lib.mdDoc ''
+        User's access level for changing data.
+
+        * view: view only permission.
+        * add: view and add permissions.
+        * edit: view, add, and edit permissions.
+        * sandstorm: permissions from the `X-Sandstorm-Permissions` request header.
+      '';
     };
 
     stateDir = mkOption {
@@ -89,6 +78,11 @@ in {
 
   };
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "hledger-web" "capabilities" ]
+      "This option has been replaced by new option `services.hledger-web.allow`.")
+  ];
+
   config = mkIf cfg.enable {
 
     users.users.hledger = {
@@ -102,16 +96,11 @@ in {
     users.groups.hledger = {};
 
     systemd.services.hledger-web = let
-      capabilityString = with cfg.capabilities; concatStringsSep "," (
-        (optional view "view")
-        ++ (optional add "add")
-        ++ (optional manage "manage")
-      );
       serverArgs = with cfg; escapeShellArgs ([
         "--serve"
         "--host=${host}"
         "--port=${toString port}"
-        "--capabilities=${capabilityString}"
+        "--allow=${allow}"
         (optionalString (cfg.baseUrl != null) "--base-url=${cfg.baseUrl}")
         (optionalString (cfg.serveApi) "--serve-api")
       ] ++ (map (f: "--file=${stateDir}/${f}") cfg.journalFiles)
diff --git a/nixos/modules/services/web-apps/invidious.nix b/nixos/modules/services/web-apps/invidious.nix
index 359aaabfe673a..ac7937b16e484 100644
--- a/nixos/modules/services/web-apps/invidious.nix
+++ b/nixos/modules/services/web-apps/invidious.nix
@@ -346,8 +346,8 @@ in
 
       port = lib.mkOption {
         type = types.port;
-        default = options.services.postgresql.port.default;
-        defaultText = lib.literalExpression "options.services.postgresql.port.default";
+        default = config.services.postgresql.settings.port;
+        defaultText = lib.literalExpression "config.services.postgresql.settings.port";
         description = lib.mdDoc ''
           The port of the database Invidious should use.
 
diff --git a/nixos/modules/services/web-apps/jitsi-meet.md b/nixos/modules/services/web-apps/jitsi-meet.md
index 060ef9752650a..577f82e315be5 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.md
+++ b/nixos/modules/services/web-apps/jitsi-meet.md
@@ -6,7 +6,7 @@ private, self-hosted video conferencing solution.
 ## Basic usage {#module-services-jitsi-basic-usage}
 
 A minimal configuration using Let's Encrypt for TLS certificates looks like this:
-```
+```nix
 {
   services.jitsi-meet = {
     enable = true;
@@ -22,7 +22,7 @@ A minimal configuration using Let's Encrypt for TLS certificates looks like this
 ## Configuration {#module-services-jitsi-configuration}
 
 Here is the minimal configuration with additional configurations:
-```
+```nix
 {
   services.jitsi-meet = {
     enable = true;
diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix
index c4505534d635e..f907aa68f55e1 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -188,7 +188,14 @@ in
       description = lib.mdDoc ''The port which the Excalidraw backend for Jitsi should listen to.'';
     };
 
-    secureDomain.enable = mkEnableOption (lib.mdDoc "Authenticated room creation");
+    secureDomain = {
+      enable = mkEnableOption (lib.mdDoc "Authenticated room creation");
+      authentication = mkOption {
+        type = types.str;
+        default = "internal_hashed";
+        description = lib.mdDoc ''The authentication type to be used by jitsi'';
+      };
+    };
   };
 
   config = mkIf cfg.enable {
@@ -309,7 +316,7 @@ in
         enabled = true;
         domain = cfg.hostName;
         extraConfig = ''
-          authentication = ${if cfg.secureDomain.enable then "\"internal_hashed\"" else "\"jitsi-anonymous\""}
+          authentication = ${if cfg.secureDomain.enable then "\"${cfg.secureDomain.authentication}\"" else "\"jitsi-anonymous\""}
           c2s_require_encryption = false
           admins = { "focus@auth.${cfg.hostName}" }
           smacks_max_unacked_stanzas = 5
diff --git a/nixos/modules/services/web-apps/kavita.nix b/nixos/modules/services/web-apps/kavita.nix
index c3e39f0b54761..81b8edc5e0066 100644
--- a/nixos/modules/services/web-apps/kavita.nix
+++ b/nixos/modules/services/web-apps/kavita.nix
@@ -2,7 +2,18 @@
 
 let
   cfg = config.services.kavita;
-in {
+  settingsFormat = pkgs.formats.json { };
+  appsettings = settingsFormat.generate "appsettings.json" ({ TokenKey = "@TOKEN@"; } // cfg.settings);
+in
+{
+  imports = [
+    (lib.mkChangedOptionModule [ "services" "kavita" "ipAdresses" ] [ "services" "kavita" "settings" "IpAddresses" ] (config:
+      let value = lib.getAttrFromPath [ "services" "kavita" "ipAdresses" ] config; in
+      lib.concatStringsSep "," value
+    ))
+    (lib.mkRenamedOptionModule [ "services" "kavita" "port" ] [ "services" "kavita" "settings" "Port" ])
+  ];
+
   options.services.kavita = {
     enable = lib.mkEnableOption (lib.mdDoc "Kavita reading server");
 
@@ -23,20 +34,35 @@ in {
     tokenKeyFile = lib.mkOption {
       type = lib.types.path;
       description = lib.mdDoc ''
-        A file containing the TokenKey, a secret with at 128+ bits.
-        It can be generated with `head -c 32 /dev/urandom | base64`.
+        A file containing the TokenKey, a secret with at 512+ bits.
+        It can be generated with `head -c 64 /dev/urandom | base64 --wrap=0`.
       '';
     };
-    port = lib.mkOption {
-      default = 5000;
-      type = lib.types.port;
-      description = lib.mdDoc "Port to bind to.";
-    };
-    ipAdresses = lib.mkOption {
-      default = ["0.0.0.0" "::"];
-      type = lib.types.listOf lib.types.str;
-      description = lib.mdDoc "IP Addresses to bind to. The default is to bind
-      to all IPv4 and IPv6 addresses.";
+
+    settings = lib.mkOption {
+      default = { };
+      description = lib.mdDoc ''
+        Kavita configuration options, as configured in {file}`appsettings.json`.
+      '';
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+
+        options = {
+          Port = lib.mkOption {
+            default = 5000;
+            type = lib.types.port;
+            description = lib.mdDoc "Port to bind to.";
+          };
+
+          IpAddresses = lib.mkOption {
+            default = "0.0.0.0,::";
+            type = lib.types.commas;
+            description = lib.mdDoc ''
+              IP Addresses to bind to. The default is to bind to all IPv4 and IPv6 addresses.
+            '';
+          };
+        };
+      };
     };
   };
 
@@ -46,18 +72,15 @@ in {
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
       preStart = ''
-        umask u=rwx,g=rx,o=
-        cat > "${cfg.dataDir}/config/appsettings.json" <<EOF
-        {
-          "TokenKey": "$(cat ${cfg.tokenKeyFile})",
-          "Port": ${toString cfg.port},
-          "IpAddresses": "${lib.concatStringsSep "," cfg.ipAdresses}"
-        }
-        EOF
+        install -m600 ${appsettings} ${lib.escapeShellArg cfg.dataDir}/config/appsettings.json
+        ${pkgs.replace-secret}/bin/replace-secret '@TOKEN@' \
+          ''${CREDENTIALS_DIRECTORY}/token \
+          '${cfg.dataDir}/config/appsettings.json'
       '';
       serviceConfig = {
         WorkingDirectory = cfg.dataDir;
-        ExecStart = "${lib.getExe cfg.package}";
+        LoadCredential = [ "token:${cfg.tokenKeyFile}" ];
+        ExecStart = lib.getExe cfg.package;
         Restart = "always";
         User = cfg.user;
       };
diff --git a/nixos/modules/services/web-apps/keycloak.md b/nixos/modules/services/web-apps/keycloak.md
index aa8de40d642b1..020bee4003489 100644
--- a/nixos/modules/services/web-apps/keycloak.md
+++ b/nixos/modules/services/web-apps/keycloak.md
@@ -126,16 +126,18 @@ should be set to. See the description of
 ## Example configuration {#module-services-keycloak-example-config}
 
 A basic configuration with some custom settings could look like this:
-```
-services.keycloak = {
-  enable = true;
-  settings = {
-    hostname = "keycloak.example.com";
-    hostname-strict-backchannel = true;
+```nix
+{
+  services.keycloak = {
+    enable = true;
+    settings = {
+      hostname = "keycloak.example.com";
+      hostname-strict-backchannel = true;
+    };
+    initialAdminPassword = "e6Wcm0RrtegMEHl";  # change on first login
+    sslCertificate = "/run/keys/ssl_cert";
+    sslCertificateKey = "/run/keys/ssl_key";
+    database.passwordFile = "/run/keys/db_password";
   };
-  initialAdminPassword = "e6Wcm0RrtegMEHl";  # change on first login
-  sslCertificate = "/run/keys/ssl_cert";
-  sslCertificateKey = "/run/keys/ssl_key";
-  database.passwordFile = "/run/keys/db_password";
-};
+}
 ```
diff --git a/nixos/modules/services/web-apps/komga.nix b/nixos/modules/services/web-apps/komga.nix
index 31f475fc7b048..d7ab2a9e612ef 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/lemmy.md b/nixos/modules/services/web-apps/lemmy.md
index faafe096d1382..0ed23607d00b9 100644
--- a/nixos/modules/services/web-apps/lemmy.md
+++ b/nixos/modules/services/web-apps/lemmy.md
@@ -7,13 +7,15 @@ Lemmy is a federated alternative to reddit in rust.
 the minimum to start lemmy is
 
 ```nix
-services.lemmy = {
-  enable = true;
-  settings = {
-    hostname = "lemmy.union.rocks";
-    database.createLocally = true;
+{
+  services.lemmy = {
+    enable = true;
+    settings = {
+      hostname = "lemmy.union.rocks";
+      database.createLocally = true;
+    };
+    caddy.enable = true;
   };
-  caddy.enable = true;
 }
 ```
 
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index 7fc710c6fcec1..5e9a7cc22248a 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -742,11 +742,16 @@ in {
         umask 077
         export PGPASSWORD="$(cat '${cfg.database.passwordFile}')"
       '' + ''
-        if [ `psql -c \
-                "select count(*) from pg_class c \
-                join pg_namespace s on s.oid = c.relnamespace \
-                where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
-                and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then
+        result="$(psql -t --csv -c \
+            "select count(*) from pg_class c \
+            join pg_namespace s on s.oid = c.relnamespace \
+            where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
+            and s.nspname not like 'pg_temp%';")" || error_code=$?
+        if [ "''${error_code:-0}" -ne 0 ]; then
+          echo "Failure checking if database is seeded. psql gave exit code $error_code"
+          exit "$error_code"
+        fi
+        if [ "$result" -eq 0 ]; then
           echo "Seeding database"
           SAFETY_ASSURED=1 rails db:schema:load
           rails db:seed
diff --git a/nixos/modules/services/web-apps/movim.nix b/nixos/modules/services/web-apps/movim.nix
new file mode 100644
index 0000000000000..bb88a185b4618
--- /dev/null
+++ b/nixos/modules/services/web-apps/movim.nix
@@ -0,0 +1,711 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib)
+    filterAttrsRecursive
+    generators
+    literalExpression
+    mkDefault
+    mkIf
+    mkOption
+    mkEnableOption
+    mkPackageOption
+    mkMerge
+    pipe
+    types
+    ;
+
+  cfg = config.services.movim;
+
+  defaultPHPCfg = {
+    "output_buffering" = 0;
+    "error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
+    "opcache.enable_cli" = 1;
+    "opcache.interned_strings_buffer" = 8;
+    "opcache.max_accelerated_files" = 6144;
+    "opcache.memory_consumption" = 128;
+    "opcache.revalidate_freq" = 2;
+    "opcache.fast_shutdown" = 1;
+  };
+
+  phpCfg = generators.toKeyValue
+    { mkKeyValue = generators.mkKeyValueDefault { } " = "; }
+    (defaultPHPCfg // cfg.phpCfg);
+
+  podConfigFlags =
+    let
+      bevalue = a: lib.escapeShellArg (generators.mkValueStringDefault { } a);
+    in
+    lib.concatStringsSep " "
+      (lib.attrsets.foldlAttrs
+        (acc: k: v: acc ++ lib.optional (v != null) "--${k}=${bevalue v}")
+        [ ]
+        cfg.podConfig);
+
+  package =
+    let
+      p = cfg.package.override
+        ({
+          inherit phpCfg;
+          withPgsql = cfg.database.type == "pgsql";
+          withMysql = cfg.database.type == "mysql";
+          inherit (cfg) minifyStaticFiles;
+        } // lib.optionalAttrs (lib.isAttrs cfg.minifyStaticFiles) (with cfg.minifyStaticFiles; {
+          esbuild = esbuild.package;
+          lightningcss = lightningcss.package;
+          scour = scour.package;
+        }));
+    in
+    p.overrideAttrs (finalAttrs: prevAttrs:
+      let
+        appDir = "$out/share/php/${finalAttrs.pname}";
+
+        stateDirectories = ''
+          # Symlinking in our state directories
+          rm -rf $out/.env $out/cache ${appDir}/public/cache
+          ln -s ${cfg.dataDir}/.env ${appDir}/.env
+          ln -s ${cfg.dataDir}/public/cache ${appDir}/public/cache
+          ln -s ${cfg.logDir} ${appDir}/log
+          ln -s ${cfg.runtimeDir}/cache ${appDir}/cache
+        '';
+
+        exposeComposer = ''
+          # Expose PHP Composer for scripts
+          mkdir -p $out/bin
+          echo "#!${lib.getExe pkgs.dash}" > $out/bin/movim-composer
+          echo "${finalAttrs.php.packages.composer}/bin/composer --working-dir="${appDir}" \"\$@\"" >> $out/bin/movim-composer
+          chmod +x $out/bin/movim-composer
+        '';
+
+        podConfigInputDisableReplace = lib.optionalString (podConfigFlags != "")
+          (lib.concatStringsSep "\n"
+            (lib.attrsets.foldlAttrs
+              (acc: k: v:
+                acc ++ lib.optional (v != null)
+                  # Disable all Admin panel options that were set in the
+                  # `cfg.podConfig` to prevent confusing situtions where the
+                  # values are rewritten on server reboot
+                  ''
+                    substituteInPlace ${appDir}/app/widgets/AdminMain/adminmain.tpl \
+                      --replace-warn 'name="${k}"' 'name="${k}" disabled'
+                  '')
+              [ ]
+              cfg.podConfig));
+
+        precompressStaticFilesJobs =
+          let
+            inherit (cfg.precompressStaticFiles) brotli gzip;
+
+            findTextFileNames = lib.concatStringsSep " -o "
+              (builtins.map (n: ''-iname "*.${n}"'')
+                [ "css" "ini" "js" "json" "manifest" "mjs" "svg" "webmanifest" ]);
+          in
+          lib.concatStringsSep "\n" [
+            (lib.optionalString brotli.enable ''
+              echo -n "Precompressing static files with Brotli …"
+              find ${appDir}/public -type f ${findTextFileNames} \
+                | ${lib.getExe pkgs.parallel} ${lib.escapeShellArgs [
+                    "--will-cite"
+                    "-j $NIX_BUILD_CORES"
+                    "${lib.getExe brotli.package} --keep --quality=${builtins.toString brotli.compressionLevel} --output={}.br {}"
+                   ]}
+              echo " done."
+            '')
+            (lib.optionalString gzip.enable ''
+              echo -n "Precompressing static files with Gzip …"
+              find ${appDir}/public -type f ${findTextFileNames} \
+                | ${lib.getExe pkgs.parallel} ${lib.escapeShellArgs [
+                    "--will-cite"
+                    "-j $NIX_BUILD_CORES"
+                    "${lib.getExe gzip.package} -c -${builtins.toString gzip.compressionLevel} {} > {}.gz"
+                   ]}
+              echo " done."
+            '')
+          ];
+      in
+      {
+        postInstall = lib.concatStringsSep "\n\n" [
+          prevAttrs.postInstall
+          stateDirectories
+          exposeComposer
+          podConfigInputDisableReplace
+          precompressStaticFilesJobs
+        ];
+      });
+
+  configFile = pipe cfg.settings [
+    (filterAttrsRecursive (_: v: v != null))
+    (generators.toKeyValue { })
+    (pkgs.writeText "movim-env")
+  ];
+
+  pool = "movim";
+  fpm = config.services.phpfpm.pools.${pool};
+  phpExecutionUnit = "phpfpm-${pool}";
+
+  dbService = {
+    "postgresql" = "postgresql.service";
+    "mysql" = "mysql.service";
+  }.${cfg.database.type};
+in
+{
+  options.services = {
+    movim = {
+      enable = mkEnableOption "a Movim instance";
+      package = mkPackageOption pkgs "movim" { };
+      phpPackage = mkPackageOption pkgs "php" { };
+
+      phpCfg = mkOption {
+        type = with types; attrsOf (oneOf [ int str bool ]);
+        defaultText = literalExpression (generators.toPretty { } defaultPHPCfg);
+        default = { };
+        description = "Extra PHP INI options such as `memory_limit`, `max_execution_time`, etc.";
+      };
+
+      user = mkOption {
+        type = types.nonEmptyStr;
+        default = "movim";
+        description = "User running Movim service";
+      };
+
+      group = mkOption {
+        type = types.nonEmptyStr;
+        default = "movim";
+        description = "Group running Movim service";
+      };
+
+      dataDir = mkOption {
+        type = types.nonEmptyStr;
+        default = "/var/lib/movim";
+        description = "State directory of the `movim` user which holds the application’s state & data.";
+      };
+
+      logDir = mkOption {
+        type = types.nonEmptyStr;
+        default = "/var/log/movim";
+        description = "Log directory of the `movim` user which holds the application’s logs.";
+      };
+
+      runtimeDir = mkOption {
+        type = types.nonEmptyStr;
+        default = "/run/movim";
+        description = "Runtime directory of the `movim` user which holds the application’s caches & temporary files.";
+      };
+
+      domain = mkOption {
+        type = types.nonEmptyStr;
+        description = "Fully-qualified domain name (FQDN) for the Movim instance.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8080;
+        description = "Movim daemon port.";
+      };
+
+      debug = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Debugging logs.";
+      };
+
+      verbose = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Verbose logs.";
+      };
+
+      minifyStaticFiles = mkOption {
+        type = with types; either bool (submodule {
+          options = {
+            script = mkOption {
+              type = types.submodule {
+                options = {
+                  enable = mkEnableOption "Script minification";
+                  package = mkPackageOption pkgs "esbuild" { };
+                  target = mkOption {
+                    type = with types; nullOr nonEmptyStr;
+                    default = null;
+                  };
+                };
+              };
+            };
+            style = mkOption {
+              type = types.submodule {
+                options = {
+                  enable = mkEnableOption "Script minification";
+                  package = mkPackageOption pkgs "lightningcss" { };
+                  target = mkOption {
+                    type = with types; nullOr nonEmptyStr;
+                    default = null;
+                  };
+                };
+              };
+            };
+            svg = mkOption {
+              type = types.submodule {
+                options = {
+                  enable = mkEnableOption "SVG minification";
+                  package = mkPackageOption pkgs "scour" { };
+                };
+              };
+            };
+          };
+        });
+        default = true;
+        description = "Do minification on public static files";
+      };
+
+      precompressStaticFiles = mkOption {
+        type = with types; submodule {
+          options = {
+            brotli = {
+              enable = mkEnableOption "Brotli precompression";
+              package = mkPackageOption pkgs "brotli" { };
+              compressionLevel = mkOption {
+                type = types.ints.between 0 11;
+                default = 11;
+                description = "Brotli compression level";
+              };
+            };
+            gzip = {
+              enable = mkEnableOption "Gzip precompression";
+              package = mkPackageOption pkgs "gzip" { };
+              compressionLevel = mkOption {
+                type = types.ints.between 1 9;
+                default = 9;
+                description = "Gzip compression level";
+              };
+            };
+          };
+        };
+        default = {
+          brotli.enable = true;
+          gzip.enable = false;
+        };
+        description = "Aggressively precompress static files";
+      };
+
+      podConfig = mkOption {
+        type = types.submodule {
+          options = {
+            info = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "Content of the info box on the login page";
+            };
+
+            description = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "General description of the instance";
+            };
+
+            timezone = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The server timezone";
+            };
+
+            restrictsuggestions = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = "Only suggest chatrooms, Communities and other contents that are available on the user XMPP server and related services";
+            };
+
+            chatonly = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = "Disable all the social feature (Communities, Blog…) and keep only the chat ones";
+            };
+
+            disableregistration = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = "Remove the XMPP registration flow and buttons from the interface";
+            };
+
+            loglevel = mkOption {
+              type = with types; nullOr (ints.between 0 3);
+              default = null;
+              description = "The server loglevel";
+            };
+
+            locale = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The server main locale";
+            };
+
+            xmppdomain = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The default XMPP server domain";
+            };
+
+            xmppdescription = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The default XMPP server description";
+            };
+
+            xmppwhitelist = mkOption {
+              type = with types; nullOr str;
+              default = null;
+              description = "The allowlisted XMPP servers";
+            };
+          };
+        };
+        default = { };
+        description = ''
+          Pod configuration (values from `php daemon.php config --help`).
+          Note that these values will now be disabled in the admin panel.
+        '';
+      };
+
+      settings = mkOption {
+        type = with types; attrsOf (nullOr (oneOf [ int str bool ]));
+        default = { };
+        description = ".env settings for Movim. Secrets should use `secretFile` option instead. `null`s will be culled.";
+      };
+
+      secretFile = mkOption {
+        type = with types; nullOr path;
+        default = null;
+        description = "The secret file to be sourced for the .env settings.";
+      };
+
+      database = {
+        type = mkOption {
+          type = types.enum [ "mysql" "postgresql" ];
+          example = "mysql";
+          default = "postgresql";
+          description = "Database engine to use.";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "movim";
+          description = "Database name.";
+        };
+
+        user = mkOption {
+          type = types.str;
+          default = "movim";
+          description = "Database username.";
+        };
+
+        createLocally = mkOption {
+          type = types.bool;
+          default = true;
+          description = "local database using UNIX socket authentication";
+        };
+      };
+
+      nginx = mkOption {
+        type = with types; nullOr (submodule
+          (import ../web-servers/nginx/vhost-options.nix {
+            inherit config lib;
+          }));
+        default = null;
+        example = lib.literalExpression /* nginx */ ''
+          {
+            serverAliases = [
+              "pics.''${config.networking.domain}"
+            ];
+            enableACME = true;
+            forceHttps = true;
+          }
+        '';
+        description = ''
+          With this option, you can customize an nginx virtual host which already has sensible defaults for Movim.
+          Set to `{ }` if you do not need any customization to the virtual host.
+          If enabled, then by default, the {option}`serverName` is `''${domain}`,
+          If this is set to null (the default), no nginx virtualHost will be configured.
+        '';
+      };
+
+      poolConfig = mkOption {
+        type = with types; attrsOf (oneOf [ int str bool ]);
+        default = { };
+        description = "Options for Movim’s PHP-FPM pool.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    users = {
+      users = {
+        movim = mkIf (cfg.user == "movim") {
+          isSystemUser = true;
+          group = cfg.group;
+        };
+        "${config.services.nginx.user}".extraGroups = [ cfg.group ];
+      };
+      groups = {
+        ${cfg.group} = { };
+      };
+    };
+
+    services = {
+      movim = {
+        settings = mkMerge [
+          {
+            DAEMON_URL = "//${cfg.domain}";
+            DAEMON_PORT = cfg.port;
+            DAEMON_INTERFACE = "127.0.0.1";
+            DAEMON_DEBUG = cfg.debug;
+            DAEMON_VERBOSE = cfg.verbose;
+          }
+          (mkIf cfg.database.createLocally {
+            DB_DRIVER = {
+              "postgresql" = "pgsql";
+              "mysql" = "mysql";
+            }.${cfg.database.type};
+            DB_HOST = "localhost";
+            DB_PORT = config.services.${cfg.database.type}.settings.port;
+            DB_DATABASE = cfg.database.name;
+            DB_USERNAME = cfg.database.user;
+            DB_PASSWORD = "";
+          })
+        ];
+
+        poolConfig = lib.mapAttrs' (n: v: lib.nameValuePair n (lib.mkDefault v)) {
+          "pm" = "dynamic";
+          "php_admin_value[error_log]" = "stderr";
+          "php_admin_flag[log_errors]" = true;
+          "catch_workers_output" = true;
+          "pm.max_children" = 32;
+          "pm.start_servers" = 2;
+          "pm.min_spare_servers" = 2;
+          "pm.max_spare_servers" = 8;
+          "pm.max_requests" = 500;
+        };
+      };
+
+      nginx = mkIf (cfg.nginx != null) {
+        enable = true;
+        recommendedOptimisation = true;
+        recommendedGzipSettings = true;
+        recommendedBrotliSettings = true;
+        recommendedProxySettings = true;
+        # TODO: recommended cache options already in Nginxâ‡
+        appendHttpConfig = /* nginx */ ''
+          fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nginx_cache:100m inactive=60m;
+          fastcgi_cache_key "$scheme$request_method$host$request_uri";
+        '';
+        virtualHosts."${cfg.domain}" = mkMerge [
+          cfg.nginx
+          {
+            root = lib.mkForce "${package}/share/php/movim/public";
+            locations = {
+              "/favicon.ico" = {
+                priority = 100;
+                extraConfig = /* nginx */ ''
+                  access_log off;
+                  log_not_found off;
+                '';
+              };
+              "/robots.txt" = {
+                priority = 100;
+                extraConfig = /* nginx */ ''
+                  access_log off;
+                  log_not_found off;
+                '';
+              };
+              "~ /\\.(?!well-known).*" = {
+                priority = 210;
+                extraConfig = /* nginx */ ''
+                  deny all;
+                '';
+              };
+              # Ask nginx to cache every URL starting with "/picture"
+              "/picture" = {
+                priority = 400;
+                tryFiles = "$uri $uri/ /index.php$is_args$args";
+                extraConfig = /* nginx */ ''
+                  set $no_cache 0; # Enable cache only there
+                '';
+              };
+              "/" = {
+                priority = 490;
+                tryFiles = "$uri $uri/ /index.php$is_args$args";
+                extraConfig = /* nginx */ ''
+                  # https://github.com/movim/movim/issues/314
+                  add_header Content-Security-Policy "default-src 'self'; img-src 'self' aesgcm: https:; media-src 'self' aesgcm: https:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
+                  set $no_cache 1;
+                '';
+              };
+              "~ \\.php$" = {
+                priority = 500;
+                tryFiles = "$uri =404";
+                extraConfig = /* nginx */ ''
+                  include ${config.services.nginx.package}/conf/fastcgi.conf;
+                  add_header X-Cache $upstream_cache_status;
+                  fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
+                  fastcgi_cache nginx_cache;
+                  fastcgi_cache_valid any 7d;
+                  fastcgi_cache_bypass $no_cache;
+                  fastcgi_no_cache $no_cache;
+                  fastcgi_split_path_info ^(.+\.php)(/.+)$;
+                  fastcgi_index index.php;
+                  fastcgi_pass unix:${fpm.socket};
+                '';
+              };
+              "/ws/" = {
+                priority = 900;
+                proxyPass = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/";
+                proxyWebsockets = true;
+                recommendedProxySettings = true;
+                extraConfig = /* nginx */ ''
+                  proxy_set_header X-Forwarded-Proto $scheme;
+                  proxy_redirect off;
+                '';
+              };
+            };
+            extraConfig = /* ngnix */ ''
+              index index.php;
+            '';
+          }
+        ];
+      };
+
+      mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mysql") {
+        enable = mkDefault true;
+        package = mkDefault pkgs.mariadb;
+        ensureDatabases = [ cfg.database.name ];
+        ensureUsers = [{
+          name = cfg.user;
+          ensureDBOwnership = true;
+        }];
+      };
+
+      postgresql = mkIf (cfg.database.createLocally && cfg.database.type == "postgresql") {
+        enable = mkDefault true;
+        ensureDatabases = [ cfg.database.name ];
+        ensureUsers = [{
+          name = cfg.user;
+          ensureDBOwnership = true;
+        }];
+        authentication = ''
+          host ${cfg.database.name} ${cfg.database.user} localhost trust
+        '';
+      };
+
+      phpfpm.pools.${pool} =
+        let
+          socketOwner =
+            if (cfg.nginx != null)
+            then config.services.nginx.user
+            else cfg.user;
+        in
+        {
+          phpPackage = package.php;
+          user = cfg.user;
+          group = cfg.group;
+
+          phpOptions = ''
+            error_log = 'stderr'
+            log_errors = on
+          '';
+
+          settings = {
+            "listen.owner" = socketOwner;
+            "listen.group" = cfg.group;
+            "listen.mode" = "0660";
+            "catch_workers_output" = true;
+          } // cfg.poolConfig;
+        };
+    };
+
+    systemd = {
+      services.movim-data-setup = {
+        description = "Movim setup: .env file, databases init, cache reload";
+        wantedBy = [ "multi-user.target" ];
+        requiredBy = [ "${phpExecutionUnit}.service" ];
+        before = [ "${phpExecutionUnit}.service" ];
+        after = lib.optional cfg.database.createLocally dbService;
+        requires = lib.optional cfg.database.createLocally dbService;
+
+        serviceConfig = {
+          Type = "oneshot";
+          User = cfg.user;
+          Group = cfg.group;
+          UMask = "077";
+        } // lib.optionalAttrs (cfg.secretFile != null) {
+          LoadCredential = "env-secrets:${cfg.secretFile}";
+        };
+
+        script = ''
+          # Env vars
+          rm -f ${cfg.dataDir}/.env
+          cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env
+          echo -e '\n' >> ${cfg.dataDir}/.env
+          if [[ -f "$CREDENTIALS_DIRECTORY/env-secrets"  ]]; then
+            cat "$CREDENTIALS_DIRECTORY/env-secrets" >> ${cfg.dataDir}/.env
+            echo -e '\n' >> ${cfg.dataDir}/.env
+          fi
+
+          # Caches, logs
+          mkdir -p ${cfg.dataDir}/public/cache ${cfg.logDir} ${cfg.runtimeDir}/cache
+          chmod -R ug+rw ${cfg.dataDir}/public/cache
+          chmod -R ug+rw ${cfg.logDir}
+          chmod -R ug+rwx ${cfg.runtimeDir}/cache
+
+          # Migrations
+          MOVIM_VERSION="${package.version}"
+          if [[ ! -f "${cfg.dataDir}/.migration-version" ]] || [[ "$MOVIM_VERSION" != "$(<${cfg.dataDir}/.migration-version)" ]]; then
+            ${package}/bin/movim-composer movim:migrate && echo $MOVIM_VERSION > ${cfg.dataDir}/.migration-version
+          fi
+        ''
+        + lib.optionalString (podConfigFlags != "") (
+          let
+            flags = lib.concatStringsSep " "
+              ([ "--no-interaction" ]
+                ++ lib.optional cfg.debug "-vvv"
+                ++ lib.optional (!cfg.debug && cfg.verbose) "-v");
+          in
+          ''
+            ${lib.getExe package} config ${podConfigFlags}
+          ''
+        );
+      };
+
+      services.movim = {
+        description = "Movim daemon";
+        wantedBy = [ "multi-user.target" ];
+        after = [ "movim-data-setup.service" ];
+        requires = [ "movim-data-setup.service" ]
+          ++ lib.optional cfg.database.createLocally dbService;
+        environment = {
+          PUBLIC_URL = "//${cfg.domain}";
+          WS_PORT = builtins.toString cfg.port;
+        };
+
+        serviceConfig = {
+          User = cfg.user;
+          Group = cfg.group;
+          WorkingDirectory = "${package}/share/php/movim";
+          ExecStart = "${lib.getExe package} start";
+        };
+      };
+
+      services.${phpExecutionUnit} = {
+        after = [ "movim-data-setup.service" ];
+        requires = [ "movim-data-setup.service" ]
+          ++ lib.optional cfg.database.createLocally dbService;
+      };
+
+      tmpfiles.settings."10-movim" = with cfg; {
+        "${dataDir}".d = { inherit user group; mode = "0710"; };
+        "${dataDir}/public".d = { inherit user group; mode = "0750"; };
+        "${dataDir}/public/cache".d = { inherit user group; mode = "0750"; };
+        "${runtimeDir}".d = { inherit user group; mode = "0700"; };
+        "${runtimeDir}/cache".d = { inherit user group; mode = "0700"; };
+        "${logDir}".d = { inherit user group; mode = "0700"; };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/nextcloud.md b/nixos/modules/services/web-apps/nextcloud.md
index 5db83d7e44634..06a8712b0b8ae 100644
--- a/nixos/modules/services/web-apps/nextcloud.md
+++ b/nixos/modules/services/web-apps/nextcloud.md
@@ -25,7 +25,7 @@ to `true`, Nextcloud will automatically be configured to connect to it through
 socket.
 
 A very basic configuration may look like this:
-```
+```nix
 { pkgs, ... }:
 {
   services.nextcloud = {
@@ -130,7 +130,7 @@ settings `listen.owner` &amp; `listen.group` in the
 [corresponding `phpfpm` pool](#opt-services.phpfpm.pools).
 
 An exemplary configuration may look like this:
-```
+```nix
 { config, lib, pkgs, ... }: {
   services.nginx.enable = false;
   services.nextcloud = {
@@ -205,7 +205,7 @@ If major-releases will be abandoned by upstream, we should check first if those
 in NixOS for a safe upgrade-path before removing those. In that case we should keep those
 packages, but mark them as insecure in an expression like this (in
 `<nixpkgs/pkgs/servers/nextcloud/default.nix>`):
-```
+```nix
 /* ... */
 {
   nextcloud17 = generic {
diff --git a/nixos/modules/services/web-apps/ocis.md b/nixos/modules/services/web-apps/ocis.md
new file mode 100644
index 0000000000000..9156e927ed2d4
--- /dev/null
+++ b/nixos/modules/services/web-apps/ocis.md
@@ -0,0 +1,113 @@
+# ownCloud Infinite Scale {#module-services-ocis}
+
+[ownCloud Infinite Scale](https://owncloud.dev/ocis/) (oCIS) is an open-source,
+modern file-sync and sharing platform. It is a ground-up rewrite of the well-known PHP based ownCloud server.
+
+The server setup can be automated using
+[services.ocis](#opt-services.ocis.enable). The desktop client is packaged at
+`pkgs.owncloud-client`.
+
+## Basic usage {#module-services-ocis-basic-usage}
+
+oCIS is a golang application and does not require an HTTP server (such as nginx)
+in front of it, though you may optionally use one if you will.
+
+oCIS is configured using a combination of yaml and environment variables. It is
+recommended to familiarize yourself with upstream's available configuration
+options and deployment instructions:
+
+* [Getting Started](https://owncloud.dev/ocis/getting-started/)
+* [Configuration](https://owncloud.dev/ocis/config/)
+* [Basic Setup](https://owncloud.dev/ocis/deployment/basic-remote-setup/)
+
+A very basic configuration may look like this:
+```
+{ pkgs, ... }:
+{
+  services.ocis = {
+    enable = true;
+    configDir = "/etc/ocis/config";
+  };
+}
+```
+
+This will start the oCIS server and make it available at `https://localhost:9200`
+
+However to make this configuration work you will need generate a configuration.
+You can do this with:
+
+```console
+$ nix-shell -p ocis-bin
+$ mkdir scratch/
+$ cd scratch/
+$ ocis init --config-path . --admin-password "changeme"
+```
+
+You may need to pass `--insecure true` or provide the `OCIS_INSECURE = true;` to
+[`services.ocis.environment`][mod-envFile], if TLS certificates are generated
+and managed externally (e.g. if you are using oCIS behind reverse proxy).
+
+If you want to manage the config file in your nix configuration, then it is
+encouraged to use a secrets manager like sops-nix or agenix.
+
+Be careful not to write files containing secrets to the globally readable nix
+store.
+
+Please note that current NixOS module for oCIS is configured to run in `fullstack`
+mode, which starts all the services for owncloud on single instance. This will
+start multiple ocis services and listen on multiple other ports.
+
+Current known services and their ports are as below:
+
+| Service            | Group   |  Port |
+|--------------------|---------|-------|
+| gateway            | api     |  9142 |
+| sharing            | api     |  9150 |
+| app-registry       | api     |  9242 |
+| ocdav              | web     | 45023 |
+| auth-machine       | api     |  9166 |
+| storage-system     | api     |  9215 |
+| webdav             | web     |  9115 |
+| webfinger          | web     | 46871 |
+| storage-system     | web     |  9216 |
+| web                | web     |  9100 |
+| eventhistory       | api     | 33177 |
+| ocs                | web     |  9110 |
+| storage-publiclink | api     |  9178 |
+| settings           | web     |  9190 |
+| ocm                | api     |  9282 |
+| settings           | api     |  9191 |
+| ocm                | web     |  9280 |
+| app-provider       | api     |  9164 |
+| storage-users      | api     |  9157 |
+| auth-service       | api     |  9199 |
+| thumbnails         | web     |  9186 |
+| thumbnails         | api     |  9185 |
+| storage-shares     | api     |  9154 |
+| sse                | sse     | 46833 |
+| userlog            | userlog | 45363 |
+| search             | api     |  9220 |
+| proxy              | web     |  9200 |
+| idp                | web     |  9130 |
+| frontend           | web     |  9140 |
+| groups             | api     |  9160 |
+| graph              | graph   |  9120 |
+| users              | api     |  9144 |
+| auth-basic         | api     |  9146 |
+
+## Configuration via environment variables
+
+You can also eschew the config file entirely and pass everything to oCIS via
+environment variables. For this make use of
+[`services.ocis.environment`][mod-env] for non-sensitive
+values, and
+[`services.ocis.environmentFile`][mod-envFile] for
+sensitive values.
+
+Configuration in (`services.ocis.environment`)[mod-env] overrides those from
+[`services.ocis.environmentFile`][mod-envFile] and will have highest
+precedence
+
+
+[mod-env]: #opt-services.ocis.environment
+[mod-envFile]: #opt-services.ocis.environmentFile
diff --git a/nixos/modules/services/web-apps/ocis.nix b/nixos/modules/services/web-apps/ocis.nix
new file mode 100644
index 0000000000000..b3ffec9ad9c19
--- /dev/null
+++ b/nixos/modules/services/web-apps/ocis.nix
@@ -0,0 +1,201 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  inherit (lib) types;
+  cfg = config.services.ocis;
+  defaultUser = "ocis";
+  defaultGroup = defaultUser;
+in
+{
+  options = {
+    services.ocis = {
+      enable = lib.mkEnableOption "ownCloud Infinite Scale";
+
+      package = lib.mkPackageOption pkgs "ocis-bin" { };
+
+      configDir = lib.mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/var/lib/ocis/config";
+        description = lib.mdDoc ''
+          Path to directory containing oCIS config file.
+
+          Example config can be generated by `ocis init --config-path fileName --admin-password "adminPass"`.
+          Add `--insecure true` if SSL certificates are generated and managed externally (e.g. using oCIS behind reverse proxy).
+
+          Note: This directory must contain at least a `ocis.yaml`. Ensure
+          [user](#opt-services.ocis.user) has read/write access to it. In some
+          circumstances you may need to add additional oCIS configuration files (e.g.,
+          `proxy.yaml`) to this directory.
+        '';
+      };
+
+      environmentFile = lib.mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        example = "/run/keys/ocis.env";
+        description = lib.mdDoc ''
+          An environment file as defined in {manpage}`systemd.exec(5)`.
+
+          Configuration provided in this file will override those from [configDir](#opt-services.ocis.configDir)/ocis.yaml.
+        '';
+      };
+
+      user = lib.mkOption {
+        type = types.str;
+        default = defaultUser;
+        example = "yourUser";
+        description = lib.mdDoc ''
+          The user to run oCIS as.
+          By default, a user named `${defaultUser}` will be created whose home
+          directory is [stateDir](#opt-services.ocis.stateDir).
+        '';
+      };
+
+      group = lib.mkOption {
+        type = types.str;
+        default = defaultGroup;
+        example = "yourGroup";
+        description = lib.mdDoc ''
+          The group to run oCIS under.
+          By default, a group named `${defaultGroup}` will be created.
+        '';
+      };
+
+      address = lib.mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = "Web interface address.";
+      };
+
+      port = lib.mkOption {
+        type = types.port;
+        default = 9200;
+        description = "Web interface port.";
+      };
+
+      url = lib.mkOption {
+        type = types.str;
+        default = "https://localhost:9200";
+        example = "https://some-hostname-or-ip:9200";
+        description = "Web interface address.";
+      };
+
+      stateDir = lib.mkOption {
+        default = "/var/lib/ocis";
+        type = types.str;
+        description = "ownCloud data directory.";
+      };
+
+      environment = lib.mkOption {
+        type = types.attrsOf types.str;
+        default = { };
+        description = lib.mdDoc ''
+          Extra config options.
+
+          See [the documentation](https://doc.owncloud.com/ocis/next/deployment/services/services.html) for available options.
+          See [notes for environment variables](https://doc.owncloud.com/ocis/next/deployment/services/env-var-note.html) for more information.
+
+          Note that all the attributes here will be copied to /nix/store/ and will be world readable. Options like *_PASSWORD or *_SECRET should be part of     [environmentFile](#opt-services.ocis.environmentFile) instead, and are only provided here for illustrative purpose.
+
+          Configuration here will override those from [environmentFile](#opt-services.ocis.environmentFile) and will have highest precedence, at the cost of security. Do NOT put security sensitive stuff here.
+        '';
+        example = {
+          OCIS_INSECURE = "false";
+          OCIS_LOG_LEVEL = "error";
+          OCIS_JWT_SECRET = "super_secret";
+          OCIS_TRANSFER_SECRET = "foo";
+          OCIS_MACHINE_AUTH_API_KEY = "foo";
+          OCIS_SYSTEM_USER_ID = "123";
+          OCIS_MOUNT_ID = "123";
+          OCIS_STORAGE_USERS_MOUNT_ID = "123";
+          GATEWAY_STORAGE_USERS_MOUNT_ID = "123";
+          CS3_ALLOW_INSECURE = "true";
+          OCIS_INSECURE_BACKENDS = "true";
+          TLS_INSECURE = "true";
+          TLS_SKIP_VERIFY_CLIENT_CERT = "true";
+          WEBDAV_ALLOW_INSECURE = "true";
+          IDP_TLS = "false";
+          GRAPH_APPLICATION_ID = "1234";
+          IDM_IDPSVC_PASSWORD = "password";
+          IDM_REVASVC_PASSWORD = "password";
+          IDM_SVC_PASSWORD = "password";
+          IDP_ISS = "https://localhost:9200";
+          OCIS_LDAP_BIND_PASSWORD = "password";
+          OCIS_SERVICE_ACCOUNT_ID = "foo";
+          OCIS_SERVICE_ACCOUNT_SECRET = "foo";
+          OCIS_SYSTEM_USER_API_KEY = "foo";
+          STORAGE_USERS_MOUNT_ID = "123";
+        };
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    users.users.${defaultUser} = lib.mkIf (cfg.user == defaultUser) {
+      group = cfg.group;
+      home = cfg.stateDir;
+      isSystemUser = true;
+      createHome = true;
+      description = "ownCloud Infinite Scale daemon user";
+    };
+
+    users.groups = lib.mkIf (cfg.group == defaultGroup) { ${defaultGroup} = { }; };
+
+    systemd = {
+      services.ocis = {
+        description = "ownCloud Infinite Scale Stack";
+        wantedBy = [ "multi-user.target" ];
+        environment = {
+          PROXY_HTTP_ADDR = "${cfg.address}:${toString cfg.port}";
+          OCIS_URL = cfg.url;
+          OCIS_CONFIG_DIR = if (cfg.configDir == null) then "${cfg.stateDir}/config" else cfg.configDir;
+          OCIS_BASE_DATA_PATH = cfg.stateDir;
+        } // cfg.environment;
+        serviceConfig = {
+          Type = "simple";
+          ExecStart = "${lib.getExe cfg.package} server";
+          WorkingDirectory = cfg.stateDir;
+          User = cfg.user;
+          Group = cfg.group;
+          Restart = "always";
+          EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
+          ReadWritePaths = [ cfg.stateDir ];
+          ReadOnlyPaths = [ cfg.configDir ];
+          MemoryDenyWriteExecute = true;
+          NoNewPrivileges = true;
+          PrivateTmp = true;
+          PrivateDevices = true;
+          ProtectSystem = "strict";
+          ProtectHome = true;
+          ProtectControlGroups = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectKernelLogs = true;
+          RestrictAddressFamilies = [
+            "AF_UNIX"
+            "AF_INET"
+            "AF_INET6"
+            "AF_NETLINK"
+          ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          LockPersonality = true;
+          SystemCallArchitectures = "native";
+        };
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [
+    bhankas
+    danth
+    ramblurr
+  ];
+}
diff --git a/nixos/modules/services/web-apps/outline.nix b/nixos/modules/services/web-apps/outline.nix
index 702755dfa2ab8..891d78d4045b4 100644
--- a/nixos/modules/services/web-apps/outline.nix
+++ b/nixos/modules/services/web-apps/outline.nix
@@ -783,6 +783,8 @@ in
         # This working directory is required to find stuff like the set of
         # onboarding files:
         WorkingDirectory = "${cfg.package}/share/outline";
+        # In case this directory is not in /var/lib/outline, it needs to be made writable explicitly
+        ReadWritePaths = lib.mkIf (cfg.storage.storageType == "local") [ cfg.storage.localRootDir ];
       };
     };
   };
diff --git a/nixos/modules/services/web-apps/peertube.nix b/nixos/modules/services/web-apps/peertube.nix
index 39c02c81c4231..76f8699135922 100644
--- a/nixos/modules/services/web-apps/peertube.nix
+++ b/nixos/modules/services/web-apps/peertube.nix
@@ -61,18 +61,16 @@ let
     eval -- "\$@"
   '';
 
-  peertubeCli = pkgs.writeShellScriptBin "peertube" ''
-    node ~/dist/server/tools/peertube.js $@
+  nginxCommonHeaders = lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.forceSSL ''
+    add_header Strict-Transport-Security 'max-age=31536000';
+  '' + lib.optionalString (config.services.nginx.virtualHosts.${cfg.localDomain}.quic && config.services.nginx.virtualHosts.${cfg.localDomain}.http3) ''
+    add_header Alt-Svc 'h3=":$server_port"; ma=604800';
   '';
 
-  nginxCommonHeaders = lib.optionalString cfg.enableWebHttps ''
-    add_header Strict-Transport-Security      'max-age=63072000; includeSubDomains';
-  '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 ''
-    add_header Alt-Svc                        'h3=":443"; ma=86400';
-  '' + ''
-    add_header Access-Control-Allow-Origin    '*';
-    add_header Access-Control-Allow-Methods   'GET, OPTIONS';
-    add_header Access-Control-Allow-Headers   'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
+  nginxCommonHeadersExtra = ''
+    add_header Access-Control-Allow-Origin '*';
+    add_header Access-Control-Allow-Methods 'GET, OPTIONS';
+    add_header Access-Control-Allow-Headers 'Range,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
   '';
 
 in {
@@ -330,6 +328,8 @@ in {
       }
     ];
 
+    environment.systemPackages = [ cfg.package.cli ];
+
     services.peertube.settings = lib.mkMerge [
       {
         listen = {
@@ -355,12 +355,13 @@ in {
           tmp_persistent = lib.mkDefault "/var/lib/peertube/storage/tmp_persistent/";
           bin = lib.mkDefault "/var/lib/peertube/storage/bin/";
           avatars = lib.mkDefault "/var/lib/peertube/storage/avatars/";
-          videos = lib.mkDefault "/var/lib/peertube/storage/videos/";
+          web_videos = lib.mkDefault "/var/lib/peertube/storage/web-videos/";
           streaming_playlists = lib.mkDefault "/var/lib/peertube/storage/streaming-playlists/";
           redundancy = lib.mkDefault "/var/lib/peertube/storage/redundancy/";
           logs = lib.mkDefault "/var/lib/peertube/storage/logs/";
           previews = lib.mkDefault "/var/lib/peertube/storage/previews/";
           thumbnails = lib.mkDefault "/var/lib/peertube/storage/thumbnails/";
+          storyboards = lib.mkDefault "/var/lib/peertube/storage/storyboards/";
           torrents = lib.mkDefault "/var/lib/peertube/storage/torrents/";
           captions = lib.mkDefault "/var/lib/peertube/storage/captions/";
           cache = lib.mkDefault "/var/lib/peertube/storage/cache/";
@@ -428,7 +429,7 @@ in {
 
       environment = env;
 
-      path = with pkgs; [ bashInteractive ffmpeg nodejs_18 openssl yarn python3 ];
+      path = with pkgs; [ nodejs_18 yarn ffmpeg-headless openssl ];
 
       script = ''
         #!/bin/sh
@@ -456,7 +457,7 @@ in {
         ln -sf ${cfg.package}/config/default.yaml /var/lib/peertube/config/default.yaml
         ln -sf ${cfg.package}/client/dist -T /var/lib/peertube/www/client
         ln -sf ${cfg.settings.storage.client_overrides} -T /var/lib/peertube/www/client-overrides
-        npm start
+        node dist/server
       '';
       serviceConfig = {
         Type = "simple";
@@ -488,6 +489,9 @@ in {
 
     services.nginx = lib.mkIf cfg.configureNginx {
       enable = true;
+      upstreams."peertube".servers = {
+        "127.0.0.1:${toString cfg.listenHttp}".fail_timeout = "0";
+      };
       virtualHosts."${cfg.localDomain}" = {
         root = "/var/lib/peertube/www";
 
@@ -497,14 +501,14 @@ in {
           priority = 1110;
         };
 
-        locations."= /api/v1/videos/upload-resumable" = {
+        locations."~ ^/api/v1/videos/(upload-resumable|([^/]+/source/replace-resumable))$" = {
           tryFiles = "/dev/null @api";
           priority = 1120;
 
           extraConfig = ''
-            client_max_body_size                        0;
-            proxy_request_buffering                     off;
-          '';
+            client_max_body_size 0;
+            proxy_request_buffering off;
+          '' + nginxCommonHeaders;
         };
 
         locations."~ ^/api/v1/videos/(upload|([^/]+/studio/edit))$" = {
@@ -513,13 +517,11 @@ in {
           priority = 1130;
 
           extraConfig = ''
-            client_max_body_size                        12G;
-            add_header X-File-Maximum-Size              8G always;
-          '' + lib.optionalString cfg.enableWebHttps ''
-            add_header Strict-Transport-Security        'max-age=63072000; includeSubDomains';
-          '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 ''
-            add_header Alt-Svc                          'h3=":443"; ma=86400';
-          '';
+            limit_except POST HEAD { deny all; }
+
+            client_max_body_size 12G;
+            add_header X-File-Maximum-Size 8G always;
+          '' + nginxCommonHeaders;
         };
 
         locations."~ ^/api/v1/runners/jobs/[^/]+/(update|success)$" = {
@@ -528,13 +530,9 @@ in {
           priority = 1135;
 
           extraConfig = ''
-            client_max_body_size                        12G;
-            add_header X-File-Maximum-Size              8G always;
-          '' + lib.optionalString cfg.enableWebHttps ''
-            add_header Strict-Transport-Security        'max-age=63072000; includeSubDomains';
-          '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 ''
-            add_header Alt-Svc                          'h3=":443"; ma=86400';
-          '';
+            client_max_body_size 12G;
+            add_header X-File-Maximum-Size 8G always;
+          '' + nginxCommonHeaders;
         };
 
         locations."~ ^/api/v1/(videos|video-playlists|video-channels|users/me)" = {
@@ -542,32 +540,28 @@ in {
           priority = 1140;
 
           extraConfig = ''
-            client_max_body_size                        6M;
-            add_header X-File-Maximum-Size              4M always;
-          '' + lib.optionalString cfg.enableWebHttps ''
-            add_header Strict-Transport-Security        'max-age=63072000; includeSubDomains';
-          '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 ''
-            add_header Alt-Svc                          'h3=":443"; ma=86400';
-          '';
+            client_max_body_size 6M;
+            add_header X-File-Maximum-Size 4M always;
+          '' + nginxCommonHeaders;
         };
 
         locations."@api" = {
-          proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}";
+          proxyPass = "http://peertube";
           priority = 1150;
 
           extraConfig = ''
-            proxy_set_header X-Forwarded-For            $proxy_add_x_forwarded_for;
-            proxy_set_header Host                       $host;
-            proxy_set_header X-Real-IP                  $remote_addr;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 
-            proxy_connect_timeout                       10m;
+            proxy_connect_timeout 10m;
 
-            proxy_send_timeout                          10m;
-            proxy_read_timeout                          10m;
+            proxy_send_timeout 10m;
+            proxy_read_timeout 10m;
 
-            client_max_body_size                        100k;
-            send_timeout                                10m;
-          '';
+            client_max_body_size 100k;
+            send_timeout 10m;
+          ''+ nginxCommonHeaders;
         };
 
         # Websocket
@@ -581,7 +575,7 @@ in {
           priority = 1220;
 
           extraConfig = ''
-            proxy_read_timeout                          15m;
+            proxy_read_timeout 15m;
           '';
         };
 
@@ -591,84 +585,82 @@ in {
         };
 
         locations."@api_websocket" = {
-          proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}";
+          proxyPass = "http://peertube";
           priority = 1240;
 
           extraConfig = ''
-            proxy_set_header X-Forwarded-For            $proxy_add_x_forwarded_for;
-            proxy_set_header Host                       $host;
-            proxy_set_header X-Real-IP                  $remote_addr;
-            proxy_set_header Upgrade                    $http_upgrade;
-            proxy_set_header Connection                 'upgrade';
-
-            proxy_http_version                          1.1;
-          '';
+            proxy_http_version 1.1;
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection 'upgrade';
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+          '' + nginxCommonHeaders;
         };
 
         # Bypass PeerTube for performance reasons.
         locations."~ ^/client/(assets/images/(icons/icon-36x36\.png|icons/icon-48x48\.png|icons/icon-72x72\.png|icons/icon-96x96\.png|icons/icon-144x144\.png|icons/icon-192x192\.png|icons/icon-512x512\.png|logo\.svg|favicon\.png|default-playlist\.jpg|default-avatar-account\.png|default-avatar-account-48x48\.png|default-avatar-video-channel\.png|default-avatar-video-channel-48x48\.png))$" = {
           tryFiles = "/client-overrides/$1 /client/$1 $1";
           priority = 1310;
+
+          extraConfig = nginxCommonHeaders;
         };
 
         locations."~ ^/client/(.*\.(js|css|png|svg|woff2|otf|ttf|woff|eot))$" = {
           alias = "${cfg.package}/client/dist/$1";
           priority = 1320;
           extraConfig = ''
-            add_header Cache-Control                    'public, max-age=604800, immutable';
-          '' + lib.optionalString cfg.enableWebHttps ''
-            add_header Strict-Transport-Security        'max-age=63072000; includeSubDomains';
-          '' + lib.optionalString config.services.nginx.virtualHosts.${cfg.localDomain}.http3 ''
-            add_header Alt-Svc                          'h3=":443"; ma=86400';
-          '';
+            add_header Cache-Control 'public, max-age=604800, immutable';
+          '' + nginxCommonHeaders;
         };
 
         locations."^~ /download/" = {
-          proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}";
+          proxyPass = "http://peertube";
           priority = 1410;
           extraConfig = ''
-            proxy_set_header X-Forwarded-For            $proxy_add_x_forwarded_for;
-            proxy_set_header Host                       $host;
-            proxy_set_header X-Real-IP                  $remote_addr;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 
-            proxy_limit_rate                            5M;
-          '';
+            proxy_limit_rate 5M;
+          '' + nginxCommonHeaders;
         };
 
-        locations."^~ /static/streaming-playlists/private/" = {
-          proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}";
+        locations."^~ /static/streaming-playlists/hls/private/" = {
+          proxyPass = "http://peertube";
           priority = 1420;
           extraConfig = ''
-            proxy_set_header X-Forwarded-For            $proxy_add_x_forwarded_for;
-            proxy_set_header Host                       $host;
-            proxy_set_header X-Real-IP                  $remote_addr;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 
-            proxy_limit_rate                            5M;
-          '';
+            proxy_limit_rate 5M;
+          '' + nginxCommonHeaders;
         };
 
         locations."^~ /static/web-videos/private/" = {
-          proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}";
+          proxyPass = "http://peertube";
           priority = 1430;
           extraConfig = ''
-            proxy_set_header X-Forwarded-For            $proxy_add_x_forwarded_for;
-            proxy_set_header Host                       $host;
-            proxy_set_header X-Real-IP                  $remote_addr;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 
-            proxy_limit_rate                            5M;
-          '';
+            proxy_limit_rate 5M;
+          '' + nginxCommonHeaders;
         };
 
         locations."^~ /static/webseed/private/" = {
-          proxyPass = "http://127.0.0.1:${toString cfg.listenHttp}";
+          proxyPass = "http://peertube";
           priority = 1440;
           extraConfig = ''
-            proxy_set_header X-Forwarded-For            $proxy_add_x_forwarded_for;
-            proxy_set_header Host                       $host;
-            proxy_set_header X-Real-IP                  $remote_addr;
+            proxy_set_header Host $host;
+            proxy_set_header X-Real-IP $remote_addr;
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 
-            proxy_limit_rate                            5M;
-          '';
+            proxy_limit_rate 5M;
+          '' + nginxCommonHeaders;
         };
 
         locations."^~ /static/redundancy/" = {
@@ -676,33 +668,35 @@ in {
           root = cfg.settings.storage.redundancy;
           priority = 1450;
           extraConfig = ''
-            set $peertube_limit_rate                    800k;
+            set $peertube_limit_rate 800k;
 
             if ($request_uri ~ -fragmented.mp4$) {
-              set $peertube_limit_rate                  5M;
+              set $peertube_limit_rate 5M;
             }
 
             if ($request_method = 'OPTIONS') {
               ${nginxCommonHeaders}
-              add_header Access-Control-Max-Age         1728000;
-              add_header Content-Type                   'text/plain charset=UTF-8';
-              add_header Content-Length                 0;
-              return                                    204;
+              ${nginxCommonHeadersExtra}
+              add_header Access-Control-Max-Age 1728000;
+              add_header Content-Type 'text/plain charset=UTF-8';
+              add_header Content-Length 0;
+              return 204;
             }
             if ($request_method = 'GET') {
               ${nginxCommonHeaders}
+              ${nginxCommonHeadersExtra}
 
-              access_log                                off;
+              access_log off;
             }
 
-            aio                                         threads;
-            sendfile                                    on;
-            sendfile_max_chunk                          1M;
+            aio threads;
+            sendfile on;
+            sendfile_max_chunk 1M;
 
-            limit_rate                                  $peertube_limit_rate;
-            limit_rate_after                            5M;
+            limit_rate $peertube_limit_rate;
+            limit_rate_after 5M;
 
-            rewrite ^/static/redundancy/(.*)$           /$1 break;
+            rewrite ^/static/redundancy/(.*)$ /$1 break;
           '';
         };
 
@@ -711,109 +705,111 @@ in {
           root = cfg.settings.storage.streaming_playlists;
           priority = 1460;
           extraConfig = ''
-            set $peertube_limit_rate                    800k;
+            set $peertube_limit_rate 800k;
 
             if ($request_uri ~ -fragmented.mp4$) {
-              set $peertube_limit_rate                  5M;
+              set $peertube_limit_rate 5M;
             }
 
             if ($request_method = 'OPTIONS') {
               ${nginxCommonHeaders}
-              add_header Access-Control-Max-Age         1728000;
-              add_header Content-Type                   'text/plain charset=UTF-8';
-              add_header Content-Length                 0;
-              return                                    204;
+              ${nginxCommonHeadersExtra}
+              add_header Access-Control-Max-Age 1728000;
+              add_header Content-Type 'text/plain charset=UTF-8';
+              add_header Content-Length 0;
+              return 204;
             }
             if ($request_method = 'GET') {
               ${nginxCommonHeaders}
+              ${nginxCommonHeadersExtra}
 
-              access_log                                off;
+              access_log off;
             }
 
-            aio                                         threads;
-            sendfile                                    on;
-            sendfile_max_chunk                          1M;
+            aio threads;
+            sendfile on;
+            sendfile_max_chunk 1M;
 
-            limit_rate                                  $peertube_limit_rate;
-            limit_rate_after                            5M;
+            limit_rate $peertube_limit_rate;
+            limit_rate_after 5M;
 
-            rewrite ^/static/streaming-playlists/(.*)$  /$1 break;
+            rewrite ^/static/streaming-playlists/(.*)$ /$1 break;
           '';
         };
 
         locations."^~ /static/web-videos/" = {
           tryFiles = "$uri @api";
-          root = cfg.settings.storage.streaming_playlists;
+          root = cfg.settings.storage.web_videos;
           priority = 1470;
           extraConfig = ''
-            set $peertube_limit_rate                    800k;
+            set $peertube_limit_rate 800k;
 
             if ($request_uri ~ -fragmented.mp4$) {
-              set $peertube_limit_rate                  5M;
+              set $peertube_limit_rate 5M;
             }
 
             if ($request_method = 'OPTIONS') {
               ${nginxCommonHeaders}
-              add_header Access-Control-Max-Age         1728000;
-              add_header Content-Type                   'text/plain charset=UTF-8';
-              add_header Content-Length                 0;
-              return                                    204;
+              ${nginxCommonHeadersExtra}
+              add_header Access-Control-Max-Age 1728000;
+              add_header Content-Type 'text/plain charset=UTF-8';
+              add_header Content-Length 0;
+              return 204;
             }
             if ($request_method = 'GET') {
               ${nginxCommonHeaders}
+              ${nginxCommonHeadersExtra}
 
-              access_log                                off;
+              access_log off;
             }
 
-            aio                                         threads;
-            sendfile                                    on;
-            sendfile_max_chunk                          1M;
+            aio threads;
+            sendfile on;
+            sendfile_max_chunk 1M;
 
-            limit_rate                                  $peertube_limit_rate;
-            limit_rate_after                            5M;
+            limit_rate $peertube_limit_rate;
+            limit_rate_after 5M;
 
-            rewrite ^/static/streaming-playlists/(.*)$  /$1 break;
+            rewrite ^/static/web-videos/(.*)$ /$1 break;
           '';
         };
 
         locations."^~ /static/webseed/" = {
           tryFiles = "$uri @api";
-          root = cfg.settings.storage.videos;
+          root = cfg.settings.storage.web_videos;
           priority = 1480;
           extraConfig = ''
-            set $peertube_limit_rate                    800k;
+            set $peertube_limit_rate 800k;
 
             if ($request_uri ~ -fragmented.mp4$) {
-              set $peertube_limit_rate                  5M;
+              set $peertube_limit_rate 5M;
             }
 
             if ($request_method = 'OPTIONS') {
               ${nginxCommonHeaders}
-              add_header Access-Control-Max-Age         1728000;
-              add_header Content-Type                   'text/plain charset=UTF-8';
-              add_header Content-Length                 0;
-              return                                    204;
+              ${nginxCommonHeadersExtra}
+              add_header Access-Control-Max-Age 1728000;
+              add_header Content-Type 'text/plain charset=UTF-8';
+              add_header Content-Length 0;
+              return 204;
             }
             if ($request_method = 'GET') {
               ${nginxCommonHeaders}
+              ${nginxCommonHeadersExtra}
 
-              access_log                                off;
+              access_log off;
             }
 
-            aio                                         threads;
-            sendfile                                    on;
-            sendfile_max_chunk                          1M;
+            aio threads;
+            sendfile on;
+            sendfile_max_chunk 1M;
 
-            limit_rate                                  $peertube_limit_rate;
-            limit_rate_after                            5M;
+            limit_rate $peertube_limit_rate;
+            limit_rate_after 5M;
 
-            rewrite ^/static/webseed/(.*)$              /$1 break;
+            rewrite ^/static/webseed/(.*)$ /web-videos/$1 break;
           '';
         };
-
-        extraConfig = lib.optionalString cfg.enableWebHttps ''
-          add_header Strict-Transport-Security          'max-age=63072000; includeSubDomains';
-        '';
       };
     };
 
@@ -848,7 +844,7 @@ in {
           home = cfg.package;
         };
       })
-      (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ cfg.package peertubeEnv peertubeCli pkgs.ffmpeg pkgs.nodejs_18 pkgs.yarn ])
+      (lib.attrsets.setAttrByPath [ cfg.user "packages" ] [ peertubeEnv pkgs.nodejs_18 pkgs.yarn pkgs.ffmpeg-headless ])
       (lib.mkIf cfg.redis.enableUnixSocket {${config.services.peertube.user}.extraGroups = [ "redis-peertube" ];})
     ];
 
diff --git a/nixos/modules/services/web-apps/pict-rs.md b/nixos/modules/services/web-apps/pict-rs.md
index 2fa6bb3aebced..56c51e0d72594 100644
--- a/nixos/modules/services/web-apps/pict-rs.md
+++ b/nixos/modules/services/web-apps/pict-rs.md
@@ -7,7 +7,9 @@ pict-rs is a  a simple image hosting service.
 the minimum to start pict-rs is
 
 ```nix
-services.pict-rs.enable = true;
+{
+  services.pict-rs.enable = true;
+}
 ```
 
 this will start the http server on port 8080 by default.
diff --git a/nixos/modules/services/web-apps/plausible.md b/nixos/modules/services/web-apps/plausible.md
index 1328ce69441a0..d3673eabddd41 100644
--- a/nixos/modules/services/web-apps/plausible.md
+++ b/nixos/modules/services/web-apps/plausible.md
@@ -11,7 +11,7 @@ $ openssl rand -base64 64
 ```
 
 After that, `plausible` can be deployed like this:
-```
+```nix
 {
   services.plausible = {
     enable = true;
diff --git a/nixos/modules/services/web-apps/pretix.nix b/nixos/modules/services/web-apps/pretix.nix
new file mode 100644
index 0000000000000..22ee9769aa923
--- /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, a ticket shop application for conferences, festivals, concerts, etc.";
+
+    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/rss-bridge.nix b/nixos/modules/services/web-apps/rss-bridge.nix
index 1a710f4a6a67a..0d344753de678 100644
--- a/nixos/modules/services/web-apps/rss-bridge.nix
+++ b/nixos/modules/services/web-apps/rss-bridge.nix
@@ -5,10 +5,23 @@ let
 
   poolName = "rss-bridge";
 
-  whitelist = pkgs.writeText "rss-bridge_whitelist.txt"
-    (concatStringsSep "\n" cfg.whitelist);
+  configAttr = lib.recursiveUpdate { FileCache.path = "${cfg.dataDir}/cache/"; } cfg.config;
+  cfgHalf = lib.mapAttrsRecursive (path: value: let
+    envName = lib.toUpper ("RSSBRIDGE_" + lib.concatStringsSep "_" path);
+    envValue = if lib.isList value then
+      lib.concatStringsSep "," value
+    else if lib.isBool value then
+      lib.boolToString value
+    else
+      toString value;
+  in "fastcgi_param \"${envName}\" \"${envValue}\";") configAttr;
+  cfgEnv = lib.concatStringsSep "\n" (lib.collect lib.isString cfgHalf);
 in
 {
+  imports = [
+    (mkRenamedOptionModule [ "services" "rss-bridge" "whitelist" ] [ "services" "rss-bridge" "config" "system" "enabled_bridges" ])
+  ];
+
   options = {
     services.rss-bridge = {
       enable = mkEnableOption (lib.mdDoc "rss-bridge");
@@ -56,20 +69,26 @@ in
         '';
       };
 
-      whitelist = mkOption {
-        type = types.listOf types.str;
-        default = [];
+      config = mkOption {
+        type = with types; attrsOf (attrsOf (oneOf [ bool int str (listOf str) ]));
+        default = {};
+        defaultText = options.literalExpression "FileCache.path = \"\${config.services.rss-bridge.dataDir}/cache/\"";
         example = options.literalExpression ''
-          [
-            "Facebook"
-            "Instagram"
-            "Twitter"
-          ]
+          {
+            system.enabled_bridges = [ "*" ];
+            error = {
+              output = "http";
+              report_limit = 5;
+            };
+            FileCache = {
+              enable_purge = true;
+            };
+          }
         '';
         description = lib.mdDoc ''
-          List of bridges to be whitelisted.
-          If the list is empty, rss-bridge will use whitelist.default.txt.
-          Use `[ "*" ]` to whitelist all.
+          Attribute set of arbitrary config options.
+          Please consult the documentation at the [wiki](https://rss-bridge.github.io/rss-bridge/For_Hosts/Custom_Configuration.html)
+          and [sample config](https://github.com/RSS-Bridge/rss-bridge/blob/master/config.default.ini.php) to see a list of available options.
         '';
       };
     };
@@ -93,11 +112,16 @@ in
         };
       };
     };
-    systemd.tmpfiles.rules = [
-      "d '${cfg.dataDir}/cache' 0750 ${cfg.user} ${cfg.group} - -"
-      (mkIf (cfg.whitelist != []) "L+ ${cfg.dataDir}/whitelist.txt - - - - ${whitelist}")
-      "z '${cfg.dataDir}/config.ini.php' 0750 ${cfg.user} ${cfg.group} - -"
-    ];
+    systemd.tmpfiles.settings.rss-bridge = let
+      perm = {
+        mode = "0750";
+        user = cfg.user;
+        group = cfg.group;
+      };
+    in {
+      "${configAttr.FileCache.path}".d = perm;
+      "${cfg.dataDir}/config.ini.php".z = perm;
+    };
 
     services.nginx = mkIf (cfg.virtualHost != null) {
       enable = true;
@@ -116,6 +140,7 @@ in
               fastcgi_pass unix:${config.services.phpfpm.pools.${cfg.pool}.socket};
               fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
               fastcgi_param RSSBRIDGE_DATA ${cfg.dataDir};
+              ${cfgEnv}
             '';
           };
         };
diff --git a/nixos/modules/services/web-apps/silverbullet.nix b/nixos/modules/services/web-apps/silverbullet.nix
new file mode 100644
index 0000000000000..a0c6ee34d262f
--- /dev/null
+++ b/nixos/modules/services/web-apps/silverbullet.nix
@@ -0,0 +1,123 @@
+{ config
+, pkgs
+, lib
+, ...
+}:
+let
+  cfg = config.services.silverbullet;
+  defaultUser = "silverbullet";
+  defaultGroup = defaultUser;
+  defaultSpaceDir = "/var/lib/silverbullet";
+in
+{
+  options = {
+    services.silverbullet = {
+      enable = lib.mkEnableOption (lib.mdDoc "Silverbullet, an open-source, self-hosted, offline-capable Personal Knowledge Management (PKM) web application.");
+
+      package = lib.mkPackageOptionMD pkgs "silverbullet" { };
+
+      openFirewall = lib.mkOption {
+        type = lib.types.bool;
+        default = false;
+        description = lib.mdDoc "Open port in the firewall.";
+      };
+
+      listenPort = lib.mkOption {
+        type = lib.types.int;
+        default = 3000;
+        description = lib.mdDoc "Port to listen on.";
+      };
+
+      listenAddress = lib.mkOption {
+        type = lib.types.str;
+        default = "127.0.0.1";
+        description = lib.mdDoc "Address or hostname to listen on. Defaults to 127.0.0.1.";
+      };
+
+      spaceDir = lib.mkOption {
+        type = lib.types.path;
+        default = defaultSpaceDir;
+        example = "/home/yourUser/silverbullet";
+        description = lib.mdDoc ''
+          Folder to store Silverbullet's space/workspace.
+          By default it is located at `${defaultSpaceDir}`.
+        '';
+      };
+
+      user = lib.mkOption {
+        type = lib.types.str;
+        default = defaultUser;
+        example = "yourUser";
+        description = lib.mdDoc ''
+          The user to run Silverbullet as.
+          By default, a user named `${defaultUser}` will be created whose space
+          directory is [spaceDir](#opt-services.silverbullet.spaceDir).
+        '';
+      };
+
+      group = lib.mkOption {
+        type = lib.types.str;
+        default = defaultGroup;
+        example = "yourGroup";
+        description = lib.mdDoc ''
+          The group to run Silverbullet under.
+          By default, a group named `${defaultGroup}` will be created.
+        '';
+      };
+
+      envFile = lib.mkOption {
+        type = lib.types.nullOr lib.types.path;
+        default = null;
+        example = "/etc/silverbullet.env";
+        description = lib.mdDoc ''
+          File containing extra environment variables. For example:
+
+          ```
+          SB_USER=user:password
+          SB_AUTH_TOKEN=abcdefg12345
+          ```
+        '';
+      };
+
+      extraArgs = lib.mkOption {
+        type = lib.types.listOf lib.types.str;
+        default = [ ];
+        example = [ "--db /path/to/silverbullet.db" ];
+        description = lib.mdDoc "Extra arguments passed to silverbullet.";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.silverbullet = {
+      description = "Silverbullet service";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = lib.mkIf (!lib.hasPrefix "/var/lib/" cfg.spaceDir) "mkdir -p '${cfg.spaceDir}'";
+      serviceConfig = {
+        Type = "simple";
+        User = "${cfg.user}";
+        Group = "${cfg.group}";
+        EnvironmentFile = lib.mkIf (cfg.envFile != null) "${cfg.envFile}";
+        StateDirectory = lib.mkIf (lib.hasPrefix "/var/lib/" cfg.spaceDir) (lib.last (lib.splitString "/" cfg.spaceDir));
+        ExecStart = "${lib.getExe cfg.package} --port ${toString cfg.listenPort} --hostname '${cfg.listenAddress}' '${cfg.spaceDir}' " + lib.concatStringsSep " " cfg.extraArgs;
+        Restart = "on-failure";
+      };
+    };
+
+    networking.firewall = lib.mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.listenPort ];
+    };
+
+    users.users.${defaultUser} = lib.mkIf (cfg.user == defaultUser) {
+      isSystemUser = true;
+      group = cfg.group;
+      description = "Silverbullet daemon user";
+    };
+
+    users.groups.${defaultGroup} = lib.mkIf (cfg.group == defaultGroup) { };
+  };
+
+  meta.maintainers = with lib.maintainers; [ aorith ];
+}
diff --git a/nixos/modules/services/web-apps/slskd.nix b/nixos/modules/services/web-apps/slskd.nix
index 580f66ec3ac90..15a5fd1177adf 100644
--- a/nixos/modules/services/web-apps/slskd.nix
+++ b/nixos/modules/services/web-apps/slskd.nix
@@ -2,120 +2,248 @@
 
 let
   settingsFormat = pkgs.formats.yaml {};
+  defaultUser = "slskd";
 in {
   options.services.slskd = with lib; with types; {
     enable = mkEnableOption "enable slskd";
 
-    rotateLogs = mkEnableOption "enable an unit and timer that will rotate logs in /var/slskd/logs";
+    package = mkPackageOptionMD pkgs "slskd" { };
 
-    package = mkPackageOption pkgs "slskd" { };
+    user = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = "User account under which slskd runs.";
+    };
 
-    nginx = mkOption {
-      description = lib.mdDoc "options for nginx";
-      example = {
-        enable = true;
-        domain = "example.com";
-        contextPath = "/slskd";
-      };
-      type = submodule ({name, config, ...}: {
-        options = {
-          enable = mkEnableOption "enable nginx as a reverse proxy";
+    group = mkOption {
+      type = types.str;
+      default = defaultUser;
+      description = "Group under which slskd runs.";
+    };
 
-          domainName = mkOption {
-            type = str;
-            description = "Domain you want to use";
-          };
-          contextPath = mkOption {
-            type = types.path;
-            default = "/";
-            description = lib.mdDoc ''
-              The context path, i.e., the last part of the slskd
-              URL. Typically '/' or '/slskd'. Default '/'
-            '';
-          };
-        };
-      });
+    domain = mkOption {
+      type = types.nullOr types.str;
+      description = ''
+        If non-null, enables an nginx reverse proxy virtual host at this FQDN,
+        at the path configurated with `services.slskd.web.url_base`.
+      '';
+      example = "slskd.example.com";
+    };
+
+    nginx = mkOption {
+      type = types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; });
+      default = {};
+      example = lib.literalExpression ''
+        {
+          enableACME = true;
+          forceHttps = true;
+        }
+      '';
+      description = ''
+        This option customizes the nginx virtual host set up for slskd.
+      '';
     };
 
     environmentFile = mkOption {
       type = path;
       description = ''
-        Path to a file containing secrets.
-        It must at least contain the variable `SLSKD_SLSK_PASSWORD`
+        Path to the environment file sourced on startup.
+        It must at least contain the variables `SLSKD_SLSK_USERNAME` and `SLSKD_SLSK_PASSWORD`.
+        Web interface credentials should also be set here in `SLSKD_USERNAME` and `SLSKD_PASSWORD`.
+        Other, optional credentials like SOCKS5 with `SLSKD_SLSK_PROXY_USERNAME` and `SLSKD_SLSK_PROXY_PASSWORD`
+        should all reside here instead of in the world-readable nix store.
+        Variables are documented at https://github.com/slskd/slskd/blob/master/docs/config.md
       '';
     };
 
     openFirewall = mkOption {
       type = bool;
-      description = ''
-        Whether to open the firewall for services.slskd.settings.listen_port";
-      '';
+      description = "Whether to open the firewall for the soulseek network listen port (not the web interface port).";
       default = false;
     };
 
     settings = mkOption {
-      description = lib.mdDoc ''
-        Configuration for slskd, see
-        [available options](https://github.com/slskd/slskd/blob/master/docs/config.md)
-        `APP_DIR` is set to /var/lib/slskd, where default download & incomplete directories,
-        log and databases will be created.
+      description = ''
+        Application configuration for slskd. See
+        [documentation](https://github.com/slskd/slskd/blob/master/docs/config.md).
       '';
       default = {};
       type = submodule {
         freeformType = settingsFormat.type;
         options = {
+          remote_file_management = mkEnableOption "modification of share contents through the web ui";
+
+          flags = {
+            force_share_scan = mkOption {
+              type = bool;
+              description = "Force a rescan of shares on every startup.";
+            };
+            no_version_check = mkOption {
+              type = bool;
+              default = true;
+              visible = false;
+              description = "Don't perform a version check on startup.";
+            };
+          };
+
+          directories = {
+            incomplete = mkOption {
+              type = nullOr path;
+              description = "Directory where incomplete downloading files are stored.";
+              defaultText = "/var/lib/slskd/incomplete";
+              default = null;
+            };
+            downloads = mkOption {
+              type = nullOr path;
+              description = "Directory where downloaded files are stored.";
+              defaultText = "/var/lib/slskd/downloads";
+              default = null;
+            };
+          };
+
+          shares = {
+            directories = mkOption {
+              type = listOf str;
+              description = ''
+                Paths to shared directories. See
+                [documentation](https://github.com/slskd/slskd/blob/master/docs/config.md#directories)
+                for advanced usage.
+              '';
+              example = lib.literalExpression ''[ "/home/John/Music" "!/home/John/Music/Recordings" "[Music Drive]/mnt" ]'';
+            };
+            filters = mkOption {
+              type = listOf str;
+              example = lib.literalExpression ''[ "\.ini$" "Thumbs.db$" "\.DS_Store$" ]'';
+              description = "Regular expressions of files to exclude from sharing.";
+            };
+          };
+
+          rooms = mkOption {
+            type = listOf str;
+            description = "Chat rooms to join on startup.";
+          };
 
           soulseek = {
-            username = mkOption {
+            description = mkOption {
               type = str;
-              description = "Username on the Soulseek Network";
+              description = "The user description for the Soulseek network.";
+              defaultText = "A slskd user. https://github.com/slskd/slskd";
             };
             listen_port = mkOption {
               type = port;
-              description = "Port to use for communication on the Soulseek Network";
-              default = 50000;
+              description = "The port on which to listen for incoming connections.";
+              default = 50300;
             };
           };
 
+          global = {
+            # TODO speed units
+            upload = {
+              slots = mkOption {
+                type = ints.unsigned;
+                description = "Limit of the number of concurrent upload slots.";
+              };
+              speed_limit = mkOption {
+                type = ints.unsigned;
+                description = "Total upload speed limit.";
+              };
+            };
+            download = {
+              slots = mkOption {
+                type = ints.unsigned;
+                description = "Limit of the number of concurrent download slots.";
+              };
+              speed_limit = mkOption {
+                type = ints.unsigned;
+                description = "Total upload download limit";
+              };
+            };
+          };
+
+          filters.search.request = mkOption {
+            type = listOf str;
+            example = lib.literalExpression ''[ "^.{1,2}$" ]'';
+            description = "Incoming search requests which match this filter are ignored.";
+          };
+
           web = {
             port = mkOption {
               type = port;
-              default = 5001;
-              description = "The HTTP listen port";
+              default = 5030;
+              description = "The HTTP listen port.";
             };
             url_base = mkOption {
               type = path;
-              default = config.services.slskd.nginx.contextPath;
-              defaultText = "config.services.slskd.nginx.contextPath";
-              description = lib.mdDoc ''
-                The context path, i.e., the last part of the slskd URL
-              '';
+              default = "/";
+              description = "The base path in the url for web requests.";
+            };
+            # Users should use a reverse proxy instead for https
+            https.disabled = mkOption {
+              type = bool;
+              default = true;
+              description = "Disable the built-in HTTPS server";
             };
           };
 
-          shares = {
-            directories = mkOption {
-              type = listOf str;
-              description = lib.mdDoc ''
-                Paths to your shared directories. See
-                [documentation](https://github.com/slskd/slskd/blob/master/docs/config.md#directories)
-                for advanced usage
-              '';
+          retention = {
+            transfers = {
+              upload = {
+                succeeded = mkOption {
+                  type = ints.unsigned;
+                  description = "Lifespan of succeeded upload tasks.";
+                  defaultText = "(indefinite)";
+                };
+                errored = mkOption {
+                  type = ints.unsigned;
+                  description = "Lifespan of errored upload tasks.";
+                  defaultText = "(indefinite)";
+                };
+                cancelled = mkOption {
+                  type = ints.unsigned;
+                  description = "Lifespan of cancelled upload tasks.";
+                  defaultText = "(indefinite)";
+                };
+              };
+              download = {
+                succeeded = mkOption {
+                  type = ints.unsigned;
+                  description = "Lifespan of succeeded download tasks.";
+                  defaultText = "(indefinite)";
+                };
+                errored = mkOption {
+                  type = ints.unsigned;
+                  description = "Lifespan of errored download tasks.";
+                  defaultText = "(indefinite)";
+                };
+                cancelled = mkOption {
+                  type = ints.unsigned;
+                  description = "Lifespan of cancelled download tasks.";
+                  defaultText = "(indefinite)";
+                };
+              };
+            };
+            files = {
+              complete = mkOption {
+                type = ints.unsigned;
+                description = "Lifespan of completely downloaded files in minutes.";
+                example = 20160;
+                defaultText = "(indefinite)";
+              };
+              incomplete = mkOption {
+                type = ints.unsigned;
+                description = "Lifespan of incomplete downloading files in minutes.";
+                defaultText = "(indefinite)";
+              };
             };
           };
 
-          directories = {
-            incomplete = mkOption {
-              type = nullOr path;
-              description = "Directory where downloading files are stored";
-              defaultText = "<APP_DIR>/incomplete";
-              default = null;
-            };
-            downloads = mkOption {
-              type = nullOr path;
-              description = "Directory where downloaded files are stored";
-              defaultText = "<APP_DIR>/downloads";
-              default = null;
+          logger = {
+            # Disable by default, journald already retains as needed
+            disk = mkOption {
+              type = bool;
+              description = "Whether to log to the application directory.";
+              default = false;
+              visible = false;
             };
           };
         };
@@ -126,51 +254,42 @@ in {
   config = let
     cfg = config.services.slskd;
 
-    confWithoutNullValues = (lib.filterAttrs (key: value: value != null) cfg.settings);
+    confWithoutNullValues = (lib.filterAttrsRecursive (key: value: (builtins.tryEval value).success && value != null) cfg.settings);
 
     configurationYaml = settingsFormat.generate "slskd.yml" confWithoutNullValues;
 
   in lib.mkIf cfg.enable {
 
-    users = {
-      users.slskd = {
+    # Force off, configuration file is in nix store and is immutable
+    services.slskd.settings.remote_configuration = lib.mkForce false;
+
+    users.users = lib.optionalAttrs (cfg.user == defaultUser) {
+      "${defaultUser}" = {
+        group = cfg.group;
         isSystemUser = true;
-        group = "slskd";
       };
-      groups.slskd = {};
     };
 
-    # Reverse proxy configuration
-    services.nginx.enable = true;
-    services.nginx.virtualHosts."${cfg.nginx.domainName}" = {
-      forceSSL = true;
-      enableACME = true;
-      locations = {
-        "${cfg.nginx.contextPath}" = {
-          proxyPass = "http://localhost:${toString cfg.settings.web.port}";
-          proxyWebsockets = true;
-        };
-      };
+    users.groups = lib.optionalAttrs (cfg.group == defaultUser) {
+      "${defaultUser}" = {};
     };
 
-    # Hide state & logs
-    systemd.tmpfiles.rules = [
-      "d /var/lib/slskd/data 0750 slskd slskd - -"
-      "d /var/lib/slskd/logs 0750 slskd slskd - -"
-    ];
-
     systemd.services.slskd = {
       description = "A modern client-server application for the Soulseek file sharing network";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
         Type = "simple";
-        User = "slskd";
+        User = cfg.user;
+        Group = cfg.group;
         EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
-        StateDirectory = "slskd";
+        StateDirectory = "slskd";  # Creates /var/lib/slskd and manages permissions
         ExecStart = "${cfg.package}/bin/slskd --app-dir /var/lib/slskd --config ${configurationYaml}";
         Restart = "on-failure";
         ReadOnlyPaths = map (d: builtins.elemAt (builtins.split "[^/]*(/.+)" d) 1) cfg.settings.shares.directories;
+        ReadWritePaths =
+          (lib.optional (cfg.settings.directories.incomplete != null) cfg.settings.directories.incomplete) ++
+          (lib.optional (cfg.settings.directories.downloads != null) cfg.settings.directories.downloads);
         LockPersonality = true;
         NoNewPrivileges = true;
         PrivateDevices = true;
@@ -194,18 +313,21 @@ in {
 
     networking.firewall.allowedTCPPorts = lib.optional cfg.openFirewall cfg.settings.soulseek.listen_port;
 
-    systemd.services.slskd-rotatelogs = lib.mkIf cfg.rotateLogs {
-      description = "Rotate slskd logs";
-      serviceConfig = {
-        Type = "oneshot";
-        User = "slskd";
-        ExecStart = [
-          "${pkgs.findutils}/bin/find /var/lib/slskd/logs/ -type f -mtime +10 -delete"
-          "${pkgs.findutils}/bin/find /var/lib/slskd/logs/ -type f -mtime +1  -exec ${pkgs.gzip}/bin/gzip -q {} ';'"
-        ];
-      };
-      startAt = "daily";
+    services.nginx = lib.mkIf (cfg.domain != null) {
+      enable = lib.mkDefault true;
+      virtualHosts."${cfg.domain}" = lib.mkMerge [
+        cfg.nginx
+        {
+          locations."${cfg.settings.web.url_base}" = {
+            proxyPass = "http://127.0.0.1:${toString cfg.settings.web.port}";
+            proxyWebsockets = true;
+          };
+        }
+      ];
     };
+  };
 
+  meta = {
+    maintainers = with lib.maintainers; [ ppom melvyn2 ];
   };
 }
diff --git a/nixos/modules/services/web-apps/suwayomi-server.md b/nixos/modules/services/web-apps/suwayomi-server.md
index ff1e06c8a53ae..2185556a87212 100644
--- a/nixos/modules/services/web-apps/suwayomi-server.md
+++ b/nixos/modules/services/web-apps/suwayomi-server.md
@@ -100,7 +100,10 @@ Not all the configuration options are available directly in this module, but you
       server = {
         port = 4567;
         autoDownloadNewChapters = false;
-        maxSourcesInParallel" = 6;
+        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 94dbe6f99356e..99c6ea2a36e60 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-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix
index 4f6d7e4e6c1ca..ac3c85af2a719 100644
--- a/nixos/modules/services/web-apps/zabbix.nix
+++ b/nixos/modules/services/web-apps/zabbix.nix
@@ -76,11 +76,11 @@ in
           type = types.port;
           default =
             if cfg.database.type == "mysql" then config.services.mysql.port
-            else if cfg.database.type == "pgsql" then config.services.postgresql.port
+            else if cfg.database.type == "pgsql" then config.services.postgresql.settings.port
             else 1521;
           defaultText = literalExpression ''
             if config.${opt.database.type} == "mysql" then config.${options.services.mysql.port}
-            else if config.${opt.database.type} == "pgsql" then config.${options.services.postgresql.port}
+            else if config.${opt.database.type} == "pgsql" then config.services.postgresql.settings.port
             else 1521
           '';
           description = lib.mdDoc "Database host port.";
diff --git a/nixos/modules/services/web-servers/garage.md b/nixos/modules/services/web-servers/garage.md
index 3a9b85ce06036..fbefd1914d873 100644
--- a/nixos/modules/services/web-servers/garage.md
+++ b/nixos/modules/services/web-servers/garage.md
@@ -80,7 +80,7 @@ If major-releases will be abandoned by upstream, we should check first if those
 in NixOS for a safe upgrade-path before removing those. In that case we should keep those
 packages, but mark them as insecure in an expression like this (in
 `<nixpkgs/pkgs/tools/filesystem/garage/default.nix>`):
-```
+```nix
 /* ... */
 {
   garage_0_7_3 = generic {
diff --git a/nixos/modules/services/web-servers/garage.nix b/nixos/modules/services/web-servers/garage.nix
index 48dd5b34757c1..616be978b6e5b 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 7d8bb1963d788..d6e6bb2fa14e8 100644
--- a/nixos/modules/services/x11/desktop-managers/budgie.nix
+++ b/nixos/modules/services/x11/desktop-managers/budgie.nix
@@ -43,7 +43,11 @@ let
   budgie-control-center = pkgs.budgie.budgie-control-center.override {
     enableSshSocket = config.services.openssh.startWhenNeeded;
   };
+
+  notExcluded = pkg: (!(lib.elem pkg config.environment.budgie.excludePackages));
 in {
+  meta.maintainers = lib.teams.budgie.members;
+
   options = {
     services.xserver.desktopManager.budgie = {
       enable = mkEnableOption (mdDoc "the Budgie desktop");
@@ -89,7 +93,7 @@ in {
   };
 
   config = mkIf cfg.enable {
-    services.xserver.displayManager.sessionPackages = with pkgs; [
+    services.displayManager.sessionPackages = with pkgs; [
       budgie.budgie-desktop
     ];
 
@@ -144,7 +148,6 @@ in {
           mate.atril
           mate.engrampa
           mate.mate-calc
-          mate.mate-terminal
           mate.mate-system-monitor
           vlc
 
@@ -158,6 +161,9 @@ in {
         ] config.environment.budgie.excludePackages)
       ++ cfg.sessionPath;
 
+    # Both budgie-desktop-view and nemo defaults to this emulator.
+    programs.gnome-terminal.enable = mkDefault (notExcluded pkgs.gnome.gnome-terminal);
+
     # Fonts.
     fonts.packages = [
       pkgs.noto-fonts
@@ -212,7 +218,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/cinnamon.nix b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index f5a6c05865c47..6983b51376fd4 100644
--- a/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -60,7 +60,7 @@ in
 
   config = mkMerge [
     (mkIf cfg.enable {
-      services.xserver.displayManager.sessionPackages = [ pkgs.cinnamon.cinnamon-common ];
+      services.displayManager.sessionPackages = [ pkgs.cinnamon.cinnamon-common ];
 
       services.xserver.displayManager.lightdm.greeters.slick = {
         enable = mkDefault true;
@@ -95,7 +95,7 @@ in
       '';
 
       # Default services
-      services.blueman.enable = mkDefault true;
+      services.blueman.enable = mkDefault (notExcluded pkgs.blueman);
       hardware.bluetooth.enable = mkDefault true;
       hardware.pulseaudio.enable = mkDefault true;
       security.polkit.enable = true;
@@ -228,10 +228,10 @@ in
     })
 
     (mkIf serviceCfg.apps.enable {
-      programs.geary.enable = mkDefault true;
-      programs.gnome-disks.enable = mkDefault true;
-      programs.gnome-terminal.enable = mkDefault true;
-      programs.file-roller.enable = mkDefault true;
+      programs.geary.enable = mkDefault (notExcluded pkgs.gnome.geary);
+      programs.gnome-disks.enable = mkDefault (notExcluded pkgs.gnome.gnome-disk-utility);
+      programs.gnome-terminal.enable = mkDefault (notExcluded pkgs.gnome.gnome-terminal);
+      programs.file-roller.enable = mkDefault (notExcluded pkgs.gnome.file-roller);
 
       environment.systemPackages = with pkgs // pkgs.gnome // pkgs.cinnamon; utils.removePackagesByName [
         # cinnamon team apps
diff --git a/nixos/modules/services/x11/desktop-managers/deepin.nix b/nixos/modules/services/x11/desktop-managers/deepin.nix
index e6f221201013e..61f6fece58708 100644
--- a/nixos/modules/services/x11/desktop-managers/deepin.nix
+++ b/nixos/modules/services/x11/desktop-managers/deepin.nix
@@ -38,8 +38,8 @@ in
 
   config = mkIf cfg.enable
     {
-      services.xserver.displayManager.sessionPackages = [ pkgs.deepin.dde-session ];
-      services.xserver.displayManager.defaultSession = mkDefault "dde-x11";
+      services.displayManager.sessionPackages = [ pkgs.deepin.dde-session ];
+      services.displayManager.defaultSession = mkDefault "dde-x11";
 
       # Update the DBus activation environment after launching the desktop manager.
       services.xserver.displayManager.sessionCommands = ''
@@ -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/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 33d0a7b526436..896d8dcbff40f 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -87,7 +87,7 @@ in
         default = null;
         example = "none";
         description = lib.mdDoc ''
-          **Deprecated**, please use [](#opt-services.xserver.displayManager.defaultSession) instead.
+          **Deprecated**, please use [](#opt-services.displayManager.defaultSession) instead.
 
           Default desktop manager loaded if none have been chosen.
         '';
diff --git a/nixos/modules/services/x11/desktop-managers/enlightenment.nix b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
index 28dd408c923c8..d241c63436faa 100644
--- a/nixos/modules/services/x11/desktop-managers/enlightenment.nix
+++ b/nixos/modules/services/x11/desktop-managers/enlightenment.nix
@@ -54,7 +54,7 @@ in
       "/share/locale"
     ];
 
-    services.xserver.displayManager.sessionPackages = [ pkgs.enlightenment.enlightenment ];
+    services.displayManager.sessionPackages = [ pkgs.enlightenment.enlightenment ];
 
     services.xserver.displayManager.sessionCommands = ''
       if test "$XDG_CURRENT_DESKTOP" = "Enlightenment"; then
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.md b/nixos/modules/services/x11/desktop-managers/gnome.md
index aa36f66970ec4..2b4bd06df04f2 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome.md
+++ b/nixos/modules/services/x11/desktop-managers/gnome.md
@@ -8,9 +8,11 @@ All of the core apps, optional apps, games, and core developer tools from GNOME
 
 To enable the GNOME desktop use:
 
-```
-services.xserver.desktopManager.gnome.enable = true;
-services.xserver.displayManager.gdm.enable = true;
+```nix
+{
+  services.xserver.desktopManager.gnome.enable = true;
+  services.xserver.displayManager.gdm.enable = true;
+}
 ```
 
 ::: {.note}
@@ -23,8 +25,10 @@ The default applications used in NixOS are very minimal, inspired by the default
 
 If you’d like to only use the GNOME desktop and not the apps, you can disable them with:
 
-```
-services.gnome.core-utilities.enable = false;
+```nix
+{
+  services.gnome.core-utilities.enable = false;
+}
 ```
 
 and none of them will be installed.
@@ -37,9 +41,11 @@ Note that this mechanism can only exclude core utilities, games and core develop
 
 It is also possible to disable many of the [core services](https://github.com/NixOS/nixpkgs/blob/b8ec4fd2a4edc4e30d02ba7b1a2cc1358f3db1d5/nixos/modules/services/x11/desktop-managers/gnome.nix#L329-L348). For example, if you do not need indexing files, you can disable Tracker with:
 
-```
-services.gnome.tracker-miners.enable = false;
-services.gnome.tracker.enable = false;
+```nix
+{
+  services.gnome.tracker-miners.enable = false;
+  services.gnome.tracker.enable = false;
+}
 ```
 
 Note, however, that doing so is not supported and might break some applications. Notably, GNOME Music cannot work without Tracker.
@@ -48,39 +54,47 @@ Note, however, that doing so is not supported and might break some applications.
 
 You can install all of the GNOME games with:
 
-```
-services.gnome.games.enable = true;
+```nix
+{
+  services.gnome.games.enable = true;
+}
 ```
 
 ### GNOME core developer tools {#sec-gnome-core-developer-tools}
 
 You can install GNOME core developer tools with:
 
-```
-services.gnome.core-developer-tools.enable = true;
+```nix
+{
+  services.gnome.core-developer-tools.enable = true;
+}
 ```
 
 ## Enabling GNOME Flashback {#sec-gnome-enable-flashback}
 
 GNOME Flashback provides a desktop environment based on the classic GNOME 2 architecture. You can enable the default GNOME Flashback session, which uses the Metacity window manager, with:
 
-```
-services.xserver.desktopManager.gnome.flashback.enableMetacity = true;
+```nix
+{
+  services.xserver.desktopManager.gnome.flashback.enableMetacity = true;
+}
 ```
 
 It is also possible to create custom sessions that replace Metacity with a different window manager using [](#opt-services.xserver.desktopManager.gnome.flashback.customSessions).
 
 The following example uses `xmonad` window manager:
 
-```
-services.xserver.desktopManager.gnome.flashback.customSessions = [
-  {
-    wmName = "xmonad";
-    wmLabel = "XMonad";
-    wmCommand = "${pkgs.haskellPackages.xmonad}/bin/xmonad";
-    enableGnomePanel = false;
-  }
-];
+```nix
+{
+  services.xserver.desktopManager.gnome.flashback.customSessions = [
+    {
+      wmName = "xmonad";
+      wmLabel = "XMonad";
+      wmCommand = "${pkgs.haskellPackages.xmonad}/bin/xmonad";
+      enableGnomePanel = false;
+    }
+  ];
+}
 ```
 
 ## Icons and GTK Themes {#sec-gnome-icons-and-gtk-themes}
@@ -104,12 +118,14 @@ Some packages that include Shell extensions, like `gnome.gpaste`, don’t have t
 
 You can install them like any other package:
 
-```
-environment.systemPackages = [
-  gnomeExtensions.dash-to-dock
-  gnomeExtensions.gsconnect
-  gnomeExtensions.mpris-indicator-button
-];
+```nix
+{
+  environment.systemPackages = [
+    gnomeExtensions.dash-to-dock
+    gnomeExtensions.gsconnect
+    gnomeExtensions.mpris-indicator-button
+  ];
+}
 ```
 
 Unfortunately, we lack a way for these to be managed in a completely declarative way.
@@ -136,23 +152,25 @@ You can use `dconf-editor` tool to explore which GSettings you can set.
 
 ### Example {#sec-gnome-gsettings-overrides-example}
 
-```
-services.xserver.desktopManager.gnome = {
-  extraGSettingsOverrides = ''
-    # Change default background
-    [org.gnome.desktop.background]
-    picture-uri='file://${pkgs.nixos-artwork.wallpapers.mosaic-blue.gnomeFilePath}'
-
-    # Favorite apps in gnome-shell
-    [org.gnome.shell]
-    favorite-apps=['org.gnome.Console.desktop', 'org.gnome.Nautilus.desktop']
-  '';
-
-  extraGSettingsOverridePackages = [
-    pkgs.gsettings-desktop-schemas # for org.gnome.desktop
-    pkgs.gnome.gnome-shell # for org.gnome.shell
-  ];
-};
+```nix
+{
+  services.xserver.desktopManager.gnome = {
+    extraGSettingsOverrides = ''
+      # Change default background
+      [org.gnome.desktop.background]
+      picture-uri='file://${pkgs.nixos-artwork.wallpapers.mosaic-blue.gnomeFilePath}'
+
+      # Favorite apps in gnome-shell
+      [org.gnome.shell]
+      favorite-apps=['org.gnome.Console.desktop', 'org.gnome.Nautilus.desktop']
+    '';
+
+    extraGSettingsOverridePackages = [
+      pkgs.gsettings-desktop-schemas # for org.gnome.desktop
+      pkgs.gnome.gnome-shell # for org.gnome.shell
+    ];
+  };
+}
 ```
 
 ## Frequently Asked Questions {#sec-gnome-faq}
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix
index 2cf9bc2eac37e..cc959bcf7bd58 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -261,7 +261,7 @@ in
       services.gnome.core-shell.enable = true;
       services.gnome.core-utilities.enable = mkDefault true;
 
-      services.xserver.displayManager.sessionPackages = [ pkgs.gnome.gnome-session.sessions ];
+      services.displayManager.sessionPackages = [ pkgs.gnome.gnome-session.sessions ];
 
       environment.extraInit = ''
         ${concatMapStrings (p: ''
@@ -285,7 +285,7 @@ in
     })
 
     (mkIf flashbackEnabled {
-      services.xserver.displayManager.sessionPackages =
+      services.displayManager.sessionPackages =
         let
           wmNames = map (wm: wm.wmName) flashbackWms;
           namesAreUnique = lib.unique wmNames == wmNames;
diff --git a/nixos/modules/services/x11/desktop-managers/lumina.nix b/nixos/modules/services/x11/desktop-managers/lumina.nix
index 7b694106bf7ee..9df9fe42a1ff6 100644
--- a/nixos/modules/services/x11/desktop-managers/lumina.nix
+++ b/nixos/modules/services/x11/desktop-managers/lumina.nix
@@ -27,7 +27,7 @@ in
 
   config = mkIf cfg.enable {
 
-    services.xserver.displayManager.sessionPackages = [
+    services.displayManager.sessionPackages = [
       pkgs.lumina.lumina
     ];
 
diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix
index d3bdc4326a908..3d02deba6fc79 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 f535a1d298b9f..e475442b9ef47 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.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.displayManager.sessionPackages = [ pkgs.mate.mate-wayland-session ];
+    })
+  ];
 }
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.md b/nixos/modules/services/x11/desktop-managers/pantheon.md
index 1c14ede847495..ce251ec2d3940 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.md
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.md
@@ -5,17 +5,23 @@ Pantheon is the desktop environment created for the elementary OS distribution.
 ## Enabling Pantheon {#sec-pantheon-enable}
 
 All of Pantheon is working in NixOS and the applications should be available, aside from a few [exceptions](https://github.com/NixOS/nixpkgs/issues/58161). To enable Pantheon, set
-```
-services.xserver.desktopManager.pantheon.enable = true;
+```nix
+{
+  services.xserver.desktopManager.pantheon.enable = true;
+}
 ```
 This automatically enables LightDM and Pantheon's LightDM greeter. If you'd like to disable this, set
-```
-services.xserver.displayManager.lightdm.greeters.pantheon.enable = false;
-services.xserver.displayManager.lightdm.enable = false;
+```nix
+{
+  services.xserver.displayManager.lightdm.greeters.pantheon.enable = false;
+  services.xserver.displayManager.lightdm.enable = false;
+}
 ```
 but please be aware using Pantheon without LightDM as a display manager will break screenlocking from the UI. The NixOS module for Pantheon installs all of Pantheon's default applications. If you'd like to not install Pantheon's apps, set
-```
-services.pantheon.apps.enable = false;
+```nix
+{
+  services.pantheon.apps.enable = false;
+}
 ```
 You can also use [](#opt-environment.pantheon.excludePackages) to remove any other app (like `elementary-mail`).
 
@@ -29,30 +35,33 @@ Wingpanel and Switchboard work differently than they do in other distributions,
 to configure the programs with plugs or indicators.
 
 The difference in NixOS is both these programs are patched to load plugins from a directory that is the value of an environment variable. All of which is controlled in Nix. If you need to configure the particular packages manually you can override the packages like:
-```
+```nix
 wingpanel-with-indicators.override {
   indicators = [
     pkgs.some-special-indicator
   ];
-};
+}
 
+```
+```nix
 switchboard-with-plugs.override {
   plugs = [
     pkgs.some-special-plug
   ];
-};
+}
 ```
 please note that, like how the NixOS options describe these as extra plugins, this would only add to the default plugins included with the programs. If for some reason you'd like to configure which plugins to use exactly, both packages have an argument for this:
-```
+```nix
 wingpanel-with-indicators.override {
   useDefaultIndicators = false;
   indicators = specialListOfIndicators;
-};
-
+}
+```
+```nix
 switchboard-with-plugs.override {
   useDefaultPlugs = false;
   plugs = specialListOfPlugs;
-};
+}
 ```
 this could be most useful for testing a particular plug-in in isolation.
 
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index 59bc142eeb7f9..2115f8f0ab235 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -12,6 +12,7 @@ let
     extraGSettingsOverrides = cfg.extraGSettingsOverrides;
   };
 
+  notExcluded = pkg: (!(lib.elem pkg config.environment.pantheon.excludePackages));
 in
 
 {
@@ -96,7 +97,7 @@ in
         pkgs.pantheon.pantheon-agent-geoclue2
       ] config.environment.pantheon.excludePackages;
 
-      services.xserver.displayManager.sessionPackages = [ pkgs.pantheon.elementary-session-settings ];
+      services.displayManager.sessionPackages = [ pkgs.pantheon.elementary-session-settings ];
 
       # Ensure lightdm is used when Pantheon is enabled
       # Without it screen locking will be nonfunctional because of the use of lightlocker
@@ -109,7 +110,7 @@ in
 
       # Without this, elementary LightDM greeter will pre-select non-existent `default` session
       # https://github.com/elementary/greeter/issues/368
-      services.xserver.displayManager.defaultSession = mkDefault "pantheon";
+      services.displayManager.defaultSession = mkDefault "pantheon";
 
       services.xserver.displayManager.sessionCommands = ''
         if test "$XDG_CURRENT_DESKTOP" = "Pantheon"; then
@@ -174,12 +175,22 @@ in
         # https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1443
         pkgs.pantheon.mutter
       ];
-      systemd.packages = [
-        pkgs.pantheon.gnome-settings-daemon
+      systemd.packages = with pkgs; [
+        gnome.gnome-session
+        pantheon.gala
+        pantheon.gnome-settings-daemon
+        pantheon.elementary-session-settings
       ];
       programs.dconf.enable = true;
       networking.networkmanager.enable = mkDefault true;
 
+      systemd.user.targets."gnome-session-x11-services".wants = [
+        "org.gnome.SettingsDaemon.XSettings.service"
+      ];
+      systemd.user.targets."gnome-session-x11-services-ready".wants = [
+        "org.gnome.SettingsDaemon.XSettings.service"
+      ];
+
       # Global environment
       environment.systemPackages = (with pkgs.pantheon; [
         elementary-session-settings
@@ -278,8 +289,8 @@ in
     })
 
     (mkIf serviceCfg.apps.enable {
-      programs.evince.enable = mkDefault true;
-      programs.file-roller.enable = mkDefault true;
+      programs.evince.enable = mkDefault (notExcluded pkgs.gnome.evince);
+      programs.file-roller.enable = mkDefault (notExcluded pkgs.gnome.file-roller);
 
       environment.systemPackages = utils.removePackagesByName ([
         pkgs.gnome.gnome-font-viewer
diff --git a/nixos/modules/services/x11/desktop-managers/phosh.nix b/nixos/modules/services/x11/desktop-managers/phosh.nix
index 75e02130addc5..41107788db0a5 100644
--- a/nixos/modules/services/x11/desktop-managers/phosh.nix
+++ b/nixos/modules/services/x11/desktop-managers/phosh.nix
@@ -220,7 +220,7 @@ in
 
     services.gnome.core-shell.enable = true;
     services.gnome.core-os-services.enable = true;
-    services.xserver.displayManager.sessionPackages = [ cfg.package ];
+    services.displayManager.sessionPackages = [ cfg.package ];
 
     environment.etc."phosh/phoc.ini".source =
       if builtins.isPath cfg.phocConfig then cfg.phocConfig
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index c884b4487e240..bb6e5873deff9 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.
@@ -357,7 +357,7 @@ in
         pkgs.media-player-info
       ];
 
-      services.xserver.displayManager.sddm = {
+      services.displayManager.sddm = {
         theme = mkDefault "breeze";
       };
 
@@ -403,16 +403,16 @@ in
       system.nixos-generate-config.desktopConfiguration = [
         ''
           # Enable the Plasma 5 Desktop Environment.
-          services.xserver.displayManager.sddm.enable = true;
+          services.displayManager.sddm.enable = true;
           services.xserver.desktopManager.plasma5.enable = true;
         ''
       ];
 
-      services.xserver.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-workspace ];
+      services.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-workspace ];
       # Default to be `plasma` (X11) instead of `plasmawayland`, since plasma wayland currently has
       # many tiny bugs.
       # See: https://github.com/NixOS/nixpkgs/issues/143272
-      services.xserver.displayManager.defaultSession = mkDefault "plasma";
+      services.displayManager.defaultSession = mkDefault "plasma";
 
       environment.systemPackages =
         with pkgs.plasma5Packages;
@@ -538,7 +538,7 @@ in
         };
       };
 
-      services.xserver.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-mobile ];
+      services.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-mobile ];
     })
 
     # Plasma Bigscreen
@@ -559,7 +559,7 @@ in
           kdeconnect-kde
         ];
 
-      services.xserver.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-bigscreen ];
+      services.displayManager.sessionPackages = [ pkgs.plasma5Packages.plasma-bigscreen ];
 
       # required for plasma-remotecontrollers to work correctly
       hardware.uinput.enable = true;
diff --git a/nixos/modules/services/x11/desktop-managers/surf-display.nix b/nixos/modules/services/x11/desktop-managers/surf-display.nix
index 38ebb9d02b4ac..e5f2c76f4ac25 100644
--- a/nixos/modules/services/x11/desktop-managers/surf-display.nix
+++ b/nixos/modules/services/x11/desktop-managers/surf-display.nix
@@ -119,7 +119,7 @@ in {
   };
 
   config = mkIf cfg.enable {
-    services.xserver.displayManager.sessionPackages = [
+    services.displayManager.sessionPackages = [
       pkgs.surf-display
     ];
 
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 6bc964f4c6ed7..3ba27b2015075 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/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 3e2d5780a5cb1..129bafefabe99 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -14,7 +14,6 @@ with lib;
 let
 
   cfg = config.services.xserver;
-  opt = options.services.xserver;
   xorg = pkgs.xorg;
 
   fontconfig = config.fonts.fontconfig;
@@ -40,7 +39,7 @@ let
         IFS=:
         for i in $XDG_CURRENT_DESKTOP; do
           case $i in
-            KDE|GNOME|X-NIXOS-SYSTEMD-AWARE) echo "1"; exit; ;;
+            KDE|GNOME|Pantheon|X-NIXOS-SYSTEMD-AWARE) echo "1"; exit; ;;
             *) ;;
           esac
         done
@@ -70,14 +69,14 @@ let
           source ~/.xprofile
       fi
 
-      ${optionalString cfg.displayManager.job.logToJournal ''
+      ${optionalString config.services.displayManager.logToJournal ''
         if [ -z "$_DID_SYSTEMD_CAT" ]; then
           export _DID_SYSTEMD_CAT=1
           exec ${config.systemd.package}/bin/systemd-cat -t xsession "$0" "$@"
         fi
       ''}
 
-      ${optionalString cfg.displayManager.job.logToFile ''
+      ${optionalString config.services.displayManager.logToFile ''
         exec &> >(tee ~/.xsession-errors)
       ''}
 
@@ -130,41 +129,6 @@ let
           exit 1
       fi
     '';
-
-  installedSessions = pkgs.runCommand "desktops"
-    { # trivial derivation
-      preferLocalBuild = true;
-      allowSubstitutes = false;
-    }
-    ''
-      mkdir -p "$out/share/"{xsessions,wayland-sessions}
-
-      ${concatMapStrings (pkg: ''
-        for n in ${concatStringsSep " " pkg.providedSessions}; do
-          if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \
-                    -f ${pkg}/share/xsessions/$n.desktop; then
-            echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:"
-            echo "  ${pkg}"
-            return 1
-          fi
-        done
-
-        if test -d ${pkg}/share/xsessions; then
-          ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions
-        fi
-        if test -d ${pkg}/share/wayland-sessions; then
-          ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions
-        fi
-      '') cfg.displayManager.sessionPackages}
-    '';
-
-  dmDefault = cfg.desktopManager.default;
-  # fallback default for cases when only default wm is set
-  dmFallbackDefault = if dmDefault != null then dmDefault else "none";
-  wmDefault = cfg.windowManager.default;
-
-  defaultSessionFromLegacyOptions = dmFallbackDefault + optionalString (wmDefault != null && wmDefault != "none") "+${wmDefault}";
-
 in
 
 {
@@ -215,35 +179,6 @@ in
         '';
       };
 
-      hiddenUsers = mkOption {
-        type = types.listOf types.str;
-        default = [ "nobody" ];
-        description = lib.mdDoc ''
-          A list of users which will not be shown in the display manager.
-        '';
-      };
-
-      sessionPackages = mkOption {
-        type = with types; listOf (package // {
-          description = "package with provided sessions";
-          check = p: assertMsg
-            (package.check p && p ? providedSessions
-            && p.providedSessions != [] && all isString p.providedSessions)
-            ''
-              Package, '${p.name}', did not specify any session names, as strings, in
-              'passthru.providedSessions'. This is required when used as a session package.
-
-              The session names can be looked up in:
-                ${p}/share/xsessions
-                ${p}/share/wayland-sessions
-           '';
-        });
-        default = [];
-        description = lib.mdDoc ''
-          A list of packages containing x11 or wayland session files to be passed to the display manager.
-        '';
-      };
-
       session = mkOption {
         default = [];
         type = types.listOf types.attrs;
@@ -274,51 +209,6 @@ in
         '';
       };
 
-      sessionData = mkOption {
-        description = lib.mdDoc "Data exported for display managers’ convenience";
-        internal = true;
-        default = {};
-        apply = val: {
-          wrapper = xsessionWrapper;
-          desktops = installedSessions;
-          sessionNames = concatMap (p: p.providedSessions) cfg.displayManager.sessionPackages;
-          # We do not want to force users to set defaultSession when they have only single DE.
-          autologinSession =
-            if cfg.displayManager.defaultSession != null then
-              cfg.displayManager.defaultSession
-            else if cfg.displayManager.sessionData.sessionNames != [] then
-              head cfg.displayManager.sessionData.sessionNames
-            else
-              null;
-        };
-      };
-
-      defaultSession = mkOption {
-        type = with types; nullOr str // {
-          description = "session name";
-          check = d:
-            assertMsg (d != null -> (str.check d && elem d cfg.displayManager.sessionData.sessionNames)) ''
-                Default graphical session, '${d}', not found.
-                Valid names for 'services.xserver.displayManager.defaultSession' are:
-                  ${concatStringsSep "\n  " cfg.displayManager.sessionData.sessionNames}
-              '';
-        };
-        default =
-          if dmDefault != null || wmDefault != null then
-            defaultSessionFromLegacyOptions
-          else
-            null;
-        defaultText = literalMD ''
-          Taken from display manager settings or window manager settings, if either is set.
-        '';
-        example = "gnome";
-        description = lib.mdDoc ''
-          Graphical session to pre-select in the session chooser (only effective for GDM, LightDM and SDDM).
-
-          On GDM, LightDM and SDDM, it will also be used as a session for auto-login.
-        '';
-      };
-
       importedVariables = mkOption {
         type = types.listOf (types.strMatching "[a-zA-Z_][a-zA-Z0-9_]*");
         visible = false;
@@ -327,106 +217,19 @@ in
         '';
       };
 
-      job = {
-
-        preStart = mkOption {
-          type = types.lines;
-          default = "";
-          example = "rm -f /var/log/my-display-manager.log";
-          description = lib.mdDoc "Script executed before the display manager is started.";
-        };
-
-        execCmd = mkOption {
-          type = types.str;
-          example = literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
-          description = lib.mdDoc "Command to start the display manager.";
-        };
-
-        environment = mkOption {
-          type = types.attrsOf types.unspecified;
-          default = {};
-          description = lib.mdDoc "Additional environment variables needed by the display manager.";
-        };
-
-        logToFile = mkOption {
-          type = types.bool;
-          default = false;
-          description = lib.mdDoc ''
-            Whether the display manager redirects the output of the
-            session script to {file}`~/.xsession-errors`.
-          '';
-        };
-
-        logToJournal = mkOption {
-          type = types.bool;
-          default = true;
-          description = lib.mdDoc ''
-            Whether the display manager redirects the output of the
-            session script to the systemd journal.
-          '';
-        };
-
-      };
-
-      # Configuration for automatic login. Common for all DM.
-      autoLogin = mkOption {
-        type = types.submodule ({ config, options, ... }: {
-          options = {
-            enable = mkOption {
-              type = types.bool;
-              default = config.user != null;
-              defaultText = literalExpression "config.${options.user} != null";
-              description = lib.mdDoc ''
-                Automatically log in as {option}`autoLogin.user`.
-              '';
-            };
-
-            user = mkOption {
-              type = types.nullOr types.str;
-              default = null;
-              description = lib.mdDoc ''
-                User to be used for the automatic login.
-              '';
-            };
-          };
-        });
-
-        default = {};
-        description = lib.mdDoc ''
-          Auto login configuration attrset.
-        '';
-      };
-
     };
 
   };
 
   config = {
     assertions = [
-      { assertion = cfg.displayManager.autoLogin.enable -> cfg.displayManager.autoLogin.user != null;
-        message = ''
-          services.xserver.displayManager.autoLogin.enable requires services.xserver.displayManager.autoLogin.user to be set
-        '';
-      }
       {
         assertion = cfg.desktopManager.default != null || cfg.windowManager.default != null -> cfg.displayManager.defaultSession == defaultSessionFromLegacyOptions;
-        message = "You cannot use both services.xserver.displayManager.defaultSession option and legacy options (services.xserver.desktopManager.default and services.xserver.windowManager.default).";
+        message = "You cannot use both services.displayManager.defaultSession option and legacy options (services.xserver.desktopManager.default and services.xserver.windowManager.default).";
       }
     ];
 
-    warnings =
-      mkIf (dmDefault != null || wmDefault != null) [
-        ''
-          The following options are deprecated:
-            ${concatStringsSep "\n  " (map ({c, t}: t) (filter ({c, t}: c != null) [
-            { c = dmDefault; t = "- services.xserver.desktopManager.default"; }
-            { c = wmDefault; t = "- services.xserver.windowManager.default"; }
-            ]))}
-          Please use
-            services.xserver.displayManager.defaultSession = "${defaultSessionFromLegacyOptions}";
-          instead.
-        ''
-      ];
+    services.displayManager.sessionData.wrapper = xsessionWrapper;
 
     services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X";
 
@@ -449,7 +252,7 @@ in
 
     # Create desktop files and scripts for starting sessions for WMs/DMs
     # that do not have upstream session files (those defined using services.{display,desktop,window}Manager.session options).
-    services.xserver.displayManager.sessionPackages =
+    services.displayManager.sessionPackages =
       let
         dms = filter (s: s.manage == "desktop") cfg.displayManager.session;
         wms = filter (s: s.manage == "window") cfg.displayManager.session;
@@ -511,20 +314,14 @@ in
             )
             (cartesianProductOfSets { dm = dms; wm = wms; })
           );
-
-    # Make xsessions and wayland sessions available in XDG_DATA_DIRS
-    # as some programs have behavior that depends on them being present
-    environment.sessionVariables.XDG_DATA_DIRS = lib.mkIf (cfg.displayManager.sessionPackages != [ ]) [
-      "${cfg.displayManager.sessionData.desktops}/share"
-    ];
   };
 
   imports = [
     (mkRemovedOptionModule [ "services" "xserver" "displayManager" "desktopManagerHandlesLidAndPower" ]
      "The option is no longer necessary because all display managers have already delegated lid management to systemd.")
-    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "xserver" "displayManager" "job" "logToFile" ])
-    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "xserver" "displayManager" "job" "logToJournal" ])
-    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "xserver" "displayManager" "sessionPackages" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "displayManager" "logToFile" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "displayManager" "logToJournal" ])
+    (mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "displayManager" "sessionPackages" ])
   ];
 
 }
diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix
index 400e5601dc59a..6bdfe9ea6f8cd 100644
--- a/nixos/modules/services/x11/display-managers/gdm.nix
+++ b/nixos/modules/services/x11/display-managers/gdm.nix
@@ -32,7 +32,7 @@ let
     load-module module-position-event-sounds
   '';
 
-  defaultSessionName = config.services.xserver.displayManager.defaultSession;
+  defaultSessionName = config.services.displayManager.defaultSession;
 
   setSessionScript = pkgs.callPackage ./account-service-util.nix { };
 in
@@ -41,14 +41,12 @@ in
   imports = [
     (mkRenamedOptionModule [ "services" "xserver" "displayManager" "gdm" "autoLogin" "enable" ] [
       "services"
-      "xserver"
       "displayManager"
       "autoLogin"
       "enable"
     ])
     (mkRenamedOptionModule [ "services" "xserver" "displayManager" "gdm" "autoLogin" "user" ] [
       "services"
-      "xserver"
       "displayManager"
       "autoLogin"
       "user"
@@ -148,14 +146,14 @@ in
     services.xserver.display = null;
     services.xserver.verbose = null;
 
-    services.xserver.displayManager.job =
+    services.displayManager =
       {
         environment = {
           GDM_X_SERVER_EXTRA_ARGS = toString
             (filter (arg: arg != "-terminate") cfg.xserverArgs);
           XDG_DATA_DIRS = lib.makeSearchPath "share" [
             gdm # for gnome-login.session
-            cfg.sessionData.desktops
+            config.services.displayManager.sessionData.desktops
             pkgs.gnome.gnome-control-center # for accessibility icon
             pkgs.gnome.adwaita-icon-theme
             pkgs.hicolor-icon-theme # empty icon theme as a base
@@ -169,7 +167,7 @@ in
         execCmd = "exec ${gdm}/bin/gdm";
         preStart = optionalString (defaultSessionName != null) ''
           # Set default session in session chooser to a specified values – basically ignore session history.
-          ${setSessionScript}/bin/set-session ${cfg.sessionData.autologinSession}
+          ${setSessionScript}/bin/set-session ${config.services.displayManager.sessionData.autologinSession}
         '';
       };
 
@@ -265,14 +263,14 @@ in
       daemon = mkMerge [
         { WaylandEnable = cfg.gdm.wayland; }
         # nested if else didn't work
-        (mkIf (cfg.autoLogin.enable && cfg.gdm.autoLogin.delay != 0 ) {
+        (mkIf (config.services.displayManager.autoLogin.enable && cfg.gdm.autoLogin.delay != 0 ) {
           TimedLoginEnable = true;
-          TimedLogin = cfg.autoLogin.user;
+          TimedLogin = config.services.displayManager.autoLogin.user;
           TimedLoginDelay = cfg.gdm.autoLogin.delay;
         })
-        (mkIf (cfg.autoLogin.enable && cfg.gdm.autoLogin.delay == 0 ) {
+        (mkIf (config.services.displayManager.autoLogin.enable && cfg.gdm.autoLogin.delay == 0 ) {
           AutomaticLoginEnable = true;
-          AutomaticLogin = cfg.autoLogin.user;
+          AutomaticLogin = config.services.displayManager.autoLogin.user;
         })
       ];
       debug = mkIf cfg.gdm.debug {
@@ -282,7 +280,7 @@ in
 
     environment.etc."gdm/custom.conf".source = configFile;
 
-    environment.etc."gdm/Xsession".source = config.services.xserver.displayManager.sessionData.wrapper;
+    environment.etc."gdm/Xsession".source = config.services.displayManager.sessionData.wrapper;
 
     # GDM LFS PAM modules, adapted somehow to NixOS
     security.pam.services = {
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix
index f4195c4c2dc39..8702d0b97ed2e 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix
@@ -60,7 +60,7 @@ in
 
           Note that this greeter starts only the default X session.
           You can configure the default X session using
-          [](#opt-services.xserver.displayManager.defaultSession).
+          [](#opt-services.displayManager.defaultSession).
         '';
       };
 
diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix
index dede7680ecb3a..b2ea8e6d94f21 100644
--- a/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/tiny.nix
@@ -22,7 +22,7 @@ in
 
           Note that this greeter starts only the default X session.
           You can configure the default X session using
-          [](#opt-services.xserver.displayManager.defaultSession).
+          [](#opt-services.displayManager.defaultSession).
         '';
       };
 
@@ -81,7 +81,7 @@ in
       {
         assertion = dmcfg.defaultSession != null;
         message = ''
-          Please set: services.xserver.displayManager.defaultSession
+          Please set: services.displayManager.defaultSession
         '';
       }
     ];
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 548d3c5bc46a5..cb6365bace352 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -5,9 +5,9 @@ with lib;
 let
 
   xcfg = config.services.xserver;
-  dmcfg = xcfg.displayManager;
+  dmcfg = config.services.displayManager;
   xEnv = config.systemd.services.display-manager.environment;
-  cfg = dmcfg.lightdm;
+  cfg = xcfg.displayManager.lightdm;
   sessionData = dmcfg.sessionData;
 
   setSessionScript = pkgs.callPackage ./account-service-util.nix { };
@@ -26,7 +26,7 @@ let
       else additionalArgs="-logfile /var/log/X.$display.log"
       fi
 
-      exec ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} $additionalArgs "$@"
+      exec ${xcfg.displayManager.xserverBin} ${toString xcfg.displayManager.xserverArgs} $additionalArgs "$@"
     '';
 
   usersConf = writeText "users.conf"
@@ -58,10 +58,10 @@ let
         autologin-user-timeout = ${toString cfg.autoLogin.timeout}
         autologin-session = ${sessionData.autologinSession}
       ''}
-      ${optionalString (dmcfg.setupCommands != "") ''
+      ${optionalString (xcfg.displayManager.setupCommands != "") ''
         display-setup-script=${pkgs.writeScript "lightdm-display-setup" ''
           #!${pkgs.bash}/bin/bash
-          ${dmcfg.setupCommands}
+          ${xcfg.displayManager.setupCommands}
         ''}
       ''}
       ${cfg.extraSeatDefaults}
@@ -86,14 +86,12 @@ in
     ./lightdm-greeters/mobile.nix
     (mkRenamedOptionModule [ "services" "xserver" "displayManager" "lightdm" "autoLogin" "enable" ] [
       "services"
-      "xserver"
       "displayManager"
       "autoLogin"
       "enable"
     ])
     (mkRenamedOptionModule [ "services" "xserver" "displayManager" "lightdm" "autoLogin" "user" ] [
      "services"
-     "xserver"
      "displayManager"
      "autoLogin"
      "user"
@@ -187,7 +185,7 @@ in
       }
       { assertion = dmcfg.autoLogin.enable -> sessionData.autologinSession != null;
         message = ''
-          LightDM auto-login requires that services.xserver.displayManager.defaultSession is set.
+          LightDM auto-login requires that services.displayManager.defaultSession is set.
         '';
       }
       { assertion = !cfg.greeter.enable -> (dmcfg.autoLogin.enable && cfg.autoLogin.timeout == 0);
@@ -203,12 +201,12 @@ in
 
     # Set default session in session chooser to a specified values – basically ignore session history.
     # Auto-login is already covered by a config value.
-    services.xserver.displayManager.job.preStart = optionalString (!dmcfg.autoLogin.enable && dmcfg.defaultSession != null) ''
+    services.displayManager.preStart = optionalString (!dmcfg.autoLogin.enable && dmcfg.defaultSession != null) ''
       ${setSessionScript}/bin/set-session ${dmcfg.defaultSession}
     '';
 
     # setSessionScript needs session-files in XDG_DATA_DIRS
-    services.xserver.displayManager.job.environment.XDG_DATA_DIRS = "${dmcfg.sessionData.desktops}/share/";
+    services.displayManager.environment.XDG_DATA_DIRS = "${dmcfg.sessionData.desktops}/share/";
 
     # setSessionScript wants AccountsService
     systemd.services.display-manager.wants = [
@@ -216,7 +214,7 @@ in
     ];
 
     # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
-    services.xserver.displayManager.job.execCmd = ''
+    services.displayManager.execCmd = ''
       export PATH=${lightdm}/sbin:$PATH
       exec ${lightdm}/sbin/lightdm
     '';
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
index 0861530f21e84..ce80e013e81e2 100644
--- a/nixos/modules/services/x11/display-managers/xpra.nix
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -226,7 +226,7 @@ in
       VideoRam 192000
     '';
 
-    services.xserver.displayManager.job.execCmd = ''
+    services.displayManager.execCmd = ''
       ${optionalString (cfg.pulseaudio)
         "export PULSE_COOKIE=/run/pulse/.config/pulse/cookie"}
       exec ${pkgs.xpra}/bin/xpra ${if cfg.desktop == null then "start" else "start-desktop --start=${cfg.desktop}"} \
@@ -251,7 +251,6 @@ in
 
     environment.systemPackages = [pkgs.xpra];
 
-    virtualisation.virtualbox.guest.x11 = false;
     hardware.pulseaudio.enable = mkDefault cfg.pulseaudio;
     hardware.pulseaudio.systemWide = mkDefault cfg.pulseaudio;
   };
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index e180f2693e0c6..527c95bb14ac3 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -77,7 +77,7 @@ in
         default = null;
         example = "wmii";
         description = lib.mdDoc ''
-          **Deprecated**, please use [](#opt-services.xserver.displayManager.defaultSession) instead.
+          **Deprecated**, please use [](#opt-services.displayManager.defaultSession) instead.
 
           Default window manager loaded if none have been chosen.
         '';
diff --git a/nixos/modules/services/x11/window-managers/nimdow.nix b/nixos/modules/services/x11/window-managers/nimdow.nix
index de3192876024b..9cee4bb271a5a 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/ragnarwm.nix b/nixos/modules/services/x11/window-managers/ragnarwm.nix
index 7242c8b1324c4..0f4c2660b1e07 100644
--- a/nixos/modules/services/x11/window-managers/ragnarwm.nix
+++ b/nixos/modules/services/x11/window-managers/ragnarwm.nix
@@ -18,7 +18,7 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    services.xserver.displayManager.sessionPackages = [ cfg.package ];
+    services.displayManager.sessionPackages = [ cfg.package ];
     environment.systemPackages = [ cfg.package ];
   };
 
diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix
index c35446bf405b8..2962f2851fa91 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 3d7474e18263d..b9d39aa2b2ef2 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -111,7 +111,7 @@ let
     }
       ''
         echo 'Section "Files"' >> $out
-        echo $fontpath >> $out
+        echo "$fontpath" >> $out
 
         for i in ${toString fontsForXServer}; do
           if test "''${i:0:''${#NIX_STORE}}" == "$NIX_STORE"; then
@@ -121,11 +121,9 @@ let
           fi
         done
 
-        for i in $(find ${toString cfg.modules} -type d | sort); do
-          if test $(echo $i/*.so* | wc -w) -ne 0; then
-            echo "  ModulePath \"$i\"" >> $out
-          fi
-        done
+        ${concatMapStrings (m: ''
+        echo "  ModulePath \"${m}/lib/xorg/modules\"" >> "$out"
+        '') cfg.modules}
 
         echo '${cfg.filesSection}' >> $out
         echo 'EndSection' >> $out
@@ -303,7 +301,7 @@ in
         type = types.listOf types.str;
         default = [ "modesetting" "fbdev" ];
         example = [
-          "nvidia" "nvidiaLegacy390" "nvidiaLegacy340" "nvidiaLegacy304"
+          "nvidia"
           "amdgpu-pro"
         ];
         # TODO(@oxij): think how to easily add the rest, like those nvidia things
@@ -641,28 +639,18 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
+    services.displayManager.enable = true;
 
     services.xserver.displayManager.lightdm.enable =
       let dmConf = cfg.displayManager;
           default = !(dmConf.gdm.enable
-                    || dmConf.sddm.enable
+                    || config.services.displayManager.sddm.enable
                     || dmConf.xpra.enable
                     || dmConf.sx.enable
                     || dmConf.startx.enable
                     || config.services.greetd.enable);
       in mkIf (default) (mkDefault true);
 
-    # so that the service won't be enabled when only startx is used
-    systemd.services.display-manager.enable  =
-      let dmConf = cfg.displayManager;
-          noDmUsed = !(dmConf.gdm.enable
-                    || dmConf.sddm.enable
-                    || dmConf.xpra.enable
-                    || dmConf.lightdm.enable);
-      in mkIf (noDmUsed) (mkDefault false);
-
-    hardware.opengl.enable = mkDefault true;
-
     services.xserver.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
 
     # FIXME: somehow check for unknown driver names.
@@ -696,19 +684,6 @@ in
           # -xkbdir command line option does not seems to be passed to xkbcomp.
           "X11/xkb".source = "${cfg.xkb.dir}";
         })
-      # localectl looks into 00-keyboard.conf
-      //{
-          "X11/xorg.conf.d/00-keyboard.conf".text = ''
-            Section "InputClass"
-              Identifier "Keyboard catchall"
-              MatchIsKeyboard "on"
-              Option "XkbModel" "${cfg.xkb.model}"
-              Option "XkbLayout" "${cfg.xkb.layout}"
-              Option "XkbOptions" "${cfg.xkb.options}"
-              Option "XkbVariant" "${cfg.xkb.variant}"
-            EndSection
-          '';
-        }
       # Needed since 1.18; see https://bugs.freedesktop.org/show_bug.cgi?id=89023#c5
       // (let cfgPath = "X11/xorg.conf.d/10-evdev.conf"; in
         {
@@ -728,31 +703,12 @@ in
         xorg.xprop
         xorg.xauth
         pkgs.xterm
-        pkgs.xdg-utils
         xorg.xf86inputevdev.out # get evdev.4 man page
-        pkgs.nixos-icons # needed for gnome and pantheon about dialog, nixos-manual and maybe more
       ] config.services.xserver.excludePackages
       ++ optional (elem "virtualbox" cfg.videoDrivers) xorg.xrefresh;
 
     environment.pathsToLink = [ "/share/X11" ];
 
-    xdg = {
-      autostart.enable = true;
-      menus.enable = true;
-      mime.enable = true;
-      icons.enable = true;
-    };
-
-    # The default max inotify watches is 8192.
-    # Nowadays most apps require a good number of inotify watches,
-    # the value below is used by default on several other distros.
-    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;
-
-    systemd.defaultUnit = mkIf cfg.autorun "graphical.target";
-
     systemd.services.display-manager =
       { description = "Display Manager";
 
@@ -763,17 +719,17 @@ in
         environment =
           optionalAttrs config.hardware.opengl.setLdLibraryPath
             { LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.addOpenGLRunpath.driverLink ]; }
-          // cfg.displayManager.job.environment;
+          // config.services.displayManager.environment;
 
         preStart =
           ''
-            ${cfg.displayManager.job.preStart}
+            ${config.services.displayManager.preStart}
 
             rm -f /tmp/.X0-lock
           '';
 
         # TODO: move declaring the systemd service to its own mkIf
-        script = mkIf (config.systemd.services.display-manager.enable == true) "${cfg.displayManager.job.execCmd}";
+        script = mkIf (config.systemd.services.display-manager.enable == true) "${config.services.displayManager.execCmd}";
 
         # Stop restarting if the display manager stops (crashes) 2 times
         # in one minute. Starting X typically takes 3-4s.
@@ -912,7 +868,6 @@ in
         ${cfg.extraConfig}
       '';
 
-    fonts.enableDefaultPackages = mkDefault true;
     fonts.packages = [
       (if cfg.upscaleDefaultCursor then fontcursormisc_hidpi else pkgs.xorg.fontcursormisc)
       pkgs.xorg.fontmiscmisc
diff --git a/nixos/modules/system/boot/clevis.md b/nixos/modules/system/boot/clevis.md
index dcbf55de60a83..39edc0fc38dfd 100644
--- a/nixos/modules/system/boot/clevis.md
+++ b/nixos/modules/system/boot/clevis.md
@@ -39,13 +39,17 @@ For more complete documentation on how to generate a secret with clevis, see the
 
 In order to activate unattended decryption of a resource at boot, enable the `clevis` module:
 
-```
-boot.initrd.clevis.enable = true;
+```nix
+{
+  boot.initrd.clevis.enable = true;
+}
 ```
 
 Then, specify the device you want to decrypt using a given clevis secret. Clevis will automatically try to decrypt the device at boot and will fallback to interactive unlocking if the decryption policy is not fulfilled.
-```
-boot.initrd.clevis.devices."/dev/nvme0n1p1".secretFile = ./nvme0n1p1.jwe;
+```nix
+{
+  boot.initrd.clevis.devices."/dev/nvme0n1p1".secretFile = ./nvme0n1p1.jwe;
+}
 ```
 
 Only `bcachefs`, `zfs` and `luks` encrypted devices are supported at this time.
diff --git a/nixos/modules/system/boot/initrd-ssh.nix b/nixos/modules/system/boot/initrd-ssh.nix
index 61e61f32bc5ee..43da2496d16c7 100644
--- a/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixos/modules/system/boot/initrd-ssh.nix
@@ -93,6 +93,21 @@ in
       defaultText = literalExpression "config.users.users.root.openssh.authorizedKeys.keys";
       description = lib.mdDoc ''
         Authorized keys for the root user on initrd.
+        You can combine the `authorizedKeys` and `authorizedKeyFiles` options.
+      '';
+      example = [
+        "ssh-rsa AAAAB3NzaC1yc2etc/etc/etcjwrsh8e596z6J0l7 example@host"
+        "ssh-ed25519 AAAAC3NzaCetcetera/etceteraJZMfk3QPfQ foo@bar"
+      ];
+    };
+
+    authorizedKeyFiles = mkOption {
+      type = types.listOf types.path;
+      default = config.users.users.root.openssh.authorizedKeys.keyFiles;
+      defaultText = literalExpression "config.users.users.root.openssh.authorizedKeys.keyFiles";
+      description = lib.mdDoc ''
+        Authorized keys taken from files for the root user on initrd.
+        You can combine the `authorizedKeyFiles` and `authorizedKeys` options.
       '';
     };
 
@@ -152,7 +167,7 @@ in
   in mkIf enabled {
     assertions = [
       {
-        assertion = cfg.authorizedKeys != [];
+        assertion = cfg.authorizedKeys != [] || cfg.authorizedKeyFiles != [];
         message = "You should specify at least one authorized key for initrd SSH";
       }
 
@@ -206,6 +221,9 @@ in
       ${concatStrings (map (key: ''
         echo ${escapeShellArg key} >> /root/.ssh/authorized_keys
       '') cfg.authorizedKeys)}
+      ${concatStrings (map (keyFile: ''
+        cat ${keyFile} >> /root/.ssh/authorized_keys
+      '') cfg.authorizedKeyFiles)}
 
       ${flip concatMapStrings cfg.hostKeys (path: ''
         # keys from Nix store are world-readable, which sshd doesn't like
@@ -236,9 +254,13 @@ in
 
       users.root.shell = mkIf (config.boot.initrd.network.ssh.shell != null) config.boot.initrd.network.ssh.shell;
 
-      contents."/etc/ssh/authorized_keys.d/root".text =
-        concatStringsSep "\n" config.boot.initrd.network.ssh.authorizedKeys;
-      contents."/etc/ssh/sshd_config".text = sshdConfig;
+      contents = {
+        "/etc/ssh/sshd_config".text = sshdConfig;
+        "/etc/ssh/authorized_keys.d/root".text =
+          concatStringsSep "\n" (
+            config.boot.initrd.network.ssh.authorizedKeys ++
+            (map (file: lib.fileContents file) config.boot.initrd.network.ssh.authorizedKeyFiles));
+      };
       storePaths = ["${package}/bin/sshd"];
 
       services.sshd = {
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index b0ac857feb4b8..950cff386d025 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/loader/generic-extlinux-compatible/extlinux-conf-builder.sh b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
index 1a0da0050291d..f2b281d23292b 100644
--- a/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
+++ b/nixos/modules/system/boot/loader/generic-extlinux-compatible/extlinux-conf-builder.sh
@@ -141,7 +141,13 @@ if [ "$numGenerations" -gt 0 ]; then
             | sort -n -r \
             | head -n $numGenerations); do
         link=/nix/var/nix/profiles/system-$generation-link
-        addEntry $link $generation
+        addEntry $link "${generation}-default"
+        for specialisation in $(
+            ls /nix/var/nix/profiles/system-$generation-link/specialisation \
+            | sort -n -r); do
+            link=/nix/var/nix/profiles/system-$generation-link/specialisation/$specialisation
+            addEntry $link "${generation}-${specialisation}"
+        done
     done >> $tmpFile
 fi
 
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 86a3875e2c67c..3020734783e7a 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 b6b0f64b94c81..9b0d750d12ce2 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/plymouth.nix b/nixos/modules/system/boot/plymouth.nix
index 16bca40993aeb..85f0fd4622df1 100644
--- a/nixos/modules/system/boot/plymouth.nix
+++ b/nixos/modules/system/boot/plymouth.nix
@@ -4,7 +4,6 @@ with lib;
 
 let
 
-  inherit (pkgs) nixos-icons;
   plymouth = pkgs.plymouth.override {
     systemd = config.boot.initrd.systemd.package;
   };
@@ -97,8 +96,8 @@ in
       logo = mkOption {
         type = types.path;
         # Dimensions are 48x48 to match GDM logo
-        default = "${nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png";
-        defaultText = literalExpression ''"''${nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png"'';
+        default = "${pkgs.nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png";
+        defaultText = literalExpression ''"''${pkgs.nixos-icons}/share/icons/hicolor/48x48/apps/nix-snowflake-white.png"'';
         example = literalExpression ''
           pkgs.fetchurl {
             url = "https://nixos.org/logo/nixos-hires.png";
@@ -107,6 +106,7 @@ in
         '';
         description = lib.mdDoc ''
           Logo which is displayed on the splash screen.
+          Currently supports PNG file format only.
         '';
       };
 
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 90a74c0ac5788..02a3f5113cc0a 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.nix b/nixos/modules/system/boot/systemd.nix
index a8885aee78f2b..aea6855f91c50 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -47,6 +47,9 @@ let
       "rescue.target"
       "rescue.service"
 
+      # systemd-debug-generator
+      "debug-shell.service"
+
       # Udev.
       "systemd-tmpfiles-setup-dev-early.service"
       "systemd-udevd-control.socket"
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index f83837fbc6d41..6546b500ed981 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -18,10 +18,10 @@ let
 
   cfg = config.boot.initrd.systemd;
 
-  # Copied from fedora
   upstreamUnits = [
     "basic.target"
     "ctrl-alt-del.target"
+    "debug-shell.service"
     "emergency.service"
     "emergency.target"
     "final.target"
@@ -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,10 +387,19 @@ 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}"
+      # `systemd` mounts root in initrd as read-only unless "rw" is on the kernel command line.
+      # For NixOS activation to succeed, we need to have root writable in initrd.
+      ++ lib.optional (config.boot.initrd.systemd.root == "gpt-auto") "rw";
 
     boot.initrd.systemd = {
-      initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package];
+      # bashInteractive is easier to use and also required by debug-shell.service
+      initrdBin = [pkgs.bashInteractive pkgs.coreutils cfg.package.kmod cfg.package];
       extraBin = {
         less = "${pkgs.less}/bin/less";
         mount = "${cfg.package.util-linux}/bin/mount";
@@ -448,6 +473,9 @@ in {
         "${cfg.package.util-linux}/bin/umount"
         "${cfg.package.util-linux}/bin/sulogin"
 
+        # required for script services
+        "${pkgs.runtimeShell}"
+
         # so NSS can look up usernames
         "${pkgs.glibc}/lib/libnss_files.so.2"
       ] ++ optionals (cfg.package.withCryptsetup && cfg.enableTpm2) [
@@ -495,7 +523,7 @@ in {
               case $o in
                   init=*)
                       IFS== read -r -a initParam <<< "$o"
-                      closure="$(dirname "''${initParam[1]}")"
+                      closure="''${initParam[1]}"
                       ;;
               esac
           done
@@ -506,6 +534,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 +589,5 @@ in {
         serviceConfig.Type = "oneshot";
       };
     };
-
-    boot.kernelParams = lib.mkIf (config.boot.resumeDevice != "") [ "resume=${config.boot.resumeDevice}" ];
   };
 }
diff --git a/nixos/modules/system/boot/systemd/repart.nix b/nixos/modules/system/boot/systemd/repart.nix
index 6cc387cb6f43f..7771f5fe06789 100644
--- a/nixos/modules/system/boot/systemd/repart.nix
+++ b/nixos/modules/system/boot/systemd/repart.nix
@@ -13,14 +13,14 @@ let
 
   partitionAssertions = lib.mapAttrsToList (fileName: definition:
     let
-      maxLabelLength = 36; # GPT_LABEL_MAX defined in systemd's gpt.h
+      inherit (utils.systemdUtils.lib) GPTMaxLabelLength;
       labelLength = builtins.stringLength definition.Label;
     in
     {
-      assertion = definition ? Label -> maxLabelLength >= labelLength;
+      assertion = definition ? Label -> GPTMaxLabelLength >= labelLength;
       message = ''
         The partition label '${definition.Label}' defined for '${fileName}' is ${toString labelLength}
-        characters long, but the maximum label length supported by systemd is ${toString maxLabelLength}.
+        characters long, but the maximum label length supported by systemd is ${toString GPTMaxLabelLength}.
       '';
     }
   ) cfg.partitions;
diff --git a/nixos/modules/system/boot/timesyncd.nix b/nixos/modules/system/boot/timesyncd.nix
index 2666e4cd6b284..ef17c1481abb4 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 0965b887c12e6..c8d3c2f6605f5 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/tasks/filesystems/bcachefs.nix b/nixos/modules/tasks/filesystems/bcachefs.nix
index ba33edd702f70..d7e83464391c4 100644
--- a/nixos/modules/tasks/filesystems/bcachefs.nix
+++ b/nixos/modules/tasks/filesystems/bcachefs.nix
@@ -57,7 +57,9 @@ let
   # bcachefs does not support mounting devices with colons in the path, ergo we don't (see #49671)
   firstDevice = fs: lib.head (lib.splitString ":" fs.device);
 
-  openCommand = name: fs: if config.boot.initrd.clevis.enable && (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices) then ''
+  useClevis = fs: config.boot.initrd.clevis.enable && (lib.hasAttr (firstDevice fs) config.boot.initrd.clevis.devices);
+
+  openCommand = name: fs: if useClevis fs then ''
     if clevis decrypt < /etc/clevis/${firstDevice fs}.jwe | bcachefs unlock ${firstDevice fs}
     then
       printf "unlocked ${name} using clevis\n"
@@ -92,8 +94,19 @@ let
         # As is, RemainAfterExit doesn't accomplish anything.
         RemainAfterExit = true;
       };
-      script = ''
-        ${config.boot.initrd.systemd.package}/bin/systemd-ask-password --timeout=0 "enter passphrase for ${name}" | exec ${pkgs.bcachefs-tools}/bin/bcachefs unlock "${device}"
+      script = let
+        unlock = ''${pkgs.bcachefs-tools}/bin/bcachefs unlock "${device}"'';
+        unlockInteractively = ''${config.boot.initrd.systemd.package}/bin/systemd-ask-password --timeout=0 "enter passphrase for ${name}" | exec ${unlock}'';
+      in if useClevis fs then ''
+        if ${config.boot.initrd.clevis.package}/bin/clevis decrypt < "/etc/clevis/${device}.jwe" | ${unlock}
+        then
+          printf "unlocked ${name} using clevis\n"
+        else
+          printf "falling back to interactive unlocking...\n"
+          ${unlockInteractively}
+        fi
+      '' else ''
+        ${unlockInteractively}
       '';
     };
   };
diff --git a/nixos/modules/tasks/filesystems/envfs.nix b/nixos/modules/tasks/filesystems/envfs.nix
index 365cb46ff2fe3..6719a03610d10 100644
--- a/nixos/modules/tasks/filesystems/envfs.nix
+++ b/nixos/modules/tasks/filesystems/envfs.nix
@@ -7,6 +7,7 @@ let
       device = "none";
       fsType = "envfs";
       options = [
+        "bind-mount=/bin"
         "fallback-path=${pkgs.runCommand "fallback-path" {} (''
           mkdir -p $out
           ln -s ${config.environment.usrbinenv} $out/env
@@ -15,6 +16,9 @@ let
         "nofail"
       ];
     };
+    # We need to bind-mount /bin to /usr/bin, because otherwise upgrading
+    # from envfs < 1.0.5 will cause having the old envs with no /bin bind mount.
+    # Systemd is smart enough to not mount /bin if it's already mounted.
     "/bin" = {
       device = "/usr/bin";
       fsType = "none";
diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix
index 462568b5db3e9..a447eef37f9a4 100644
--- a/nixos/modules/tasks/filesystems/nfs.nix
+++ b/nixos/modules/tasks/filesystems/nfs.nix
@@ -13,7 +13,46 @@ let
   format = pkgs.formats.ini {};
 
   idmapdConfFile = format.generate "idmapd.conf" cfg.idmapd.settings;
-  nfsConfFile = pkgs.writeText "nfs.conf" cfg.extraConfig;
+
+  # merge parameters from services.nfs.server
+  nfsConfSettings =
+    optionalAttrs (cfg.server.nproc != null) {
+      nfsd.threads = cfg.server.nproc;
+    } // optionalAttrs (cfg.server.hostName != null) {
+      nfsd.host= cfg.hostName;
+    } // optionalAttrs (cfg.server.mountdPort != null) {
+      mountd.port = cfg.server.mountdPort;
+    } // optionalAttrs (cfg.server.statdPort != null) {
+      statd.port = cfg.server.statdPort;
+    } // optionalAttrs (cfg.server.lockdPort != null) {
+      lockd.port = cfg.server.lockdPort;
+      lockd.udp-port = cfg.server.lockdPort;
+    };
+
+  nfsConfDeprecated = cfg.extraConfig + ''
+    [nfsd]
+    threads=${toString cfg.server.nproc}
+    ${optionalString (cfg.server.hostName != null) "host=${cfg.server.hostName}"}
+    ${cfg.server.extraNfsdConfig}
+
+    [mountd]
+    ${optionalString (cfg.server.mountdPort != null) "port=${toString cfg.server.mountdPort}"}
+
+    [statd]
+    ${optionalString (cfg.server.statdPort != null) "port=${toString cfg.server.statdPort}"}
+
+    [lockd]
+    ${optionalString (cfg.server.lockdPort != null) ''
+      port=${toString cfg.server.lockdPort}
+      udp-port=${toString cfg.server.lockdPort}
+    ''}
+  '';
+
+  nfsConfFile =
+    if cfg.settings != {}
+    then format.generate "nfs.conf" (recursiveUpdate nfsConfSettings cfg.settings)
+    else pkgs.writeText "nfs.conf" nfsConfDeprecated;
+
   requestKeyConfFile = pkgs.writeText "request-key.conf" ''
     create id_resolver * * ${pkgs.nfs-utils}/bin/nfsidmap -t 600 %k %d
   '';
@@ -46,6 +85,19 @@ in
           }
         '';
       };
+      settings = mkOption {
+        type = format.type;
+        default = {};
+        description = lib.mdDoc ''
+          General configuration for NFS daemons and tools.
+          See nfs.conf(5) and related man pages for details.
+        '';
+        example = literalExpression ''
+          {
+            mountd.manage-gids = true;
+          }
+        '';
+      };
       extraConfig = mkOption {
         type = types.lines;
         default = "";
@@ -60,6 +112,17 @@ in
 
   config = mkIf (config.boot.supportedFilesystems.nfs or config.boot.supportedFilesystems.nfs4 or false) {
 
+    warnings =
+      (optional (cfg.extraConfig != "") ''
+        `services.nfs.extraConfig` is deprecated. Use `services.nfs.settings` instead.
+      '') ++ (optional (cfg.server.extraNfsdConfig != "") ''
+        `services.nfs.server.extraNfsdConfig` is deprecated. Use `services.nfs.settings` instead.
+      '');
+    assertions = [{
+      assertion = cfg.settings != {} -> cfg.extraConfig == "" && cfg.server.extraNfsdConfig == "";
+      message = "`services.nfs.settings` cannot be used together with `services.nfs.extraConfig` and `services.nfs.server.extraNfsdConfig`.";
+    }];
+
     services.rpcbind.enable = true;
 
     services.nfs.idmapd.settings = {
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index d11424c11c810..a3e332acbda90 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -576,6 +576,8 @@ in
             copy_bin_and_libs ${cfgZfs.package}/sbin/zfs
             copy_bin_and_libs ${cfgZfs.package}/sbin/zdb
             copy_bin_and_libs ${cfgZfs.package}/sbin/zpool
+            copy_bin_and_libs ${cfgZfs.package}/lib/udev/vdev_id
+            copy_bin_and_libs ${cfgZfs.package}/lib/udev/zvol_id
           '';
         extraUtilsCommandsTest =
           mkIf (!config.boot.initrd.systemd.enable) ''
@@ -632,7 +634,12 @@ in
             zfs = "${cfgZfs.package}/sbin/zfs";
             awk = "${pkgs.gawk}/bin/awk";
           };
+          storePaths = [
+            "${cfgZfs.package}/lib/udev/vdev_id"
+            "${cfgZfs.package}/lib/udev/zvol_id"
+          ];
         };
+        services.udev.packages = [cfgZfs.package]; # to hook zvol naming, in stage 1
       };
 
       systemd.shutdownRamfs.contents."/etc/systemd/system-shutdown/zpool".source = pkgs.writeShellScript "zpool-sync-shutdown" ''
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 6aa718c1975d7..cf4a734b3f053 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"
+        ];
       })
     ];
 
@@ -166,7 +170,7 @@ in
       # thing, but for VM tests it should provide a bit more
       # determinism (e.g. if the VM runs at lower speed, then
       # timeouts in the VM should also be delayed).
-      "clock=acpi_pm"
+      "clocksource=acpi_pm"
     ];
 
     # `xwininfo' is used by the test driver to query open windows.
@@ -212,7 +216,7 @@ in
     # uses credentials to set passwords on users.
     users.users.root.hashedPasswordFile = mkOverride 150 "${pkgs.writeText "hashed-password.root" ""}";
 
-    services.xserver.displayManager.job.logToJournal = true;
+    services.displayManager.logToJournal = true;
 
     # Make sure we use the Guest Agent from the QEMU package for testing
     # to reduce the closure size required for the tests.
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index c7fe1bed51592..77730178422c3 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/digital-ocean-config.nix b/nixos/modules/virtualisation/digital-ocean-config.nix
index e004b7880aad0..ea297e0c23a7d 100644
--- a/nixos/modules/virtualisation/digital-ocean-config.nix
+++ b/nixos/modules/virtualisation/digital-ocean-config.nix
@@ -31,7 +31,7 @@ with lib;
       hostName = config.networking.hostName;
       doMetadataFile = "/run/do-metadata/v1.json";
     in mkMerge [{
-      fileSystems."/" = {
+      fileSystems."/" = lib.mkDefault {
         device = "/dev/disk/by-label/nixos";
         autoResize = true;
         fsType = "ext4";
@@ -41,11 +41,7 @@ with lib;
         kernelParams = [ "console=ttyS0" "panic=1" "boot.panic_on_fail" ];
         initrd.kernelModules = [ "virtio_scsi" ];
         kernelModules = [ "virtio_pci" "virtio_net" ];
-        loader = {
-          grub.device = "/dev/vda";
-          timeout = 0;
-          grub.configurationLimit = 0;
-        };
+        loader.grub.devices = lib.mkDefault ["/dev/vda"];
       };
       services.openssh = {
         enable = mkDefault true;
diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix
index da7873c7bec86..08e8288fb2038 100644
--- a/nixos/modules/virtualisation/incus.nix
+++ b/nixos/modules/virtualisation/incus.nix
@@ -1,8 +1,98 @@
-{ config, lib, pkgs, ... }:
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
 
 let
   cfg = config.virtualisation.incus;
   preseedFormat = pkgs.formats.yaml { };
+
+  serverBinPath = ''${pkgs.qemu_kvm}/libexec:${
+    lib.makeBinPath (
+      with pkgs;
+      [
+        cfg.package
+
+        acl
+        attr
+        bash
+        btrfs-progs
+        cdrkit
+        coreutils
+        criu
+        dnsmasq
+        e2fsprogs
+        findutils
+        getent
+        gnugrep
+        gnused
+        gnutar
+        gptfdisk
+        gzip
+        iproute2
+        iptables
+        kmod
+        lvm2
+        minio
+        nftables
+        qemu_kvm
+        qemu-utils
+        rsync
+        squashfsTools
+        systemd
+        thin-provisioning-tools
+        util-linux
+        virtiofsd
+        xz
+
+        (writeShellScriptBin "apparmor_parser" ''
+          exec '${apparmor-parser}/bin/apparmor_parser' -I '${apparmor-profiles}/etc/apparmor.d' "$@"
+        '')
+      ]
+      ++ lib.optionals config.boot.zfs.enabled [
+        config.boot.zfs.package
+        "${config.boot.zfs.package}/lib/udev"
+      ]
+      ++ lib.optionals config.virtualisation.vswitch.enable [ config.virtualisation.vswitch.package ]
+    )
+  }'';
+
+  # https://github.com/lxc/incus/blob/cff35a29ee3d7a2af1f937cbb6cf23776941854b/internal/server/instance/drivers/driver_qemu.go#L123
+  OVMF2MB = pkgs.OVMF.override {
+    secureBoot = true;
+    fdSize2MB = true;
+  };
+  ovmf-prefix = if pkgs.stdenv.hostPlatform.isAarch64 then "AAVMF" else "OVMF";
+  ovmf = pkgs.linkFarm "incus-ovmf" [
+    # 2MB must remain the default or existing VMs will fail to boot. New VMs will prefer 4MB
+    {
+      name = "OVMF_CODE.fd";
+      path = "${OVMF2MB.fd}/FV/${ovmf-prefix}_CODE.fd";
+    }
+    {
+      name = "OVMF_VARS.fd";
+      path = "${OVMF2MB.fd}/FV/${ovmf-prefix}_VARS.fd";
+    }
+    {
+      name = "OVMF_VARS.ms.fd";
+      path = "${OVMF2MB.fd}/FV/${ovmf-prefix}_VARS.fd";
+    }
+
+    {
+      name = "OVMF_CODE.4MB.fd";
+      path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_CODE.fd";
+    }
+    {
+      name = "OVMF_VARS.4MB.fd";
+      path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd";
+    }
+    {
+      name = "OVMF_VARS.4MB.ms.fd";
+      path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd";
+    }
+  ];
 in
 {
   meta = {
@@ -11,26 +101,31 @@ in
 
   options = {
     virtualisation.incus = {
-      enable = lib.mkEnableOption (lib.mdDoc ''
+      enable = lib.mkEnableOption ''
         incusd, a daemon that manages containers and virtual machines.
 
         Users in the "incus-admin" group can interact with
         the daemon (e.g. to start or stop containers) using the
         {command}`incus` command line tool, among others.
-      '');
+      '';
 
-      package = lib.mkPackageOption pkgs "incus" { };
+      package = lib.mkPackageOption pkgs "incus-lts" { };
 
       lxcPackage = lib.mkPackageOption pkgs "lxc" { };
 
+      clientPackage = lib.mkOption {
+        type = lib.types.package;
+        default = cfg.package.client;
+        defaultText = lib.literalExpression "config.virtualisation.incus.package.client";
+        description = "The incus client package to use. This package is added to PATH.";
+      };
+
       preseed = lib.mkOption {
-        type = lib.types.nullOr (
-          lib.types.submodule { freeformType = preseedFormat.type; }
-        );
+        type = lib.types.nullOr (lib.types.submodule { freeformType = preseedFormat.type; });
 
         default = null;
 
-        description = lib.mdDoc ''
+        description = ''
           Configuration for Incus preseed, see
           <https://linuxcontainers.org/incus/docs/main/howto/initialize/#non-interactive-configuration>
           for supported values.
@@ -80,18 +175,16 @@ in
         };
       };
 
-      socketActivation = lib.mkEnableOption (
-        lib.mdDoc ''
-          socket-activation for starting incus.service. Enabling this option
-          will stop incus.service from starting automatically on boot.
-        ''
-      );
+      socketActivation = lib.mkEnableOption (''
+        socket-activation for starting incus.service. Enabling this option
+        will stop incus.service from starting automatically on boot.
+      '');
 
       startTimeout = lib.mkOption {
         type = lib.types.ints.unsigned;
         default = 600;
         apply = toString;
-        description = lib.mdDoc ''
+        description = ''
           Time to wait (in seconds) for incusd to become ready to process requests.
           If incusd does not reply within the configured time, `incus.service` will be
           considered failed and systemd will attempt to restart it.
@@ -99,9 +192,12 @@ in
       };
 
       ui = {
-        enable = lib.mkEnableOption (lib.mdDoc "(experimental) Incus UI");
+        enable = lib.mkEnableOption "(experimental) Incus UI";
 
-        package = lib.mkPackageOption pkgs [ "incus" "ui" ] { };
+        package = lib.mkPackageOption pkgs [
+          "incus"
+          "ui"
+        ] { };
       };
     };
   };
@@ -109,7 +205,12 @@ in
   config = lib.mkIf cfg.enable {
     assertions = [
       {
-        assertion = !(config.networking.firewall.enable && !config.networking.nftables.enable && config.virtualisation.incus.enable);
+        assertion =
+          !(
+            config.networking.firewall.enable
+            && !config.networking.nftables.enable
+            && config.virtualisation.incus.enable
+          );
         message = "Incus on NixOS is unsupported using iptables. Set `networking.nftables.enable = true;`";
       }
     ];
@@ -137,7 +238,12 @@ in
       "vhost_vsock"
     ] ++ lib.optionals (!config.networking.nftables.enable) [ "iptable_mangle" ];
 
-    environment.systemPackages = [ cfg.package ];
+    environment.systemPackages = [
+      cfg.clientPackage
+
+      # gui console support
+      pkgs.spice-gtk
+    ];
 
     # Note: the following options are also declared in virtualisation.lxc, but
     # the latter can't be simply enabled to reuse the formers, because it
@@ -164,31 +270,24 @@ in
         "network-online.target"
         "lxcfs.service"
         "incus.socket"
-      ]
-        ++ lib.optional config.virtualisation.vswitch.enable "ovs-vswitchd.service";
+      ] ++ lib.optionals config.virtualisation.vswitch.enable [ "ovs-vswitchd.service" ];
 
       requires = [
         "lxcfs.service"
         "incus.socket"
-      ]
-        ++ lib.optional config.virtualisation.vswitch.enable "ovs-vswitchd.service";
+      ] ++ lib.optionals config.virtualisation.vswitch.enable [ "ovs-vswitchd.service" ];
 
-      wants = [
-        "network-online.target"
-      ];
+      wants = [ "network-online.target" ];
 
-      path = lib.optionals config.boot.zfs.enabled [
-        config.boot.zfs.package
-        "${config.boot.zfs.package}/lib/udev"
-      ]
-        ++ lib.optional config.virtualisation.vswitch.enable config.virtualisation.vswitch.package;
-
-      environment = lib.mkMerge [ {
-        # Override Path to the LXC template configuration directory
-        INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
-      } (lib.mkIf (cfg.ui.enable) {
-        "INCUS_UI" = cfg.ui.package;
-      }) ];
+      environment = lib.mkMerge [
+        {
+          INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
+          INCUS_OVMF_PATH = ovmf;
+          INCUS_USBIDS_PATH = "${pkgs.hwdata}/share/hwdata/usb.ids";
+          PATH = lib.mkForce serverBinPath;
+        }
+        (lib.mkIf (cfg.ui.enable) { "INCUS_UI" = cfg.ui.package; })
+      ];
 
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/incusd --group incus-admin";
@@ -222,15 +321,13 @@ in
     systemd.services.incus-preseed = lib.mkIf (cfg.preseed != null) {
       description = "Incus initialization with preseed file";
 
-      wantedBy = ["incus.service"];
-      after = ["incus.service"];
-      bindsTo = ["incus.service"];
-      partOf = ["incus.service"];
+      wantedBy = [ "incus.service" ];
+      after = [ "incus.service" ];
+      bindsTo = [ "incus.service" ];
+      partOf = [ "incus.service" ];
 
       script = ''
-        ${cfg.package}/bin/incus admin init --preseed <${
-          preseedFormat.generate "incus-preseed.yaml" cfg.preseed
-        }
+        ${cfg.package}/bin/incus admin init --preseed <${preseedFormat.generate "incus-preseed.yaml" cfg.preseed}
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
index e0d61b1754949..aa692cfff9044 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
@@ -33,7 +33,7 @@ in {
         '';
       };
 
-      package = lib.mkPackageOption pkgs "lxd" { };
+      package = lib.mkPackageOption pkgs "lxd-lts" { };
 
       lxcPackage = lib.mkPackageOption pkgs "lxc" {
         extraDescription = ''
@@ -139,7 +139,7 @@ in {
       ui = {
         enable = lib.mkEnableOption (lib.mdDoc "(experimental) LXD UI");
 
-        package = lib.mkPackageOption pkgs [ "lxd-unwrapped" "ui" ] { };
+        package = lib.mkPackageOption pkgs [ "lxd-ui" ] { };
       };
     };
   };
diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix
index d4fa707b2dd5f..bde1ff9eeb98d 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}'
+                              '';
+                            }
                           ];
                         };
                       };
@@ -828,7 +834,10 @@ in
               script = startScript containerConfig;
               postStart = postStartScript containerConfig;
               serviceConfig = serviceDirectives containerConfig;
-              unitConfig.RequiresMountsFor = lib.optional (!containerConfig.ephemeral) "${stateDirectory}/%i";
+              unitConfig.RequiresMountsFor = lib.optional (!containerConfig.ephemeral) "${stateDirectory}/%i"
+                ++ builtins.map
+                  (d: if d.hostPath != null then d.hostPath else d.mountPoint)
+                  (builtins.attrValues cfg.bindMounts);
               environment.root = if containerConfig.ephemeral then "/run/nixos-containers/%i" else "${stateDirectory}/%i";
             } // (
             optionalAttrs containerConfig.autoStart
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index b5a8b08eee70d..a8e5db4f8a5ba 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -186,7 +186,7 @@ let
         NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
         # VM needs writable EFI vars
         if ! test -e "$NIX_EFI_VARS"; then
-        ${if cfg.useBootLoader then
+        ${if cfg.efi.keepVariables then
             # We still need the EFI var from the make-disk-image derivation
             # because our "switch-to-configuration" process might
             # write into it and we want to keep this data.
@@ -905,6 +905,13 @@ in
             Defaults to OVMF.
           '';
       };
+
+      keepVariables = mkOption {
+        type = types.bool;
+        default = cfg.useBootLoader;
+        defaultText = literalExpression "cfg.useBootLoader";
+        description = "Whether to keep EFI variable values from the generated system image";
+      };
     };
 
     virtualisation.tpm = {
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
index 94f70c65436c1..c2606968d3bed 100644
--- a/nixos/modules/virtualisation/virtualbox-guest.nix
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -5,14 +5,32 @@
 with lib;
 
 let
-
   cfg = config.virtualisation.virtualbox.guest;
   kernel = config.boot.kernelPackages;
 
-in
+  mkVirtualBoxUserService = serviceArgs: {
+    description = "VirtualBox Guest User Services ${serviceArgs}";
 
-{
+    wantedBy = [ "graphical-session.target" ];
+    partOf = [ "graphical-session.target" ];
+
+    # The graphical session may not be ready when starting the service
+    # Hence, check if the DISPLAY env var is set, otherwise fail, wait and retry again
+    startLimitBurst = 20;
+
+    unitConfig.ConditionVirtualization = "oracle";
 
+    # Check if the display environment is ready, otherwise fail
+    preStart = "${pkgs.bash}/bin/bash -c \"if [ -z $DISPLAY ]; then exit 1; fi\"";
+    serviceConfig = {
+      ExecStart = "@${kernel.virtualboxGuestAdditions}/bin/VBoxClient --foreground ${serviceArgs}";
+      # Wait after a failure, hoping that the display environment is ready after waiting
+      RestartSec = 2;
+      Restart = "always";
+    };
+  };
+in
+{
   ###### interface
 
   options.virtualisation.virtualbox.guest = {
@@ -22,32 +40,45 @@ in
       description = lib.mdDoc "Whether to enable the VirtualBox service and other guest additions.";
     };
 
-    x11 = mkOption {
+    clipboard = mkOption {
       default = true;
       type = types.bool;
-      description = lib.mdDoc "Whether to enable x11 graphics";
+      description = lib.mdDoc "Whether to enable clipboard support.";
+    };
+
+    seamless = mkOption {
+      default = true;
+      type = types.bool;
+      description = lib.mdDoc "Whether to enable seamless mode. When activated windows from the guest appear next to the windows of the host.";
+    };
+
+    draganddrop = mkOption {
+      default = true;
+      type = types.bool;
+      description = lib.mdDoc "Whether to enable drag and drop support.";
     };
   };
 
   ###### implementation
 
-  config = mkIf cfg.enable (mkMerge [{
-    assertions = [{
-      assertion = pkgs.stdenv.hostPlatform.isx86;
-      message = "Virtualbox not currently supported on ${pkgs.stdenv.hostPlatform.system}";
-    }];
+  config = mkIf cfg.enable (mkMerge [
+    {
+      assertions = [{
+        assertion = pkgs.stdenv.hostPlatform.isx86;
+        message = "Virtualbox not currently supported on ${pkgs.stdenv.hostPlatform.system}";
+      }];
 
-    environment.systemPackages = [ kernel.virtualboxGuestAdditions ];
+      environment.systemPackages = [ kernel.virtualboxGuestAdditions ];
 
-    boot.extraModulePackages = [ kernel.virtualboxGuestAdditions ];
+      boot.extraModulePackages = [ kernel.virtualboxGuestAdditions ];
 
-    boot.supportedFilesystems = [ "vboxsf" ];
-    boot.initrd.supportedFilesystems = [ "vboxsf" ];
+      boot.supportedFilesystems = [ "vboxsf" ];
+      boot.initrd.supportedFilesystems = [ "vboxsf" ];
 
-    users.groups.vboxsf.gid = config.ids.gids.vboxsf;
+      users.groups.vboxsf.gid = config.ids.gids.vboxsf;
 
-    systemd.services.virtualbox =
-      { description = "VirtualBox Guest Services";
+      systemd.services.virtualbox = {
+        description = "VirtualBox Guest Services";
 
         wantedBy = [ "multi-user.target" ];
         requires = [ "dev-vboxguest.device" ];
@@ -58,36 +89,27 @@ in
         serviceConfig.ExecStart = "@${kernel.virtualboxGuestAdditions}/bin/VBoxService VBoxService --foreground";
       };
 
-    services.udev.extraRules =
-      ''
-        # /dev/vboxuser is necessary for VBoxClient to work.  Maybe we
-        # should restrict this to logged-in users.
-        KERNEL=="vboxuser",  OWNER="root", GROUP="root", MODE="0666"
-
-        # Allow systemd dependencies on vboxguest.
-        SUBSYSTEM=="misc", KERNEL=="vboxguest", TAG+="systemd"
-      '';
-  } (mkIf cfg.x11 {
-    services.xserver.videoDrivers = [ "vmware" "virtualbox" "modesetting" ];
-
-    services.xserver.config =
-      ''
-        Section "InputDevice"
-          Identifier "VBoxMouse"
-          Driver "vboxmouse"
-        EndSection
-      '';
-
-    services.xserver.serverLayoutSection =
-      ''
-        InputDevice "VBoxMouse"
-      '';
-
-    services.xserver.displayManager.sessionCommands =
-      ''
-        PATH=${makeBinPath [ pkgs.gnugrep pkgs.which pkgs.xorg.xorgserver.out ]}:$PATH \
-          ${kernel.virtualboxGuestAdditions}/bin/VBoxClient-all
-      '';
-  })]);
-
+      services.udev.extraRules =
+        ''
+          # /dev/vboxuser is necessary for VBoxClient to work.  Maybe we
+          # should restrict this to logged-in users.
+          KERNEL=="vboxuser",  OWNER="root", GROUP="root", MODE="0666"
+
+          # Allow systemd dependencies on vboxguest.
+          SUBSYSTEM=="misc", KERNEL=="vboxguest", TAG+="systemd"
+        '';
+
+      systemd.user.services.virtualboxClientVmsvga = mkVirtualBoxUserService "--vmsvga-session";
+    }
+    (
+      mkIf cfg.clipboard {
+        systemd.user.services.virtualboxClientClipboard = mkVirtualBoxUserService "--clipboard";
+      }
+    )
+    (
+      mkIf cfg.seamless {
+        systemd.user.services.virtualboxClientSeamless = mkVirtualBoxUserService "--seamless";
+      }
+    )
+  ]);
 }
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index 459700514ffc5..96b24feeb0631 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -169,11 +169,6 @@ in rec {
         (onFullSupported "nixpkgs.jdk")
         (onSystems ["x86_64-linux"] "nixpkgs.mesa_i686") # i686 sanity check + useful
         ["nixpkgs.tarball"]
-
-        # Ensure that nixpkgs-check-by-name is available in nixos-unstable,
-        # so that a pre-built version can be used in CI for PR's
-        # See ../pkgs/test/nixpkgs-check-by-name/README.md
-        (onSystems ["x86_64-linux"] "nixpkgs.tests.nixpkgs-check-by-name")
       ];
     };
 }
diff --git a/nixos/release.nix b/nixos/release.nix
index ff60b0b79f6d9..2f31973569bf9 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -441,7 +441,7 @@ in rec {
 
     kde = makeClosure ({ ... }:
       { services.xserver.enable = true;
-        services.xserver.displayManager.sddm.enable = true;
+        services.displayManager.sddm.enable = true;
         services.xserver.desktopManager.plasma5.enable = true;
       });
 
diff --git a/nixos/tests/agda.nix b/nixos/tests/agda.nix
index 6f51300111acf..a8e90725c06b0 100644
--- a/nixos/tests/agda.nix
+++ b/nixos/tests/agda.nix
@@ -25,13 +25,6 @@ in
   };
 
   testScript = ''
-    assert (
-        "${pkgs.agdaPackages.lib.interfaceFile "Everything.agda"}" == "Everything.agdai"
-    ), "wrong interface file for Everything.agda"
-    assert (
-        "${pkgs.agdaPackages.lib.interfaceFile "tmp/Everything.agda.md"}" == "tmp/Everything.agdai"
-    ), "wrong interface file for tmp/Everything.agda.md"
-
     # Minimal script that typechecks
     machine.succeed("touch TestEmpty.agda")
     machine.succeed("agda TestEmpty.agda")
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index e9d9cb914867d..8961bbc075c94 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -219,6 +219,7 @@ in {
   containers-physical_interfaces = handleTest ./containers-physical_interfaces.nix {};
   containers-portforward = handleTest ./containers-portforward.nix {};
   containers-reloadable = handleTest ./containers-reloadable.nix {};
+  containers-require-bind-mounts = handleTest ./containers-require-bind-mounts.nix {};
   containers-restart_networking = handleTest ./containers-restart_networking.nix {};
   containers-tmpfs = handleTest ./containers-tmpfs.nix {};
   containers-unified-hierarchy = handleTest ./containers-unified-hierarchy.nix {};
@@ -233,6 +234,7 @@ in {
   croc = handleTest ./croc.nix {};
   darling = handleTest ./darling.nix {};
   dae = handleTest ./dae.nix {};
+  davis = handleTest ./davis.nix {};
   dconf = handleTest ./dconf.nix {};
   deconz = handleTest ./deconz.nix {};
   deepin = handleTest ./deepin.nix {};
@@ -290,8 +292,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 {};
@@ -309,6 +311,7 @@ in {
   firefox-devedition = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-devedition; };
   firefox-esr    = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr; }; # used in `tested` job
   firefox-esr-115 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-115; };
+  firefoxpwa = handleTest ./firefoxpwa.nix {};
   firejail = handleTest ./firejail.nix {};
   firewall = handleTest ./firewall.nix { nftables = false; };
   firewall-nftables = handleTest ./firewall.nix { nftables = true; };
@@ -456,7 +459,7 @@ in {
   kerberos = handleTest ./kerberos/default.nix {};
   kernel-generic = handleTest ./kernel-generic.nix {};
   kernel-latest-ath-user-regd = handleTest ./kernel-latest-ath-user-regd.nix {};
-  kernel-rust = handleTestOn ["x86_64-linux"] ./kernel-rust.nix {};
+  kernel-rust = handleTest ./kernel-rust.nix {};
   keter = handleTest ./keter.nix {};
   kexec = handleTest ./kexec.nix {};
   keycloak = discoverTests (import ./keycloak.nix);
@@ -464,7 +467,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,12 +516,15 @@ 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 {};
   matrix-conduit = handleTest ./matrix/conduit.nix {};
   matrix-synapse = handleTest ./matrix/synapse.nix {};
   matrix-synapse-workers = handleTest ./matrix/synapse-workers.nix {};
+  mautrix-meta-postgres = handleTest ./matrix/mautrix-meta-postgres.nix {};
+  mautrix-meta-sqlite = handleTest ./matrix/mautrix-meta-sqlite.nix {};
   mattermost = handleTest ./mattermost.nix {};
   mealie = handleTest ./mealie.nix {};
   mediamtx = handleTest ./mediamtx.nix {};
@@ -528,6 +534,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 {};
@@ -540,7 +547,9 @@ in {
   mobilizon = handleTest ./mobilizon.nix {};
   mod_perl = handleTest ./mod_perl.nix {};
   molly-brown = handleTest ./molly-brown.nix {};
+  mollysocket = handleTest ./mollysocket.nix { };
   monado = handleTest ./monado.nix {};
+  monetdb = handleTest ./monetdb.nix {};
   monica = handleTest ./web-apps/monica.nix {};
   mongodb = handleTest ./mongodb.nix {};
   moodle = handleTest ./moodle.nix {};
@@ -549,6 +558,7 @@ in {
   morty = handleTest ./morty.nix {};
   mosquitto = handleTest ./mosquitto.nix {};
   moosefs = handleTest ./moosefs.nix {};
+  movim = discoverTests (import ./web-apps/movim { inherit handleTestOn; });
   mpd = handleTest ./mpd.nix {};
   mpv = handleTest ./mpv.nix {};
   mtp = handleTest ./mtp.nix {};
@@ -560,6 +570,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 +590,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.scripted = handleTest ./networking/networkd-and-scripted.nix { networkd = false; };
@@ -613,6 +625,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 {};
@@ -639,6 +652,8 @@ in {
   nvmetcfg = handleTest ./nvmetcfg.nix {};
   nzbget = handleTest ./nzbget.nix {};
   nzbhydra2 = handleTest ./nzbhydra2.nix {};
+  ocis = handleTest ./ocis.nix {};
+  oddjobd = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./oddjobd.nix {};
   oh-my-zsh = handleTest ./oh-my-zsh.nix {};
   ollama = handleTest ./ollama.nix {};
   ombi = handleTest ./ombi.nix {};
@@ -690,6 +705,7 @@ in {
   pgmanage = handleTest ./pgmanage.nix {};
   pgvecto-rs = handleTest ./pgvecto-rs.nix {};
   phosh = handleTest ./phosh.nix {};
+  photonvision = handleTest ./photonvision.nix {};
   photoprism = handleTest ./photoprism.nix {};
   php = handleTest ./php {};
   php81 = handleTest ./php { php = pkgs.php81; };
@@ -728,6 +744,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 {};
@@ -761,6 +778,7 @@ in {
   redis = handleTest ./redis.nix {};
   redmine = handleTest ./redmine.nix {};
   restartByActivationScript = handleTest ./restart-by-activation-script.nix {};
+  restic-rest-server = handleTest ./restic-rest-server.nix {};
   restic = handleTest ./restic.nix {};
   retroarch = handleTest ./retroarch.nix {};
   rkvm = handleTest ./rkvm {};
@@ -782,6 +800,7 @@ in {
   sanoid = handleTest ./sanoid.nix {};
   scaphandre = handleTest ./scaphandre.nix {};
   schleuder = handleTest ./schleuder.nix {};
+  scion-freestanding-deployment = handleTest ./scion/freestanding-deployment {};
   scrutiny = handleTest ./scrutiny.nix {};
   sddm = handleTest ./sddm.nix {};
   seafile = handleTest ./seafile.nix {};
@@ -796,6 +815,7 @@ in {
   shattered-pixel-dungeon = handleTest ./shattered-pixel-dungeon.nix {};
   shiori = handleTest ./shiori.nix {};
   signal-desktop = handleTest ./signal-desktop.nix {};
+  silverbullet = handleTest ./silverbullet.nix {};
   simple = handleTest ./simple.nix {};
   sing-box = handleTest ./sing-box.nix {};
   slimserver = handleTest ./slimserver.nix {};
@@ -808,6 +828,7 @@ in {
   soapui = handleTest ./soapui.nix {};
   soft-serve = handleTest ./soft-serve.nix {};
   sogo = handleTest ./sogo.nix {};
+  soju = handleTest ./soju.nix {};
   solanum = handleTest ./solanum.nix {};
   sonarr = handleTest ./sonarr.nix {};
   sonic-server = handleTest ./sonic-server.nix {};
@@ -893,6 +914,7 @@ in {
   systemd-sysusers-immutable = runTest ./systemd-sysusers-immutable.nix;
   systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
   systemd-timesyncd-nscd-dnssec = handleTest ./systemd-timesyncd-nscd-dnssec.nix {};
+  systemd-user-linger = handleTest ./systemd-user-linger.nix {};
   systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {};
   systemd-misc = handleTest ./systemd-misc.nix {};
   systemd-userdbd = handleTest ./systemd-userdbd.nix {};
@@ -902,6 +924,7 @@ in {
   tang = handleTest ./tang.nix {};
   taskserver = handleTest ./taskserver.nix {};
   tayga = handleTest ./tayga.nix {};
+  technitium-dns-server = handleTest ./technitium-dns-server.nix {};
   teeworlds = handleTest ./teeworlds.nix {};
   telegraf = handleTest ./telegraf.nix {};
   teleport = handleTest ./teleport.nix {};
@@ -953,6 +976,7 @@ in {
   user-activation-scripts = handleTest ./user-activation-scripts.nix {};
   user-expiry = runTest ./user-expiry.nix;
   user-home-mode = handleTest ./user-home-mode.nix {};
+  ustreamer = handleTest ./ustreamer.nix {};
   uwsgi = handleTest ./uwsgi.nix {};
   v2ray = handleTest ./v2ray.nix {};
   varnish60 = handleTest ./varnish.nix { package = pkgs.varnish60; };
@@ -972,6 +996,7 @@ in {
   vsftpd = handleTest ./vsftpd.nix {};
   warzone2100 = handleTest ./warzone2100.nix {};
   wasabibackend = handleTest ./wasabibackend.nix {};
+  wastebin = handleTest ./wastebin.nix {};
   watchdogd = handleTest ./watchdogd.nix {};
   webhook = runTest ./webhook.nix;
   wiki-js = handleTest ./wiki-js.nix {};
@@ -979,6 +1004,7 @@ in {
   wireguard = handleTest ./wireguard {};
   without-nix = handleTest ./without-nix.nix {};
   wmderland = handleTest ./wmderland.nix {};
+  workout-tracker = handleTest ./workout-tracker.nix {};
   wpa_supplicant = handleTest ./wpa_supplicant.nix {};
   wordpress = handleTest ./wordpress.nix {};
   wrappers = handleTest ./wrappers.nix {};
diff --git a/nixos/tests/armagetronad.nix b/nixos/tests/armagetronad.nix
index ff2841dedd218..d59827354b771 100644
--- a/nixos/tests/armagetronad.nix
+++ b/nixos/tests/armagetronad.nix
@@ -1,4 +1,9 @@
-import ./make-test-python.nix ({ pkgs, ...} :
+{ system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../.. { inherit system config; }
+}:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
 
 let
   user = "alice";
@@ -16,7 +21,8 @@ let
       test-support.displayManager.auto.user = user;
     };
 
-in {
+in
+makeTest {
   name = "armagetronad";
   meta = with pkgs.lib.maintainers; {
     maintainers = [ numinit ];
@@ -269,4 +275,4 @@ in {
         srv.node.wait_until_fails(f"ss --numeric --udp --listening | grep -q {srv.port}")
     '';
 
-})
+}
diff --git a/nixos/tests/ayatana-indicators.nix b/nixos/tests/ayatana-indicators.nix
index a7de640f9e37c..5709ad2a1af69 100644
--- a/nixos/tests/ayatana-indicators.nix
+++ b/nixos/tests/ayatana-indicators.nix
@@ -21,8 +21,8 @@ in {
     services.xserver = {
       enable = true;
       desktopManager.mate.enable = true;
-      displayManager.defaultSession = lib.mkForce "mate";
     };
+    services.displayManager.defaultSession = lib.mkForce "mate";
 
     services.ayatana-indicators = {
       enable = true;
diff --git a/nixos/tests/budgie.nix b/nixos/tests/budgie.nix
index ca898bba1bc4a..203e718c8c6d9 100644
--- a/nixos/tests/budgie.nix
+++ b/nixos/tests/budgie.nix
@@ -18,6 +18,10 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       };
     };
 
+    # We don't ship gnome-text-editor in Budgie module, we add this line mainly
+    # to catch eval issues related to this option.
+    environment.budgie.excludePackages = [ pkgs.gnome-text-editor ];
+
     services.xserver.desktopManager.budgie = {
       enable = true;
       extraPlugins = [
@@ -29,6 +33,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 +53,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/castopod.nix b/nixos/tests/castopod.nix
index 4435ec617d4e6..29bf8e8cacd89 100644
--- a/nixos/tests/castopod.nix
+++ b/nixos/tests/castopod.nix
@@ -4,74 +4,218 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
   meta = with lib.maintainers; {
     maintainers = [ alexoundos misuzu ];
   };
+
   nodes.castopod = { nodes, ... }: {
+    # otherwise 500 MiB file upload fails!
+    virtualisation.diskSize = 512 + 3 * 512;
+
     networking.firewall.allowedTCPPorts = [ 80 ];
-    networking.extraHosts = ''
-      127.0.0.1 castopod.example.com
-    '';
+    networking.extraHosts =
+      lib.strings.concatStringsSep "\n"
+        (lib.attrsets.mapAttrsToList
+          (name: _: "127.0.0.1 ${name}")
+          nodes.castopod.services.nginx.virtualHosts);
+
     services.castopod = {
       enable = true;
       database.createLocally = true;
       localDomain = "castopod.example.com";
+      maxUploadSize = "512M";
     };
-    environment.systemPackages =
-      let
-        username = "admin";
-        email = "admin@castood.example.com";
-        password = "v82HmEp5";
-        testRunner = pkgs.writers.writePython3Bin "test-runner"
-          {
-            libraries = [ pkgs.python3Packages.selenium ];
-            flakeIgnore = [
-              "E501"
-            ];
-          } ''
-          from selenium.webdriver.common.by import By
-          from selenium.webdriver import Firefox
-          from selenium.webdriver.firefox.options import Options
-          from selenium.webdriver.support.ui import WebDriverWait
-          from selenium.webdriver.support import expected_conditions as EC
-
-          options = Options()
-          options.add_argument('--headless')
-          driver = Firefox(options=options)
-          try:
-              driver.implicitly_wait(20)
-              driver.get('http://castopod.example.com/cp-install')
-
-              wait = WebDriverWait(driver, 10)
-
-              wait.until(EC.title_contains("installer"))
-
-              driver.find_element(By.CSS_SELECTOR, '#username').send_keys(
-                  '${username}'
-              )
-              driver.find_element(By.CSS_SELECTOR, '#email').send_keys(
-                  '${email}'
-              )
-              driver.find_element(By.CSS_SELECTOR, '#password').send_keys(
-                  '${password}'
-              )
-              driver.find_element(By.XPATH, "//button[contains(., 'Finish install')]").click()
-
-              wait.until(EC.title_contains("Auth"))
-
-              driver.find_element(By.CSS_SELECTOR, '#email').send_keys(
-                  '${email}'
-              )
-              driver.find_element(By.CSS_SELECTOR, '#password').send_keys(
-                  '${password}'
-              )
-              driver.find_element(By.XPATH, "//button[contains(., 'Login')]").click()
-
-              wait.until(EC.title_contains("Admin dashboard"))
-          finally:
-              driver.close()
-              driver.quit()
-        '';
-      in
-      [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
   };
+
+  nodes.client = { nodes, pkgs, lib, ... }:
+    let
+      domain = nodes.castopod.services.castopod.localDomain;
+
+      getIP = node:
+        (builtins.head node.networking.interfaces.eth1.ipv4.addresses).address;
+
+      targetPodcastSize = 500 * 1024 * 1024;
+      lameMp3Bitrate = 348300;
+      lameMp3FileAdjust = -800;
+      targetPodcastDuration = toString
+        ((targetPodcastSize + lameMp3FileAdjust) / (lameMp3Bitrate / 8));
+      bannerWidth = 3000;
+      banner = pkgs.runCommand "gen-castopod-cover.jpg" { } ''
+        ${pkgs.imagemagick}/bin/magick `
+        `-background green -bordercolor white -gravity northwest xc:black `
+        `-duplicate 99 `
+        `-seed 1 -resize "%[fx:rand()*72+24]" `
+        `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 16x36 `
+        `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "150x50!" `
+        `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append `
+        `-resize ${toString bannerWidth} -quality 1 $out
+      '';
+
+      coverWidth = toString 3000;
+      cover = pkgs.runCommand "gen-castopod-banner.jpg" { } ''
+        ${pkgs.imagemagick}/bin/magick `
+        `-background white -bordercolor white -gravity northwest xc:black `
+        `-duplicate 99 `
+        `-seed 1 -resize "%[fx:rand()*72+24]" `
+        `-seed 0 -rotate "%[fx:rand()*360]" -border 6x6 -splice 36x36 `
+        `-seed 0 -rotate "%[fx:floor(rand()*4)*90]" -resize "144x144!" `
+        `+append -crop 10x1@ +repage -roll "+%[fx:(t%2)*72]+0" -append `
+        `-resize ${coverWidth} -quality 1 $out
+      '';
+    in
+    {
+      networking.extraHosts =
+        lib.strings.concatStringsSep "\n"
+          (lib.attrsets.mapAttrsToList
+            (name: _: "${getIP nodes.castopod} ${name}")
+            nodes.castopod.services.nginx.virtualHosts);
+
+      environment.systemPackages =
+        let
+          username = "admin";
+          email = "admin@${domain}";
+          password = "Abcd1234";
+          podcastTitle = "Some Title";
+          episodeTitle = "Episode Title";
+          browser-test = pkgs.writers.writePython3Bin "browser-test"
+            {
+              libraries = [ pkgs.python3Packages.selenium ];
+              flakeIgnore = [ "E124" "E501" ];
+            } ''
+            from selenium.webdriver.common.by import By
+            from selenium.webdriver import Firefox
+            from selenium.webdriver.firefox.options import Options
+            from selenium.webdriver.firefox.service import Service
+            from selenium.webdriver.support.ui import WebDriverWait
+            from selenium.webdriver.support import expected_conditions as EC
+            from subprocess import STDOUT
+            import logging
+
+            selenium_logger = logging.getLogger("selenium")
+            selenium_logger.setLevel(logging.DEBUG)
+            selenium_logger.addHandler(logging.StreamHandler())
+
+            options = Options()
+            options.add_argument('--headless')
+            service = Service(log_output=STDOUT)
+            driver = Firefox(options=options, service=service)
+            driver = Firefox(options=options)
+            driver.implicitly_wait(30)
+
+            # install ##########################################################
+
+            driver.get('http://${domain}/cp-install')
+
+            wait = WebDriverWait(driver, 20)
+
+            wait.until(EC.title_contains("installer"))
+
+            driver.find_element(By.CSS_SELECTOR, '#username').send_keys(
+                '${username}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#email').send_keys(
+                '${email}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#password').send_keys(
+                '${password}'
+            )
+            driver.find_element(By.XPATH,
+                                "//button[contains(., 'Finish install')]"
+            ).click()
+
+            wait.until(EC.title_contains("Auth"))
+
+            driver.find_element(By.CSS_SELECTOR, '#email').send_keys(
+                '${email}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#password').send_keys(
+                '${password}'
+            )
+            driver.find_element(By.XPATH,
+                                "//button[contains(., 'Login')]"
+            ).click()
+
+            wait.until(EC.title_contains("Admin dashboard"))
+
+            # create podcast ###################################################
+
+            driver.get('http://${domain}/admin/podcasts/new')
+
+            wait.until(EC.title_contains("Create podcast"))
+
+            driver.find_element(By.CSS_SELECTOR, '#cover').send_keys(
+                '${cover}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#banner').send_keys(
+                '${banner}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#title').send_keys(
+                '${podcastTitle}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#handle').send_keys(
+                'some_handle'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#description').send_keys(
+                'Some description'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#owner_name').send_keys(
+                'Owner Name'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#owner_email').send_keys(
+                'owner@email.xyz'
+            )
+            driver.find_element(By.XPATH,
+                                "//button[contains(., 'Create podcast')]"
+            ).click()
+
+            wait.until(EC.title_contains("${podcastTitle}"))
+
+            driver.find_element(By.XPATH,
+                                "//span[contains(., 'Add an episode')]"
+            ).click()
+
+            wait.until(EC.title_contains("Add an episode"))
+
+            # upload podcast ###################################################
+
+            driver.find_element(By.CSS_SELECTOR, '#audio_file').send_keys(
+                '/tmp/podcast.mp3'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#cover').send_keys(
+                '${cover}'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#description').send_keys(
+                'Episode description'
+            )
+            driver.find_element(By.CSS_SELECTOR, '#title').send_keys(
+                '${episodeTitle}'
+            )
+            driver.find_element(By.XPATH,
+                                "//button[contains(., 'Create episode')]"
+            ).click()
+
+            wait.until(EC.title_contains("${episodeTitle}"))
+
+            driver.close()
+            driver.quit()
+          '';
+        in
+        [
+          pkgs.firefox-unwrapped
+          pkgs.geckodriver
+          browser-test
+          (pkgs.writeShellApplication {
+            name = "build-mp3";
+            runtimeInputs = with pkgs; [ sox lame ];
+            text = ''
+              out=/tmp/podcast.mp3
+              sox -n -r 48000 -t wav - synth ${targetPodcastDuration} sine 440 `
+              `| lame --noreplaygain -cbr -q 9 -b 320 - $out
+              FILESIZE="$(stat -c%s $out)"
+              [ "$FILESIZE" -gt 0 ]
+              [ "$FILESIZE" -le "${toString targetPodcastSize}" ]
+            '';
+          })
+        ];
+    };
+
   testScript = ''
     start_all()
     castopod.wait_for_unit("castopod-setup.service")
@@ -79,9 +223,11 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
     castopod.wait_for_unit("nginx.service")
     castopod.wait_for_open_port(80)
     castopod.wait_until_succeeds("curl -sS -f http://castopod.example.com")
-    castopod.succeed("curl -s http://localhost/cp-install | grep 'Create your Super Admin account' > /dev/null")
 
-    with subtest("Create superadmin and log in"):
-        castopod.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner")
+    client.succeed("build-mp3")
+
+    with subtest("Create superadmin, log in, create and upload a podcast"):
+        client.succeed(\
+          "PYTHONUNBUFFERED=1 systemd-cat -t browser-test browser-test")
   '';
 })
diff --git a/nixos/tests/cinnamon-wayland.nix b/nixos/tests/cinnamon-wayland.nix
index 1629ead16f41f..19529d820d9c1 100644
--- a/nixos/tests/cinnamon-wayland.nix
+++ b/nixos/tests/cinnamon-wayland.nix
@@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     imports = [ ./common/user-account.nix ];
     services.xserver.enable = true;
     services.xserver.desktopManager.cinnamon.enable = true;
-    services.xserver.displayManager = {
+    services.displayManager = {
       autoLogin.enable = true;
       autoLogin.user = nodes.machine.users.users.alice.name;
       defaultSession = "cinnamon-wayland";
diff --git a/nixos/tests/cinnamon.nix b/nixos/tests/cinnamon.nix
index eab907d0b712c..694308152149b 100644
--- a/nixos/tests/cinnamon.nix
+++ b/nixos/tests/cinnamon.nix
@@ -8,6 +8,10 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     services.xserver.enable = true;
     services.xserver.desktopManager.cinnamon.enable = true;
 
+    # We don't ship gnome-text-editor in Cinnamon module, we add this line mainly
+    # to catch eval issues related to this option.
+    environment.cinnamon.excludePackages = [ pkgs.gnome-text-editor ];
+
     # For the sessionPath subtest.
     services.xserver.desktopManager.cinnamon.sessionPath = [ pkgs.gnome.gpaste ];
   };
diff --git a/nixos/tests/common/auto.nix b/nixos/tests/common/auto.nix
index ac56bed4a88f1..a2e42e59c0442 100644
--- a/nixos/tests/common/auto.nix
+++ b/nixos/tests/common/auto.nix
@@ -30,12 +30,10 @@ in
   ###### implementation
 
   config = lib.mkIf cfg.enable {
-    services.xserver.displayManager = {
-      lightdm.enable = true;
-      autoLogin = {
-        enable = true;
-        user = cfg.user;
-      };
+    services.xserver.displayManager.lightdm.enable = true;
+    services.displayManager.autoLogin = {
+      enable = true;
+      user = cfg.user;
     };
 
     # lightdm by default doesn't allow auto login for root, which is
diff --git a/nixos/tests/common/x11.nix b/nixos/tests/common/x11.nix
index 0d76a0e972ff5..b79cedb864de4 100644
--- a/nixos/tests/common/x11.nix
+++ b/nixos/tests/common/x11.nix
@@ -12,6 +12,6 @@
 
   # Use IceWM as the window manager.
   # Don't use a desktop manager.
-  services.xserver.displayManager.defaultSession = lib.mkDefault "none+icewm";
+  services.displayManager.defaultSession = lib.mkDefault "none+icewm";
   services.xserver.windowManager.icewm.enable = true;
 }
diff --git a/nixos/tests/containers-require-bind-mounts.nix b/nixos/tests/containers-require-bind-mounts.nix
new file mode 100644
index 0000000000000..5f986fd3e2803
--- /dev/null
+++ b/nixos/tests/containers-require-bind-mounts.nix
@@ -0,0 +1,35 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "containers-require-bind-mounts";
+  meta.maintainers = with lib.maintainers; [ kira-bruneau ];
+
+  nodes.machine = {
+    containers.require-bind-mounts = {
+      bindMounts = { "/srv/data" = {}; };
+      config = {};
+    };
+
+    virtualisation.fileSystems = {
+      "/srv/data" = {
+        fsType = "tmpfs";
+        options = [ "noauto" ];
+      };
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("default.target")
+
+    assert "require-bind-mounts" in machine.succeed("nixos-container list")
+    assert "down" in machine.succeed("nixos-container status require-bind-mounts")
+    assert "inactive" in machine.fail("systemctl is-active srv-data.mount")
+
+    with subtest("bind mount host paths must be mounted to run container"):
+      machine.succeed("nixos-container start require-bind-mounts")
+      assert "up" in machine.succeed("nixos-container status require-bind-mounts")
+      assert "active" in machine.succeed("systemctl status srv-data.mount")
+
+      machine.succeed("systemctl stop srv-data.mount")
+      assert "down" in machine.succeed("nixos-container status require-bind-mounts")
+      assert "inactive" in machine.fail("systemctl is-active srv-data.mount")
+    '';
+})
diff --git a/nixos/tests/davis.nix b/nixos/tests/davis.nix
new file mode 100644
index 0000000000000..68958cee7a430
--- /dev/null
+++ b/nixos/tests/davis.nix
@@ -0,0 +1,59 @@
+import ./make-test-python.nix (
+  { lib, pkgs, ... }:
+
+  {
+    name = "davis";
+
+    meta.maintainers = pkgs.davis.meta.maintainers;
+
+    nodes.machine =
+      { config, ... }:
+      {
+        virtualisation = {
+          memorySize = 512;
+        };
+
+        services.davis = {
+          enable = true;
+          hostname = "davis.example.com";
+          database = {
+            driver = "postgresql";
+          };
+          mail = {
+            dsnFile = "${pkgs.writeText "davisMailDns" "smtp://username:password@example.com:25"}";
+            inviteFromAddress = "dav@example.com";
+          };
+          adminLogin = "admin";
+          appSecretFile = "${pkgs.writeText "davisAppSecret" "52882ef142066e09ab99ce816ba72522e789505caba224"}";
+          adminPasswordFile = "${pkgs.writeText "davisAdminPass" "nixos"}";
+          nginx = { };
+        };
+      };
+
+    testScript = ''
+      start_all()
+      machine.wait_for_unit("postgresql.service")
+      machine.wait_for_unit("davis-env-setup.service")
+      machine.wait_for_unit("davis-db-migrate.service")
+      machine.wait_for_unit("nginx.service")
+      machine.wait_for_unit("phpfpm-davis.service")
+
+      with subtest("welcome screen loads"):
+          machine.succeed(
+              "curl -sSfL --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/ | grep '<title>Davis</title>'"
+          )
+
+      with subtest("login works"):
+          csrf_token = machine.succeed(
+              "curl -c /tmp/cookies -sSfL --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/login | grep '_csrf_token' | sed -E 's,.*value=\"(.*)\".*,\\1,g'"
+          )
+          r = machine.succeed(
+              f"curl -b /tmp/cookies --resolve davis.example.com:80:127.0.0.1 http://davis.example.com/login -X POST -F username=admin -F password=nixos -F _csrf_token={csrf_token.strip()} -D headers"
+          )
+          print(r)
+          machine.succeed(
+            "[[ $(grep -i 'location: ' headers | cut -d: -f2- | xargs echo) == /dashboard* ]]"
+          )
+    '';
+  }
+)
diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix
index 7d91076600f97..c8a227eb2cf7b 100644
--- a/nixos/tests/docker-tools.nix
+++ b/nixos/tests/docker-tools.nix
@@ -178,6 +178,14 @@ in {
             "docker load --input='${examples.bashUncompressed}'",
             "docker rmi ${examples.bashUncompressed.imageName}",
         )
+        docker.succeed(
+            "docker load --input='${examples.bashLayeredUncompressed}'",
+            "docker rmi ${examples.bashLayeredUncompressed.imageName}",
+        )
+        docker.succeed(
+            "docker load --input='${examples.bashLayeredZstdCompressed}'",
+            "docker rmi ${examples.bashLayeredZstdCompressed.imageName}",
+        )
 
     with subtest(
         "Check if the nix store is correctly initialized by listing "
diff --git a/nixos/tests/drawterm.nix b/nixos/tests/drawterm.nix
index 1d444bb55433b..3594343853c01 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/drbd.nix b/nixos/tests/drbd.nix
index bede7206d706c..defbad6933932 100644
--- a/nixos/tests/drbd.nix
+++ b/nixos/tests/drbd.nix
@@ -31,11 +31,11 @@ import ./make-test-python.nix (
               }
 
               on drbd1 {
-                address ${nodes.drbd1.config.networking.primaryIPAddress}:${toString drbdPort};
+                address ${nodes.drbd1.networking.primaryIPAddress}:${toString drbdPort};
               }
 
               on drbd2 {
-                address ${nodes.drbd2.config.networking.primaryIPAddress}:${toString drbdPort};
+                address ${nodes.drbd2.networking.primaryIPAddress}:${toString drbdPort};
               }
             }
           '';
@@ -45,7 +45,7 @@ import ./make-test-python.nix (
   {
     name = "drbd";
     meta = with pkgs.lib.maintainers; {
-      maintainers = [ ryantm astro ];
+      maintainers = [ ryantm astro birkb ];
     };
 
     nodes.drbd1 = drbdConfig;
diff --git a/nixos/tests/etcd-cluster.nix b/nixos/tests/etcd/etcd-cluster.nix
index c77c0dd73c252..734d56dbc2233 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 79857778ae1b5..a32d0f9a55d17 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/firefoxpwa.nix b/nixos/tests/firefoxpwa.nix
new file mode 100644
index 0000000000000..374d67b01ac60
--- /dev/null
+++ b/nixos/tests/firefoxpwa.nix
@@ -0,0 +1,36 @@
+import ./make-test-python.nix ({ lib, ... }:
+
+{
+  name = "firefoxpwa";
+  meta.maintainers = with lib.maintainers; [ camillemndn ];
+
+  nodes.machine =
+    { pkgs, ... }:
+    {
+      imports = [ ./common/x11.nix ];
+      environment.systemPackages = with pkgs; [ firefoxpwa jq ];
+
+      programs.firefox = {
+        enable = true;
+        nativeMessagingHosts.packages = [ pkgs.firefoxpwa ];
+      };
+
+      services.jellyfin.enable = true;
+    };
+
+  enableOCR = true;
+
+  testScript = ''
+    machine.start()
+
+    with subtest("Install a progressive web app"):
+        machine.wait_for_unit("jellyfin.service")
+        machine.wait_for_open_port(8096)
+        machine.succeed("firefoxpwa site install http://localhost:8096/web/manifest.json >&2")
+
+    with subtest("Launch the progressive web app"):
+        machine.succeed("firefoxpwa site launch $(jq -r < ~/.local/share/firefoxpwa/config.json '.sites | keys[0]') >&2")
+        machine.wait_for_window("Jellyfin")
+        machine.wait_for_text("Jellyfin")
+  '';
+})
diff --git a/nixos/tests/forgejo.nix b/nixos/tests/forgejo.nix
index 6acd6acb50fa9..b14df0a2c74f9 100644
--- a/nixos/tests/forgejo.nix
+++ b/nixos/tests/forgejo.nix
@@ -108,6 +108,12 @@ let
 
         assert "BEGIN PGP PUBLIC KEY BLOCK" in server.succeed("curl http://localhost:3000/api/v1/signing-key.gpg")
 
+        api_version = json.loads(server.succeed("curl http://localhost:3000/api/forgejo/v1/version")).get("version")
+        assert "development" != api_version and "-gitea-" in api_version, (
+            "/api/forgejo/v1/version should not return 'development' "
+            + f"but should contain a gitea compatibility version string. Got '{api_version}' instead."
+        )
+
         server.succeed(
             "curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. "
             + "Please contact your site administrator.'"
diff --git a/nixos/tests/freetube.nix b/nixos/tests/freetube.nix
index faa5349382270..10f0773cb884c 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/gitlab.nix b/nixos/tests/gitlab.nix
index c4d69a56c93ad..52fe588930dfc 100644
--- a/nixos/tests/gitlab.nix
+++ b/nixos/tests/gitlab.nix
@@ -89,6 +89,10 @@ in {
           dbFile = pkgs.writeText "dbsecret" "we2quaeZ";
           jwsFile = pkgs.runCommand "oidcKeyBase" {} "${pkgs.openssl}/bin/openssl genrsa 2048 > $out";
         };
+
+        # reduce memory usage
+        sidekiq.concurrency = 1;
+        puma.workers = 2;
       };
     };
   };
diff --git a/nixos/tests/gnome-flashback.nix b/nixos/tests/gnome-flashback.nix
index f486dabc5c40b..e0a1d256c8c20 100644
--- a/nixos/tests/gnome-flashback.nix
+++ b/nixos/tests/gnome-flashback.nix
@@ -14,16 +14,17 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
       services.xserver.displayManager = {
         gdm.enable = true;
         gdm.debug = true;
-        autoLogin = {
-          enable = true;
-          user = user.name;
-        };
+      };
+
+      services.displayManager.autoLogin = {
+        enable = true;
+        user = user.name;
       };
 
       services.xserver.desktopManager.gnome.enable = true;
       services.xserver.desktopManager.gnome.debug = true;
       services.xserver.desktopManager.gnome.flashback.enableMetacity = true;
-      services.xserver.displayManager.defaultSession = "gnome-flashback-metacity";
+      services.displayManager.defaultSession = "gnome-flashback-metacity";
     };
 
   testScript = { nodes, ... }: let
diff --git a/nixos/tests/gnome-xorg.nix b/nixos/tests/gnome-xorg.nix
index 6ca700edcac38..c8ffb459edece 100644
--- a/nixos/tests/gnome-xorg.nix
+++ b/nixos/tests/gnome-xorg.nix
@@ -15,15 +15,16 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
       services.xserver.displayManager = {
         gdm.enable = true;
         gdm.debug = true;
-        autoLogin = {
-          enable = true;
-          user = user.name;
-        };
+      };
+
+      services.displayManager.autoLogin = {
+        enable = true;
+        user = user.name;
       };
 
       services.xserver.desktopManager.gnome.enable = true;
       services.xserver.desktopManager.gnome.debug = true;
-      services.xserver.displayManager.defaultSession = "gnome-xorg";
+      services.displayManager.defaultSession = "gnome-xorg";
 
       systemd.user.services = {
         "org.gnome.Shell@x11" = {
diff --git a/nixos/tests/gnome.nix b/nixos/tests/gnome.nix
index 91182790cb248..98d61c7ea1723 100644
--- a/nixos/tests/gnome.nix
+++ b/nixos/tests/gnome.nix
@@ -12,10 +12,11 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
       services.xserver.displayManager = {
         gdm.enable = true;
         gdm.debug = true;
-        autoLogin = {
-          enable = true;
-          user = "alice";
-        };
+      };
+
+      services.displayManager.autoLogin = {
+        enable = true;
+        user = "alice";
       };
 
       services.xserver.desktopManager.gnome.enable = true;
diff --git a/nixos/tests/goss.nix b/nixos/tests/goss.nix
index 6b772d19215e3..2e77b2734464f 100644
--- a/nixos/tests/goss.nix
+++ b/nixos/tests/goss.nix
@@ -28,10 +28,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
         };
         group.root.exists = true;
         kernel-param."kernel.ostype".value = "Linux";
-        service.goss = {
-          enabled = true;
-          running = true;
-        };
         user.root.exists = true;
       };
     };
@@ -46,8 +42,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     with subtest("returns health status"):
       result = json.loads(machine.succeed("curl -sS http://localhost:8080/healthz"))
 
-      assert len(result["results"]) == 10, f".results should be an array of 10 items, was {result['results']!r}"
+      assert len(result["results"]) == 8, f".results should be an array of 10 items, was {result['results']!r}"
       assert result["summary"]["failed-count"] == 0, f".summary.failed-count should be zero, was {result['summary']['failed-count']}"
-      assert result["summary"]["test-count"] == 10, f".summary.test-count should be 10, was {result['summary']['test-count']}"
+      assert result["summary"]["test-count"] == 8, f".summary.test-count should be 10, was {result['summary']['test-count']}"
     '';
 })
diff --git a/nixos/tests/herbstluftwm.nix b/nixos/tests/herbstluftwm.nix
index b6965914360e7..2a8b391947e74 100644
--- a/nixos/tests/herbstluftwm.nix
+++ b/nixos/tests/herbstluftwm.nix
@@ -8,7 +8,7 @@ import ./make-test-python.nix ({ lib, ...} : {
   nodes.machine = { pkgs, lib, ... }: {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
     test-support.displayManager.auto.user = "alice";
-    services.xserver.displayManager.defaultSession = lib.mkForce "none+herbstluftwm";
+    services.displayManager.defaultSession = lib.mkForce "none+herbstluftwm";
     services.xserver.windowManager.herbstluftwm.enable = true;
     environment.systemPackages = [ pkgs.dzen2 ]; # needed for upstream provided panel
   };
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index 296aa9ba68b92..6de287f63e081 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/hledger-web.nix b/nixos/tests/hledger-web.nix
index f8919f7d4bd06..09941ca5c517c 100644
--- a/nixos/tests/hledger-web.nix
+++ b/nixos/tests/hledger-web.nix
@@ -19,7 +19,7 @@ rec {
         host = "127.0.0.1";
         port = 5000;
         enable = true;
-        capabilities.manage = true;
+        allow = "edit";
       };
       networking.firewall.allowedTCPPorts = [ config.services.hledger-web.port ];
       systemd.services.hledger-web.preStart = ''
diff --git a/nixos/tests/hydra/common.nix b/nixos/tests/hydra/common.nix
index 2bce03418e1ff..f31518b1e2a20 100644
--- a/nixos/tests/hydra/common.nix
+++ b/nixos/tests/hydra/common.nix
@@ -36,13 +36,6 @@
       '';
     };
     services.postfix.enable = true;
-    nix = {
-      distributedBuilds = true;
-      buildMachines = [{
-        hostName = "localhost";
-        systems = [ system ];
-      }];
-      settings.substituters = [];
-    };
+    nix.settings.substituters = [];
   };
 }
diff --git a/nixos/tests/i3wm.nix b/nixos/tests/i3wm.nix
index b216650d8192b..c02ce86db8b20 100644
--- a/nixos/tests/i3wm.nix
+++ b/nixos/tests/i3wm.nix
@@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
   nodes.machine = { lib, ... }: {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
     test-support.displayManager.auto.user = "alice";
-    services.xserver.displayManager.defaultSession = lib.mkForce "none+i3";
+    services.displayManager.defaultSession = lib.mkForce "none+i3";
     services.xserver.windowManager.i3.enable = true;
   };
 
diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix
index 9260f70da98c2..a71c5355046a5 100644
--- a/nixos/tests/incus/container.nix
+++ b/nixos/tests/incus/container.nix
@@ -1,20 +1,21 @@
-import ../make-test-python.nix ({ pkgs, lib, extra ? {}, ... } :
+import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", ... } :
 
 let
   releases = import ../../release.nix {
-    configuration = {
-      # Building documentation makes the test unnecessarily take a longer time:
-      documentation.enable = lib.mkForce false;
+    configuration = lib.recursiveUpdate {
+        # Building documentation makes the test unnecessarily take a longer time:
+        documentation.enable = lib.mkForce false;
 
-      boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
-    } // extra;
+        boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
+    }
+    extra;
   };
 
   container-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system};
   container-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system};
 in
 {
-  name = "incus-container";
+  inherit name;
 
   meta = {
     maintainers = lib.teams.lxc.members;
diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix
index 474a621c5ce91..b850c4fba018d 100644
--- a/nixos/tests/incus/default.nix
+++ b/nixos/tests/incus/default.nix
@@ -5,15 +5,22 @@
   handleTestOn,
 }:
 {
-  container-old-init = import ./container.nix { inherit system pkgs; };
-  container-new-init = import ./container.nix { inherit system pkgs; extra = {
-    # Enable new systemd init
-    boot.initrd.systemd.enable = true;
-  }; };
+  container-legacy-init = import ./container.nix {
+    name = "container-legacy-init";
+    inherit system pkgs;
+  };
+  container-systemd-init = import ./container.nix {
+    name = "container-systemd-init";
+    inherit system pkgs;
+    extra = {
+      boot.initrd.systemd.enable = true;
+    };
+  };
   lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; };
   openvswitch = import ./openvswitch.nix { inherit system pkgs; };
   preseed = import ./preseed.nix { inherit system pkgs; };
   socket-activated = import ./socket-activated.nix { inherit system pkgs; };
-  ui = import ./ui.nix {inherit system pkgs;};
+  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/lxd-to-incus.nix b/nixos/tests/incus/lxd-to-incus.nix
index 262f63c0f26fb..e93b76591eca4 100644
--- a/nixos/tests/incus/lxd-to-incus.nix
+++ b/nixos/tests/incus/lxd-to-incus.nix
@@ -95,7 +95,7 @@ import ../make-test-python.nix (
       machine.wait_for_unit("incus.service")
 
       with machine.nested("run migration"):
-          machine.succeed("lxd-to-incus --yes")
+          machine.succeed("${pkgs.incus}/bin/lxd-to-incus --yes")
 
       with machine.nested("verify resources migrated to incus"):
           machine.succeed("incus config show container")
diff --git a/nixos/tests/incus/storage.nix b/nixos/tests/incus/storage.nix
new file mode 100644
index 0000000000000..190f4f7451c20
--- /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 662017935412c..1dd55dada042a 100644
--- a/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixos/tests/installer-systemd-stage-1.nix
@@ -37,6 +37,9 @@
     clevisLuksFallback
     clevisZfs
     clevisZfsFallback
+    gptAutoRoot
+    clevisBcachefs
+    clevisBcachefsFallback
     ;
 
 }
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 97bb7f8def595..7e835041eb39f 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -51,7 +51,7 @@ let
           boot.loader.systemd-boot.enable = true;
         ''}
 
-        boot.initrd.secrets."/etc/secret" = ./secret;
+        boot.initrd.secrets."/etc/secret" = "/etc/nixos/secret";
 
         ${optionalString clevisTest ''
           boot.kernelParams = [ "console=tty0" "ip=192.168.1.1:::255.255.255.0::eth1:none" ];
@@ -80,38 +80,24 @@ let
   # a test script fragment `createPartitions', which must create
   # partitions and filesystems.
   testScriptFun = { bootLoader, createPartitions, grubDevice, grubUseEfi, grubIdentifier
-                  , postInstallCommands, preBootCommands, postBootCommands, extraConfig
+                  , postInstallCommands, postBootCommands, extraConfig
                   , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest
+                  , disableFileSystems
                   }:
     let
-      qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
-      isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
-      qemu = qemu-common.qemuBinary pkgs.qemu_test;
-    in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then ''
-      machine.succeed("true")
-    '' else ''
+      startTarget = ''
+        ${optionalString clevisTest "tpm.start()"}
+        target.start()
+        ${postBootCommands}
+        target.wait_for_unit("multi-user.target")
+      '';
+    in ''
+      ${optionalString clevisTest ''
       import os
       import subprocess
 
       tpm_folder = os.environ['NIX_BUILD_TOP']
 
-      startcommand = "${qemu} -m 2048"
-
-      ${optionalString clevisTest ''
-        startcommand += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"
-        startcommand += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""
-      ''}
-      ${optionalString isEfi ''
-        startcommand +=" -drive if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware} -drive if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}"
-      ''}
-
-      image_dir = machine.state_dir
-      disk_image = os.path.join(image_dir, "machine.qcow2")
-      startcommand += f" -drive file={disk_image},if=virtio,werror=report"
-
-      def create_machine_named(name):
-          return create_machine(startcommand, name=name)
-
       class Tpm:
             def __init__(self):
                 self.start()
@@ -142,30 +128,31 @@ let
       os.mkdir(f"{tpm_folder}/swtpm")
       tpm = Tpm()
       tpm.check()
+      ''}
 
-      start_all()
+      installer.start()
       ${optionalString clevisTest ''
+      tang.start()
       tang.wait_for_unit("sockets.target")
       tang.systemctl("start network-online.target")
       tang.wait_for_unit("network-online.target")
-      machine.systemctl("start network-online.target")
-      machine.wait_for_unit("network-online.target")
+      installer.systemctl("start network-online.target")
+      installer.wait_for_unit("network-online.target")
       ''}
-      machine.wait_for_unit("multi-user.target")
-
+      installer.wait_for_unit("multi-user.target")
 
       with subtest("Assert readiness of login prompt"):
-          machine.succeed("echo hello")
+          installer.succeed("echo hello")
 
       with subtest("Wait for hard disks to appear in /dev"):
-          machine.succeed("udevadm settle")
+          installer.succeed("udevadm settle")
 
       ${createPartitions}
 
       with subtest("Create the NixOS configuration"):
-          machine.succeed("nixos-generate-config --root /mnt")
-          machine.succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2")
-          machine.copy_from_host(
+          installer.succeed("nixos-generate-config ${optionalString disableFileSystems "--no-filesystems"} --root /mnt")
+          installer.succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2")
+          installer.copy_from_host(
               "${ makeConfig {
                     inherit bootLoader grubDevice grubIdentifier
                             grubUseEfi extraConfig clevisTest;
@@ -173,13 +160,13 @@ let
               }",
               "/mnt/etc/nixos/configuration.nix",
           )
-          machine.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret")
+          installer.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret")
 
       ${optionalString clevisTest ''
         with subtest("Create the Clevis secret with Tang"):
-             machine.systemctl("start network-online.target")
-             machine.wait_for_unit("network-online.target")
-             machine.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''}
+             installer.systemctl("start network-online.target")
+             installer.wait_for_unit("network-online.target")
+             installer.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''}
 
       ${optionalString clevisFallbackTest ''
         with subtest("Shutdown Tang to check fallback to interactive prompt"):
@@ -187,13 +174,13 @@ let
       ''}
 
       with subtest("Perform the installation"):
-          machine.succeed("nixos-install < /dev/null >&2")
+          installer.succeed("nixos-install < /dev/null >&2")
 
       with subtest("Do it again to make sure it's idempotent"):
-          machine.succeed("nixos-install < /dev/null >&2")
+          installer.succeed("nixos-install < /dev/null >&2")
 
       with subtest("Check that we can build things in nixos-enter"):
-          machine.succeed(
+          installer.succeed(
               """
               nixos-enter -- nix-build --option substitute false -E 'derivation {
                   name = "t";
@@ -208,48 +195,48 @@ let
       ${postInstallCommands}
 
       with subtest("Shutdown system after installation"):
-          machine.succeed("umount -R /mnt")
-          machine.succeed("sync")
-          machine.shutdown()
+          installer.succeed("umount -R /mnt")
+          installer.succeed("sync")
+          installer.shutdown()
 
-      # Now see if we can boot the installation.
-      machine = create_machine_named("boot-after-install")
+      # We're actually the same machine, just booting differently this time.
+      target.state_dir = installer.state_dir
 
-      # For example to enter LUKS passphrase.
-      ${preBootCommands}
+      # Now see if we can boot the installation.
+      ${startTarget}
 
       with subtest("Assert that /boot get mounted"):
-          machine.wait_for_unit("local-fs.target")
+          target.wait_for_unit("local-fs.target")
           ${if bootLoader == "grub"
-              then ''machine.succeed("test -e /boot/grub")''
-              else ''machine.succeed("test -e /boot/loader/loader.conf")''
+              then ''target.succeed("test -e /boot/grub")''
+              else ''target.succeed("test -e /boot/loader/loader.conf")''
           }
 
       with subtest("Check whether /root has correct permissions"):
-          assert "700" in machine.succeed("stat -c '%a' /root")
+          assert "700" in target.succeed("stat -c '%a' /root")
 
       with subtest("Assert swap device got activated"):
           # uncomment once https://bugs.freedesktop.org/show_bug.cgi?id=86930 is resolved
-          machine.wait_for_unit("swap.target")
-          machine.succeed("cat /proc/swaps | grep -q /dev")
+          target.wait_for_unit("swap.target")
+          target.succeed("cat /proc/swaps | grep -q /dev")
 
       with subtest("Check that the store is in good shape"):
-          machine.succeed("nix-store --verify --check-contents >&2")
+          target.succeed("nix-store --verify --check-contents >&2")
 
       with subtest("Check whether the channel works"):
-          machine.succeed("nix-env -iA nixos.procps >&2")
-          assert ".nix-profile" in machine.succeed("type -tP ps | tee /dev/stderr")
+          target.succeed("nix-env -iA nixos.procps >&2")
+          assert ".nix-profile" in target.succeed("type -tP ps | tee /dev/stderr")
 
       with subtest(
           "Check that the daemon works, and that non-root users can run builds "
           "(this will build a new profile generation through the daemon)"
       ):
-          machine.succeed("su alice -l -c 'nix-env -iA nixos.procps' >&2")
+          target.succeed("su alice -l -c 'nix-env -iA nixos.procps' >&2")
 
       with subtest("Configure system with writable Nix store on next boot"):
           # we're not using copy_from_host here because the installer image
           # doesn't know about the host-guest sharing mechanism.
-          machine.copy_from_host_via_shell(
+          target.copy_from_host_via_shell(
               "${ makeConfig {
                     inherit bootLoader grubDevice grubIdentifier
                             grubUseEfi extraConfig clevisTest;
@@ -260,25 +247,23 @@ let
           )
 
       with subtest("Check whether nixos-rebuild works"):
-          machine.succeed("nixos-rebuild switch >&2")
+          target.succeed("nixos-rebuild switch >&2")
 
       # FIXME: Nix 2.4 broke nixos-option, someone has to fix it.
       # with subtest("Test nixos-option"):
-      #     kernel_modules = machine.succeed("nixos-option boot.initrd.kernelModules")
+      #     kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules")
       #     assert "virtio_console" in kernel_modules
       #     assert "List of modules" in kernel_modules
       #     assert "qemu-guest.nix" in kernel_modules
 
-      machine.shutdown()
+      target.shutdown()
 
       # Check whether a writable store build works
-      machine = create_machine_named("rebuild-switch")
-      ${preBootCommands}
-      machine.wait_for_unit("multi-user.target")
+      ${startTarget}
 
       # we're not using copy_from_host here because the installer image
       # doesn't know about the host-guest sharing mechanism.
-      machine.copy_from_host_via_shell(
+      target.copy_from_host_via_shell(
           "${ makeConfig {
                 inherit bootLoader grubDevice grubIdentifier
                 grubUseEfi extraConfig clevisTest;
@@ -287,73 +272,62 @@ let
           }",
           "/etc/nixos/configuration.nix",
       )
-      machine.succeed("nixos-rebuild boot >&2")
-      machine.shutdown()
+      target.succeed("nixos-rebuild boot >&2")
+      target.shutdown()
 
-      # And just to be sure, check that the machine still boots after
-      # "nixos-rebuild switch".
-      machine = create_machine_named("boot-after-rebuild-switch")
-      ${preBootCommands}
-      machine.wait_for_unit("network.target")
+      # And just to be sure, check that the target still boots after "nixos-rebuild switch".
+      ${startTarget}
+      target.wait_for_unit("network.target")
 
       # Sanity check, is it the configuration.nix we generated?
-      hostname = machine.succeed("hostname").strip()
+      hostname = target.succeed("hostname").strip()
       assert hostname == "thatworked"
 
-      ${postBootCommands}
-      machine.shutdown()
+      target.shutdown()
 
       # Tests for validating clone configuration entries in grub menu
     ''
     + optionalString testSpecialisationConfig ''
-      # Reboot Machine
-      machine = create_machine_named("clone-default-config")
-      ${preBootCommands}
-      machine.wait_for_unit("multi-user.target")
+      # Reboot target
+      ${startTarget}
 
       with subtest("Booted configuration name should be 'Home'"):
           # This is not the name that shows in the grub menu.
           # The default configuration is always shown as "Default"
-          machine.succeed("cat /run/booted-system/configuration-name >&2")
-          assert "Home" in machine.succeed("cat /run/booted-system/configuration-name")
+          target.succeed("cat /run/booted-system/configuration-name >&2")
+          assert "Home" in target.succeed("cat /run/booted-system/configuration-name")
 
       with subtest("We should **not** find a file named /etc/gitconfig"):
-          machine.fail("test -e /etc/gitconfig")
+          target.fail("test -e /etc/gitconfig")
 
       with subtest("Set grub to boot the second configuration"):
-          machine.succeed("grub-reboot 1")
+          target.succeed("grub-reboot 1")
 
-      ${postBootCommands}
-      machine.shutdown()
+      target.shutdown()
 
-      # Reboot Machine
-      machine = create_machine_named("clone-alternate-config")
-      ${preBootCommands}
+      # Reboot target
+      ${startTarget}
 
-      machine.wait_for_unit("multi-user.target")
       with subtest("Booted configuration name should be Work"):
-          machine.succeed("cat /run/booted-system/configuration-name >&2")
-          assert "Work" in machine.succeed("cat /run/booted-system/configuration-name")
+          target.succeed("cat /run/booted-system/configuration-name >&2")
+          assert "Work" in target.succeed("cat /run/booted-system/configuration-name")
 
       with subtest("We should find a file named /etc/gitconfig"):
-          machine.succeed("test -e /etc/gitconfig")
+          target.succeed("test -e /etc/gitconfig")
 
-      ${postBootCommands}
-      machine.shutdown()
+      target.shutdown()
     ''
     + optionalString testFlakeSwitch ''
-      ${preBootCommands}
-      machine.start()
+      ${startTarget}
 
       with subtest("Configure system with flake"):
         # TODO: evaluate as user?
-        machine.succeed("""
+        target.succeed("""
           mkdir /root/my-config
           mv /etc/nixos/hardware-configuration.nix /root/my-config/
-          mv /etc/nixos/secret /root/my-config/
           rm /etc/nixos/configuration.nix
         """)
-        machine.copy_from_host_via_shell(
+        target.copy_from_host_via_shell(
           "${makeConfig {
                inherit bootLoader grubDevice grubIdentifier grubUseEfi extraConfig clevisTest;
                forceGrubReinstallCount = 1;
@@ -361,11 +335,11 @@ let
             }}",
           "/root/my-config/configuration.nix",
         )
-        machine.copy_from_host_via_shell(
+        target.copy_from_host_via_shell(
           "${./installer/flake.nix}",
           "/root/my-config/flake.nix",
         )
-        machine.succeed("""
+        target.succeed("""
           # for some reason the image does not have `pkgs.path`, so
           # we use readlink to find a Nixpkgs source.
           pkgs=$(readlink -f /nix/var/nix/profiles/per-user/root/channels)/nixos
@@ -377,36 +351,32 @@ let
         """)
 
       with subtest("Switch to flake based config"):
-        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+        target.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
 
-      ${postBootCommands}
-      machine.shutdown()
+      target.shutdown()
 
-      ${preBootCommands}
-      machine.start()
-
-      machine.wait_for_unit("multi-user.target")
+      ${startTarget}
 
       with subtest("nix-channel command is not available anymore"):
-        machine.succeed("! which nix-channel")
+        target.succeed("! which nix-channel")
 
       # Note that the channel profile is still present on disk, but configured
       # not to be used.
       with subtest("builtins.nixPath is now empty"):
-        machine.succeed("""
+        target.succeed("""
           [[ "[ ]" == "$(nix-instantiate builtins.nixPath --eval --expr)" ]]
         """)
 
       with subtest("<nixpkgs> does not resolve"):
-        machine.succeed("""
+        target.succeed("""
           ! nix-instantiate '<nixpkgs>' --eval --expr
         """)
 
       with subtest("Evaluate flake config in fresh env without nix-channel"):
-        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+        target.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
 
       with subtest("Evaluate flake config in fresh env without channel profiles"):
-        machine.succeed("""
+        target.succeed("""
           (
             exec 1>&2
             rm -v /root/.nix-channels
@@ -414,16 +384,15 @@ let
             rm -vrf /nix/var/nix/profiles/per-user/root/channels*
           )
         """)
-        machine.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
+        target.succeed("nixos-rebuild switch --flake /root/my-config#xyz")
 
-      ${postBootCommands}
-      machine.shutdown()
+      target.shutdown()
     '';
 
 
   makeInstallerTest = name:
     { createPartitions
-    , postInstallCommands ? "", preBootCommands ? "", postBootCommands ? ""
+    , postInstallCommands ? "", postBootCommands ? ""
     , extraConfig ? ""
     , extraInstallerConfig ? {}
     , bootLoader ? "grub" # either "grub" or "systemd-boot"
@@ -433,19 +402,41 @@ let
     , testFlakeSwitch ? false
     , clevisTest ? false
     , clevisFallbackTest ? false
+    , disableFileSystems ? false
     }:
-    makeTest {
+    let
+      isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
+    in makeTest {
       inherit enableOCR;
       name = "installer-" + name;
       meta = {
         # put global maintainers here, individuals go into makeInstallerTest fkt call
         maintainers = (meta.maintainers or []);
+        # non-EFI tests can only run on x86
+        platforms = if isEfi then platforms.linux else [ "x86_64-linux" "i686-linux" ];
       };
-      nodes = {
+      nodes = let
+        commonConfig = {
+          # builds stuff in the VM, needs more juice
+          virtualisation.diskSize = 8 * 1024;
+          virtualisation.cores = 8;
+          virtualisation.memorySize = 2048;
 
-        # The configuration of the machine used to run "nixos-install".
-        machine = { pkgs, ... }: {
+          # both installer and target need to use the same drive
+          virtualisation.diskImage = "./target.qcow2";
+
+          # and the same TPM options
+          virtualisation.qemu.options = mkIf (clevisTest) [
+            "-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock"
+            "-tpmdev emulator,id=tpm0,chardev=chrtpm"
+            "-device tpm-tis,tpmdev=tpm0"
+          ];
+        };
+      in {
+        # The configuration of the system used to run "nixos-install".
+        installer = {
           imports = [
+            commonConfig
             ../modules/profiles/installation-device.nix
             ../modules/profiles/base.nix
             extraInstallerConfig
@@ -456,11 +447,6 @@ let
           # root filesystem.
           virtualisation.fileSystems."/".autoFormat = systemdStage1;
 
-          # builds stuff in the VM, needs more juice
-          virtualisation.diskSize = 8 * 1024;
-          virtualisation.cores = 8;
-          virtualisation.memorySize = 2048;
-
           boot.initrd.systemd.enable = systemdStage1;
 
           # Use a small /dev/vdb as the root disk for the
@@ -468,17 +454,6 @@ let
           # the same during and after installation.
           virtualisation.emptyDiskImages = [ 512 ];
           virtualisation.rootDevice = "/dev/vdb";
-          virtualisation.bootLoaderDevice = "/dev/vda";
-          virtualisation.qemu.diskInterface = "virtio";
-          virtualisation.qemu.options = mkIf (clevisTest) [
-            "-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock"
-            "-tpmdev emulator,id=tpm0,chardev=chrtpm"
-            "-device tpm-tis,tpmdev=tpm0"
-          ];
-          # We don't want to have any networking in the guest apart from the clevis tests.
-          virtualisation.vlans = mkIf (!clevisTest) [];
-
-          boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true;
 
           hardware.enableAllFirmware = mkForce false;
 
@@ -518,7 +493,13 @@ let
           in [
             (pkgs.grub2.override { inherit zfsSupport; })
             (pkgs.grub2_efi.override { inherit zfsSupport; })
-          ]) ++ optionals clevisTest [ pkgs.klibc ];
+          ])
+          ++ optionals (bootLoader == "systemd-boot") [
+            pkgs.zstd.bin
+            pkgs.mypy
+            pkgs.bootspec
+          ]
+          ++ optionals clevisTest [ pkgs.klibc ];
 
           nix.settings = {
             substituters = mkForce [];
@@ -527,6 +508,18 @@ let
           };
         };
 
+        target = {
+          imports = [ commonConfig ];
+          virtualisation.useBootLoader = true;
+          virtualisation.useEFIBoot = isEfi;
+          virtualisation.useDefaultFilesystems = false;
+          virtualisation.efi.keepVariables = false;
+
+          virtualisation.fileSystems."/" = {
+            device = "/dev/disk/by-label/this-is-not-real-and-will-never-be-used";
+            fsType = "ext4";
+          };
+        };
       } // optionalAttrs clevisTest {
         tang = {
           services.tang = {
@@ -539,15 +532,16 @@ let
       };
 
       testScript = testScriptFun {
-        inherit bootLoader createPartitions postInstallCommands preBootCommands postBootCommands
+        inherit bootLoader createPartitions postInstallCommands postBootCommands
                 grubDevice grubIdentifier grubUseEfi extraConfig
-                testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest;
+                testSpecialisationConfig testFlakeSwitch clevisTest clevisFallbackTest
+                disableFileSystems;
       };
     };
 
     makeLuksRootTest = name: luksFormatOpts: makeInstallerTest name {
       createPartitions = ''
-        machine.succeed(
+        installer.succeed(
             "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
             + " mkpart primary ext2 1M 100MB"  # /boot
             + " mkpart primary linux-swap 100M 1024M"
@@ -569,10 +563,9 @@ let
         boot.kernelParams = lib.mkAfter [ "console=tty0" ];
       '';
       enableOCR = true;
-      preBootCommands = ''
-        machine.start()
-        machine.wait_for_text("[Pp]assphrase for")
-        machine.send_chars("supersecret\n")
+      postBootCommands = ''
+        target.wait_for_text("[Pp]assphrase for")
+        target.send_chars("supersecret\n")
       '';
     };
 
@@ -580,7 +573,7 @@ let
   # one big filesystem partition.
   simple-test-config = {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary linux-swap 1M 1024M"
           + " mkpart primary ext2 1024M -1s",
@@ -599,7 +592,7 @@ let
 
   simple-uefi-grub-config = {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel gpt"
           + " mkpart ESP fat32 1M 100MiB"  # /boot
           + " set 1 boot on"
@@ -653,7 +646,7 @@ let
       environment.systemPackages = with pkgs; [ keyutils clevis ];
     };
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
         "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
         + " mkpart primary ext2 1M 100MB"
         + " mkpart primary linux-swap 100M 1024M"
@@ -677,13 +670,9 @@ let
       # not know the UUID in advance.
       fileSystems."/" = lib.mkForce { device = "/dev/vda3"; fsType = "bcachefs"; };
     '';
-    preBootCommands = ''
-      tpm = Tpm()
-      tpm.check()
-    '' + optionalString fallback ''
-      machine.start()
-      machine.wait_for_text("enter passphrase for")
-      machine.send_chars("password\n")
+    postBootCommands = optionalString fallback ''
+      target.wait_for_text("enter passphrase for")
+      target.send_chars("password\n")
     '';
   };
 
@@ -695,7 +684,7 @@ let
       environment.systemPackages = with pkgs; [ clevis ];
     };
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
         "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
         + " mkpart primary ext2 1M 100MB"
         + " mkpart primary linux-swap 100M 1024M"
@@ -716,17 +705,13 @@ let
     extraConfig = ''
       boot.initrd.clevis.devices."crypt-root".secretFile = "/etc/nixos/clevis-secret.jwe";
     '';
-    preBootCommands = ''
-      tpm = Tpm()
-      tpm.check()
-    '' + optionalString fallback ''
-      machine.start()
+    postBootCommands = optionalString fallback ''
       ${if systemdStage1 then ''
-      machine.wait_for_text("Please enter")
+      target.wait_for_text("Please enter")
       '' else ''
-      machine.wait_for_text("Passphrase for")
+      target.wait_for_text("Passphrase for")
       ''}
-      machine.send_chars("password\n")
+      target.send_chars("password\n")
     '';
   };
 
@@ -739,7 +724,7 @@ let
       environment.systemPackages = with pkgs; [ clevis ];
     };
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
         "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
         + " mkpart primary ext2 1M 100MB"
         + " mkpart primary linux-swap 100M 1024M"
@@ -767,17 +752,13 @@ let
       boot.zfs.devNodes = "/dev/disk/by-uuid/";
       networking.hostId = "00000000";
     '';
-    preBootCommands = ''
-      tpm = Tpm()
-      tpm.check()
-    '' + optionalString fallback ''
-      machine.start()
+    postBootCommands = optionalString fallback ''
       ${if systemdStage1 then ''
-      machine.wait_for_text("Enter key for rpool/root")
+      target.wait_for_text("Enter key for rpool/root")
       '' else ''
-      machine.wait_for_text("Key load error")
+      target.wait_for_text("Key load error")
       ''}
-      machine.send_chars("password\n")
+      target.send_chars("password\n")
     '';
   };
 
@@ -798,7 +779,7 @@ in {
   # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
   simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel gpt"
           + " mkpart ESP fat32 1M 100MiB"  # /boot
           + " set 1 boot on"
@@ -825,7 +806,7 @@ in {
   # Same as the previous, but now with a separate /boot partition.
   separateBoot = makeInstallerTest "separateBoot" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary ext2 1M 100MB"  # /boot
           + " mkpart primary linux-swap 100MB 1024M"
@@ -845,7 +826,7 @@ in {
   # Same as the previous, but with fat32 /boot.
   separateBootFat = makeInstallerTest "separateBootFat" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary ext2 1M 100MB"  # /boot
           + " mkpart primary linux-swap 100MB 1024M"
@@ -877,7 +858,7 @@ in {
     '';
 
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary ext2 1M 256MB"   # /boot
           + " mkpart primary linux-swap 256MB 1280M"
@@ -929,8 +910,8 @@ in {
     # umount & export bpool before shutdown
     # this is a fix for "cannot import 'bpool': pool was previously in use from another system."
     postInstallCommands = ''
-      machine.succeed("umount /mnt/boot")
-      machine.succeed("zpool export bpool")
+      installer.succeed("umount /mnt/boot")
+      installer.succeed("zpool export bpool")
     '';
   };
 
@@ -951,7 +932,7 @@ in {
     '';
 
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary 1M 100MB"  # /boot
           + " mkpart primary linux-swap 100M 1024M"
@@ -977,7 +958,7 @@ in {
   # that contains the logical swap and root partitions.
   lvm = makeInstallerTest "lvm" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary 1M 2048M"  # PV1
           + " set 1 lvm on"
@@ -1010,7 +991,7 @@ in {
   # keyfile is configured
   encryptedFSWithKeyfile = makeInstallerTest "encryptedFSWithKeyfile" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
           + " mkpart primary ext2 1M 100MB"  # /boot
           + " mkpart primary linux-swap 100M 1024M"
@@ -1049,7 +1030,7 @@ in {
   # LVM-on-LUKS and a keyfile in initrd.secrets to enter the passphrase once
   fullDiskEncryption = makeInstallerTest "fullDiskEncryption" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda -- mklabel gpt"
           + " mkpart ESP fat32 1M 100MiB"  # /boot/efi
           + " set 1 boot on"
@@ -1080,23 +1061,22 @@ in {
       boot.loader.grub.enableCryptodisk = true;
       boot.loader.efi.efiSysMountPoint = "/boot/efi";
 
-      boot.initrd.secrets."/luks.key" = ./luks.key;
+      boot.initrd.secrets."/luks.key" = "/etc/nixos/luks.key";
       boot.initrd.luks.devices.crypt =
         { device  = "/dev/vda2";
           keyFile = "/luks.key";
         };
     '';
     enableOCR = true;
-    preBootCommands = ''
-      machine.start()
-      machine.wait_for_text("Enter passphrase for")
-      machine.send_chars("supersecret\n")
+    postBootCommands = ''
+      target.wait_for_text("Enter passphrase for")
+      target.send_chars("supersecret\n")
     '';
   };
 
   swraid = makeInstallerTest "swraid" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda --"
           + " mklabel msdos"
           + " mkpart primary ext2 1M 100MB"  # /boot
@@ -1125,15 +1105,14 @@ in {
           "udevadm settle",
       )
     '';
-    preBootCommands = ''
-      machine.start()
-      machine.fail("dmesg | grep 'immediate safe mode'")
+    postBootCommands = ''
+      target.fail("dmesg | grep 'immediate safe mode'")
     '';
   };
 
   bcache = makeInstallerTest "bcache" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "flock /dev/vda parted --script /dev/vda --"
           + " mklabel msdos"
           + " mkpart primary ext2 1M 100MB"  # /boot
@@ -1162,7 +1141,7 @@ in {
     };
 
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
         "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
         + " mkpart primary ext2 1M 100MB"          # /boot
         + " mkpart primary linux-swap 100M 1024M"  # swap
@@ -1194,18 +1173,17 @@ in {
     '';
 
     enableOCR = true;
-    preBootCommands = ''
-      machine.start()
+    postBootCommands = ''
       # Enter it wrong once
-      machine.wait_for_text("enter passphrase for ")
-      machine.send_chars("wrong\n")
+      target.wait_for_text("enter passphrase for ")
+      target.send_chars("wrong\n")
       # Then enter it right.
-      machine.wait_for_text("enter passphrase for ")
-      machine.send_chars("password\n")
+      target.wait_for_text("enter passphrase for ")
+      target.send_chars("password\n")
     '';
 
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
         "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
         + " mkpart primary ext2 1M 100MB"          # /boot
         + " mkpart primary linux-swap 100M 1024M"  # swap
@@ -1232,7 +1210,7 @@ in {
     };
 
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
         "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
         + " mkpart primary ext2 1M 100MB"          # /boot
         + " mkpart primary linux-swap 100M 1024M"  # swap
@@ -1253,7 +1231,7 @@ in {
   # Test using labels to identify volumes in grub
   simpleLabels = makeInstallerTest "simpleLabels" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "sgdisk -Z /dev/vda",
           "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
           "mkswap /dev/vda2 -L swap",
@@ -1270,7 +1248,7 @@ in {
   simpleProvided = makeInstallerTest "simpleProvided" {
     createPartitions = ''
       uuid = "$(blkid -s UUID -o value /dev/vda2)"
-      machine.succeed(
+      installer.succeed(
           "sgdisk -Z /dev/vda",
           "sgdisk -n 1:0:+1M -n 2:0:+100M -n 3:0:+1G -N 4 -t 1:ef02 -t 2:8300 "
           + "-t 3:8200 -t 4:8300 -c 2:boot -c 4:root /dev/vda",
@@ -1279,9 +1257,9 @@ in {
           "mkfs.ext4 -L boot /dev/vda2",
           "mkfs.ext4 -L root /dev/vda4",
       )
-      machine.execute(f"ln -s ../../vda2 /dev/disk/by-uuid/{uuid}")
-      machine.execute("ln -s ../../vda4 /dev/disk/by-label/root")
-      machine.succeed(
+      installer.execute(f"ln -s ../../vda2 /dev/disk/by-uuid/{uuid}")
+      installer.execute("ln -s ../../vda4 /dev/disk/by-label/root")
+      installer.succeed(
           "mount /dev/disk/by-label/root /mnt",
           "mkdir /mnt/boot",
           f"mount /dev/disk/by-uuid/{uuid} /mnt/boot",
@@ -1293,7 +1271,7 @@ in {
   # Simple btrfs grub testing
   btrfsSimple = makeInstallerTest "btrfsSimple" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "sgdisk -Z /dev/vda",
           "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
           "mkswap /dev/vda2 -L swap",
@@ -1307,7 +1285,7 @@ in {
   # Test to see if we can detect /boot and /nix on subvolumes
   btrfsSubvols = makeInstallerTest "btrfsSubvols" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "sgdisk -Z /dev/vda",
           "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
           "mkswap /dev/vda2 -L swap",
@@ -1329,7 +1307,7 @@ in {
   # Test to see if we can detect default and aux subvolumes correctly
   btrfsSubvolDefault = makeInstallerTest "btrfsSubvolDefault" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "sgdisk -Z /dev/vda",
           "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
           "mkswap /dev/vda2 -L swap",
@@ -1355,7 +1333,7 @@ in {
   # Test to see if we can deal with subvols that need to be escaped in fstab
   btrfsSubvolEscape = makeInstallerTest "btrfsSubvolEscape" {
     createPartitions = ''
-      machine.succeed(
+      installer.succeed(
           "sgdisk -Z /dev/vda",
           "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
           "mkswap /dev/vda2 -L swap",
@@ -1382,7 +1360,7 @@ in {
 } // optionalAttrs systemdStage1 {
   stratisRoot = makeInstallerTest "stratisRoot" {
     createPartitions = ''
-      machine.succeed(
+      installer.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
@@ -1414,4 +1392,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 = ''
+      installer.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/invidious.nix b/nixos/tests/invidious.nix
index e31cd87f6a004..372b47b56c345 100644
--- a/nixos/tests/invidious.nix
+++ b/nixos/tests/invidious.nix
@@ -18,7 +18,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           host invidious invidious samenet scram-sha-256
         '';
       };
-      networking.firewall.allowedTCPPorts = [ config.services.postgresql.port ];
+      networking.firewall.allowedTCPPorts = [ config.services.postgresql.settings.port ];
     };
     machine = { config, lib, pkgs, ... }: {
       services.invidious = {
diff --git a/nixos/tests/k3s/multi-node.nix b/nixos/tests/k3s/multi-node.nix
index 932b4639b39c8..20279f3ca4b93 100644
--- a/nixos/tests/k3s/multi-node.nix
+++ b/nixos/tests/k3s/multi-node.nix
@@ -128,9 +128,7 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
       };
     };
 
-    meta = with pkgs.lib.maintainers; {
-      maintainers = [ euank ];
-    };
+    meta.maintainers = k3s.meta.maintainers;
 
     testScript = ''
       machines = [server, server2, agent]
diff --git a/nixos/tests/k3s/single-node.nix b/nixos/tests/k3s/single-node.nix
index e059603b9c9d7..fd64a050e61ef 100644
--- a/nixos/tests/k3s/single-node.nix
+++ b/nixos/tests/k3s/single-node.nix
@@ -25,9 +25,7 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
   in
   {
     name = "${k3s.name}-single-node";
-    meta = with pkgs.lib.maintainers; {
-      maintainers = [ euank ];
-    };
+    meta.maintainers = k3s.meta.maintainers;
 
     nodes.machine = { pkgs, ... }: {
       environment.systemPackages = with pkgs; [ k3s gzip ];
diff --git a/nixos/tests/kavita.nix b/nixos/tests/kavita.nix
index f27b3fffbcf64..bb55e1fb29d43 100644
--- a/nixos/tests/kavita.nix
+++ b/nixos/tests/kavita.nix
@@ -1,4 +1,4 @@
-import ./make-test-python.nix ({ pkgs, ...} : {
+import ./make-test-python.nix ({ pkgs, ... }: {
   name = "kavita";
   meta = with pkgs.lib.maintainers; {
     maintainers = [ misterio77 ];
@@ -8,29 +8,35 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     kavita = { config, pkgs, ... }: {
       services.kavita = {
         enable = true;
-        port = 5000;
-        tokenKeyFile = builtins.toFile "kavita.key" "QfpjFvjT83BLtZ74GE3U3Q==";
+        tokenKeyFile = builtins.toFile "kavita.key" "d26ba694b455271a8872415830fb7b5c58f8da98f9ef7f58b2ca4c34bd406512";
       };
     };
   };
 
-  testScript = let
-    regUrl = "http://kavita:5000/api/Account/register";
-    payload = builtins.toFile "payload.json" (builtins.toJSON {
-      username = "foo";
-      password = "correcthorsebatterystaple";
-      email = "foo@bar";
-    });
-  in ''
-    kavita.start
-    kavita.wait_for_unit("kavita.service")
+  testScript =
+    let
+      regUrl = "http://kavita:5000/api/Account/register";
+      loginUrl = "http://kavita:5000/api/Account/login";
+      localeUrl = "http://kavita:5000/api/locale";
+    in
+    ''
+      import json
 
-    # Check that static assets are working
-    kavita.wait_until_succeeds("curl http://kavita:5000/site.webmanifest | grep Kavita")
+      kavita.start
+      kavita.wait_for_unit("kavita.service")
 
-    # Check that registration is working
-    kavita.succeed("curl -fX POST ${regUrl} --json @${payload}")
-    # But only for the first one
-    kavita.fail("curl -fX POST ${regUrl} --json @${payload}")
-  '';
+      # Check that static assets are working
+      kavita.wait_until_succeeds("curl http://kavita:5000/site.webmanifest | grep Kavita")
+
+      # Check that registration is working
+      kavita.succeed("""curl -fX POST ${regUrl} --json '{"username": "foo", "password": "correcthorsebatterystaple"}'""")
+      # But only for the first one
+      kavita.fail("""curl -fX POST ${regUrl} --json '{"username": "foo", "password": "correcthorsebatterystaple"}'""")
+
+      # Log in and retrieve token
+      session = json.loads(kavita.succeed("""curl -fX POST ${loginUrl} --json '{"username": "foo", "password": "correcthorsebatterystaple"}'"""))
+      # Check list of locales
+      locales = json.loads(kavita.succeed(f"curl -fX GET ${localeUrl} -H 'Authorization: Bearer {session['token']}'"))
+      assert len(locales) > 0, "expected a list of locales"
+    '';
 })
diff --git a/nixos/tests/kea.nix b/nixos/tests/kea.nix
index c8ecf771fa13a..98a8e93a07609 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/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 9714a94382ee0..5f0e7b3e37cd7 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -31,7 +31,6 @@ let
       linux_5_15_hardened
       linux_6_1_hardened
       linux_6_6_hardened
-      linux_6_7_hardened
       linux_rt_5_4
       linux_rt_5_10
       linux_rt_5_15
diff --git a/nixos/tests/kernel-rust.nix b/nixos/tests/kernel-rust.nix
index 1f269173ec2e3..f32d433260616 100644
--- a/nixos/tests/kernel-rust.nix
+++ b/nixos/tests/kernel-rust.nix
@@ -4,7 +4,7 @@
 }:
 
 let
-  inherit (pkgs.lib) const filterAttrs mapAttrs;
+  inherit (pkgs.lib) const filterAttrs mapAttrs meta;
 
   kernelRustTest = kernelPackages: import ./make-test-python.nix ({ lib, ... }: {
     name = "kernel-rust";
@@ -38,6 +38,8 @@ let
       inherit (builtins.tryEval (
         x.rust-out-of-tree-module or null != null
       )) success value;
-    in success && value))
+      available =
+        meta.availableOn pkgs.stdenv.hostPlatform x.rust-out-of-tree-module;
+    in success && value && available))
     pkgs.linuxKernel.vanillaPackages;
 in mapAttrs (const kernelRustTest) kernels
diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix
index 228e57d1cdd6f..67b412c80961d 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 ede085632c634..274ad580cebc9 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 4e9ab9a36d137..8ed0f47887c7d 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/lightdm.nix b/nixos/tests/lightdm.nix
index 94cebd4a630ab..730983a804134 100644
--- a/nixos/tests/lightdm.nix
+++ b/nixos/tests/lightdm.nix
@@ -8,7 +8,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     imports = [ ./common/user-account.nix ];
     services.xserver.enable = true;
     services.xserver.displayManager.lightdm.enable = true;
-    services.xserver.displayManager.defaultSession = "none+icewm";
+    services.displayManager.defaultSession = "none+icewm";
     services.xserver.windowManager.icewm.enable = true;
   };
 
diff --git a/nixos/tests/lvm2/default.nix b/nixos/tests/lvm2/default.nix
index e0358ec2806fa..84f24cbc38593 100644
--- a/nixos/tests/lvm2/default.nix
+++ b/nixos/tests/lvm2/default.nix
@@ -36,9 +36,14 @@ lib.listToAttrs (
     lib.flip lib.concatMap kernelVersionsToTest (version:
       let
         v' = lib.replaceStrings [ "." ] [ "_" ] version;
+        mkXfsFlags = lib.optionalString (lib.versionOlder version "5.10") " -m bigtime=0 -m inobtcount=0 "
+                     + lib.optionalString (lib.versionOlder version "5.19") " -i nrext64=0 ";
       in
       lib.flip lib.mapAttrsToList tests (name: t:
-        lib.nameValuePair "lvm-${name}-linux-${v'}" (lib.optionalAttrs (builtins.elem version (t.kernelFilter kernelVersionsToTest)) (t.test ({ kernelPackages = pkgs."linuxPackages_${v'}"; } // builtins.removeAttrs t [ "test" "kernelFilter" ])))
+        lib.nameValuePair "lvm-${name}-linux-${v'}" (lib.optionalAttrs (builtins.elem version (t.kernelFilter kernelVersionsToTest)) (t.test ({
+          kernelPackages = pkgs."linuxPackages_${v'}";
+          inherit mkXfsFlags;
+        } // builtins.removeAttrs t [ "test" "kernelFilter" ])))
       )
     )
   )
diff --git a/nixos/tests/lvm2/systemd-stage-1.nix b/nixos/tests/lvm2/systemd-stage-1.nix
index 1c95aadfcb3f1..7f106e1b0dd64 100644
--- a/nixos/tests/lvm2/systemd-stage-1.nix
+++ b/nixos/tests/lvm2/systemd-stage-1.nix
@@ -1,4 +1,4 @@
-{ kernelPackages ? null, flavour }: let
+{ kernelPackages ? null, flavour, mkXfsFlags ? "" }: let
   preparationCode = {
     raid = ''
       machine.succeed("vgcreate test_vg /dev/vdb /dev/vdc")
@@ -71,7 +71,7 @@ in import ../make-test-python.nix ({ pkgs, lib, ... }: {
     boot.loader.systemd-boot.enable = true;
     boot.loader.efi.canTouchEfiVariables = true;
 
-    environment.systemPackages = with pkgs; [ e2fsprogs ]; # for mkfs.ext4
+    environment.systemPackages = with pkgs; [ xfsprogs ];
     boot = {
       initrd.systemd = {
         enable = true;
@@ -88,7 +88,7 @@ in import ../make-test-python.nix ({ pkgs, lib, ... }: {
     machine.wait_for_unit("multi-user.target")
     # Create a VG for the root
     ${preparationCode}
-    machine.succeed("mkfs.ext4 /dev/test_vg/test_lv")
+    machine.succeed("mkfs.xfs ${mkXfsFlags} /dev/test_vg/test_lv")
     machine.succeed("mkdir -p /mnt && mount /dev/test_vg/test_lv /mnt && echo hello > /mnt/test && umount /mnt")
 
     # Boot from LVM
diff --git a/nixos/tests/lvm2/thinpool.nix b/nixos/tests/lvm2/thinpool.nix
index f49c8980613ce..325bb87460b71 100644
--- a/nixos/tests/lvm2/thinpool.nix
+++ b/nixos/tests/lvm2/thinpool.nix
@@ -1,4 +1,4 @@
-{ kernelPackages ? null }:
+{ kernelPackages ? null, mkXfsFlags ? "" }:
 import ../make-test-python.nix ({ pkgs, lib, ... }: {
   name = "lvm2-thinpool";
   meta.maintainers = lib.teams.helsinki-systems.members;
@@ -18,7 +18,8 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: {
   };
 
   testScript = let
-    mkXfsFlags = lib.optionalString (lib.versionOlder kernelPackages.kernel.version "5.10") "-m bigtime=0 -m inobtcount=0";
+    mkXfsFlags = lib.optionalString (lib.versionOlder kernelPackages.kernel.version "5.10") " -m bigtime=0 -m inobtcount=0 "
+               + lib.optionalString (lib.versionOlder kernelPackages.kernel.version "5.19") " -i nrext64=0 ";
   in ''
     machine.succeed("vgcreate test_vg /dev/vdb")
     machine.succeed("lvcreate -L 512M -T test_vg/test_thin_pool")
diff --git a/nixos/tests/lvm2/vdo.nix b/nixos/tests/lvm2/vdo.nix
index 75c1fc094e97f..18d25b7b366d0 100644
--- a/nixos/tests/lvm2/vdo.nix
+++ b/nixos/tests/lvm2/vdo.nix
@@ -1,4 +1,4 @@
-{ kernelPackages ? null }:
+{ kernelPackages ? null, mkXfsFlags ? "" }:
 import ../make-test-python.nix ({ pkgs, lib, ... }: {
   name = "lvm2-vdo";
   meta.maintainers = lib.teams.helsinki-systems.members;
@@ -17,7 +17,7 @@ import ../make-test-python.nix ({ pkgs, lib, ... }: {
   testScript = ''
     machine.succeed("vgcreate test_vg /dev/vdb")
     machine.succeed("lvcreate --type vdo -n vdo_lv -L 6G -V 12G test_vg/vdo_pool_lv")
-    machine.succeed("mkfs.xfs -K /dev/test_vg/vdo_lv")
+    machine.succeed("mkfs.xfs ${mkXfsFlags} -K /dev/test_vg/vdo_lv")
     machine.succeed("mkdir /mnt; mount /dev/test_vg/vdo_lv /mnt")
     assert "/dev/mapper/test_vg-vdo_lv" == machine.succeed("findmnt -no SOURCE /mnt").strip()
     machine.succeed("umount /mnt")
diff --git a/nixos/tests/maestral.nix b/nixos/tests/maestral.nix
index 67a265926187d..52cc32cd0f4bb 100644
--- a/nixos/tests/maestral.nix
+++ b/nixos/tests/maestral.nix
@@ -29,11 +29,14 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       gui = { ... }: common {
         services.xserver = {
           enable = true;
-          displayManager.sddm.enable = true;
-          displayManager.defaultSession = "plasma";
           desktopManager.plasma5.enable = true;
           desktopManager.plasma5.runUsingSystemd = true;
-          displayManager.autoLogin = {
+        };
+
+        services.displayManager = {
+          sddm.enable = true;
+          defaultSession = "plasma";
+          autoLogin = {
             enable = true;
             user = "alice";
           };
diff --git a/nixos/tests/mate-wayland.nix b/nixos/tests/mate-wayland.nix
new file mode 100644
index 0000000000000..e5c96d2af7470
--- /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.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 48582e18d520c..1252ec43cf3d5 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/matrix/mautrix-meta-postgres.nix b/nixos/tests/matrix/mautrix-meta-postgres.nix
new file mode 100644
index 0000000000000..c9a45788afaf6
--- /dev/null
+++ b/nixos/tests/matrix/mautrix-meta-postgres.nix
@@ -0,0 +1,221 @@
+import ../make-test-python.nix ({ pkgs, ... }:
+  let
+    homeserverDomain = "server";
+    homeserverUrl = "http://server:8008";
+    userName = "alice";
+    botUserName = "instagrambot";
+
+    asToken = "this-is-my-totally-randomly-generated-as-token";
+    hsToken = "this-is-my-totally-randomly-generated-hs-token";
+  in
+  {
+    name = "mautrix-meta-postgres";
+    meta.maintainers = pkgs.mautrix-meta.meta.maintainers;
+
+    nodes = {
+      server = { config, pkgs, ... }: {
+        services.postgresql = {
+          enable = true;
+
+          ensureUsers = [
+            {
+              name = "mautrix-meta-instagram";
+              ensureDBOwnership = true;
+            }
+          ];
+
+          ensureDatabases = [
+            "mautrix-meta-instagram"
+          ];
+        };
+
+        systemd.services.mautrix-meta-instagram = {
+          wants = [ "postgres.service" ];
+          after = [ "postgres.service" ];
+        };
+
+        services.matrix-synapse = {
+          enable = true;
+          settings = {
+            database.name = "sqlite3";
+
+            enable_registration = true;
+
+            # don't use this in production, always use some form of verification
+            enable_registration_without_verification = true;
+
+            listeners = [ {
+              # The default but tls=false
+              bind_addresses = [
+                "0.0.0.0"
+              ];
+              port = 8008;
+              resources = [ {
+                "compress" = true;
+                "names" = [ "client" ];
+              } {
+                "compress" = false;
+                "names" = [ "federation" ];
+              } ];
+              tls = false;
+              type = "http";
+            } ];
+          };
+        };
+
+        services.mautrix-meta.instances.instagram = {
+          enable = true;
+
+          environmentFile = pkgs.writeText ''my-secrets'' ''
+            AS_TOKEN=${asToken}
+            HS_TOKEN=${hsToken}
+          '';
+
+          settings = {
+            homeserver = {
+              address = homeserverUrl;
+              domain = homeserverDomain;
+            };
+
+            appservice = {
+              port = 8009;
+
+              as_token = "$AS_TOKEN";
+              hs_token = "$HS_TOKEN";
+
+              database = {
+                type = "postgres";
+                uri = "postgres:///mautrix-meta-instagram?host=/var/run/postgresql";
+              };
+
+              bot.username = botUserName;
+            };
+
+            bridge.permissions."@${userName}:server" = "user";
+          };
+        };
+
+        networking.firewall.allowedTCPPorts = [ 8008 8009 ];
+      };
+
+      client = { pkgs, ... }: {
+        environment.systemPackages = [
+          (pkgs.writers.writePython3Bin "do_test"
+          {
+            libraries = [ pkgs.python3Packages.matrix-nio ];
+            flakeIgnore = [
+              # We don't live in the dark ages anymore.
+              # Languages like Python that are whitespace heavy will overrun
+              # 79 characters..
+              "E501"
+            ];
+          } ''
+              import sys
+              import functools
+              import asyncio
+
+              from nio import AsyncClient, RoomMessageNotice, RoomCreateResponse, RoomInviteResponse
+
+
+              async def message_callback(matrix: AsyncClient, msg: str, _r, e):
+                  print("Received matrix text message: ", e)
+                  assert msg in e.body
+                  exit(0)  # Success!
+
+
+              async def run(homeserver: str):
+                  matrix = AsyncClient(homeserver)
+                  response = await matrix.register("${userName}", "foobar")
+                  print("Matrix register response: ", response)
+
+                  # Open a DM with the bridge bot
+                  response = await matrix.room_create()
+                  print("Matrix create room response:", response)
+                  assert isinstance(response, RoomCreateResponse)
+                  room_id = response.room_id
+
+                  response = await matrix.room_invite(room_id, "@${botUserName}:${homeserverDomain}")
+                  assert isinstance(response, RoomInviteResponse)
+
+                  callback = functools.partial(
+                      message_callback, matrix, "Hello, I'm an Instagram bridge bot."
+                  )
+                  matrix.add_event_callback(callback, RoomMessageNotice)
+
+                  print("Waiting for matrix message...")
+                  await matrix.sync_forever(timeout=30000)
+
+
+              if __name__ == "__main__":
+                  asyncio.run(run(sys.argv[1]))
+            ''
+          )
+        ];
+      };
+    };
+
+    testScript = ''
+      def extract_token(data):
+          stdout = data[1]
+          stdout = stdout.strip()
+          line = stdout.split('\n')[-1]
+          return line.split(':')[-1].strip("\" '\n")
+
+      def get_token_from(token, file):
+          data = server.execute(f"cat {file} | grep {token}")
+          return extract_token(data)
+
+      def get_as_token_from(file):
+          return get_token_from("as_token", file)
+
+      def get_hs_token_from(file):
+          return get_token_from("hs_token", file)
+
+      config_yaml = "/var/lib/mautrix-meta-instagram/config.yaml"
+      registration_yaml = "/var/lib/mautrix-meta-instagram/meta-registration.yaml"
+
+      expected_as_token = "${asToken}"
+      expected_hs_token = "${hsToken}"
+
+      start_all()
+
+      with subtest("start the server"):
+          # bridge
+          server.wait_for_unit("mautrix-meta-instagram.service")
+
+          # homeserver
+          server.wait_for_unit("matrix-synapse.service")
+
+          server.wait_for_open_port(8008)
+          # Bridge only opens the port after it contacts the homeserver
+          server.wait_for_open_port(8009)
+
+      with subtest("ensure messages can be exchanged"):
+          client.succeed("do_test ${homeserverUrl} >&2")
+
+      with subtest("ensure as_token, hs_token match from environment file"):
+          as_token = get_as_token_from(config_yaml)
+          hs_token = get_hs_token_from(config_yaml)
+          as_token_registration = get_as_token_from(registration_yaml)
+          hs_token_registration = get_hs_token_from(registration_yaml)
+
+          assert as_token == expected_as_token, f"as_token in config should match the one specified (is: {as_token}, expected: {expected_as_token})"
+          assert hs_token == expected_hs_token, f"hs_token in config should match the one specified (is: {hs_token}, expected: {expected_hs_token})"
+          assert as_token_registration == expected_as_token, f"as_token in registration should match the one specified (is: {as_token_registration}, expected: {expected_as_token})"
+          assert hs_token_registration == expected_hs_token, f"hs_token in registration should match the one specified (is: {hs_token_registration}, expected: {expected_hs_token})"
+
+      with subtest("ensure as_token and hs_token stays same after restart"):
+          server.systemctl("restart mautrix-meta-instagram")
+          server.wait_for_open_port(8009)
+
+          as_token = get_as_token_from(config_yaml)
+          hs_token = get_hs_token_from(config_yaml)
+          as_token_registration = get_as_token_from(registration_yaml)
+          hs_token_registration = get_hs_token_from(registration_yaml)
+
+          assert as_token == expected_as_token, f"as_token in config should match the one specified (is: {as_token}, expected: {expected_as_token})"
+          assert hs_token == expected_hs_token, f"hs_token in config should match the one specified (is: {hs_token}, expected: {expected_hs_token})"
+          assert as_token_registration == expected_as_token, f"as_token in registration should match the one specified (is: {as_token_registration}, expected: {expected_as_token})"
+          assert hs_token_registration == expected_hs_token, f"hs_token in registration should match the one specified (is: {hs_token_registration}, expected: {expected_hs_token})"
+    '';
+  })
diff --git a/nixos/tests/matrix/mautrix-meta-sqlite.nix b/nixos/tests/matrix/mautrix-meta-sqlite.nix
new file mode 100644
index 0000000000000..b5e580620049a
--- /dev/null
+++ b/nixos/tests/matrix/mautrix-meta-sqlite.nix
@@ -0,0 +1,247 @@
+import ../make-test-python.nix ({ pkgs, ... }:
+  let
+    homeserverDomain = "server";
+    homeserverUrl = "http://server:8008";
+    username = "alice";
+    instagramBotUsername = "instagrambot";
+    facebookBotUsername = "facebookbot";
+  in
+  {
+    name = "mautrix-meta-sqlite";
+    meta.maintainers = pkgs.mautrix-meta.meta.maintainers;
+
+    nodes = {
+      server = { config, pkgs, ... }: {
+        services.matrix-synapse = {
+          enable = true;
+          settings = {
+            database.name = "sqlite3";
+
+            enable_registration = true;
+
+            # don't use this in production, always use some form of verification
+            enable_registration_without_verification = true;
+
+            listeners = [ {
+              # The default but tls=false
+              bind_addresses = [
+                "0.0.0.0"
+              ];
+              port = 8008;
+              resources = [ {
+                "compress" = true;
+                "names" = [ "client" ];
+              } {
+                "compress" = false;
+                "names" = [ "federation" ];
+              } ];
+              tls = false;
+              type = "http";
+            } ];
+          };
+        };
+
+        services.mautrix-meta.instances.facebook = {
+          enable = true;
+
+          settings = {
+            homeserver = {
+              address = homeserverUrl;
+              domain = homeserverDomain;
+            };
+
+            appservice = {
+              port = 8009;
+
+              bot.username = facebookBotUsername;
+            };
+
+            bridge.permissions."@${username}:server" = "user";
+          };
+        };
+
+        services.mautrix-meta.instances.instagram = {
+          enable = true;
+
+          settings = {
+            homeserver = {
+              address = homeserverUrl;
+              domain = homeserverDomain;
+            };
+
+            appservice = {
+              port = 8010;
+
+              bot.username = instagramBotUsername;
+            };
+
+            bridge.permissions."@${username}:server" = "user";
+          };
+        };
+
+        networking.firewall.allowedTCPPorts = [ 8008 ];
+      };
+
+      client = { pkgs, ... }: {
+        environment.systemPackages = [
+          (pkgs.writers.writePython3Bin "register_user"
+          {
+            libraries = [ pkgs.python3Packages.matrix-nio ];
+            flakeIgnore = [
+              # We don't live in the dark ages anymore.
+              # Languages like Python that are whitespace heavy will overrun
+              # 79 characters..
+              "E501"
+            ];
+          } ''
+              import sys
+              import asyncio
+
+              from nio import AsyncClient
+
+
+              async def run(username: str, homeserver: str):
+                  matrix = AsyncClient(homeserver)
+
+                  response = await matrix.register(username, "foobar")
+                  print("Matrix register response: ", response)
+
+
+              if __name__ == "__main__":
+                  asyncio.run(run(sys.argv[1], sys.argv[2]))
+            ''
+          )
+          (pkgs.writers.writePython3Bin "do_test"
+          {
+            libraries = [ pkgs.python3Packages.matrix-nio ];
+            flakeIgnore = [
+              # We don't live in the dark ages anymore.
+              # Languages like Python that are whitespace heavy will overrun
+              # 79 characters..
+              "E501"
+            ];
+          } ''
+              import sys
+              import functools
+              import asyncio
+
+              from nio import AsyncClient, RoomMessageNotice, RoomCreateResponse, RoomInviteResponse
+
+
+              async def message_callback(matrix: AsyncClient, msg: str, _r, e):
+                  print("Received matrix text message: ", e)
+                  assert msg in e.body
+                  exit(0)  # Success!
+
+
+              async def run(username: str, bot_username: str, homeserver: str):
+                  matrix = AsyncClient(homeserver, f"@{username}:${homeserverDomain}")
+
+                  response = await matrix.login("foobar")
+                  print("Matrix login response: ", response)
+
+                  # Open a DM with the bridge bot
+                  response = await matrix.room_create()
+                  print("Matrix create room response:", response)
+                  assert isinstance(response, RoomCreateResponse)
+                  room_id = response.room_id
+
+                  response = await matrix.room_invite(room_id, f"@{bot_username}:${homeserverDomain}")
+                  assert isinstance(response, RoomInviteResponse)
+
+                  callback = functools.partial(
+                      message_callback, matrix, "Hello, I'm an Instagram bridge bot."
+                  )
+                  matrix.add_event_callback(callback, RoomMessageNotice)
+
+                  print("Waiting for matrix message...")
+                  await matrix.sync_forever(timeout=30000)
+
+
+              if __name__ == "__main__":
+                  asyncio.run(run(sys.argv[1], sys.argv[2], sys.argv[3]))
+            ''
+          )
+        ];
+      };
+    };
+
+    testScript = ''
+      def extract_token(data):
+          stdout = data[1]
+          stdout = stdout.strip()
+          line = stdout.split('\n')[-1]
+          return line.split(':')[-1].strip("\" '\n")
+
+      def get_token_from(token, file):
+          data = server.execute(f"cat {file} | grep {token}")
+          return extract_token(data)
+
+      def get_as_token_from(file):
+          return get_token_from("as_token", file)
+
+      def get_hs_token_from(file):
+          return get_token_from("hs_token", file)
+
+      config_yaml = "/var/lib/mautrix-meta-facebook/config.yaml"
+      registration_yaml = "/var/lib/mautrix-meta-facebook/meta-registration.yaml"
+
+      start_all()
+
+      with subtest("wait for bridges and homeserver"):
+          # bridge
+          server.wait_for_unit("mautrix-meta-facebook.service")
+          server.wait_for_unit("mautrix-meta-instagram.service")
+
+          # homeserver
+          server.wait_for_unit("matrix-synapse.service")
+
+          server.wait_for_open_port(8008)
+          # Bridges only open the port after they contact the homeserver
+          server.wait_for_open_port(8009)
+          server.wait_for_open_port(8010)
+
+      with subtest("register user"):
+          client.succeed("register_user ${username} ${homeserverUrl} >&2")
+
+      with subtest("ensure messages can be exchanged"):
+          client.succeed("do_test ${username} ${facebookBotUsername} ${homeserverUrl} >&2")
+          client.succeed("do_test ${username} ${instagramBotUsername} ${homeserverUrl} >&2")
+
+      with subtest("ensure as_token and hs_token stays same after restart"):
+          generated_as_token_facebook = get_as_token_from(config_yaml)
+          generated_hs_token_facebook = get_hs_token_from(config_yaml)
+
+          generated_as_token_facebook_registration = get_as_token_from(registration_yaml)
+          generated_hs_token_facebook_registration = get_hs_token_from(registration_yaml)
+
+          # Indirectly checks the as token is not set to something like empty string or "null"
+          assert len(generated_as_token_facebook) > 20, f"as_token ({generated_as_token_facebook}) is too short, something went wrong"
+          assert len(generated_hs_token_facebook) > 20, f"hs_token ({generated_hs_token_facebook}) is too short, something went wrong"
+
+          assert generated_as_token_facebook == generated_as_token_facebook_registration, f"as_token should be the same in registration ({generated_as_token_facebook_registration}) and configuration ({generated_as_token_facebook}) files"
+          assert generated_hs_token_facebook == generated_hs_token_facebook_registration, f"hs_token should be the same in registration ({generated_hs_token_facebook_registration}) and configuration ({generated_hs_token_facebook}) files"
+
+          server.systemctl("restart mautrix-meta-facebook")
+          server.systemctl("restart mautrix-meta-instagram")
+
+          server.wait_for_open_port(8009)
+          server.wait_for_open_port(8010)
+
+          new_as_token_facebook = get_as_token_from(config_yaml)
+          new_hs_token_facebook = get_hs_token_from(config_yaml)
+
+          assert generated_as_token_facebook == new_as_token_facebook, f"as_token should stay the same after restart inside the configuration file (is: {new_as_token_facebook}, was: {generated_as_token_facebook})"
+          assert generated_hs_token_facebook == new_hs_token_facebook, f"hs_token should stay the same after restart inside the configuration file (is: {new_hs_token_facebook}, was: {generated_hs_token_facebook})"
+
+          new_as_token_facebook = get_as_token_from(registration_yaml)
+          new_hs_token_facebook = get_hs_token_from(registration_yaml)
+
+          assert generated_as_token_facebook == new_as_token_facebook, f"as_token should stay the same after restart inside the registration file (is: {new_as_token_facebook}, was: {generated_as_token_facebook})"
+          assert generated_hs_token_facebook == new_hs_token_facebook, f"hs_token should stay the same after restart inside the registration file (is: {new_hs_token_facebook}, was: {generated_hs_token_facebook})"
+
+      with subtest("ensure messages can be exchanged after restart"):
+          client.succeed("do_test ${username} ${instagramBotUsername} ${homeserverUrl} >&2")
+          client.succeed("do_test ${username} ${facebookBotUsername} ${homeserverUrl} >&2")
+    '';
+  })
diff --git a/nixos/tests/mihomo.nix b/nixos/tests/mihomo.nix
new file mode 100644
index 0000000000000..472d10050f7fb
--- /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/miniflux.nix b/nixos/tests/miniflux.nix
index 6d38224448ed6..2adf9010051cb 100644
--- a/nixos/tests/miniflux.nix
+++ b/nixos/tests/miniflux.nix
@@ -76,7 +76,7 @@ in
       systemd.services.postgresql.postStart = lib.mkAfter ''
         $PSQL -tAd miniflux -c 'CREATE EXTENSION hstore;'
       '';
-      networking.firewall.allowedTCPPorts = [ config.services.postgresql.port ];
+      networking.firewall.allowedTCPPorts = [ config.services.postgresql.settings.port ];
     };
     externalDb = { ... }: {
       security.apparmor.enable = true;
diff --git a/nixos/tests/miriway.nix b/nixos/tests/miriway.nix
index 24e6ec6367cdb..94373bb75a915 100644
--- a/nixos/tests/miriway.nix
+++ b/nixos/tests/miriway.nix
@@ -19,10 +19,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       user = "alice";
     };
 
-    services.xserver = {
-      enable = true;
-      displayManager.defaultSession = lib.mkForce "miriway";
-    };
+    services.xserver.enable = true;
+    services.displayManager.defaultSession = lib.mkForce "miriway";
 
     programs.miriway = {
       enable = true;
diff --git a/nixos/tests/mollysocket.nix b/nixos/tests/mollysocket.nix
new file mode 100644
index 0000000000000..8cbd0c0272e09
--- /dev/null
+++ b/nixos/tests/mollysocket.nix
@@ -0,0 +1,27 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+
+let
+  port = 1234;
+in {
+  name = "mollysocket";
+  meta.maintainers = with lib.maintainers; [ dotlambda ];
+
+  nodes.mollysocket = { ... }: {
+    services.mollysocket = {
+      enable = true;
+      settings = {
+        inherit port;
+      };
+    };
+  };
+
+  testScript = ''
+    import json
+
+    mollysocket.wait_for_unit("mollysocket.service")
+    mollysocket.wait_for_open_port(${toString port})
+
+    out = mollysocket.succeed("curl --fail http://127.0.0.1:${toString port}")
+    assert json.loads(out)["mollysocket"]["version"] == "${toString pkgs.mollysocket.version}"
+  '';
+})
diff --git a/nixos/tests/monetdb.nix b/nixos/tests/monetdb.nix
new file mode 100644
index 0000000000000..acbf01f769756
--- /dev/null
+++ b/nixos/tests/monetdb.nix
@@ -0,0 +1,77 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+  let creds = pkgs.writeText ".monetdb" ''
+        user=monetdb
+        password=monetdb
+      '';
+      createUser = pkgs.writeText "createUser.sql" ''
+        CREATE USER "voc" WITH PASSWORD 'voc' NAME 'VOC Explorer' SCHEMA "sys";
+        CREATE SCHEMA "voc" AUTHORIZATION "voc";
+        ALTER USER "voc" SET SCHEMA "voc";
+      '';
+      credsVoc = pkgs.writeText ".monetdb" ''
+        user=voc
+        password=voc
+      '';
+      transaction = pkgs.writeText "transaction" ''
+        START TRANSACTION;
+        CREATE TABLE test (id int, data varchar(30));
+        ROLLBACK;
+      '';
+      vocData = pkgs.fetchzip {
+        url = "https://dev.monetdb.org/Assets/VOC/voc_dump.zip";
+        hash = "sha256-sQ5acTsSAiXQfOgt2PhN7X7Z9TZGZtLrPPxgQT2pCGQ=";
+      };
+      onboardPeople = pkgs.writeText "onboardPeople" ''
+        CREATE VIEW onboard_people AS
+        SELECT * FROM (
+        SELECT 'craftsmen' AS type, craftsmen.* FROM craftsmen
+        UNION ALL
+        SELECT 'impotenten' AS type, impotenten.* FROM impotenten
+        UNION ALL
+        SELECT 'passengers' AS type, passengers.* FROM passengers
+        UNION ALL
+        SELECT 'seafarers' AS type, seafarers.* FROM seafarers
+        UNION ALL
+        SELECT 'soldiers' AS type, soldiers.* FROM soldiers
+        UNION ALL
+        SELECT 'total' AS type, total.* FROM total
+        ) AS onboard_people_table;
+        SELECT type, COUNT(*) AS total
+        FROM onboard_people GROUP BY type ORDER BY type;
+      '';
+      onboardExpected = pkgs.lib.strings.replaceStrings ["\n"] ["\\n"] ''
+        +------------+-------+
+        | type       | total |
+        +============+=======+
+        | craftsmen  |  2349 |
+        | impotenten |   938 |
+        | passengers |  2813 |
+        | seafarers  |  4468 |
+        | soldiers   |  4177 |
+        | total      |  2467 |
+        +------------+-------+
+      '';
+  in {
+    name = "monetdb";
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ StillerHarpo ];
+    };
+    nodes.machine.services.monetdb.enable = true;
+    testScript = ''
+      machine.start()
+      machine.wait_for_unit("monetdb")
+      machine.succeed("monetdbd create mydbfarm")
+      machine.succeed("monetdbd start mydbfarm")
+      machine.succeed("monetdb create voc")
+      machine.succeed("monetdb release voc")
+      machine.succeed("cp ${creds} ./.monetdb")
+      assert "hello world" in machine.succeed("mclient -d voc -s \"SELECT 'hello world'\"")
+      machine.succeed("mclient -d voc ${createUser}")
+      machine.succeed("cp ${credsVoc} ./.monetdb")
+      machine.succeed("mclient -d voc ${transaction}")
+      machine.succeed("mclient -d voc ${vocData}/voc_dump.sql")
+      assert "8131" in machine.succeed("mclient -d voc -s \"SELECT count(*) FROM voyages\"")
+      assert "${onboardExpected}" in machine.succeed("mclient -d voc ${onboardPeople}")
+
+  '';
+  })
diff --git a/nixos/tests/mongodb.nix b/nixos/tests/mongodb.nix
index 68be6926865ec..97729e38864c4 100644
--- a/nixos/tests/mongodb.nix
+++ b/nixos/tests/mongodb.nix
@@ -33,7 +33,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
     nodes = {
       node = {...}: {
         environment.systemPackages = with pkgs; [
-          mongodb-4_4
           mongodb-5_0
         ];
       };
@@ -42,7 +41,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
     testScript = ''
       node.start()
     ''
-      + runMongoDBTest pkgs.mongodb-4_4
       + runMongoDBTest pkgs.mongodb-5_0
       + ''
         node.shutdown()
diff --git a/nixos/tests/mycelium/default.nix b/nixos/tests/mycelium/default.nix
new file mode 100644
index 0000000000000..9174c49d70869
--- /dev/null
+++ b/nixos/tests/mycelium/default.nix
@@ -0,0 +1,57 @@
+import ../make-test-python.nix ({ lib, ... }: let
+  peer1-ip = "538:f40f:1c51:9bd9:9569:d3f6:d0a1:b2df";
+  peer2-ip = "5b6:6776:fee0:c1f3:db00:b6a8:d013:d38f";
+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 0000000000000..db1cf9e72fe4b
--- /dev/null
+++ b/nixos/tests/mycelium/peer1.key
@@ -0,0 +1 @@
+sì	B0㔟ŽdûRæÈôÌH¶5œu?à»í…^
\ 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 0000000000000..7e757de48efbe
--- /dev/null
+++ b/nixos/tests/mycelium/peer2.key
@@ -0,0 +1 @@
+ÏXÿ1®yGÏÕ…ŸSAM»eÈÀ«¾‡œÝ7]
\ No newline at end of file
diff --git a/nixos/tests/nimdow.nix b/nixos/tests/nimdow.nix
new file mode 100644
index 0000000000000..0656ef04be483
--- /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.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 0000000000000..907e886def351
--- /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/nix-ld.nix b/nixos/tests/nix-ld.nix
index 8733f5b0c3978..9b851f88617a0 100644
--- a/nixos/tests/nix-ld.nix
+++ b/nixos/tests/nix-ld.nix
@@ -1,17 +1,39 @@
-import ./make-test-python.nix ({ lib, pkgs, ...} :
+{ system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../.. { inherit system config; }
+}:
+let
+  inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
+  shared =
+    { config, pkgs, ... }:
+    {
+      programs.nix-ld.enable = true;
+      environment.systemPackages = [
+        (pkgs.runCommand "patched-hello" { } ''
+          install -D -m755 ${pkgs.hello}/bin/hello $out/bin/hello
+          patchelf $out/bin/hello --set-interpreter $(cat ${config.programs.nix-ld.package}/nix-support/ldpath)
+        '')
+      ];
+    };
+in
 {
-  name = "nix-ld";
-  nodes.machine = { pkgs, ... }: {
-    programs.nix-ld.enable = true;
-    environment.systemPackages = [
-      (pkgs.runCommand "patched-hello" {} ''
-        install -D -m755 ${pkgs.hello}/bin/hello $out/bin/hello
-        patchelf $out/bin/hello --set-interpreter $(cat ${pkgs.nix-ld}/nix-support/ldpath)
-      '')
-    ];
+  nix-ld = makeTest {
+    name = "nix-ld";
+    nodes.machine = shared;
+    testScript = ''
+      start_all()
+      machine.succeed("hello")
+    '';
   };
-  testScript = ''
-    start_all()
-    machine.succeed("hello")
- '';
-})
+  nix-ld-rs = makeTest {
+    name = "nix-ld-rs";
+    nodes.machine = {
+      imports = [ shared ];
+      programs.nix-ld.package = pkgs.nix-ld-rs;
+    };
+    testScript = ''
+      start_all()
+      machine.succeed("hello")
+    '';
+  };
+}
diff --git a/nixos/tests/nixops/default.nix b/nixos/tests/nixops/default.nix
index 8477e5059fcaf..6468b8c382249 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/nixos-rebuild-install-bootloader.nix b/nixos/tests/nixos-rebuild-install-bootloader.nix
index 3ade90ea24a74..94554a93bd63b 100644
--- a/nixos/tests/nixos-rebuild-install-bootloader.nix
+++ b/nixos/tests/nixos-rebuild-install-bootloader.nix
@@ -60,7 +60,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       # Need to run `nixos-rebuild` twice because the first run will install
       # GRUB anyway
       with subtest("Switch system again and install bootloader"):
-          result = machine.succeed("nixos-rebuild switch --install-bootloader")
+          result = machine.succeed("nixos-rebuild switch --install-bootloader 2>&1")
           # install-grub2.pl messages
           assert "updating GRUB 2 menu..." in result
           assert "installing the GRUB 2 boot loader on /dev/vda..." in result
diff --git a/nixos/tests/ocis.nix b/nixos/tests/ocis.nix
new file mode 100644
index 0000000000000..35461e2467490
--- /dev/null
+++ b/nixos/tests/ocis.nix
@@ -0,0 +1,217 @@
+import ./make-test-python.nix (
+  { lib, pkgs, ... }:
+
+  let
+    # this is a demo user created by IDM_CREATE_DEMO_USERS=true
+    demoUser = "einstein";
+    demoPassword = "relativity";
+
+    adminUser = "admin";
+    adminPassword = "hunter2";
+    testRunner =
+      pkgs.writers.writePython3Bin "test-runner"
+        {
+          libraries = [ pkgs.python3Packages.selenium ];
+          flakeIgnore = [ "E501" ];
+        }
+        ''
+          import sys
+          from selenium.webdriver.common.by import By
+          from selenium.webdriver import Firefox
+          from selenium.webdriver.firefox.options import Options
+          from selenium.webdriver.support.ui import WebDriverWait
+          from selenium.webdriver.support import expected_conditions as EC
+
+          options = Options()
+          options.add_argument('--headless')
+          driver = Firefox(options=options)
+
+          user = sys.argv[1]
+          password = sys.argv[2]
+          driver.implicitly_wait(20)
+          driver.get('https://localhost:9200/login')
+          wait = WebDriverWait(driver, 10)
+          wait.until(EC.title_contains("Sign in"))
+          driver.find_element(By.XPATH, '//*[@id="oc-login-username"]').send_keys(user)
+          driver.find_element(By.XPATH, '//*[@id="oc-login-password"]').send_keys(password)
+          driver.find_element(By.XPATH, '//*[@id="root"]//button').click()
+          wait.until(EC.title_contains("Personal"))
+        '';
+
+    # This was generated with `ocis init --config-path testconfig/ --admin-password "hunter2" --insecure true`.
+    testConfig = ''
+      token_manager:
+        jwt_secret: kaKYgfso*d9GA-yTM.&BTOUEuMz%Ai0H
+      machine_auth_api_key: sGWRG1JZ&qe&pe@N1HKK4#qH*B&@xLnO
+      system_user_api_key: h+m4aHPUtOtUJFKrc5B2=04C=7fDZaT-
+      transfer_secret: 4-R6AfUjQn0P&+h2+$skf0lJqmre$j=x
+      system_user_id: db180e0a-b38a-4edf-a4cd-a3d358248537
+      admin_user_id: ea623f50-742d-4fd0-95bb-c61767b070d4
+      graph:
+        application:
+          id: 11971eab-d560-4b95-a2d4-50726676bbd0
+        events:
+          tls_insecure: true
+        spaces:
+          insecure: true
+        identity:
+          ldap:
+            bind_password: ^F&Vn7@mYGYGuxr$#qm^gGy@FVq=.w=y
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      idp:
+        ldap:
+          bind_password: bv53IjS28x.nxth*%aRbE70%4TGNXbLU
+      idm:
+        service_user_passwords:
+          admin_password: hunter2
+          idm_password: ^F&Vn7@mYGYGuxr$#qm^gGy@FVq=.w=y
+          reva_password: z-%@fWipLliR8lD#fl.0teC#9QbhJ^eb
+          idp_password: bv53IjS28x.nxth*%aRbE70%4TGNXbLU
+      proxy:
+        oidc:
+          insecure: true
+        insecure_backends: true
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      frontend:
+        app_handler:
+          insecure: true
+        archiver:
+          insecure: true
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      auth_basic:
+        auth_providers:
+          ldap:
+            bind_password: z-%@fWipLliR8lD#fl.0teC#9QbhJ^eb
+      auth_bearer:
+        auth_providers:
+          oidc:
+            insecure: true
+      users:
+        drivers:
+          ldap:
+            bind_password: z-%@fWipLliR8lD#fl.0teC#9QbhJ^eb
+      groups:
+        drivers:
+          ldap:
+            bind_password: z-%@fWipLliR8lD#fl.0teC#9QbhJ^eb
+      ocdav:
+        insecure: true
+      ocm:
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      thumbnails:
+        thumbnail:
+          transfer_secret: 2%11!zAu*AYE&=d*8dfoZs8jK&5ZMm*%
+          webdav_allow_insecure: true
+          cs3_allow_insecure: true
+      search:
+        events:
+          tls_insecure: true
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      audit:
+        events:
+          tls_insecure: true
+      settings:
+        service_account_ids:
+        - df39a290-3f3e-4e39-b67b-8b810ca2abac
+      sharing:
+        events:
+          tls_insecure: true
+      storage_users:
+        events:
+          tls_insecure: true
+        mount_id: ef72cb8b-809c-4592-bfd2-1df603295205
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      notifications:
+        notifications:
+          events:
+            tls_insecure: true
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      nats:
+        nats:
+          tls_skip_verify_client_cert: true
+      gateway:
+        storage_registry:
+          storage_users_mount_id: ef72cb8b-809c-4592-bfd2-1df603295205
+      userlog:
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      auth_service:
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE
+      clientlog:
+        service_account:
+          service_account_id: df39a290-3f3e-4e39-b67b-8b810ca2abac
+          service_account_secret: .demKypQ$=pGl+yRar!#YaFjLYCr4YwE'';
+  in
+
+  {
+    name = "ocis";
+
+    meta.maintainers = with lib.maintainers; [
+      bhankas
+      ramblurr
+    ];
+
+    nodes.machine =
+      { config, ... }:
+      {
+        virtualisation.memorySize = 2048;
+        environment.systemPackages = [
+          pkgs.firefox-unwrapped
+          pkgs.geckodriver
+          testRunner
+        ];
+
+        # if you do this in production, dont put secrets in this file because it will be written to the world readable nix store
+        environment.etc."ocis/ocis.env".text = ''
+          ADMIN_PASSWORD=${adminPassword}
+          IDM_CREATE_DEMO_USERS=true
+        '';
+
+        # if you do this in production, dont put secrets in this file because it will be written to the world readable nix store
+        environment.etc."ocis/config/ocis.yaml".text = testConfig;
+
+        services.ocis = {
+          enable = true;
+          configDir = "/etc/ocis/config";
+          environment = {
+            OCIS_INSECURE = "true";
+          };
+          environmentFile = "/etc/ocis/ocis.env";
+        };
+      };
+
+    testScript = ''
+      start_all()
+      machine.wait_for_unit("ocis.service")
+      machine.wait_for_open_port(9200)
+      # wait for ocis to fully come up
+      machine.sleep(5)
+
+      with subtest("ocis bin works"):
+          machine.succeed("${lib.getExe pkgs.ocis-bin} version")
+
+      with subtest("use the web interface to log in with a demo user"):
+          machine.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner ${demoUser} ${demoPassword}")
+
+      with subtest("use the web interface to log in with the provisioned admin user"):
+          machine.succeed("PYTHONUNBUFFERED=1 systemd-cat -t test-runner test-runner ${adminUser} ${adminPassword}")
+    '';
+  }
+)
diff --git a/nixos/tests/oddjobd.nix b/nixos/tests/oddjobd.nix
new file mode 100644
index 0000000000000..cc2d4079eebc9
--- /dev/null
+++ b/nixos/tests/oddjobd.nix
@@ -0,0 +1,23 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "oddjobd";
+  meta.maintainers = [ lib.maintainers.anthonyroussel ];
+
+  nodes.machine = { ... } : {
+    environment.systemPackages = [
+      pkgs.oddjob
+    ];
+
+    programs.oddjobd.enable = true;
+  };
+
+  testScript = ''
+    start_all()
+
+    machine.wait_for_unit("oddjobd.service")
+    machine.wait_for_file("/run/oddjobd.pid")
+
+    with subtest("send oddjob listall request"):
+      result = machine.succeed("oddjob_request -s com.redhat.oddjob -o /com/redhat/oddjob -i com.redhat.oddjob listall")
+      assert ('(service="com.redhat.oddjob",object="/com/redhat/oddjob",interface="com.redhat.oddjob",method="listall")' in result)
+  '';
+})
diff --git a/nixos/tests/opensearch.nix b/nixos/tests/opensearch.nix
index 2887ac9677656..7d37583464cb0 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/pantheon.nix b/nixos/tests/pantheon.nix
index 69a28c397bedc..d2a4a009af53d 100644
--- a/nixos/tests/pantheon.nix
+++ b/nixos/tests/pantheon.nix
@@ -13,6 +13,13 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     services.xserver.enable = true;
     services.xserver.desktopManager.pantheon.enable = true;
 
+    # We ship pantheon.appcenter by default when this is enabled.
+    services.flatpak.enable = true;
+
+    # We don't ship gnome-text-editor in Pantheon module, we add this line mainly
+    # to catch eval issues related to this option.
+    environment.pantheon.excludePackages = [ pkgs.gnome-text-editor ];
+
     environment.systemPackages = [ pkgs.xdotool ];
   };
 
@@ -50,11 +57,11 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
             machine.wait_until_succeeds(f"pgrep -f {i}")
         for i in ["gala", "io.elementary.wingpanel", "plank"]:
             machine.wait_for_window(i)
-        machine.wait_for_unit("bamfdaemon.service", "${user.name}")
-        machine.wait_for_unit("io.elementary.files.xdg-desktop-portal.service", "${user.name}")
+        for i in ["bamfdaemon.service", "io.elementary.files.xdg-desktop-portal.service"]:
+            machine.wait_for_unit(i, "${user.name}")
 
     with subtest("Check if various environment variables are set"):
-        cmd = "xargs --null --max-args=1 echo < /proc/$(pgrep -xf /run/current-system/sw/bin/gala)/environ"
+        cmd = "xargs --null --max-args=1 echo < /proc/$(pgrep -xf ${pkgs.pantheon.gala}/bin/gala)/environ"
         machine.succeed(f"{cmd} | grep 'XDG_CURRENT_DESKTOP' | grep 'Pantheon'")
         # Hopefully from the sessionPath option.
         machine.succeed(f"{cmd} | grep 'XDG_DATA_DIRS' | grep 'gsettings-schemas/pantheon-agent-geoclue2'")
diff --git a/nixos/tests/pg_anonymizer.nix b/nixos/tests/pg_anonymizer.nix
index 2960108e37c34..b26e4dca05809 100644
--- a/nixos/tests/pg_anonymizer.nix
+++ b/nixos/tests/pg_anonymizer.nix
@@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     services.postgresql = {
       enable = true;
       extraPlugins = ps: [ ps.anonymizer ];
-      settings.shared_preload_libraries = "anon";
+      settings.shared_preload_libraries = [ "anon" ];
     };
   };
 
diff --git a/nixos/tests/pgmanage.nix b/nixos/tests/pgmanage.nix
index 6f8f2f9653408..6e72b32eca369 100644
--- a/nixos/tests/pgmanage.nix
+++ b/nixos/tests/pgmanage.nix
@@ -21,7 +21,7 @@ in
         pgmanage = {
           enable = true;
           connections = {
-            ${conn} = "hostaddr=127.0.0.1 port=${toString config.services.postgresql.port} dbname=postgres";
+            ${conn} = "hostaddr=127.0.0.1 port=${toString config.services.postgresql.settings.port} dbname=postgres";
           };
         };
       };
diff --git a/nixos/tests/photonvision.nix b/nixos/tests/photonvision.nix
new file mode 100644
index 0000000000000..2cadaa4bc02e4
--- /dev/null
+++ b/nixos/tests/photonvision.nix
@@ -0,0 +1,21 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+{
+  name = "photonvision";
+
+  nodes = {
+    machine = { pkgs, ... }: {
+      services.photonvision = {
+        enable = true;
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("photonvision.service")
+    machine.wait_for_open_port(5800)
+  '';
+
+  meta.maintainers = with lib.maintainers; [ max-niederman ];
+})
+
diff --git a/nixos/tests/plasma-bigscreen.nix b/nixos/tests/plasma-bigscreen.nix
index 2fe90fa9b539c..050937f33442d 100644
--- a/nixos/tests/plasma-bigscreen.nix
+++ b/nixos/tests/plasma-bigscreen.nix
@@ -11,10 +11,10 @@ import ./make-test-python.nix ({ pkgs, ...} :
   {
     imports = [ ./common/user-account.nix ];
     services.xserver.enable = true;
-    services.xserver.displayManager.sddm.enable = true;
-    services.xserver.displayManager.defaultSession = "plasma-bigscreen-x11";
+    services.displayManager.sddm.enable = true;
+    services.displayManager.defaultSession = "plasma-bigscreen-x11";
     services.xserver.desktopManager.plasma5.bigscreen.enable = true;
-    services.xserver.displayManager.autoLogin = {
+    services.displayManager.autoLogin = {
       enable = true;
       user = "alice";
     };
diff --git a/nixos/tests/plasma5-systemd-start.nix b/nixos/tests/plasma5-systemd-start.nix
index 31a313af308b4..891d4df2409f2 100644
--- a/nixos/tests/plasma5-systemd-start.nix
+++ b/nixos/tests/plasma5-systemd-start.nix
@@ -12,11 +12,14 @@ import ./make-test-python.nix ({ pkgs, ...} :
     imports = [ ./common/user-account.nix ];
     services.xserver = {
       enable = true;
-      displayManager.sddm.enable = true;
-      displayManager.defaultSession = "plasma";
       desktopManager.plasma5.enable = true;
       desktopManager.plasma5.runUsingSystemd = true;
-      displayManager.autoLogin = {
+    };
+
+    services.displayManager = {
+      sddm.enable = true;
+      defaultSession = "plasma";
+      autoLogin = {
         enable = true;
         user = "alice";
       };
diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix
index fb8a5b73832ea..1bff37981da3f 100644
--- a/nixos/tests/plasma5.nix
+++ b/nixos/tests/plasma5.nix
@@ -11,11 +11,11 @@ import ./make-test-python.nix ({ pkgs, ...} :
   {
     imports = [ ./common/user-account.nix ];
     services.xserver.enable = true;
-    services.xserver.displayManager.sddm.enable = true;
-    services.xserver.displayManager.defaultSession = "plasma";
+    services.displayManager.sddm.enable = true;
+    services.displayManager.defaultSession = "plasma";
     services.xserver.desktopManager.plasma5.enable = true;
     environment.plasma5.excludePackages = [ pkgs.plasma5Packages.elisa ];
-    services.xserver.displayManager.autoLogin = {
+    services.displayManager.autoLogin = {
       enable = true;
       user = "alice";
     };
diff --git a/nixos/tests/plasma6.nix b/nixos/tests/plasma6.nix
index ec5b3f24ef749..7c8fba130e681 100644
--- a/nixos/tests/plasma6.nix
+++ b/nixos/tests/plasma6.nix
@@ -11,12 +11,12 @@ import ./make-test-python.nix ({ pkgs, ...} :
   {
     imports = [ ./common/user-account.nix ];
     services.xserver.enable = true;
-    services.xserver.displayManager.sddm.enable = true;
+    services.displayManager.sddm.enable = true;
     # FIXME: this should be testing Wayland
-    services.xserver.displayManager.defaultSession = "plasmax11";
-    services.xserver.desktopManager.plasma6.enable = true;
+    services.displayManager.defaultSession = "plasmax11";
+    services.desktopManager.plasma6.enable = true;
     environment.plasma6.excludePackages = [ pkgs.kdePackages.elisa ];
-    services.xserver.displayManager.autoLogin = {
+    services.displayManager.autoLogin = {
       enable = true;
       user = "alice";
     };
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 632656ad57953..3dc368e320ff2 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/ragnarwm.nix b/nixos/tests/ragnarwm.nix
index f7c588b920081..6dc08a805ab12 100644
--- a/nixos/tests/ragnarwm.nix
+++ b/nixos/tests/ragnarwm.nix
@@ -8,7 +8,7 @@ import ./make-test-python.nix ({ lib, ...} : {
   nodes.machine = { pkgs, lib, ... }: {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
     test-support.displayManager.auto.user = "alice";
-    services.xserver.displayManager.defaultSession = lib.mkForce "ragnar";
+    services.displayManager.defaultSession = lib.mkForce "ragnar";
     services.xserver.windowManager.ragnarwm.enable = true;
 
     # Setup the default terminal of Ragnar
diff --git a/nixos/tests/redis.nix b/nixos/tests/redis.nix
index 94b50d07be6dc..6c84701c9c0a9 100644
--- a/nixos/tests/redis.nix
+++ b/nixos/tests/redis.nix
@@ -1,44 +1,87 @@
-import ./make-test-python.nix ({ pkgs, lib, ... }:
 {
-  name = "redis";
-  meta.maintainers = with lib.maintainers; [ flokli ];
-
-  nodes = {
-    machine =
-      { pkgs, lib, ... }:
-
-      {
-        services.redis.servers."".enable = true;
-        services.redis.servers."test".enable = true;
-
-        users.users = lib.listToAttrs (map (suffix: lib.nameValuePair "member${suffix}" {
-          createHome = false;
-          description = "A member of the redis${suffix} group";
-          isNormalUser = true;
-          extraGroups = [ "redis${suffix}" ];
-        }) ["" "-test"]);
-      };
+  system ? builtins.currentSystem,
+  config ? { },
+  pkgs ? import ../../.. { inherit system config; },
+
+  lib ? pkgs.lib,
+}:
+let
+  makeTest = import ./make-test-python.nix;
+  mkTestName =
+    pkg: "${pkg.pname}_${builtins.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor pkg.version)}";
+  redisPackages = {
+    inherit (pkgs) redis keydb;
   };
+  makeRedisTest =
+    {
+      package,
+      name ? mkTestName package,
+    }:
+    makeTest {
+      inherit name;
+      meta.maintainers = [
+        lib.maintainers.flokli
+        lib.teams.helsinki-systems.members
+      ];
+
+      nodes = {
+        machine =
+          { lib, ... }:
+
+          {
+            services = {
+              redis = {
+                inherit package;
+                servers."".enable = true;
+                servers."test".enable = true;
+              };
+            };
+
+            users.users = lib.listToAttrs (
+              map
+                (
+                  suffix:
+                  lib.nameValuePair "member${suffix}" {
+                    createHome = false;
+                    description = "A member of the redis${suffix} group";
+                    isNormalUser = true;
+                    extraGroups = [ "redis${suffix}" ];
+                  }
+                )
+                [
+                  ""
+                  "-test"
+                ]
+            );
+          };
+      };
 
-  testScript = { nodes, ... }: let
-    inherit (nodes.machine.config.services) redis;
-    in ''
-    start_all()
-    machine.wait_for_unit("redis")
-    machine.wait_for_unit("redis-test")
+      testScript =
+        { nodes, ... }:
+        let
+          inherit (nodes.machine.services) redis;
+        in
+        ''
+          start_all()
+          machine.wait_for_unit("redis")
+          machine.wait_for_unit("redis-test")
 
-    # The unnamed Redis server still opens a port for backward-compatibility
-    machine.wait_for_open_port(6379)
+          # The unnamed Redis server still opens a port for backward-compatibility
+          machine.wait_for_open_port(6379)
 
-    machine.wait_for_file("${redis.servers."".unixSocket}")
-    machine.wait_for_file("${redis.servers."test".unixSocket}")
+          machine.wait_for_file("${redis.servers."".unixSocket}")
+          machine.wait_for_file("${redis.servers."test".unixSocket}")
 
-    # The unix socket is accessible to the redis group
-    machine.succeed('su member -c "redis-cli ping | grep PONG"')
-    machine.succeed('su member-test -c "redis-cli ping | grep PONG"')
+          # The unix socket is accessible to the redis group
+          machine.succeed('su member -c "${pkgs.redis}/bin/redis-cli ping | grep PONG"')
+          machine.succeed('su member-test -c "${pkgs.redis}/bin/redis-cli ping | grep PONG"')
 
-    machine.succeed("redis-cli ping | grep PONG")
-    machine.succeed("redis-cli -s ${redis.servers."".unixSocket} ping | grep PONG")
-    machine.succeed("redis-cli -s ${redis.servers."test".unixSocket} ping | grep PONG")
-  '';
-})
+          machine.succeed("${pkgs.redis}/bin/redis-cli ping | grep PONG")
+          machine.succeed("${pkgs.redis}/bin/redis-cli -s ${redis.servers."".unixSocket} ping | grep PONG")
+          machine.succeed("${pkgs.redis}/bin/redis-cli -s ${
+            redis.servers."test".unixSocket
+          } ping | grep PONG")
+        '';
+    };
+in
+lib.mapAttrs (_: package: makeRedisTest { inherit package; }) redisPackages
diff --git a/nixos/tests/redlib.nix b/nixos/tests/redlib.nix
new file mode 100644
index 0000000000000..e4bde25e30a63
--- /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/redmine.nix b/nixos/tests/redmine.nix
index 621b3e6a36eee..16fb2e2c64a61 100644
--- a/nixos/tests/redmine.nix
+++ b/nixos/tests/redmine.nix
@@ -39,6 +39,7 @@ let
     meta.maintainers = [ maintainers.aanderse ];
   };
 in {
+  sqlite3 = redmineTest { name = "sqlite3"; type = "sqlite3"; };
   mysql = redmineTest { name = "mysql"; type = "mysql2"; };
   pgsql = redmineTest { name = "pgsql"; type = "postgresql"; };
 }
diff --git a/nixos/tests/restic-rest-server.nix b/nixos/tests/restic-rest-server.nix
new file mode 100644
index 0000000000000..1d38ddbe513c9
--- /dev/null
+++ b/nixos/tests/restic-rest-server.nix
@@ -0,0 +1,122 @@
+import ./make-test-python.nix (
+  { pkgs, ... }:
+
+  let
+    remoteRepository = "rest:http://restic_rest_server:8001/";
+
+    backupPrepareCommand = ''
+      touch /root/backupPrepareCommand
+      test ! -e /root/backupCleanupCommand
+    '';
+
+    backupCleanupCommand = ''
+      rm /root/backupPrepareCommand
+      touch /root/backupCleanupCommand
+    '';
+
+    testDir = pkgs.stdenvNoCC.mkDerivation {
+      name = "test-files-to-backup";
+      unpackPhase = "true";
+      installPhase = ''
+        mkdir $out
+        echo some_file > $out/some_file
+        echo some_other_file > $out/some_other_file
+        mkdir $out/a_dir
+        echo a_file > $out/a_dir/a_file
+      '';
+    };
+
+    passwordFile = "${pkgs.writeText "password" "correcthorsebatterystaple"}";
+    paths = [ "/opt" ];
+    exclude = [ "/opt/excluded_file_*" ];
+    pruneOpts = [
+      "--keep-daily 2"
+      "--keep-weekly 1"
+      "--keep-monthly 1"
+      "--keep-yearly 99"
+    ];
+  in
+  {
+    name = "restic-rest-server";
+
+    nodes = {
+      restic_rest_server = {
+        services.restic.server = {
+          enable = true;
+          extraFlags = [ "--no-auth" ];
+          listenAddress = "8001";
+        };
+        networking.firewall.allowedTCPPorts = [ 8001 ];
+      };
+      server = {
+        services.restic.backups = {
+          remotebackup = {
+            inherit passwordFile paths exclude pruneOpts backupPrepareCommand backupCleanupCommand;
+            repository = remoteRepository;
+            initialize = true;
+            timerConfig = null; # has no effect here, just checking that it doesn't break the service
+          };
+          remoteprune = {
+            inherit passwordFile;
+            repository = remoteRepository;
+            pruneOpts = [ "--keep-last 1" ];
+          };
+        };
+      };
+    };
+
+    testScript = ''
+      restic_rest_server.start()
+      server.start()
+      restic_rest_server.wait_for_unit("restic-rest-server.socket")
+      restic_rest_server.wait_for_open_port(8001)
+      server.wait_for_unit("dbus.socket")
+      server.fail(
+          "restic-remotebackup snapshots",
+      )
+      server.succeed(
+          # set up
+          "cp -rT ${testDir} /opt",
+          "touch /opt/excluded_file_1 /opt/excluded_file_2",
+
+          # test that remotebackup runs custom commands and produces a snapshot
+          "timedatectl set-time '2016-12-13 13:45'",
+          "systemctl start restic-backups-remotebackup.service",
+          "rm /root/backupCleanupCommand",
+          'restic-remotebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"',
+
+          # test that restoring that snapshot produces the same directory
+          "mkdir /tmp/restore-1",
+          "restic-remotebackup restore latest -t /tmp/restore-1",
+          "diff -ru ${testDir} /tmp/restore-1/opt",
+
+          # test that we can create four snapshots in remotebackup and rclonebackup
+          "timedatectl set-time '2017-12-13 13:45'",
+          "systemctl start restic-backups-remotebackup.service",
+          "rm /root/backupCleanupCommand",
+
+          "timedatectl set-time '2018-12-13 13:45'",
+          "systemctl start restic-backups-remotebackup.service",
+          "rm /root/backupCleanupCommand",
+
+          "timedatectl set-time '2018-12-14 13:45'",
+          "systemctl start restic-backups-remotebackup.service",
+          "rm /root/backupCleanupCommand",
+
+          "timedatectl set-time '2018-12-15 13:45'",
+          "systemctl start restic-backups-remotebackup.service",
+          "rm /root/backupCleanupCommand",
+
+          "timedatectl set-time '2018-12-16 13:45'",
+          "systemctl start restic-backups-remotebackup.service",
+          "rm /root/backupCleanupCommand",
+
+          'restic-remotebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 4"',
+
+          # test that remoteprune brings us back to 1 snapshot in remotebackup
+          "systemctl start restic-backups-remoteprune.service",
+          'restic-remotebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"',
+      )
+    '';
+  }
+)
diff --git a/nixos/tests/scion/freestanding-deployment/README.rst b/nixos/tests/scion/freestanding-deployment/README.rst
new file mode 100644
index 0000000000000..b2448a2dc9add
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/README.rst
@@ -0,0 +1,12 @@
+This NixOS VM test implements the network topology outlined in https://github.com/scionproto/scion/blob/27983125bccac6b84d1f96f406853aab0e460405/doc/tutorials/deploy.rst#sample-scion-demo-topology, below is an excerpt from that document
+
+Sample SCION Demo Topology
+..........................
+
+The topology of the ISD includes the inter-AS connections to neighboring ASes, and defines the underlay IP/UDP addresses of services and routers running in this AS. This is specified in topology files - this guide later explains how to configure these files. A following graphic depicts the topology on a high level.
+
+.. figure:: https://github.com/scionproto/scion/raw/27983125bccac6b84d1f96f406853aab0e460405/doc/tutorials/deploy/SCION-deployment-guide.drawio.png
+   :width: 95 %
+   :figwidth: 100 %
+
+   *Figure 1 - Topology of the sample SCION demo environment. It consists of 1 ISD, 3 core ASes and 2 non-core ASes.*
diff --git a/nixos/tests/scion/freestanding-deployment/default.nix b/nixos/tests/scion/freestanding-deployment/default.nix
new file mode 100644
index 0000000000000..0c9686fbfbadf
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/default.nix
@@ -0,0 +1,172 @@
+# implements https://github.com/scionproto/scion/blob/27983125bccac6b84d1f96f406853aab0e460405/doc/tutorials/deploy.rst
+import ../../make-test-python.nix ({ pkgs, ... }:
+let
+  trust-root-configuration-keys = pkgs.runCommand "generate-trc-keys.sh" {
+    buildInputs = [
+      pkgs.scion
+    ];
+  } ''
+    set -euo pipefail
+
+    mkdir /tmp/tutorial-scion-certs && cd /tmp/tutorial-scion-certs
+    mkdir AS{1..5}
+
+    # Create voting and root keys and (self-signed) certificates for core ASes
+    pushd AS1
+    scion-pki certificate create --not-after=3650d --profile=sensitive-voting <(echo '{"isd_as": "42-ffaa:1:1", "common_name": "42-ffaa:1:1 sensitive voting cert"}') sensitive-voting.pem sensitive-voting.key
+    scion-pki certificate create --not-after=3650d --profile=regular-voting <(echo '{"isd_as": "42-ffaa:1:1", "common_name": "42-ffaa:1:1 regular voting cert"}') regular-voting.pem regular-voting.key
+    scion-pki certificate create --not-after=3650d --profile=cp-root <(echo '{"isd_as": "42-ffaa:1:1", "common_name": "42-ffaa:1:1 cp root cert"}') cp-root.pem cp-root.key
+    popd
+
+    pushd AS2
+    scion-pki certificate create --not-after=3650d --profile=cp-root <(echo '{"isd_as": "42-ffaa:1:2", "common_name": "42-ffaa:1:2 cp root cert"}') cp-root.pem cp-root.key
+    popd
+
+    pushd AS3
+    scion-pki certificate create --not-after=3650d --profile=sensitive-voting <(echo '{"isd_as": "42-ffaa:1:3", "common_name": "42-ffaa:1:3 sensitive voting cert"}') sensitive-voting.pem sensitive-voting.key
+    scion-pki certificate create --not-after=3650d --profile=regular-voting <(echo '{"isd_as": "42-ffaa:1:3", "common_name": "42-ffaa:1:3 regular voting cert"}') regular-voting.pem regular-voting.key
+    popd
+
+    # Create the TRC (Trust Root Configuration)
+    mkdir tmp
+    echo '
+    isd = 42
+    description = "Demo ISD 42"
+    serial_version = 1
+    base_version = 1
+    voting_quorum = 2
+
+    core_ases = ["ffaa:1:1", "ffaa:1:2", "ffaa:1:3"]
+    authoritative_ases = ["ffaa:1:1", "ffaa:1:2", "ffaa:1:3"]
+    cert_files = ["AS1/sensitive-voting.pem", "AS1/regular-voting.pem", "AS1/cp-root.pem", "AS2/cp-root.pem", "AS3/sensitive-voting.pem", "AS3/regular-voting.pem"]
+
+    [validity]
+    not_before = '$(date +%s)'
+    validity = "365d"' \
+    > trc-B1-S1-pld.tmpl
+
+    scion-pki trc payload --out=tmp/ISD42-B1-S1.pld.der --template trc-B1-S1-pld.tmpl
+    rm trc-B1-S1-pld.tmpl
+
+    # Sign and bundle the TRC
+    scion-pki trc sign tmp/ISD42-B1-S1.pld.der AS1/sensitive-voting.{pem,key} --out tmp/ISD42-B1-S1.AS1-sensitive.trc
+    scion-pki trc sign tmp/ISD42-B1-S1.pld.der AS1/regular-voting.{pem,key} --out tmp/ISD42-B1-S1.AS1-regular.trc
+    scion-pki trc sign tmp/ISD42-B1-S1.pld.der AS3/sensitive-voting.{pem,key} --out tmp/ISD42-B1-S1.AS3-sensitive.trc
+    scion-pki trc sign tmp/ISD42-B1-S1.pld.der AS3/regular-voting.{pem,key} --out tmp/ISD42-B1-S1.AS3-regular.trc
+
+    scion-pki trc combine tmp/ISD42-B1-S1.AS{1,3}-{sensitive,regular}.trc --payload tmp/ISD42-B1-S1.pld.der --out ISD42-B1-S1.trc
+    rm tmp -r
+
+    # Create CA key and certificate for issuing ASes
+    pushd AS1
+    scion-pki certificate create --profile=cp-ca <(echo '{"isd_as": "42-ffaa:1:1", "common_name": "42-ffaa:1:1 CA cert"}') cp-ca.pem cp-ca.key --ca cp-root.pem --ca-key cp-root.key
+    popd
+    pushd AS2
+    scion-pki certificate create --profile=cp-ca <(echo '{"isd_as": "42-ffaa:1:2", "common_name": "42-ffaa:1:2 CA cert"}') cp-ca.pem cp-ca.key --ca cp-root.pem --ca-key cp-root.key
+    popd
+
+    # Create AS key and certificate chains
+    scion-pki certificate create --profile=cp-as <(echo '{"isd_as": "42-ffaa:1:1", "common_name": "42-ffaa:1:1 AS cert"}') AS1/cp-as.pem AS1/cp-as.key --ca AS1/cp-ca.pem --ca-key AS1/cp-ca.key --bundle
+    scion-pki certificate create --profile=cp-as <(echo '{"isd_as": "42-ffaa:1:2", "common_name": "42-ffaa:1:2 AS cert"}') AS2/cp-as.pem AS2/cp-as.key --ca AS2/cp-ca.pem --ca-key AS2/cp-ca.key --bundle
+    scion-pki certificate create --profile=cp-as <(echo '{"isd_as": "42-ffaa:1:3", "common_name": "42-ffaa:1:3 AS cert"}') AS3/cp-as.pem AS3/cp-as.key --ca AS1/cp-ca.pem --ca-key AS1/cp-ca.key --bundle
+    scion-pki certificate create --profile=cp-as <(echo '{"isd_as": "42-ffaa:1:4", "common_name": "42-ffaa:1:4 AS cert"}') AS4/cp-as.pem AS4/cp-as.key --ca AS1/cp-ca.pem --ca-key AS1/cp-ca.key --bundle
+    scion-pki certificate create --profile=cp-as <(echo '{"isd_as": "42-ffaa:1:5", "common_name": "42-ffaa:1:5 AS cert"}') AS5/cp-as.pem AS5/cp-as.key --ca AS2/cp-ca.pem --ca-key AS2/cp-ca.key --bundle
+
+    for i in {1..5}
+    do
+      mkdir -p $out/AS$i
+      cp AS$i/cp-as.{key,pem} $out/AS$i
+    done
+
+    mv *.trc $out
+  '';
+  imports = hostId: [
+    ({
+      services.scion = {
+        enable = true;
+        bypassBootstrapWarning = true;
+      };
+      networking = {
+        useNetworkd = true;
+        useDHCP = false;
+      };
+      systemd.network.networks."01-eth1" = {
+        name = "eth1";
+        networkConfig.Address = "192.168.1.${toString hostId}/24";
+      };
+      environment.etc = {
+        "scion/topology.json".source = ./topology${toString hostId}.json;
+        "scion/crypto/as".source = trust-root-configuration-keys + "/AS${toString hostId}";
+        "scion/certs/ISD42-B1-S1.trc".source = trust-root-configuration-keys + "/ISD42-B1-S1.trc";
+        "scion/keys/master0.key".text = "U${toString hostId}v4k23ZXjGDwDofg/Eevw==";
+        "scion/keys/master1.key".text = "dBMko${toString hostId}qMS8DfrN/zP2OUdA==";
+      };
+      environment.systemPackages = [
+        pkgs.scion
+      ];
+    })
+  ];
+in
+{
+  name = "scion-test";
+  nodes = {
+    scion01 = { ... }: {
+      imports = (imports 1);
+    };
+    scion02 = { ... }: {
+      imports = (imports 2);
+    };
+    scion03 = { ... }: {
+      imports = (imports 3);
+    };
+    scion04 = { ... }: {
+      imports = (imports 4);
+    };
+    scion05 = { ... }: {
+      imports = (imports 5);
+    };
+  };
+  testScript = let
+    pingAll = pkgs.writeShellScript "ping-all-scion.sh" ''
+      addresses="42-ffaa:1:1 42-ffaa:1:2 42-ffaa:1:3 42-ffaa:1:4 42-ffaa:1:5"
+      timeout=100
+      wait_for_all() {
+        for as in "$@"
+        do
+          scion showpaths $as --no-probe > /dev/null
+          return 1
+        done
+        return 0
+      }
+      ping_all() {
+        for as in "$@"
+        do
+          scion ping "$as,127.0.0.1" -c 3
+        done
+        return 0
+      }
+      for i in $(seq 0 $timeout); do
+        wait_for_all $addresses && exit 0
+        ping_all $addresses && exit 0
+        sleep 1
+      done
+    '';
+  in
+  ''
+    # List of AS instances
+    machines = [scion01, scion02, scion03, scion04, scion05]
+
+    # Wait for scion-control.service on all instances
+    for i in machines:
+        i.wait_for_unit("scion-control.service")
+
+    # Execute pingAll command on all instances
+    for i in machines:
+        i.succeed("${pingAll} >&2")
+
+    # Restart scion-dispatcher and ping again to test robustness
+    for i in machines:
+        i.succeed("systemctl restart scion-dispatcher >&2")
+        i.succeed("${pingAll} >&2")
+  '';
+})
diff --git a/nixos/tests/scion/freestanding-deployment/topology1.json b/nixos/tests/scion/freestanding-deployment/topology1.json
new file mode 100644
index 0000000000000..de51515eebc2d
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/topology1.json
@@ -0,0 +1,51 @@
+{
+  "attributes": [
+    "core"
+  ],
+  "isd_as": "42-ffaa:1:1",
+  "mtu": 1472,
+  "control_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "discovery_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "border_routers": {
+    "br": {
+      "internal_addr": "127.0.0.1:31002",
+      "interfaces": {
+        "1": {
+          "underlay": {
+            "public": "192.168.1.1:50014",
+            "remote": "192.168.1.4:50014"
+          },
+          "isd_as": "42-ffaa:1:4",
+          "link_to": "child",
+          "mtu": 1472
+        },
+        "2": {
+          "underlay": {
+            "public": "192.168.1.1:50012",
+            "remote": "192.168.1.2:50012"
+          },
+          "isd_as": "42-ffaa:1:2",
+          "link_to": "core",
+          "mtu": 1472
+        },
+        "3": {
+          "underlay": {
+            "public": "192.168.1.1:50013",
+            "remote": "192.168.1.3:50013"
+          },
+          "isd_as": "42-ffaa:1:3",
+          "link_to": "core",
+          "mtu": 1472
+        }
+      }
+    }
+  }
+}
diff --git a/nixos/tests/scion/freestanding-deployment/topology2.json b/nixos/tests/scion/freestanding-deployment/topology2.json
new file mode 100644
index 0000000000000..f8e10d5d1f75d
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/topology2.json
@@ -0,0 +1,51 @@
+{
+  "attributes": [
+    "core"
+  ],
+  "isd_as": "42-ffaa:1:2",
+  "mtu": 1472,
+  "control_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "discovery_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "border_routers": {
+    "br": {
+      "internal_addr": "127.0.0.1:31002",
+      "interfaces": {
+        "1": {
+          "underlay": {
+            "public": "192.168.1.2:50012",
+            "remote": "192.168.1.1:50012"
+          },
+          "isd_as": "42-ffaa:1:1",
+          "link_to": "core",
+          "mtu": 1472
+        },
+        "2": {
+          "underlay": {
+            "public": "192.168.1.2:50023",
+            "remote": "192.168.1.3:50023"
+          },
+          "isd_as": "42-ffaa:1:3",
+          "link_to": "core",
+          "mtu": 1472
+        },
+        "3": {
+          "underlay": {
+            "public": "192.168.1.2:50025",
+            "remote": "192.168.1.5:50025"
+          },
+          "isd_as": "42-ffaa:1:5",
+          "link_to": "child",
+          "mtu": 1472
+        }
+      }
+    }
+  }
+}
diff --git a/nixos/tests/scion/freestanding-deployment/topology3.json b/nixos/tests/scion/freestanding-deployment/topology3.json
new file mode 100644
index 0000000000000..53cee431885b3
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/topology3.json
@@ -0,0 +1,60 @@
+{
+  "attributes": [
+    "core"
+  ],
+  "isd_as": "42-ffaa:1:3",
+  "mtu": 1472,
+  "control_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "discovery_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "border_routers": {
+    "br": {
+      "internal_addr": "127.0.0.1:31002",
+      "interfaces": {
+        "1": {
+          "underlay": {
+            "public": "192.168.1.3:50013",
+            "remote": "192.168.1.1:50013"
+          },
+          "isd_as": "42-ffaa:1:1",
+          "link_to": "core",
+          "mtu": 1472
+        },
+        "2": {
+          "underlay": {
+            "public": "192.168.1.3:50023",
+            "remote": "192.168.1.2:50023"
+          },
+          "isd_as": "42-ffaa:1:2",
+          "link_to": "core",
+          "mtu": 1472
+        },
+        "3": {
+          "underlay": {
+            "public": "192.168.1.3:50034",
+            "remote": "192.168.1.4:50034"
+          },
+          "isd_as": "42-ffaa:1:4",
+          "link_to": "child",
+          "mtu": 1472
+        },
+        "4": {
+          "underlay": {
+            "public": "192.168.1.3:50035",
+            "remote": "192.168.1.5:50035"
+          },
+          "isd_as": "42-ffaa:1:5",
+          "link_to": "child",
+          "mtu": 1472
+        }
+      }
+    }
+  }
+}
diff --git a/nixos/tests/scion/freestanding-deployment/topology4.json b/nixos/tests/scion/freestanding-deployment/topology4.json
new file mode 100644
index 0000000000000..03c507a4daf58
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/topology4.json
@@ -0,0 +1,40 @@
+{
+  "attributes": [],
+  "isd_as": "42-ffaa:1:4",
+  "mtu": 1472,
+  "control_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "discovery_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "border_routers": {
+    "br": {
+      "internal_addr": "127.0.0.1:31002",
+      "interfaces": {
+        "1": {
+          "underlay": {
+            "public": "192.168.1.4:50014",
+            "remote": "192.168.1.1:50014"
+          },
+          "isd_as": "42-ffaa:1:1",
+          "link_to": "parent",
+          "mtu": 1472
+        },
+        "2": {
+          "underlay": {
+            "public": "192.168.1.4:50034",
+            "remote": "192.168.1.3:50034"
+          },
+          "isd_as": "42-ffaa:1:3",
+          "link_to": "parent",
+          "mtu": 1472
+        }
+      }
+    }
+  }
+}
diff --git a/nixos/tests/scion/freestanding-deployment/topology5.json b/nixos/tests/scion/freestanding-deployment/topology5.json
new file mode 100644
index 0000000000000..6114c1f73c2a7
--- /dev/null
+++ b/nixos/tests/scion/freestanding-deployment/topology5.json
@@ -0,0 +1,40 @@
+{
+  "attributes": [],
+  "isd_as": "42-ffaa:1:5",
+  "mtu": 1472,
+  "control_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "discovery_service": {
+    "cs": {
+      "addr": "127.0.0.1:31000"
+    }
+  },
+  "border_routers": {
+    "br": {
+      "internal_addr": "127.0.0.1:31002",
+      "interfaces": {
+        "1": {
+          "underlay": {
+            "public": "192.168.1.5:50025",
+            "remote": "192.168.1.2:50025"
+          },
+          "isd_as": "42-ffaa:1:2",
+          "link_to": "parent",
+          "mtu": 1472
+        },
+        "2": {
+          "underlay": {
+            "public": "192.168.1.5:50035",
+            "remote": "192.168.1.3:50035"
+          },
+          "isd_as": "42-ffaa:1:3",
+          "link_to": "parent",
+          "mtu": 1472
+        }
+      }
+    }
+  }
+}
diff --git a/nixos/tests/sddm.nix b/nixos/tests/sddm.nix
index b6c05deac05e4..3ca105cf9713d 100644
--- a/nixos/tests/sddm.nix
+++ b/nixos/tests/sddm.nix
@@ -15,8 +15,8 @@ let
       nodes.machine = { ... }: {
         imports = [ ./common/user-account.nix ];
         services.xserver.enable = true;
-        services.xserver.displayManager.sddm.enable = true;
-        services.xserver.displayManager.defaultSession = "none+icewm";
+        services.displayManager.sddm.enable = true;
+        services.displayManager.defaultSession = "none+icewm";
         services.xserver.windowManager.icewm.enable = true;
       };
 
@@ -44,14 +44,14 @@ let
       nodes.machine = { ... }: {
         imports = [ ./common/user-account.nix ];
         services.xserver.enable = true;
-        services.xserver.displayManager = {
+        services.displayManager = {
           sddm.enable = true;
           autoLogin = {
             enable = true;
             user = "alice";
           };
         };
-        services.xserver.displayManager.defaultSession = "none+icewm";
+        services.displayManager.defaultSession = "none+icewm";
         services.xserver.windowManager.icewm.enable = true;
       };
 
diff --git a/nixos/tests/silverbullet.nix b/nixos/tests/silverbullet.nix
new file mode 100644
index 0000000000000..e7e3cf5365583
--- /dev/null
+++ b/nixos/tests/silverbullet.nix
@@ -0,0 +1,47 @@
+import ./make-test-python.nix ({ lib, ... }: {
+  name = "silverbullet";
+  meta.maintainers = with lib.maintainers; [ aorith ];
+
+  nodes.simple = { ... }: {
+    services.silverbullet.enable = true;
+  };
+
+  nodes.configured = { pkgs, ... }: {
+    users.users.test.isNormalUser = true;
+    users.groups.test = { };
+
+    services.silverbullet = {
+      enable = true;
+      package = pkgs.silverbullet;
+      listenPort = 3001;
+      listenAddress = "localhost";
+      spaceDir = "/home/test/silverbullet";
+      user = "test";
+      group = "test";
+      envFile = pkgs.writeText "silverbullet.env" ''
+        SB_USER=user:password
+        SB_AUTH_TOKEN=test
+      '';
+      extraArgs = [ "--reindex" "--db /home/test/silverbullet/custom.db" ];
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    PORT = ${builtins.toString nodes.simple.services.silverbullet.listenPort}
+    ADDRESS = "${nodes.simple.services.silverbullet.listenAddress}"
+    SPACEDIR = "${nodes.simple.services.silverbullet.spaceDir}"
+    simple.wait_for_unit("silverbullet.service")
+    simple.wait_for_open_port(PORT)
+    simple.succeed(f"curl --max-time 5 -s -v -o /dev/null --fail http://{ADDRESS}:{PORT}/")
+    simple.succeed(f"test -d '{SPACEDIR}'")
+
+    PORT = ${builtins.toString nodes.configured.services.silverbullet.listenPort}
+    ADDRESS = "${nodes.configured.services.silverbullet.listenAddress}"
+    SPACEDIR = "${nodes.configured.services.silverbullet.spaceDir}"
+    configured.wait_for_unit("silverbullet.service")
+    configured.wait_for_open_port(PORT)
+    assert int(configured.succeed(f"curl --max-time 5 -s -o /dev/null -w '%{{http_code}}' -XPUT -d 'test' --fail http://{ADDRESS}:{PORT}/test.md -H'Authorization: Bearer test'")) == 200
+    assert int(configured.fail(f"curl --max-time 5 -s -o /dev/null -w '%{{http_code}}' -XPUT -d 'test' --fail http://{ADDRESS}:{PORT}/test.md -H'Authorization: Bearer wrong'")) == 401
+    configured.succeed(f"test -d '{SPACEDIR}'")
+  '';
+})
diff --git a/nixos/tests/soju.nix b/nixos/tests/soju.nix
new file mode 100644
index 0000000000000..23da36f7b3aba
--- /dev/null
+++ b/nixos/tests/soju.nix
@@ -0,0 +1,31 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+let
+  certs = import ./common/acme/server/snakeoil-certs.nix;
+  domain = certs.domain;
+
+  user = "testuser";
+  pass = "hunter2";
+in
+{
+  name = "soju";
+  meta.maintainers = with lib.maintainers; [ Benjamin-L ];
+
+  nodes.machine = { ... }: {
+    services.soju = {
+      enable = true;
+      adminSocket.enable = true;
+      hostName = domain;
+      tlsCertificate = certs.${domain}.cert;
+      tlsCertificateKey = certs.${domain}.key;
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    machine.wait_for_unit("soju")
+    machine.wait_for_file("/run/soju/admin")
+
+    machine.succeed("sojuctl user create -username ${user} -password ${pass}")
+  '';
+})
diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix
index 5ffdf180d5e3f..a57d66f82eac9 100644
--- a/nixos/tests/switch-test.nix
+++ b/nixos/tests/switch-test.nix
@@ -691,9 +691,9 @@ in {
     with subtest("continuing from an aborted switch"):
         # An aborted switch will write into a file what it tried to start
         # and a second switch should continue from this
-        machine.succeed("echo dbus.service > /run/nixos/start-list")
+        machine.succeed("echo dbus-broker.service > /run/nixos/start-list")
         out = switch_to_specialisation("${machine}", "modifiedSystemConf")
-        assert_contains(out, "starting the following units: dbus.service\n")
+        assert_contains(out, "starting the following units: dbus-broker.service\n")
 
     with subtest("fstab mounts"):
         switch_to_specialisation("${machine}", "")
@@ -732,7 +732,7 @@ in {
         out = switch_to_specialisation("${machine}", "")
         assert_contains(out, "stopping the following units: test.mount\n")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: dbus.service\n")
+        assert_contains(out, "reloading the following units: dbus-broker.service\n")
         assert_lacks(out, "\nrestarting the following units:")
         assert_lacks(out, "\nstarting the following units:")
         assert_lacks(out, "the following new units were started:")
@@ -740,7 +740,7 @@ in {
         out = switch_to_specialisation("${machine}", "storeMountModified")
         assert_lacks(out, "stopping the following units:")
         assert_contains(out, "NOT restarting the following changed units: -.mount")
-        assert_contains(out, "reloading the following units: dbus.service\n")
+        assert_contains(out, "reloading the following units: dbus-broker.service\n")
         assert_lacks(out, "\nrestarting the following units:")
         assert_lacks(out, "\nstarting the following units:")
         assert_lacks(out, "the following new units were started:")
@@ -751,7 +751,7 @@ in {
         out = switch_to_specialisation("${machine}", "swap")
         assert_lacks(out, "stopping the following units:")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: dbus.service\n")
+        assert_contains(out, "reloading the following units: dbus-broker.service\n")
         assert_lacks(out, "\nrestarting the following units:")
         assert_lacks(out, "\nstarting the following units:")
         assert_contains(out, "the following new units were started: swapfile.swap")
@@ -760,7 +760,7 @@ in {
         assert_contains(out, "stopping swap device: /swapfile")
         assert_lacks(out, "stopping the following units:")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: dbus.service\n")
+        assert_contains(out, "reloading the following units: dbus-broker.service\n")
         assert_lacks(out, "\nrestarting the following units:")
         assert_lacks(out, "\nstarting the following units:")
         assert_lacks(out, "the following new units were started:")
@@ -781,7 +781,7 @@ in {
         assert_lacks(out, "installing dummy bootloader")  # test does not install a bootloader
         assert_lacks(out, "stopping the following units:")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: dbus.service\n")  # huh
+        assert_contains(out, "reloading the following units: dbus-broker.service\n")  # huh
         assert_lacks(out, "\nrestarting the following units:")
         assert_lacks(out, "\nstarting the following units:")
         assert_contains(out, "the following new units were started: test.service\n")
@@ -858,7 +858,7 @@ in {
         assert_lacks(out, "installing dummy bootloader")  # test does not install a bootloader
         assert_lacks(out, "stopping the following units:")
         assert_lacks(out, "NOT restarting the following changed units:")
-        assert_contains(out, "reloading the following units: dbus.service\n")  # huh
+        assert_contains(out, "reloading the following units: dbus-broker.service\n")  # huh
         assert_lacks(out, "\nrestarting the following units:")
         assert_lacks(out, "\nstarting the following units:")
         assert_contains(out, "the following new units were started: test.service\n")
diff --git a/nixos/tests/systemd-machinectl.nix b/nixos/tests/systemd-machinectl.nix
index 02b4d9c590b59..9d761c6d4d8b8 100644
--- a/nixos/tests/systemd-machinectl.nix
+++ b/nixos/tests/systemd-machinectl.nix
@@ -1,149 +1,177 @@
 import ./make-test-python.nix ({ pkgs, ... }:
-  let
-
-    container = {
-      # We re-use the NixOS container option ...
-      boot.isContainer = true;
-      # ... and revert unwanted defaults
-      networking.useHostResolvConf = false;
-
-      # use networkd to obtain systemd network setup
-      networking.useNetworkd = true;
-      networking.useDHCP = false;
-
-      # systemd-nspawn expects /sbin/init
-      boot.loader.initScript.enable = true;
-
-      imports = [ ../modules/profiles/minimal.nix ];
+let
+
+  container = { config, ... }: {
+    # We re-use the NixOS container option ...
+    boot.isContainer = true;
+    # ... and revert unwanted defaults
+    networking.useHostResolvConf = false;
+
+    # use networkd to obtain systemd network setup
+    networking.useNetworkd = true;
+    networking.useDHCP = false;
+
+    # systemd-nspawn expects /sbin/init
+    boot.loader.initScript.enable = true;
+
+    imports = [ ../modules/profiles/minimal.nix ];
+
+    system.stateVersion = config.system.nixos.version;
+  };
+
+  containerSystem = (import ../lib/eval-config.nix {
+    inherit (pkgs) system;
+    modules = [ container ];
+  }).config.system.build.toplevel;
+
+  containerName = "container";
+  containerRoot = "/var/lib/machines/${containerName}";
+
+  containerTarball = pkgs.callPackage ../lib/make-system-tarball.nix {
+    storeContents = [
+      {
+        object = containerSystem;
+        symlink = "/nix/var/nix/profiles/system";
+      }
+    ];
+
+    contents = [
+      {
+        source = containerSystem + "/etc/os-release";
+        target = "/etc/os-release";
+      }
+      {
+        source = containerSystem + "/init";
+        target = "/sbin/init";
+      }
+    ];
+  };
+in
+{
+  name = "systemd-machinectl";
+
+  nodes.machine = { lib, ... }: {
+    # use networkd to obtain systemd network setup
+    networking.useNetworkd = true;
+    networking.useDHCP = false;
+
+    # do not try to access cache.nixos.org
+    nix.settings.substituters = lib.mkForce [ ];
+
+    # auto-start container
+    systemd.targets.machines.wants = [ "systemd-nspawn@${containerName}.service" ];
+
+    virtualisation.additionalPaths = [ containerSystem containerTarball ];
+
+    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";
+      };
     };
 
-    containerSystem = (import ../lib/eval-config.nix {
-      inherit (pkgs) system;
-      modules = [ container ];
-    }).config.system.build.toplevel;
-
-    containerName = "container";
-    containerRoot = "/var/lib/machines/${containerName}";
-
-  in
-  {
-    name = "systemd-machinectl";
-
-    nodes.machine = { lib, ... }: {
-      # use networkd to obtain systemd network setup
-      networking.useNetworkd = true;
-      networking.useDHCP = false;
-
-      # do not try to access cache.nixos.org
-      nix.settings.substituters = lib.mkForce [ ];
-
-      # auto-start container
-      systemd.targets.machines.wants = [ "systemd-nspawn@${containerName}.service" ];
-
-      virtualisation.additionalPaths = [ containerSystem ];
-
-      systemd.tmpfiles.rules = [
-        "d /var/lib/machines/shared-decl 0755 root root - -"
+    systemd.services."systemd-nspawn@${containerName}" = {
+      serviceConfig.Environment = [
+        # Disable tmpfs for /tmp
+        "SYSTEMD_NSPAWN_TMPFS_TMP=0"
       ];
-      systemd.nspawn.shared-decl = {
-        execConfig = {
-          Boot = false;
-          Parameters = "${containerSystem}/init";
-        };
-        filesConfig = {
-          BindReadOnly = "/nix/store";
-        };
-      };
+      overrideStrategy = "asDropin";
+    };
 
-      systemd.services."systemd-nspawn@${containerName}" = {
-        serviceConfig.Environment = [
-          # Disable tmpfs for /tmp
-          "SYSTEMD_NSPAWN_TMPFS_TMP=0"
-        ];
-        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
+    '';
+  };
 
-      # 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");
 
-    testScript = ''
-      start_all()
-      machine.wait_for_unit("default.target");
+    # 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");
 
-      # 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}");
 
-      # 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");
 
-      # 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}");
 
-      # 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");
 
-      # 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");
+    # Allow systemd-nspawn to apply user namespace on immutable files
+    machine.succeed("chattr -i ${containerRoot}/var/empty");
 
-      # Allow systemd-nspawn to apply user namespace on immutable files
-      machine.succeed("chattr -i ${containerRoot}/var/empty");
+    # Test machinectl start
+    machine.succeed("machinectl start ${containerName}");
+    machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
 
-      # Test machinectl start
-      machine.succeed("machinectl start ${containerName}");
-      machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
+    # Test nss_mymachines without nscd
+    machine.succeed('LD_LIBRARY_PATH="/run/current-system/sw/lib" getent -s hosts:mymachines hosts ${containerName}');
 
-      # Test nss_mymachines without nscd
-      machine.succeed('LD_LIBRARY_PATH="/run/current-system/sw/lib" getent -s hosts:mymachines hosts ${containerName}');
+    # Test nss_mymachines via nscd
+    machine.succeed("getent hosts ${containerName}");
 
-      # 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 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 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}");
 
-      # Test systemd-nspawn network configuration
-      machine.succeed("ping -n -c 1 ${containerName}");
+    # Test systemd-nspawn uses a user namespace
+    machine.succeed("test $(machinectl status ${containerName} | grep 'UID Shift: ' | wc -l) = 1")
 
-      # Test systemd-nspawn uses a user namespace
-      machine.succeed("test $(machinectl status ${containerName} | grep 'UID Shift: ' | wc -l) = 1")
+    # Test systemd-nspawn reboot
+    machine.succeed("machinectl shell ${containerName} /run/current-system/sw/bin/reboot");
+    machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
 
-      # Test systemd-nspawn reboot
-      machine.succeed("machinectl shell ${containerName} /run/current-system/sw/bin/reboot");
-      machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
+    # Test machinectl reboot
+    machine.succeed("machinectl reboot ${containerName}");
+    machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
 
-      # Test machinectl reboot
-      machine.succeed("machinectl reboot ${containerName}");
-      machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
+    # Restart machine
+    machine.shutdown()
+    machine.start()
+    machine.wait_for_unit("default.target");
 
-      # Restart machine
-      machine.shutdown()
-      machine.start()
-      machine.wait_for_unit("default.target");
+    # Test auto-start
+    machine.succeed("machinectl show ${containerName}")
 
-      # Test auto-start
-      machine.succeed("machinectl show ${containerName}")
+    # Test machinectl stop
+    machine.succeed("machinectl stop ${containerName}");
+    machine.wait_until_succeeds("test $(systemctl is-active systemd-nspawn@${containerName}) = inactive");
 
-      # Test machinectl stop
-      machine.succeed("machinectl stop ${containerName}");
-      machine.wait_until_succeeds("test $(systemctl is-active systemd-nspawn@${containerName}) = inactive");
+    # Test tmpfs for /tmp
+    machine.fail("mountpoint /tmp");
 
-      # Test tmpfs for /tmp
-      machine.fail("mountpoint /tmp");
+    # Show to to delete the container
+    machine.succeed("chattr -i ${containerRoot}/var/empty");
+    machine.succeed("rm -rf ${containerRoot}");
 
-      # Show to to delete the container
-      machine.succeed("chattr -i ${containerRoot}/var/empty");
-      machine.succeed("rm -rf ${containerRoot}");
-    '';
-  }
-)
+    # Test import tarball, start, stop and remove
+    machine.succeed("machinectl import-tar ${containerTarball}/tarball/*.tar* ${containerName}");
+    machine.succeed("machinectl start ${containerName}");
+    machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
+    machine.succeed("machinectl stop ${containerName}");
+    machine.wait_until_succeeds("test $(systemctl is-active systemd-nspawn@${containerName}) = inactive");
+    machine.succeed("machinectl remove ${containerName}");
+  '';
+})
diff --git a/nixos/tests/systemd-user-linger.nix b/nixos/tests/systemd-user-linger.nix
new file mode 100644
index 0000000000000..2c3d71668979f
--- /dev/null
+++ b/nixos/tests/systemd-user-linger.nix
@@ -0,0 +1,39 @@
+import ./make-test-python.nix (
+  { lib, ... }:
+  {
+    name = "systemd-user-linger";
+
+    nodes.machine =
+      { ... }:
+      {
+        users.users = {
+          alice = {
+            isNormalUser = true;
+            linger = true;
+            uid = 1000;
+          };
+
+          bob = {
+            isNormalUser = true;
+            linger = false;
+            uid = 10001;
+          };
+        };
+      };
+
+    testScript =
+      { ... }:
+      ''
+        machine.wait_for_file("/var/lib/systemd/linger/alice")
+        machine.succeed("systemctl status user-1000.slice")
+
+        machine.fail("test -e /var/lib/systemd/linger/bob")
+        machine.fail("systemctl status user-1001.slice")
+
+        with subtest("missing users have linger purged"):
+            machine.succeed("touch /var/lib/systemd/linger/missing")
+            machine.systemctl("restart linger-users")
+            machine.succeed("test ! -e /var/lib/systemd/linger/missing")
+      '';
+  }
+)
diff --git a/nixos/tests/technitium-dns-server.nix b/nixos/tests/technitium-dns-server.nix
new file mode 100644
index 0000000000000..016c9d4ecead5
--- /dev/null
+++ b/nixos/tests/technitium-dns-server.nix
@@ -0,0 +1,21 @@
+import ./make-test-python.nix ({pkgs, lib, ...}:
+{
+  name = "technitium-dns-server";
+
+  nodes = {
+    machine = {pkgs, ...}: {
+      services.technitium-dns-server = {
+        enable = true;
+        openFirewall = true;
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("technitium-dns-server.service")
+    machine.wait_for_open_port(53)
+  '';
+
+  meta.maintainers = with lib.maintainers; [ fabianrig ];
+})
diff --git a/nixos/tests/tracee.nix b/nixos/tests/tracee.nix
index 3dadc0f9fdb33..1c241f3ec4983 100644
--- a/nixos/tests/tracee.nix
+++ b/nixos/tests/tracee.nix
@@ -1,7 +1,13 @@
-import ./make-test-python.nix ({ pkgs, ... }: {
+import ./make-test-python.nix ({ pkgs, ... }: rec {
   name = "tracee-integration";
   meta.maintainers = pkgs.tracee.meta.maintainers;
 
+  passthru.hello-world-builder = pkgs: pkgs.dockerTools.buildImage {
+    name = "hello-world";
+    tag = "latest";
+    config.Cmd = [ "${pkgs.hello}/bin/hello" ];
+  };
+
   nodes = {
     machine = { config, pkgs, ... }: {
       # EventFilters/trace_only_events_from_new_containers and
@@ -12,57 +18,48 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       environment.systemPackages = with pkgs; [
         # required by Test_EventFilters/trace_events_from_ls_and_which_binary_in_separate_scopes
         which
-        # build the go integration tests as a binary
-        (tracee.overrideAttrs (oa: {
-          pname = oa.pname + "-integration";
-          postPatch = oa.postPatch or "" + ''
-            # prepare tester.sh (which will be embedded in the test binary)
-            patchShebangs tests/integration/tester.sh
-
-            # fix the test to look at nixos paths for running programs
-            substituteInPlace tests/integration/integration_test.go \
-              --replace "bin=/usr/bin/" "comm=" \
-              --replace "binary=/usr/bin/" "comm=" \
-              --replace "/usr/bin/dockerd" "dockerd" \
-              --replace "/usr/bin" "/run/current-system/sw/bin"
-          '';
-          nativeBuildInputs = oa.nativeBuildInputs or [ ] ++ [ makeWrapper ];
-          buildPhase = ''
-            runHook preBuild
-            # just build the static lib we need for the go test binary
-            make $makeFlags ''${enableParallelBuilding:+-j$NIX_BUILD_CORES} bpf-core ./dist/btfhub
-
-            # then compile the tests to be ran later
-            CGO_LDFLAGS="$(pkg-config --libs libbpf)" go test -tags core,ebpf,integration -p 1 -c -o $GOPATH/tracee-integration ./tests/integration/...
-            runHook postBuild
-          '';
-          doCheck = false;
-          outputs = [ "out" ];
-          installPhase = ''
-            mkdir -p $out/bin
-            mv $GOPATH/tracee-integration $out/bin/
-          '';
-          doInstallCheck = false;
-
-          meta = oa.meta // {
-            outputsToInstall = [];
-          };
-        }))
+        # the go integration tests as a binary
+        tracee.passthru.tests.integration-test-cli
       ];
     };
   };
 
-  testScript = ''
-    machine.wait_for_unit("docker.service")
+  testScript =
+    let
+      skippedTests = [
+        # these comm tests for some reason do not resolve.
+        # something about the test is different as it works fine if I replicate
+        # the policies and run tracee myself but doesn't work in the integration
+        # test either with the automatic run or running the commands by hand
+        # while it's searching.
+        "Test_EventFilters/comm:_event:_args:_trace_event_set_in_a_specific_policy_with_args_from_ls_command"
+        "Test_EventFilters/comm:_event:_trace_events_set_in_two_specific_policies_from_ls_and_uname_commands"
+
+        # worked at some point, seems to be flakey
+        "Test_EventFilters/pid:_event:_args:_trace_event_sched_switch_with_args_from_pid_0"
+      ];
+    in
+    ''
+      with subtest("prepare for integration tests"):
+        machine.wait_for_unit("docker.service")
+        machine.succeed('which bash')
+
+        # EventFilters/trace_only_events_from_new_containers also requires a container called "hello-world"
+        machine.succeed('docker load < ${passthru.hello-world-builder pkgs}')
 
-    with subtest("run integration tests"):
-      # EventFilters/trace_only_events_from_new_containers also requires a container called "alpine"
-      machine.succeed('tar c -C ${pkgs.pkgsStatic.busybox} . | docker import - alpine --change "ENTRYPOINT [\"sleep\"]"')
+        # exec= needs fully resolved paths
+        machine.succeed(
+          'mkdir /tmp/testdir',
+          'cp $(which who) /tmp/testdir/who',
+          'cp $(which uname) /tmp/testdir/uname',
+        )
 
-      # Test_EventFilters/trace_event_set_in_a_specific_scope expects to be in a dir that includes "integration"
-      print(machine.succeed(
-        'mkdir /tmp/integration',
-        'cd /tmp/integration && tracee-integration -test.v'
-      ))
-  '';
+      with subtest("run integration tests"):
+        # Test_EventFilters/trace_event_set_in_a_specific_scope expects to be in a dir that includes "integration"
+        # tests must be ran with 1 process
+        print(machine.succeed(
+          'mkdir /tmp/integration',
+          'cd /tmp/integration && export PATH="/tmp/testdir:$PATH" && integration.test -test.v -test.parallel 1 -test.skip="^${builtins.concatStringsSep "$|^" skippedTests}$"'
+        ))
+    '';
 })
diff --git a/nixos/tests/unifi.nix b/nixos/tests/unifi.nix
index d371bafd69652..789b11b55985c 100644
--- a/nixos/tests/unifi.nix
+++ b/nixos/tests/unifi.nix
@@ -31,8 +31,6 @@ let
     '';
   };
 in with pkgs; {
-  unifiLTS = makeAppTest unifiLTS;
-  unifi5 = makeAppTest unifi5;
-  unifi6 = makeAppTest unifi6;
   unifi7 = makeAppTest unifi7;
+  unifi8 = makeAppTest unifi8;
 }
diff --git a/nixos/tests/ustreamer.nix b/nixos/tests/ustreamer.nix
new file mode 100644
index 0000000000000..1354eb03a3269
--- /dev/null
+++ b/nixos/tests/ustreamer.nix
@@ -0,0 +1,75 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "ustreamer-vmtest";
+  nodes = {
+    client = {...}: {
+      environment.systemPackages = [ pkgs.curl ];
+    };
+    camera = {config, ...}: let
+      configFile = pkgs.writeText "akvcam-configFile" ''
+        [Cameras]
+        cameras/size = 2
+
+        cameras/1/type = output
+        cameras/1/mode = mmap, userptr, rw
+        cameras/1/description = Virtual Camera (output device)
+        cameras/1/formats = 2
+        cameras/1/videonr = 7
+
+        cameras/2/type = capture
+        cameras/2/mode = mmap, rw
+        cameras/2/description = Virtual Camera
+        cameras/2/formats = 1, 2
+        cameras/2/videonr = 9
+
+        [Connections]
+        connections/size = 1
+        connections/1/connection = 1:2
+
+        [Formats]
+        formats/size = 2
+
+        formats/1/format = YUY2
+        formats/1/width = 640
+        formats/1/height = 480
+        formats/1/fps = 30
+
+        formats/2/format = RGB24, YUY2
+        formats/2/width = 640
+        formats/2/height = 480
+        formats/2/fps = 20/1, 15/2
+      '';
+    in {
+      environment.systemPackages = [ pkgs.ustreamer ];
+      networking.firewall.enable = false;
+      systemd.services.ustreamer = {
+        description = "ustreamer service";
+        wantedBy = ["multi-user.target"];
+        serviceConfig = {
+          DynamicUser = true;
+          ExecStart = "${pkgs.ustreamer}/bin/ustreamer --host=0.0.0.0 --port 8000 --device /dev/video9 --device-timeout=8";
+          PrivateTmp = true;
+          BindReadOnlyPaths = "/dev/video9";
+          SupplementaryGroups = [
+            "video"
+          ];
+          Restart = "always";
+        };
+      };
+      boot.extraModulePackages = [config.boot.kernelPackages.akvcam];
+      boot.kernelModules = ["akvcam"];
+      boot.extraModprobeConfig = ''
+        options akvcam config_file=${configFile}
+      '';
+    };
+  };
+
+  testScript = ''
+    start_all()
+
+    camera.wait_for_unit("ustreamer.service")
+    camera.wait_for_open_port(8000)
+
+    client.wait_for_unit("multi-user.target")
+    client.succeed("curl http://camera:8000")
+  '';
+})
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index 9d2f0e6ab060e..28ff170e36107 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -106,7 +106,7 @@ let
 
                   wait = WebDriverWait(driver, 10)
 
-                  wait.until(EC.title_contains("Create account"))
+                  wait.until(EC.title_contains("Vaultwarden Web"))
 
                   driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_email').send_keys(
                       '${userEmail}'
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index d817ce927ff8d..76d5244b3ee34 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/wastebin.nix b/nixos/tests/wastebin.nix
new file mode 100644
index 0000000000000..1cf0ff80ae99d
--- /dev/null
+++ b/nixos/tests/wastebin.nix
@@ -0,0 +1,19 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "wastebin";
+
+  meta = {
+    maintainers = with lib.maintainers; [ pinpox ];
+  };
+
+  nodes.machine = { pkgs, ... }: {
+    services.wastebin = {
+      enable = true;
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("wastebin.service")
+    machine.wait_for_open_port(8088)
+    machine.succeed("curl --fail http://localhost:8088/")
+  '';
+})
diff --git a/nixos/tests/web-apps/mastodon/remote-databases.nix b/nixos/tests/web-apps/mastodon/remote-databases.nix
index fa6430a993531..55243658ec6a9 100644
--- a/nixos/tests/web-apps/mastodon/remote-databases.nix
+++ b/nixos/tests/web-apps/mastodon/remote-databases.nix
@@ -33,7 +33,7 @@ in
         extraHosts = hosts;
         firewall.allowedTCPPorts = [
           config.services.redis.servers.mastodon.port
-          config.services.postgresql.port
+          config.services.postgresql.settings.port
         ];
       };
 
diff --git a/nixos/tests/web-apps/movim/default.nix b/nixos/tests/web-apps/movim/default.nix
new file mode 100644
index 0000000000000..5d6314e2b41be
--- /dev/null
+++ b/nixos/tests/web-apps/movim/default.nix
@@ -0,0 +1,8 @@
+{ system ? builtins.currentSystem, handleTestOn }:
+
+let
+  supportedSystems = [ "x86_64-linux" "i686-linux" ];
+in
+{
+  standard = handleTestOn supportedSystems ./standard.nix { inherit system; };
+}
diff --git a/nixos/tests/web-apps/movim/standard.nix b/nixos/tests/web-apps/movim/standard.nix
new file mode 100644
index 0000000000000..470d81d8f7229
--- /dev/null
+++ b/nixos/tests/web-apps/movim/standard.nix
@@ -0,0 +1,102 @@
+import ../../make-test-python.nix ({ lib, pkgs, ... }:
+
+let
+  movim = {
+    domain = "movim.local";
+    info = "No ToS in tests";
+    description = "NixOS testing server";
+  };
+  xmpp = {
+    domain = "xmpp.local";
+    admin = rec {
+      JID = "${username}@${xmpp.domain}";
+      username = "romeo";
+      password = "juliet";
+    };
+  };
+in
+{
+  name = "movim-standard";
+
+  meta = {
+    maintainers = with pkgs.lib.maintainers; [ toastal ];
+  };
+
+  nodes = {
+    server = { pkgs, ... }: {
+      services.movim = {
+        inherit (movim) domain;
+        enable = true;
+        verbose = true;
+        podConfig = {
+          inherit (movim) description info;
+          xmppdomain = xmpp.domain;
+        };
+        nginx = { };
+      };
+
+      services.prosody = {
+        enable = true;
+        xmppComplianceSuite = false;
+        disco_items = [
+          { url = "upload.${xmpp.domain}"; description = "File Uploads"; }
+        ];
+        virtualHosts."${xmpp.domain}" = {
+          inherit (xmpp) domain;
+          enabled = true;
+          extraConfig = ''
+            Component "pubsub.${xmpp.domain}" "pubsub"
+                pubsub_max_items = 10000
+                expose_publisher = true
+
+            Component "upload.${xmpp.domain}" "http_file_share"
+                http_external_url = "http://upload.${xmpp.domain}"
+                http_file_share_expires_after = 300 * 24 * 60 * 60
+                http_file_share_size_limit = 1024 * 1024 * 1024
+                http_file_share_daily_quota = 4 * 1024 * 1024 * 1024
+          '';
+        };
+        extraConfig = ''
+          pep_max_items = 10000
+
+          http_paths = {
+              file_share = "/";
+          }
+        '';
+      };
+
+      networking.extraHosts = ''
+        127.0.0.1 ${movim.domain}
+        127.0.0.1 ${xmpp.domain}
+      '';
+    };
+  };
+
+  testScript = /* python */ ''
+    server.wait_for_unit("phpfpm-movim.service")
+    server.wait_for_unit("nginx.service")
+    server.wait_for_open_port(80)
+
+    server.wait_for_unit("prosody.service")
+    server.succeed('prosodyctl status | grep "Prosody is running"')
+    server.succeed("prosodyctl register ${xmpp.admin.username} ${xmpp.domain} ${xmpp.admin.password}")
+
+    server.wait_for_unit("movim.service")
+
+    # Test unauthenticated
+    server.fail("curl -L --fail-with-body --max-redirs 0 http://${movim.domain}/chat")
+
+    # Test basic Websocket
+    server.succeed("echo \"\" | ${lib.getExe pkgs.websocat} 'ws://${movim.domain}/ws/?path=login&offset=0' --origin 'http://${movim.domain}'")
+
+    # Test login + create cookiejar
+    login_html = server.succeed("curl --fail-with-body -c /tmp/cookies http://${movim.domain}/login")
+    assert "${movim.description}" in login_html
+    assert "${movim.info}" in login_html
+
+    # Test authentication POST
+    server.succeed("curl --fail-with-body -b /tmp/cookies -X POST --data-urlencode 'username=${xmpp.admin.JID}' --data-urlencode 'password=${xmpp.admin.password}' http://${movim.domain}/login")
+
+    server.succeed("curl -L --fail-with-body --max-redirs 1 -b /tmp/cookies http://${movim.domain}/chat")
+  '';
+})
diff --git a/nixos/tests/web-apps/peertube.nix b/nixos/tests/web-apps/peertube.nix
index 0e5f39c08a023..83c7cf03701e0 100644
--- a/nixos/tests/web-apps/peertube.nix
+++ b/nixos/tests/web-apps/peertube.nix
@@ -17,16 +17,18 @@ import ../make-test-python.nix ({pkgs, ...}:
       services.postgresql = {
         enable = true;
         enableTCPIP = true;
+        ensureDatabases = [ "peertube_test" ];
+        ensureUsers = [
+          {
+            name = "peertube_test";
+            ensureDBOwnership = true;
+          }
+        ];
         authentication = ''
-          hostnossl peertube_local peertube_test 192.168.2.11/32 md5
+          hostnossl peertube_test peertube_test 192.168.2.11/32 md5
         '';
         initialScript = pkgs.writeText "postgresql_init.sql" ''
           CREATE ROLE peertube_test LOGIN PASSWORD '0gUN0C1mgST6czvjZ8T9';
-          CREATE DATABASE peertube_local TEMPLATE template0 ENCODING UTF8;
-          GRANT ALL PRIVILEGES ON DATABASE peertube_local TO peertube_test;
-          \connect peertube_local
-          CREATE EXTENSION IF NOT EXISTS pg_trgm;
-          CREATE EXTENSION IF NOT EXISTS unaccent;
         '';
       };
 
@@ -41,6 +43,9 @@ import ../make-test-python.nix ({pkgs, ...}:
     server = { pkgs, ... }: {
       environment = {
         etc = {
+          "peertube/password-init-root".text = ''
+            PT_INITIAL_ROOT_PASSWORD=zw4SqYVdcsXUfRX8aaFX
+          '';
           "peertube/secrets-peertube".text = ''
             063d9c60d519597acef26003d5ecc32729083965d09181ef3949200cbe5f09ee
           '';
@@ -70,13 +75,15 @@ import ../make-test-python.nix ({pkgs, ...}:
         localDomain = "peertube.local";
         enableWebHttps = false;
 
+        serviceEnvironmentFile = "/etc/peertube/password-init-root";
+
         secrets = {
           secretsFile = "/etc/peertube/secrets-peertube";
         };
 
         database = {
           host = "192.168.2.10";
-          name = "peertube_local";
+          name = "peertube_test";
           user = "peertube_test";
           passwordFile = "/etc/peertube/password-posgressql-db";
         };
@@ -99,7 +106,7 @@ import ../make-test-python.nix ({pkgs, ...}:
     };
 
     client = {
-      environment.systemPackages = [ pkgs.jq ];
+      environment.systemPackages = [ pkgs.jq pkgs.peertube.cli ];
       networking = {
        interfaces.eth1 = {
           ipv4.addresses = [
@@ -130,7 +137,10 @@ import ../make-test-python.nix ({pkgs, ...}:
     client.succeed("curl --fail http://peertube.local:9000/api/v1/config/about | jq -r '.instance.name' | grep 'PeerTube\ Test\ Server'")
 
     # Check PeerTube CLI version
-    assert "${pkgs.peertube.version}" in server.succeed('su - peertube -s /bin/sh -c "peertube --version"')
+    client.succeed('peertube-cli auth add -u "http://peertube.local:9000" -U "root" --password "zw4SqYVdcsXUfRX8aaFX"')
+    client.succeed('peertube-cli auth list | grep "http://peertube.local:9000"')
+    client.succeed('peertube-cli auth del "http://peertube.local:9000"')
+    client.fail('peertube-cli auth list | grep "http://peertube.local:9000"')
 
     client.shutdown()
     server.shutdown()
diff --git a/nixos/tests/web-apps/pretix.nix b/nixos/tests/web-apps/pretix.nix
new file mode 100644
index 0000000000000..559316f9b85cb
--- /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"))
+  '';
+}
diff --git a/nixos/tests/wmderland.nix b/nixos/tests/wmderland.nix
index ebfd443763e1e..c60751c44e2cc 100644
--- a/nixos/tests/wmderland.nix
+++ b/nixos/tests/wmderland.nix
@@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
   nodes.machine = { lib, ... }: {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
     test-support.displayManager.auto.user = "alice";
-    services.xserver.displayManager.defaultSession = lib.mkForce "none+wmderland";
+    services.displayManager.defaultSession = lib.mkForce "none+wmderland";
     services.xserver.windowManager.wmderland.enable = true;
 
     systemd.services.setupWmderlandConfig = {
diff --git a/nixos/tests/workout-tracker.nix b/nixos/tests/workout-tracker.nix
new file mode 100644
index 0000000000000..1ad509edf2d4c
--- /dev/null
+++ b/nixos/tests/workout-tracker.nix
@@ -0,0 +1,29 @@
+import ./make-test-python.nix (
+  { lib, pkgs, ... }:
+
+  {
+    name = "workout-tracker";
+
+    meta.maintainers = with lib.maintainers; [ bhankas ];
+
+    nodes.machine =
+      { config, ... }:
+      {
+        virtualisation.memorySize = 2048;
+
+        services.workout-tracker.enable = true;
+      };
+
+    testScript = ''
+      start_all()
+      machine.wait_for_unit("workout-tracker.service")
+      # wait for workout-tracker to fully come up
+
+      with subtest("workout-tracker service starts"):
+          machine.wait_until_succeeds(
+              "curl -sSfL http://localhost:8080/ > /dev/null",
+              timeout=30
+          )
+    '';
+  }
+)
diff --git a/nixos/tests/xfce.nix b/nixos/tests/xfce.nix
index 9620e9188cbf5..d97f07d752712 100644
--- a/nixos/tests/xfce.nix
+++ b/nixos/tests/xfce.nix
@@ -10,13 +10,11 @@ import ./make-test-python.nix ({ pkgs, ...} : {
       ];
 
       services.xserver.enable = true;
+      services.xserver.displayManager.lightdm.enable = true;
 
-      services.xserver.displayManager = {
-        lightdm.enable = true;
-        autoLogin = {
-          enable = true;
-          user = "alice";
-        };
+      services.displayManager.autoLogin = {
+        enable = true;
+        user = "alice";
       };
 
       services.xserver.desktopManager.xfce.enable = true;
diff --git a/nixos/tests/xmonad-xdg-autostart.nix b/nixos/tests/xmonad-xdg-autostart.nix
index 2577a9ce2ea13..f1780072f9740 100644
--- a/nixos/tests/xmonad-xdg-autostart.nix
+++ b/nixos/tests/xmonad-xdg-autostart.nix
@@ -5,7 +5,7 @@ import ./make-test-python.nix ({ lib, ... }: {
   nodes.machine = { pkgs, config, ... }: {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
     test-support.displayManager.auto.user = "alice";
-    services.xserver.displayManager.defaultSession = "none+xmonad";
+    services.displayManager.defaultSession = "none+xmonad";
     services.xserver.windowManager.xmonad.enable = true;
     services.xserver.desktopManager.runXdgAutostartIfNone = true;
 
diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix
index ec48c3e112750..c61e96886e2c5 100644
--- a/nixos/tests/xmonad.nix
+++ b/nixos/tests/xmonad.nix
@@ -61,7 +61,7 @@ in {
   nodes.machine = { pkgs, ... }: {
     imports = [ ./common/x11.nix ./common/user-account.nix ];
     test-support.displayManager.auto.user = "alice";
-    services.xserver.displayManager.defaultSession = "none+xmonad";
+    services.displayManager.defaultSession = "none+xmonad";
     services.xserver.windowManager.xmonad = {
       enable = true;
       enableConfiguredRecompile = true;