about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/adding-custom-packages.section.md35
-rw-r--r--nixos/doc/manual/configuration/xfce.chapter.md11
-rw-r--r--nixos/doc/manual/default.nix6
-rw-r--r--nixos/doc/manual/development/activation-script.section.md2
-rw-r--r--nixos/doc/manual/development/option-declarations.section.md9
-rw-r--r--nixos/doc/manual/development/option-types.section.md19
-rw-r--r--nixos/doc/manual/development/writing-nixos-tests.section.md4
-rw-r--r--nixos/doc/manual/from_md/configuration/adding-custom-packages.section.xml116
-rw-r--r--nixos/doc/manual/from_md/configuration/xfce.chapter.xml14
-rw-r--r--nixos/doc/manual/from_md/development/activation-script.section.xml2
-rw-r--r--nixos/doc/manual/from_md/development/option-declarations.section.xml12
-rw-r--r--nixos/doc/manual/from_md/development/option-types.section.xml37
-rw-r--r--nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml3
-rw-r--r--nixos/doc/manual/from_md/installation/building-nixos.chapter.xml5
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml14
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2211.section.xml224
-rw-r--r--nixos/doc/manual/installation/building-nixos.chapter.md3
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md6
-rw-r--r--nixos/doc/manual/release-notes/rl-2211.section.md76
-rw-r--r--nixos/lib/make-ext4-fs.nix9
-rw-r--r--nixos/lib/make-multi-disk-zfs-image.nix2
-rw-r--r--nixos/lib/make-options-doc/default.nix23
-rw-r--r--nixos/lib/make-options-doc/mergeJSON.py152
-rw-r--r--nixos/lib/make-options-doc/options-to-docbook.xsl21
-rw-r--r--nixos/lib/make-single-disk-zfs-image.nix2
-rw-r--r--nixos/lib/systemd-unit-options.nix11
-rw-r--r--nixos/lib/test-driver/setup.py1
-rw-r--r--nixos/lib/test-driver/test_driver/vlan.py8
-rw-r--r--nixos/lib/testing-python.nix4
-rw-r--r--nixos/modules/config/appstream.nix4
-rw-r--r--nixos/modules/config/console.nix4
-rw-r--r--nixos/modules/config/debug-info.nix19
-rw-r--r--nixos/modules/config/fonts/fontconfig.nix17
-rw-r--r--nixos/modules/config/fonts/fontdir.nix8
-rw-r--r--nixos/modules/config/i18n.nix18
-rw-r--r--nixos/modules/config/locale.nix19
-rw-r--r--nixos/modules/config/nsswitch.nix24
-rw-r--r--nixos/modules/config/pulseaudio.nix26
-rw-r--r--nixos/modules/config/qt5.nix72
-rw-r--r--nixos/modules/config/resolvconf.nix21
-rw-r--r--nixos/modules/config/unix-odbc-drivers.nix6
-rw-r--r--nixos/modules/config/users-groups.nix16
-rw-r--r--nixos/modules/config/xdg/autostart.nix4
-rw-r--r--nixos/modules/config/xdg/icons.nix4
-rw-r--r--nixos/modules/config/xdg/menus.nix4
-rw-r--r--nixos/modules/config/xdg/portal.nix27
-rw-r--r--nixos/modules/config/xdg/portals/lxqt.nix49
-rw-r--r--nixos/modules/config/xdg/sounds.nix4
-rw-r--r--nixos/modules/hardware/ckb-next.nix2
-rw-r--r--nixos/modules/hardware/cpu/intel-sgx.nix14
-rw-r--r--nixos/modules/hardware/device-tree.nix18
-rw-r--r--nixos/modules/hardware/ksm.nix4
-rw-r--r--nixos/modules/hardware/raid/hpsa.nix2
-rw-r--r--nixos/modules/hardware/video/nvidia.nix28
-rw-r--r--nixos/modules/i18n/input-method/fcitx.nix4
-rw-r--r--nixos/modules/i18n/input-method/ibus.nix6
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix3
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix160
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt89
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-pc.nix163
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix172
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball.nix93
-rw-r--r--nixos/modules/installer/netboot/netboot.nix2
-rw-r--r--nixos/modules/installer/sd-card/sd-image.nix15
-rw-r--r--nixos/modules/installer/tools/get-version-suffix7
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl15
-rw-r--r--nixos/modules/installer/tools/tools.nix19
-rw-r--r--nixos/modules/misc/documentation.nix31
-rw-r--r--nixos/modules/misc/ids.nix27
-rw-r--r--nixos/modules/misc/label.nix5
-rw-r--r--nixos/modules/misc/man-db.nix12
-rw-r--r--nixos/modules/misc/mandoc.nix8
-rw-r--r--nixos/modules/misc/nixpkgs.nix108
-rw-r--r--nixos/modules/misc/nixpkgs/test.nix61
-rw-r--r--nixos/modules/misc/version.nix16
-rw-r--r--nixos/modules/module-list.nix20
-rw-r--r--nixos/modules/profiles/minimal.nix3
-rw-r--r--nixos/modules/programs/_1password-gui.nix17
-rw-r--r--nixos/modules/programs/_1password.nix17
-rw-r--r--nixos/modules/programs/atop.nix20
-rw-r--r--nixos/modules/programs/bash/bash.nix20
-rw-r--r--nixos/modules/programs/clickshare.nix21
-rw-r--r--nixos/modules/programs/dconf.nix4
-rw-r--r--nixos/modules/programs/dmrconfig.nix2
-rw-r--r--nixos/modules/programs/fish.nix30
-rw-r--r--nixos/modules/programs/less.nix20
-rw-r--r--nixos/modules/programs/nano.nix6
-rw-r--r--nixos/modules/programs/proxychains.nix34
-rw-r--r--nixos/modules/programs/qt5ct.nix30
-rw-r--r--nixos/modules/programs/spacefm.nix10
-rw-r--r--nixos/modules/programs/starship.nix4
-rw-r--r--nixos/modules/programs/streamdeck-ui.nix28
-rw-r--r--nixos/modules/programs/thunar.nix45
-rw-r--r--nixos/modules/programs/xfconf.nix27
-rw-r--r--nixos/modules/rename.nix3
-rw-r--r--nixos/modules/security/lock-kernel-modules.nix4
-rw-r--r--nixos/modules/security/pam.nix26
-rw-r--r--nixos/modules/security/polkit.nix8
-rw-r--r--nixos/modules/security/systemd-confinement.nix11
-rw-r--r--nixos/modules/services/audio/hqplayerd.nix2
-rw-r--r--nixos/modules/services/audio/mpd.nix1
-rw-r--r--nixos/modules/services/audio/navidrome.nix3
-rw-r--r--nixos/modules/services/audio/roon-server.nix4
-rw-r--r--nixos/modules/services/backup/postgresql-backup.nix29
-rw-r--r--nixos/modules/services/backup/restic.nix11
-rw-r--r--nixos/modules/services/backup/zrepl.nix11
-rw-r--r--nixos/modules/services/cluster/k3s/default.nix20
-rw-r--r--nixos/modules/services/cluster/kubernetes/kubelet.nix6
-rw-r--r--nixos/modules/services/continuous-integration/github-runner.nix24
-rw-r--r--nixos/modules/services/continuous-integration/hydra/default.nix7
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/job-builder.nix23
-rw-r--r--nixos/modules/services/databases/cassandra.nix127
-rw-r--r--nixos/modules/services/databases/dgraph.nix148
-rw-r--r--nixos/modules/services/databases/openldap.nix3
-rw-r--r--nixos/modules/services/databases/redis.nix12
-rw-r--r--nixos/modules/services/databases/riak.nix162
-rw-r--r--nixos/modules/services/desktops/pipewire/daemon/minimal.conf.json2
-rw-r--r--nixos/modules/services/desktops/pipewire/pipewire.nix5
-rw-r--r--nixos/modules/services/desktops/pipewire/wireplumber.nix21
-rw-r--r--nixos/modules/services/development/jupyter/default.nix3
-rw-r--r--nixos/modules/services/development/jupyter/kernel-options.nix9
-rw-r--r--nixos/modules/services/display-managers/greetd.nix2
-rw-r--r--nixos/modules/services/games/minecraft-server.nix30
-rw-r--r--nixos/modules/services/hardware/argonone.nix58
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix18
-rw-r--r--nixos/modules/services/logging/heartbeat.nix12
-rw-r--r--nixos/modules/services/logging/logrotate.nix13
-rw-r--r--nixos/modules/services/mail/mailman.nix173
-rw-r--r--nixos/modules/services/mail/schleuder.nix162
-rw-r--r--nixos/modules/services/matrix/appservice-irc.nix3
-rw-r--r--nixos/modules/services/matrix/mautrix-telegram.nix1
-rw-r--r--nixos/modules/services/matrix/synapse.nix18
-rw-r--r--nixos/modules/services/matrix/synapse.xml235
-rw-r--r--nixos/modules/services/misc/dictd.nix4
-rw-r--r--nixos/modules/services/misc/geoipupdate.nix53
-rw-r--r--nixos/modules/services/misc/gitlab.nix30
-rw-r--r--nixos/modules/services/misc/gollum.nix9
-rw-r--r--nixos/modules/services/misc/jellyfin.nix67
-rw-r--r--nixos/modules/services/misc/paperless.nix2
-rw-r--r--nixos/modules/services/misc/polaris.nix151
-rw-r--r--nixos/modules/services/misc/radarr.nix10
-rw-r--r--nixos/modules/services/monitoring/grafana-agent.nix143
-rw-r--r--nixos/modules/services/monitoring/grafana.nix5
-rw-r--r--nixos/modules/services/monitoring/graphite.nix160
-rw-r--r--nixos/modules/services/monitoring/netdata.nix6
-rw-r--r--nixos/modules/services/monitoring/parsedmarc.nix163
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/mail.nix21
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/node.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/postfix.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix6
-rw-r--r--nixos/modules/services/network-filesystems/glusterfs.nix3
-rw-r--r--nixos/modules/services/network-filesystems/ipfs.nix4
-rw-r--r--nixos/modules/services/networking/bitlbee.nix1
-rw-r--r--nixos/modules/services/networking/ddclient.nix7
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix2
-rw-r--r--nixos/modules/services/networking/flannel.nix11
-rw-r--r--nixos/modules/services/networking/i2pd.nix15
-rw-r--r--nixos/modules/services/networking/lokinet.nix157
-rw-r--r--nixos/modules/services/networking/minidlna.nix260
-rw-r--r--nixos/modules/services/networking/mosquitto.nix40
-rw-r--r--nixos/modules/services/networking/murmur.nix13
-rw-r--r--nixos/modules/services/networking/mxisd.nix17
-rw-r--r--nixos/modules/services/networking/prosody.nix79
-rw-r--r--nixos/modules/services/networking/radvd.nix17
-rw-r--r--nixos/modules/services/networking/routedns.nix84
-rw-r--r--nixos/modules/services/networking/searx.nix8
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix4
-rw-r--r--nixos/modules/services/networking/stunnel.nix158
-rw-r--r--nixos/modules/services/networking/syncthing.nix124
-rw-r--r--nixos/modules/services/networking/tailscale.nix15
-rw-r--r--nixos/modules/services/networking/trickster.nix20
-rw-r--r--nixos/modules/services/networking/wg-quick.nix8
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix2
-rw-r--r--nixos/modules/services/networking/yggdrasil.nix31
-rw-r--r--nixos/modules/services/security/pass-secret-service.nix27
-rw-r--r--nixos/modules/services/security/privacyidea.nix65
-rw-r--r--nixos/modules/services/security/vault.nix28
-rw-r--r--nixos/modules/services/security/vaultwarden/default.nix72
-rw-r--r--nixos/modules/services/system/cachix-agent/default.nix18
-rw-r--r--nixos/modules/services/tracing/tempo.nix68
-rw-r--r--nixos/modules/services/web-apps/atlassian/confluence.nix57
-rw-r--r--nixos/modules/services/web-apps/atlassian/crowd.nix31
-rw-r--r--nixos/modules/services/web-apps/atlassian/jira.nix46
-rw-r--r--nixos/modules/services/web-apps/calibre-web.nix2
-rw-r--r--nixos/modules/services/web-apps/cryptpad.nix54
-rw-r--r--nixos/modules/services/web-apps/healthchecks.nix249
-rw-r--r--nixos/modules/services/web-apps/hedgedoc.nix36
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.nix25
-rw-r--r--nixos/modules/services/web-apps/lemmy.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix71
-rw-r--r--nixos/modules/services/web-apps/onlyoffice.nix288
-rw-r--r--nixos/modules/services/web-apps/openwebrx.nix4
-rw-r--r--nixos/modules/services/web-apps/phylactery.nix51
-rw-r--r--nixos/modules/services/web-apps/tt-rss.nix1
-rw-r--r--nixos/modules/services/web-servers/caddy/default.nix2
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix4
-rw-r--r--nixos/modules/services/web-servers/nginx/location-options.nix11
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix4
-rw-r--r--nixos/modules/services/x11/desktop-managers/lxqt.nix3
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.nix51
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix234
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix25
-rw-r--r--nixos/modules/services/x11/display-managers/xpra.nix9
-rw-r--r--nixos/modules/system/boot/loader/generations-dir/generations-dir.nix10
-rw-r--r--nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix2
-rw-r--r--nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py2
-rw-r--r--nixos/modules/system/boot/networkd.nix7
-rw-r--r--nixos/modules/system/boot/resolved.nix63
-rw-r--r--nixos/modules/system/boot/systemd.nix70
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix42
-rw-r--r--nixos/modules/system/boot/systemd/user.nix5
-rw-r--r--nixos/modules/system/boot/tmp.nix10
-rw-r--r--nixos/modules/system/etc/etc.nix30
-rw-r--r--nixos/modules/tasks/encrypted-devices.nix14
-rw-r--r--nixos/modules/tasks/filesystems.nix3
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix4
-rw-r--r--nixos/modules/tasks/lvm.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces-scripted.nix6
-rw-r--r--nixos/modules/tasks/network-interfaces-systemd.nix25
-rw-r--r--nixos/modules/tasks/snapraid.nix24
-rw-r--r--nixos/modules/tasks/swraid.nix2
-rw-r--r--nixos/modules/virtualisation/anbox.nix12
-rw-r--r--nixos/modules/virtualisation/build-vm.nix8
-rw-r--r--nixos/modules/virtualisation/containers.nix20
-rw-r--r--nixos/modules/virtualisation/cri-o.nix4
-rw-r--r--nixos/modules/virtualisation/digital-ocean-image.nix12
-rw-r--r--nixos/modules/virtualisation/digital-ocean-init.nix8
-rw-r--r--nixos/modules/virtualisation/docker-rootless.nix12
-rw-r--r--nixos/modules/virtualisation/lxc-container.nix8
-rw-r--r--nixos/modules/virtualisation/oci-containers.nix103
-rw-r--r--nixos/modules/virtualisation/openvswitch.nix8
-rw-r--r--nixos/modules/virtualisation/parallels-guest.nix61
-rw-r--r--nixos/modules/virtualisation/proxmox-image.nix22
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix84
-rw-r--r--nixos/modules/virtualisation/railcar.nix124
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix28
-rw-r--r--nixos/modules/virtualisation/waydroid.nix2
-rw-r--r--nixos/modules/virtualisation/xen-dom0.nix30
-rw-r--r--nixos/release.nix54
-rw-r--r--nixos/tests/acme.nix2
-rw-r--r--nixos/tests/airsonic.nix3
-rw-r--r--nixos/tests/all-tests.nix19
-rw-r--r--nixos/tests/containers-custom-pkgs.nix2
-rw-r--r--nixos/tests/convos.nix2
-rw-r--r--nixos/tests/cryptpad.nix18
-rw-r--r--nixos/tests/docker-tools-cross.nix6
-rw-r--r--nixos/tests/docker-tools.nix5
-rw-r--r--nixos/tests/fcitx/default.nix1
-rw-r--r--nixos/tests/firefox.nix19
-rw-r--r--nixos/tests/grafana-agent.nix32
-rw-r--r--nixos/tests/graphite.nix12
-rw-r--r--nixos/tests/hardened.nix9
-rw-r--r--nixos/tests/hedgedoc.nix4
-rw-r--r--nixos/tests/home-assistant.nix55
-rw-r--r--nixos/tests/installed-tests/default.nix1
-rw-r--r--nixos/tests/installed-tests/flatpak-builder.nix1
-rw-r--r--nixos/tests/installed-tests/geocode-glib.nix13
-rw-r--r--nixos/tests/ipfs.nix17
-rw-r--r--nixos/tests/jellyfin.nix24
-rw-r--r--nixos/tests/jenkins.nix21
-rw-r--r--nixos/tests/jitsi-meet.nix3
-rw-r--r--nixos/tests/k3s-single-node-docker.nix84
-rw-r--r--nixos/tests/kea.nix2
-rw-r--r--nixos/tests/kernel-generic.nix2
-rw-r--r--nixos/tests/kubernetes/dns.nix12
-rw-r--r--nixos/tests/kubernetes/rbac.nix6
-rw-r--r--nixos/tests/libuiohook.nix21
-rw-r--r--nixos/tests/lighttpd.nix21
-rw-r--r--nixos/tests/maddy.nix2
-rw-r--r--nixos/tests/matrix/appservice-irc.nix3
-rw-r--r--nixos/tests/matrix/mjolnir.nix1
-rw-r--r--nixos/tests/minecraft-server.nix1
-rw-r--r--nixos/tests/networking.nix43
-rw-r--r--nixos/tests/nextcloud/default.nix4
-rw-r--r--nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix118
-rw-r--r--nixos/tests/nginx-http3.nix3
-rw-r--r--nixos/tests/non-default-filesystems.nix54
-rw-r--r--nixos/tests/pam/pam-u2f.nix3
-rw-r--r--nixos/tests/paperless.nix2
-rw-r--r--nixos/tests/pass-secret-service.nix69
-rw-r--r--nixos/tests/pgadmin4.nix18
-rw-r--r--nixos/tests/plasma5.nix8
-rw-r--r--nixos/tests/podgrab.nix4
-rw-r--r--nixos/tests/polaris.nix31
-rw-r--r--nixos/tests/prometheus-exporters.nix10
-rw-r--r--nixos/tests/pykms.nix14
-rw-r--r--nixos/tests/restic.nix9
-rw-r--r--nixos/tests/riak.nix18
-rw-r--r--nixos/tests/schleuder.nix128
-rw-r--r--nixos/tests/signal-desktop.nix2
-rw-r--r--nixos/tests/stunnel.nix174
-rw-r--r--nixos/tests/swap-partition.nix48
-rw-r--r--nixos/tests/systemd-networkd-vrf.nix65
-rw-r--r--nixos/tests/terminal-emulators.nix1
-rw-r--r--nixos/tests/traefik.nix22
-rw-r--r--nixos/tests/vault-dev.nix35
-rw-r--r--nixos/tests/vaultwarden.nix31
-rw-r--r--nixos/tests/vengi-tools.nix2
-rw-r--r--nixos/tests/virtualbox.nix29
-rw-r--r--nixos/tests/vscodium.nix8
-rw-r--r--nixos/tests/web-apps/healthchecks.nix42
-rw-r--r--nixos/tests/web-apps/phylactery.nix20
-rw-r--r--nixos/tests/web-servers/unit-php.nix17
-rw-r--r--nixos/tests/wireguard/wg-quick.nix5
-rw-r--r--nixos/tests/xrdp.nix2
-rw-r--r--nixos/tests/zeronet-conservancy.nix25
308 files changed, 6555 insertions, 3353 deletions
diff --git a/nixos/doc/manual/configuration/adding-custom-packages.section.md b/nixos/doc/manual/configuration/adding-custom-packages.section.md
index 5d1198fb0f418..9219396722f03 100644
--- a/nixos/doc/manual/configuration/adding-custom-packages.section.md
+++ b/nixos/doc/manual/configuration/adding-custom-packages.section.md
@@ -1,11 +1,18 @@
 # Adding Custom Packages {#sec-custom-packages}
 
 It's possible that a package you need is not available in NixOS. In that
-case, you can do two things. First, you can clone the Nixpkgs
-repository, add the package to your clone, and (optionally) submit a
-patch or pull request to have it accepted into the main Nixpkgs repository.
-This is described in detail in the [Nixpkgs manual](https://nixos.org/nixpkgs/manual).
-In short, you clone Nixpkgs:
+case, you can do two things. Either you can package it with Nix, or you can try
+to use prebuilt packages from upstream. Due to the peculiarities of NixOS, it
+is important to note that building software from source is often easier than
+using pre-built executables.
+
+## Building with Nix {#sec-custom-packages-nix}
+
+This can be done either in-tree or out-of-tree. For an in-tree build, you can
+clone the Nixpkgs repository, add the package to your clone, and (optionally)
+submit a patch or pull request to have it accepted into the main Nixpkgs
+repository. This is described in detail in the [Nixpkgs
+manual](https://nixos.org/nixpkgs/manual). In short, you clone Nixpkgs:
 
 ```ShellSession
 $ git clone https://github.com/NixOS/nixpkgs
@@ -72,3 +79,21 @@ $ nix-build my-hello.nix
 $ ./result/bin/hello
 Hello, world!
 ```
+
+## Using pre-built executables {#sec-custom-packages-prebuilt}
+
+Most pre-built executables will not work on NixOS. There are two notable
+exceptions: flatpaks and AppImages. For flatpaks see the [dedicated
+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 ];
+```
+
+Then instead of running the AppImage "as-is", run `appimage-run foo.appimage`.
+
+To make other pre-built executables work on NixOS, you need to package them
+with Nix and special helpers like `autoPatchelfHook` or `buildFHSUserEnv`. See
+the [Nixpkgs manual](https://nixos.org/nixpkgs/manual) for details. This
+is complex and often doing a source build is easier.
diff --git a/nixos/doc/manual/configuration/xfce.chapter.md b/nixos/doc/manual/configuration/xfce.chapter.md
index b0ef6682aae86..ee60d465e3b30 100644
--- a/nixos/doc/manual/configuration/xfce.chapter.md
+++ b/nixos/doc/manual/configuration/xfce.chapter.md
@@ -24,11 +24,16 @@ Some Xfce programs are not installed automatically. To install them
 manually (system wide), put them into your
 [](#opt-environment.systemPackages) from `pkgs.xfce`.
 
-## Thunar Plugins {#sec-xfce-thunar-plugins .unnumbered}
+## Thunar {#sec-xfce-thunar-plugins .unnumbered}
+
+Thunar (the Xfce file manager) is automatically enabled when Xfce is
+enabled. To enable Thunar without enabling Xfce, use the configuration
+option [](#opt-programs.thunar.enable) instead of simply adding
+`pkgs.xfce.thunar` to [](#opt-environment.systemPackages).
 
 If you\'d like to add extra plugins to Thunar, add them to
-[](#opt-services.xserver.desktopManager.xfce.thunarPlugins).
-You shouldn\'t just add them to [](#opt-environment.systemPackages).
+[](#opt-programs.thunar.plugins). You shouldn\'t just add them to
+[](#opt-environment.systemPackages).
 
 ## Troubleshooting {#sec-xfce-troubleshooting .unnumbered}
 
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index bcb5d0d02f74f..1cd769b6a544d 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -133,12 +133,12 @@ let
           # ^ redirect assumes xmllint doesn’t print to stdout
       }
 
-      lintrng manual-combined.xml
-      lintrng man-pages-combined.xml
-
       mkdir $out
       cp manual-combined.xml $out/
       cp man-pages-combined.xml $out/
+
+      lintrng $out/manual-combined.xml
+      lintrng $out/man-pages-combined.xml
     '';
 
   olinkDB = runCommand "manual-olinkdb"
diff --git a/nixos/doc/manual/development/activation-script.section.md b/nixos/doc/manual/development/activation-script.section.md
index df6836624040e..1aee252fddea1 100644
--- a/nixos/doc/manual/development/activation-script.section.md
+++ b/nixos/doc/manual/development/activation-script.section.md
@@ -54,7 +54,7 @@ possiblility into account that they have to create them first.
 ## NixOS snippets {#sec-activation-script-nixos-snippets}
 
 There are some snippets NixOS enables by default because disabling them would
-most likely break you system. This section lists a few of them and what they
+most likely break your system. This section lists a few of them and what they
 do:
 
 - `binsh` creates `/bin/sh` which points to the runtime shell
diff --git a/nixos/doc/manual/development/option-declarations.section.md b/nixos/doc/manual/development/option-declarations.section.md
index ef7255557a109..79914f2cb6ca9 100644
--- a/nixos/doc/manual/development/option-declarations.section.md
+++ b/nixos/doc/manual/development/option-declarations.section.md
@@ -56,7 +56,14 @@ The function `mkOption` accepts the following arguments.
 `description`
 
 :   A textual description of the option, in DocBook format, that will be
-    included in the NixOS manual.
+    included in the NixOS manual. During the migration process from DocBook
+    to CommonMark the description may also be written in CommonMark, but has
+    to be wrapped in `lib.mdDoc` to differentiate it from DocBook. See
+    the nixpkgs manual for [the list of CommonMark extensions](
+    https://nixos.org/nixpkgs/manual/#sec-contributing-markup)
+    supported by NixOS documentation.
+
+    New documentation should preferably be written as CommonMark.
 
 ## Utility functions for common option patterns {#sec-option-declarations-util}
 
diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md
index d32d4fc50ad79..9b35e66301447 100644
--- a/nixos/doc/manual/development/option-types.section.md
+++ b/nixos/doc/manual/development/option-types.section.md
@@ -220,6 +220,25 @@ Value types are types that take a value parameter.
         requires using a function:
         `the-submodule = { ... }: { options = { ... }; }`.
 
+`types.deferredModule`
+
+:   Whereas `submodule` represents an option tree, `deferredModule` represents
+    a module value, such as a module file or a configuration.
+
+    It can be set multiple times.
+
+    Module authors can use its value in `imports`, in `submoduleWith`'s `modules`
+    or in `evalModules`' `modules` parameter, among other places.
+
+    Note that `imports` must be evaluated before the module fixpoint. Because
+    of this, deferred modules can only be imported into "other" fixpoints, such
+    as submodules.
+
+    One use case for this type is the type of a "default" module that allow the
+    user to affect all submodules in an `attrsOf submodule` at once. This is
+    more convenient and discoverable than expecting the module user to
+    type-merge with the `attrsOf submodule` option.
+
 ## Composed Types {#sec-option-types-composed}
 
 Composed types are types that take a type as parameter. `listOf
diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md
index f4f4056ad9889..da965ce09e369 100644
--- a/nixos/doc/manual/development/writing-nixos-tests.section.md
+++ b/nixos/doc/manual/development/writing-nixos-tests.section.md
@@ -362,7 +362,6 @@ with foo_running:
     ...  # Put `foo` through its paces
 ```
 
-
 `polling_condition` takes the following (optional) arguments:
 
 `seconds_interval`
@@ -407,6 +406,9 @@ import ./make-test-python.nix
 
   nodes = { };
 
+  # Type checking on extra packages doesn't work yet
+  skipTypeCheck = true;
+
   testScript = ''
     import numpy as np
     assert str(np.zeros(4) == "array([0., 0., 0., 0.])")
diff --git a/nixos/doc/manual/from_md/configuration/adding-custom-packages.section.xml b/nixos/doc/manual/from_md/configuration/adding-custom-packages.section.xml
index 4fa40d61966e7..07f541666cbe1 100644
--- a/nixos/doc/manual/from_md/configuration/adding-custom-packages.section.xml
+++ b/nixos/doc/manual/from_md/configuration/adding-custom-packages.section.xml
@@ -2,40 +2,50 @@
   <title>Adding Custom Packages</title>
   <para>
     It’s possible that a package you need is not available in NixOS. In
-    that case, you can do two things. First, you can clone the Nixpkgs
-    repository, add the package to your clone, and (optionally) submit a
-    patch or pull request to have it accepted into the main Nixpkgs
-    repository. This is described in detail in the
-    <link xlink:href="https://nixos.org/nixpkgs/manual">Nixpkgs
-    manual</link>. In short, you clone Nixpkgs:
+    that case, you can do two things. Either you can package it with
+    Nix, or you can try to use prebuilt packages from upstream. Due to
+    the peculiarities of NixOS, it is important to note that building
+    software from source is often easier than using pre-built
+    executables.
   </para>
-  <programlisting>
+  <section xml:id="sec-custom-packages-nix">
+    <title>Building with Nix</title>
+    <para>
+      This can be done either in-tree or out-of-tree. For an in-tree
+      build, you can clone the Nixpkgs repository, add the package to
+      your clone, and (optionally) submit a patch or pull request to
+      have it accepted into the main Nixpkgs repository. This is
+      described in detail in the
+      <link xlink:href="https://nixos.org/nixpkgs/manual">Nixpkgs
+      manual</link>. In short, you clone Nixpkgs:
+    </para>
+    <programlisting>
 $ git clone https://github.com/NixOS/nixpkgs
 $ cd nixpkgs
 </programlisting>
-  <para>
-    Then you write and test the package as described in the Nixpkgs
-    manual. Finally, you add it to
-    <xref linkend="opt-environment.systemPackages" />, e.g.
-  </para>
-  <programlisting language="bash">
+    <para>
+      Then you write and test the package as described in the Nixpkgs
+      manual. Finally, you add it to
+      <xref linkend="opt-environment.systemPackages" />, e.g.
+    </para>
+    <programlisting language="bash">
 environment.systemPackages = [ pkgs.my-package ];
 </programlisting>
-  <para>
-    and you run <literal>nixos-rebuild</literal>, specifying your own
-    Nixpkgs tree:
-  </para>
-  <programlisting>
+    <para>
+      and you run <literal>nixos-rebuild</literal>, specifying your own
+      Nixpkgs tree:
+    </para>
+    <programlisting>
 # nixos-rebuild switch -I nixpkgs=/path/to/my/nixpkgs
 </programlisting>
-  <para>
-    The second possibility is to add the package outside of the Nixpkgs
-    tree. For instance, here is how you specify a build of the
-    <link xlink:href="https://www.gnu.org/software/hello/">GNU
-    Hello</link> package directly in
-    <literal>configuration.nix</literal>:
-  </para>
-  <programlisting language="bash">
+    <para>
+      The second possibility is to add the package outside of the
+      Nixpkgs tree. For instance, here is how you specify a build of the
+      <link xlink:href="https://www.gnu.org/software/hello/">GNU
+      Hello</link> package directly in
+      <literal>configuration.nix</literal>:
+    </para>
+    <programlisting language="bash">
 environment.systemPackages =
   let
     my-hello = with pkgs; stdenv.mkDerivation rec {
@@ -48,17 +58,17 @@ environment.systemPackages =
   in
   [ my-hello ];
 </programlisting>
-  <para>
-    Of course, you can also move the definition of
-    <literal>my-hello</literal> into a separate Nix expression, e.g.
-  </para>
-  <programlisting language="bash">
+    <para>
+      Of course, you can also move the definition of
+      <literal>my-hello</literal> into a separate Nix expression, e.g.
+    </para>
+    <programlisting language="bash">
 environment.systemPackages = [ (import ./my-hello.nix) ];
 </programlisting>
-  <para>
-    where <literal>my-hello.nix</literal> contains:
-  </para>
-  <programlisting language="bash">
+    <para>
+      where <literal>my-hello.nix</literal> contains:
+    </para>
+    <programlisting language="bash">
 with import &lt;nixpkgs&gt; {}; # bring all of Nixpkgs into scope
 
 stdenv.mkDerivation rec {
@@ -69,12 +79,40 @@ stdenv.mkDerivation rec {
   };
 }
 </programlisting>
-  <para>
-    This allows testing the package easily:
-  </para>
-  <programlisting>
+    <para>
+      This allows testing the package easily:
+    </para>
+    <programlisting>
 $ nix-build my-hello.nix
 $ ./result/bin/hello
 Hello, world!
 </programlisting>
+  </section>
+  <section xml:id="sec-custom-packages-prebuilt">
+    <title>Using pre-built executables</title>
+    <para>
+      Most pre-built executables will not work on NixOS. There are two
+      notable exceptions: flatpaks and AppImages. For flatpaks see the
+      <link linkend="module-services-flatpak">dedicated section</link>.
+      AppImages will not run <quote>as-is</quote> on NixOS. First you
+      need to install <literal>appimage-run</literal>: add to
+      <literal>/etc/nixos/configuration.nix</literal>
+    </para>
+    <programlisting language="bash">
+environment.systemPackages = [ pkgs.appimage-run ];
+</programlisting>
+    <para>
+      Then instead of running the AppImage <quote>as-is</quote>, run
+      <literal>appimage-run foo.appimage</literal>.
+    </para>
+    <para>
+      To make other pre-built executables work on NixOS, you need to
+      package them with Nix and special helpers like
+      <literal>autoPatchelfHook</literal> or
+      <literal>buildFHSUserEnv</literal>. See the
+      <link xlink:href="https://nixos.org/nixpkgs/manual">Nixpkgs
+      manual</link> for details. This is complex and often doing a
+      source build is easier.
+    </para>
+  </section>
 </section>
diff --git a/nixos/doc/manual/from_md/configuration/xfce.chapter.xml b/nixos/doc/manual/from_md/configuration/xfce.chapter.xml
index f96ef2e8c483c..42e70d1d81d30 100644
--- a/nixos/doc/manual/from_md/configuration/xfce.chapter.xml
+++ b/nixos/doc/manual/from_md/configuration/xfce.chapter.xml
@@ -27,13 +27,19 @@ services.picom = {
     <literal>pkgs.xfce</literal>.
   </para>
   <section xml:id="sec-xfce-thunar-plugins">
-    <title>Thunar Plugins</title>
+    <title>Thunar</title>
     <para>
-      If you'd like to add extra plugins to Thunar, add them to
-      <xref linkend="opt-services.xserver.desktopManager.xfce.thunarPlugins" />.
-      You shouldn't just add them to
+      Thunar (the Xfce file manager) is automatically enabled when Xfce
+      is enabled. To enable Thunar without enabling Xfce, use the
+      configuration option <xref linkend="opt-programs.thunar.enable" />
+      instead of simply adding <literal>pkgs.xfce.thunar</literal> to
       <xref linkend="opt-environment.systemPackages" />.
     </para>
+    <para>
+      If you'd like to add extra plugins to Thunar, add them to
+      <xref linkend="opt-programs.thunar.plugins" />. You shouldn't just
+      add them to <xref linkend="opt-environment.systemPackages" />.
+    </para>
   </section>
   <section xml:id="sec-xfce-troubleshooting">
     <title>Troubleshooting</title>
diff --git a/nixos/doc/manual/from_md/development/activation-script.section.xml b/nixos/doc/manual/from_md/development/activation-script.section.xml
index 0d9e911216efa..981ebf37e60f0 100644
--- a/nixos/doc/manual/from_md/development/activation-script.section.xml
+++ b/nixos/doc/manual/from_md/development/activation-script.section.xml
@@ -73,7 +73,7 @@ system.activationScripts.my-activation-script = {
     <title>NixOS snippets</title>
     <para>
       There are some snippets NixOS enables by default because disabling
-      them would most likely break you system. This section lists a few
+      them would most likely break your system. This section lists a few
       of them and what they do:
     </para>
     <itemizedlist spacing="compact">
diff --git a/nixos/doc/manual/from_md/development/option-declarations.section.xml b/nixos/doc/manual/from_md/development/option-declarations.section.xml
index 381163dd7c74d..03ec48f35fd78 100644
--- a/nixos/doc/manual/from_md/development/option-declarations.section.xml
+++ b/nixos/doc/manual/from_md/development/option-declarations.section.xml
@@ -94,7 +94,17 @@ options = {
       <listitem>
         <para>
           A textual description of the option, in DocBook format, that
-          will be included in the NixOS manual.
+          will be included in the NixOS manual. During the migration
+          process from DocBook to CommonMark the description may also be
+          written in CommonMark, but has to be wrapped in
+          <literal>lib.mdDoc</literal> to differentiate it from DocBook.
+          See the nixpkgs manual for
+          <link xlink:href="https://nixos.org/nixpkgs/manual/#sec-contributing-markup">the
+          list of CommonMark extensions</link> supported by NixOS
+          documentation.
+        </para>
+        <para>
+          New documentation should preferably be written as CommonMark.
         </para>
       </listitem>
     </varlistentry>
diff --git a/nixos/doc/manual/from_md/development/option-types.section.xml b/nixos/doc/manual/from_md/development/option-types.section.xml
index c67e183581c2c..929d5302ed417 100644
--- a/nixos/doc/manual/from_md/development/option-types.section.xml
+++ b/nixos/doc/manual/from_md/development/option-types.section.xml
@@ -427,6 +427,43 @@
           </itemizedlist>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>types.deferredModule</literal>
+        </term>
+        <listitem>
+          <para>
+            Whereas <literal>submodule</literal> represents an option
+            tree, <literal>deferredModule</literal> represents a module
+            value, such as a module file or a configuration.
+          </para>
+          <para>
+            It can be set multiple times.
+          </para>
+          <para>
+            Module authors can use its value in
+            <literal>imports</literal>, in
+            <literal>submoduleWith</literal><quote>s
+            <literal>modules</literal> or in
+            <literal>evalModules</literal></quote>
+            <literal>modules</literal> parameter, among other places.
+          </para>
+          <para>
+            Note that <literal>imports</literal> must be evaluated
+            before the module fixpoint. Because of this, deferred
+            modules can only be imported into <quote>other</quote>
+            fixpoints, such as submodules.
+          </para>
+          <para>
+            One use case for this type is the type of a
+            <quote>default</quote> module that allow the user to affect
+            all submodules in an <literal>attrsOf submodule</literal> at
+            once. This is more convenient and discoverable than
+            expecting the module user to type-merge with the
+            <literal>attrsOf submodule</literal> option.
+          </para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </section>
   <section xml:id="sec-option-types-composed">
diff --git a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml
index 46367bdd345d1..7e4af03829067 100644
--- a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml
+++ b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml
@@ -680,6 +680,9 @@ import ./make-test-python.nix
 
   nodes = { };
 
+  # Type checking on extra packages doesn't work yet
+  skipTypeCheck = true;
+
   testScript = ''
     import numpy as np
     assert str(np.zeros(4) == &quot;array([0., 0., 0., 0.])&quot;)
diff --git a/nixos/doc/manual/from_md/installation/building-nixos.chapter.xml b/nixos/doc/manual/from_md/installation/building-nixos.chapter.xml
index e7a76a6d715d8..ea2d01bebcc2f 100644
--- a/nixos/doc/manual/from_md/installation/building-nixos.chapter.xml
+++ b/nixos/doc/manual/from_md/installation/building-nixos.chapter.xml
@@ -33,9 +33,14 @@
   </para>
   <section xml:id="sec-building-image-instructions">
     <title>Practical Instructions</title>
+    <para>
+      To build an ISO image for the channel
+      <literal>nixos-unstable</literal>:
+    </para>
     <programlisting>
 $ git clone https://github.com/NixOS/nixpkgs.git
 $ cd nixpkgs/nixos
+$ git switch nixos-unstable
 $ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix default.nix
 </programlisting>
     <para>
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
index 5208671e4dab0..245250e709147 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml
@@ -148,6 +148,14 @@
       </listitem>
       <listitem>
         <para>
+          <link xlink:href="https://gitlab.com/DarkElvenAngel/argononed">argonone</link>,
+          a replacement daemon for the Raspberry Pi Argon One power
+          button and cooler. Available at
+          <link xlink:href="options.html#opt-services.hardware.argonone.enable">services.hardware.argonone</link>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <link xlink:href="https://github.com/JustArchiNET/ArchiSteamFarm">ArchiSteamFarm</link>,
           a C# application with primary purpose of idling Steam cards
           from multiple accounts simultaneously. Available as
@@ -2778,6 +2786,12 @@ sudo cp /var/lib/redis/dump.rdb /var/lib/redis-peertube/dump.rdb
       </listitem>
       <listitem>
         <para>
+          <literal>zfs</literal> was updated from 2.1.4 to 2.1.5,
+          enabling it to be used with Linux kernel 5.18.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <literal>stdenv.mkDerivation</literal> now supports a
           self-referencing <literal>finalAttrs:</literal> parameter
           containing the final <literal>mkDerivation</literal> arguments
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
index 9580006878108..be3adc4d3bed9 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
@@ -33,9 +33,80 @@
       </listitem>
       <listitem>
         <para>
+          The <literal>nixpkgs.hostPlatform</literal> and
+          <literal>nixpkgs.buildPlatform</literal> options have been
+          added. These cover and override the
+          <literal>nixpkgs.{system,localSystem,crossSystem}</literal>
+          options.
+        </para>
+        <itemizedlist spacing="compact">
+          <listitem>
+            <para>
+              <literal>hostPlatform</literal> is the platform or
+              <quote><literal>system</literal></quote> string of the
+              NixOS system described by the configuration.
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              <literal>buildPlatform</literal> is the platform that is
+              responsible for building the NixOS configuration. It
+              defaults to the <literal>hostPlatform</literal>, for a
+              non-cross build configuration. To cross compile, set
+              <literal>buildPlatform</literal> to a different value.
+            </para>
+          </listitem>
+        </itemizedlist>
+        <para>
+          The new options convey the same information, but with fewer
+          options, and following the Nixpkgs terminology.
+        </para>
+        <para>
+          The existing options
+          <literal>nixpkgs.{system,localSystem,crossSystem}</literal>
+          have not been formally deprecated, to allow for evaluation of
+          the change and to allow for a transition period so that in
+          time the ecosystem can switch without breaking compatibility
+          with any supported NixOS release.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <literal>nixos-generate-config</literal> now generates
+          configurations that can be built in pure mode. This is
+          achieved by setting the new
+          <literal>nixpkgs.hostPlatform</literal> option.
+        </para>
+        <para>
+          You may have to unset the <literal>system</literal> parameter
+          in <literal>lib.nixosSystem</literal>, or similarly remove
+          definitions of the
+          <literal>nixpkgs.{system,localSystem,crossSystem}</literal>
+          options.
+        </para>
+        <para>
+          Alternatively, you can remove the
+          <literal>hostPlatform</literal> line and use NixOS like you
+          would in NixOS 22.05 and earlier.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           PHP now defaults to PHP 8.1, updated from 8.0.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          <literal>hardware.nvidia</literal> has a new option
+          <literal>open</literal> that can be used to opt in the
+          opensource version of NVIDIA kernel driver. Note that the
+          driver’s support for GeForce and Workstation GPUs is still
+          alpha quality, see
+          <link xlink:href="https://developer.nvidia.com/blog/nvidia-releases-open-source-gpu-kernel-modules/">NVIDIA
+          Releases Open-Source GPU Kernel Modules</link> for the
+          official announcement.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-22.11-new-services">
@@ -73,11 +144,25 @@
       </listitem>
       <listitem>
         <para>
+          <link xlink:href="https://schleuder.org/">schleuder</link>, a
+          mailing list manager with PGP support. Enable using
+          <link linkend="opt-services.schleuder.enable">services.schleuder</link>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <link xlink:href="https://www.expressvpn.com">expressvpn</link>,
           the CLI client for ExpressVPN. Available as
           <link linkend="opt-services.expressvpn.enable">services.expressvpn</link>.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://www.grafana.com/oss/tempo/">Grafana
+          Tempo</link>, a distributed tracing store. Available as
+          <link linkend="opt-services.tempo.enable">services.tempo</link>.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-22.11-incompatibilities">
@@ -102,6 +187,29 @@
       </listitem>
       <listitem>
         <para>
+          <literal>ngrok</literal> has been upgraded from 2.3.40 to
+          3.0.4. Please see
+          <link xlink:href="https://ngrok.com/docs/guides/upgrade-v2-v3">the
+          upgrade guide</link> and
+          <link xlink:href="https://ngrok.com/docs/ngrok-agent/changelog">changelog</link>.
+          Notably, breaking changes are that the config file format has
+          changed and support for single hypen arguments was dropped.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <literal>i18n.supportedLocales</literal> is now by default
+          only generated with the locales set in
+          <literal>i18n.defaultLocale</literal> and
+          <literal>i18n.extraLocaleSettings</literal>. This got
+          partially copied over from the minimal profile and reduces the
+          final system size by up to 200MB. If you require all locales
+          installed set the option to
+          <literal>[ &quot;all&quot; ]</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>isPowerPC</literal> predicate, found on
           <literal>platform</literal> attrsets
           (<literal>hostPlatform</literal>,
@@ -117,10 +225,69 @@
       </listitem>
       <listitem>
         <para>
+          <literal>bsp-layout</literal> no longer uses the command
+          <literal>cycle</literal> to switch to other window layouts, as
+          it got replaced by the commands <literal>previous</literal>
+          and <literal>next</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The Barco ClickShare driver/client package
+          <literal>pkgs.clickshare-csc1</literal> and the option
+          <literal>programs.clickshare-csc1.enable</literal> have been
+          removed, as it requires <literal>qt4</literal>, which reached
+          its end-of-life 2015 and will no longer be supported by
+          nixpkgs.
+          <link xlink:href="https://www.barco.com/de/support/knowledge-base/4380-can-i-use-linux-os-with-clickshare-base-units">According
+          to Barco</link> many of their base unit models can be used
+          with Google Chrome and the Google Cast extension.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           PHP 7.4 is no longer supported due to upstream not supporting
           this version for the entire lifecycle of the 22.11 release.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          riak package removed along with
+          <literal>services.riak</literal> module, due to lack of
+          maintainer to update the package.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The <literal>services.graphite.api</literal> and
+          <literal>services.graphite.beacon</literal> NixOS options, and
+          the <literal>python3.pkgs.graphite_api</literal>,
+          <literal>python3.pkgs.graphite_beacon</literal> and
+          <literal>python3.pkgs.influxgraph</literal> packages, have
+          been removed due to lack of upstream maintenance.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The <literal>meta.mainProgram</literal> attribute of packages
+          in <literal>wineWowPackages</literal> now defaults to
+          <literal>&quot;wine64&quot;</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          (Neo)Vim can not be configured with
+          <literal>configure.pathogen</literal> anymore to reduce
+          maintainance burden. Use <literal>configure.packages</literal>
+          instead.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <literal>k3s</literal> no longer supports docker as runtime
+          due to upstream dropping support.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-22.11-notable-changes">
@@ -128,6 +295,14 @@
     <itemizedlist>
       <listitem>
         <para>
+          The <literal>xplr</literal> package has been updated from
+          0.18.0 to 0.19.0, which brings some breaking changes. See the
+          <link xlink:href="https://github.com/sayanarijit/xplr/releases/tag/v0.19.0">upstream
+          release notes</link> for more details.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           A new module was added for the Saleae Logic device family,
           providing the options
           <literal>hardware.saleae-logic.enable</literal> and
@@ -136,6 +311,13 @@
       </listitem>
       <listitem>
         <para>
+          The Redis module now disables RDB persistence when
+          <literal>services.redis.servers.&lt;name&gt;.save = []</literal>
+          instead of using the Redis default.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           Matrix Synapse now requires entries in the
           <literal>state_group_edges</literal> table to be unique, in
           order to prevent accidentally introducing duplicate
@@ -147,11 +329,53 @@
       </listitem>
       <listitem>
         <para>
+          <literal>dockerTools.buildImage</literal> deprecates the
+          misunderstood <literal>contents</literal> parameter, in favor
+          of <literal>copyToRoot</literal>. Use
+          <literal>copyToRoot = buildEnv { ... };</literal> or similar
+          if you intend to add packages to <literal>/bin</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           memtest86+ was updated from 5.00-coreboot-002 to 6.00-beta2.
           It is now the upstream version from https://www.memtest.org/,
           as coreboot’s fork is no longer available.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          Add udev rules for the Teensy family of microcontrollers.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The <literal>pass-secret-service</literal> package now
+          includes systemd units from upstream, so adding it to the
+          NixOS <literal>services.dbus.packages</literal> option will
+          make it start automatically as a systemd user service when an
+          application tries to talk to the libsecret D-Bus API.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          There is a new module for the <literal>thunar</literal>
+          program (the Xfce file manager), which depends on the
+          <literal>xfconf</literal> dbus service, and also has a dbus
+          service and a systemd unit. The option
+          <literal>services.xserver.desktopManager.xfce.thunarPlugins</literal>
+          has been renamed to
+          <literal>programs.thunar.plugins</literal>, and in a future
+          release it may be removed.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          There is a new module for the <literal>xfconf</literal>
+          program (the Xfce configuration storage system), which has a
+          dbus service.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
 </section>
diff --git a/nixos/doc/manual/installation/building-nixos.chapter.md b/nixos/doc/manual/installation/building-nixos.chapter.md
index 27d7e1d385539..17da261fbdaa4 100644
--- a/nixos/doc/manual/installation/building-nixos.chapter.md
+++ b/nixos/doc/manual/installation/building-nixos.chapter.md
@@ -18,9 +18,12 @@ enforced values with `mkForce`.
 
 ## Practical Instructions {#sec-building-image-instructions}
 
+To build an ISO image for the channel `nixos-unstable`:
+
 ```ShellSession
 $ git clone https://github.com/NixOS/nixpkgs.git
 $ cd nixpkgs/nixos
+$ git switch nixos-unstable
 $ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix default.nix
 ```
 
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index faf941f569966..e83a7cd43b876 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -61,6 +61,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [apfs](https://github.com/linux-apfs/linux-apfs-rw), a kernel module for mounting the Apple File System (APFS).
 
+- [argonone](https://gitlab.com/DarkElvenAngel/argononed), a replacement daemon for the Raspberry Pi Argon One power button and cooler. Available at [services.hardware.argonone](options.html#opt-services.hardware.argonone.enable).
+
 - [ArchiSteamFarm](https://github.com/JustArchiNET/ArchiSteamFarm), a C# application with primary purpose of idling Steam cards from multiple accounts simultaneously. Available as [services.archisteamfarm](#opt-services.archisteamfarm.enable).
 
 - [BaGet](https://loic-sharma.github.io/BaGet/), a lightweight NuGet and symbol server. Available at [services.baget](#opt-services.baget.enable).
@@ -971,7 +973,9 @@ In addition to numerous new and upgraded packages, this release has the followin
   was changed.
   
 - The new [`postgresqlTestHook`](https://nixos.org/manual/nixpkgs/stable/#sec-postgresqlTestHook) runs a PostgreSQL server for the duration of package checks.
-  
+
+- `zfs` was updated from 2.1.4 to 2.1.5, enabling it to be used with Linux kernel 5.18.
+
 - `stdenv.mkDerivation` now supports a self-referencing `finalAttrs:` parameter
   containing the final `mkDerivation` arguments including overrides.
   `drv.overrideAttrs` now supports two parameters `finalAttrs: previousAttrs:`.
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index 1a14885ed8c38..3f9afe13f1d99 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -17,8 +17,37 @@ In addition to numerous new and upgraded packages, this release has the followin
   built for `stdenv.hostPlatform` (i.e. produced by `stdenv.cc`) by evaluating
   `stdenv.buildPlatform.canExecute stdenv.hostPlatform`.
 
+- The `nixpkgs.hostPlatform` and `nixpkgs.buildPlatform` options have been added.
+  These cover and override the `nixpkgs.{system,localSystem,crossSystem}` options.
+
+   - `hostPlatform` is the platform or "`system`" string of the NixOS system
+     described by the configuration.
+   - `buildPlatform` is the platform that is responsible for building the NixOS
+     configuration. It defaults to the `hostPlatform`, for a non-cross
+     build configuration. To cross compile, set `buildPlatform` to a different
+     value.
+
+  The new options convey the same information, but with fewer options, and
+  following the Nixpkgs terminology.
+
+  The existing options `nixpkgs.{system,localSystem,crossSystem}` have not
+  been formally deprecated, to allow for evaluation of the change and to allow
+  for a transition period so that in time the ecosystem can switch without
+  breaking compatibility with any supported NixOS release.
+
+- `nixos-generate-config` now generates configurations that can be built in pure
+  mode. This is achieved by setting the new `nixpkgs.hostPlatform` option.
+
+  You may have to unset the `system` parameter in `lib.nixosSystem`, or similarly
+  remove definitions of the `nixpkgs.{system,localSystem,crossSystem}` options.
+
+  Alternatively, you can remove the `hostPlatform` line and use NixOS like you
+  would in NixOS 22.05 and earlier.
+
 - PHP now defaults to PHP 8.1, updated from 8.0.
 
+- `hardware.nvidia` has a new option `open` that can be used to opt in the opensource version of NVIDIA kernel driver. Note that the driver's support for GeForce and Workstation GPUs is still alpha quality, see [NVIDIA Releases Open-Source GPU Kernel Modules](https://developer.nvidia.com/blog/nvidia-releases-open-source-gpu-kernel-modules/) for the official announcement.
+
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
 ## New Services {#sec-release-22.11-new-services}
@@ -31,8 +60,12 @@ In addition to numerous new and upgraded packages, this release has the followin
   Available as [services.infnoise](options.html#opt-services.infnoise.enable).
 - [persistent-evdev](https://github.com/aiberia/persistent-evdev), a daemon to add virtual proxy devices that mirror a physical input device but persist even if the underlying hardware is hot-plugged. Available as [services.persistent-evdev](#opt-services.persistent-evdev.enable).
 
+- [schleuder](https://schleuder.org/), a mailing list manager with PGP support. Enable using [services.schleuder](#opt-services.schleuder.enable).
+
 - [expressvpn](https://www.expressvpn.com), the CLI client for ExpressVPN. Available as [services.expressvpn](#opt-services.expressvpn.enable).
 
+- [Grafana Tempo](https://www.grafana.com/oss/tempo/), a distributed tracing store. Available as [services.tempo](#opt-services.tempo.enable).
+
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
 ## Backward Incompatibilities {#sec-release-22.11-incompatibilities}
@@ -45,19 +78,62 @@ In addition to numerous new and upgraded packages, this release has the followin
   `lib.systems.parse.isCompatible` still exists, but has changed semantically:
   Architectures with differing endianness modes are *no longer considered compatible*.
 
+- `ngrok` has been upgraded from 2.3.40 to 3.0.4. Please see [the upgrade guide](https://ngrok.com/docs/guides/upgrade-v2-v3)
+  and [changelog](https://ngrok.com/docs/ngrok-agent/changelog). Notably, breaking changes are that the config file format has
+  changed and support for single hypen arguments was dropped.
+
+- `i18n.supportedLocales` is now by default only generated with the locales set in `i18n.defaultLocale` and `i18n.extraLocaleSettings`.
+  This got partially copied over from the minimal profile and reduces the final system size by up to 200MB.
+  If you require all locales installed set the option to ``[ "all" ]``.
+
 - The `isPowerPC` predicate, found on `platform` attrsets (`hostPlatform`, `buildPlatform`, `targetPlatform`, etc) has been removed in order to reduce confusion.  The predicate was was defined such that it matches only the 32-bit big-endian members of the POWER/PowerPC family, despite having a name which would imply a broader set of systems.  If you were using this predicate, you can replace `foo.isPowerPC` with `(with foo; isPower && is32bit && isBigEndian)`.
 
+- `bsp-layout` no longer uses the command `cycle` to switch to other window layouts, as it got replaced by the commands `previous` and `next`.
+
+- The Barco ClickShare driver/client package `pkgs.clickshare-csc1` and the option `programs.clickshare-csc1.enable` have been removed,
+  as it requires `qt4`, which reached its end-of-life 2015 and will no longer be supported by nixpkgs.
+  [According to Barco](https://www.barco.com/de/support/knowledge-base/4380-can-i-use-linux-os-with-clickshare-base-units) many of their base unit models can be used with Google Chrome and the Google Cast extension.
+
 - PHP 7.4 is no longer supported due to upstream not supporting this
   version for the entire lifecycle of the 22.11 release.
 
+- riak package removed along with `services.riak` module, due to lack of maintainer to update the package.
+
+- The `services.graphite.api` and `services.graphite.beacon` NixOS options, and
+  the `python3.pkgs.graphite_api`, `python3.pkgs.graphite_beacon` and
+  `python3.pkgs.influxgraph` packages, have been removed due to lack of upstream
+  maintenance.
+
+- The `meta.mainProgram` attribute of packages in `wineWowPackages` now defaults to `"wine64"`.
+
+- (Neo)Vim can not be configured with `configure.pathogen` anymore to reduce maintainance burden.
+Use `configure.packages` instead.
+
+- `k3s` no longer supports docker as runtime due to upstream dropping support.
+
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
 ## Other Notable Changes {#sec-release-22.11-notable-changes}
 
+- The `xplr` package has been updated from 0.18.0 to 0.19.0, which brings some breaking changes. See the [upstream release notes](https://github.com/sayanarijit/xplr/releases/tag/v0.19.0) for more details.
+
 - A new module was added for the Saleae Logic device family, providing the options `hardware.saleae-logic.enable` and `hardware.saleae-logic.package`.
 
+- The Redis module now disables RDB persistence when `services.redis.servers.<name>.save = []` instead of using the Redis default.
+
 - Matrix Synapse now requires entries in the `state_group_edges` table to be unique, in order to prevent accidentally introducing duplicate information (for example, because a database backup was restored multiple times). If your Synapse database already has duplicate rows in this table, this could fail with an error and require manual remediation.
 
+- `dockerTools.buildImage` deprecates the misunderstood `contents` parameter, in favor of `copyToRoot`.
+  Use `copyToRoot = buildEnv { ... };` or similar if you intend to add packages to `/bin`.
+
 - memtest86+ was updated from 5.00-coreboot-002 to 6.00-beta2. It is now the upstream version from https://www.memtest.org/, as coreboot's fork is no longer available.
 
+- Add udev rules for the Teensy family of microcontrollers.
+
+- The `pass-secret-service` package now includes systemd units from upstream, so adding it to the NixOS `services.dbus.packages` option will make it start automatically as a systemd user service when an application tries to talk to the libsecret D-Bus API.
+
+- There is a new module for the `thunar` program (the Xfce file manager), which depends on the `xfconf` dbus service, and also has a dbus service and a systemd unit. The option `services.xserver.desktopManager.xfce.thunarPlugins` has been renamed to `programs.thunar.plugins`, and in a future release it may be removed.
+
+- There is a new module for the `xfconf` program (the Xfce configuration storage system), which has a dbus service.
+
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix
index 416beeb32f2fa..b8e1b8d24c48b 100644
--- a/nixos/lib/make-ext4-fs.nix
+++ b/nixos/lib/make-ext4-fs.nix
@@ -78,6 +78,15 @@ pkgs.stdenv.mkDerivation {
       # get rid of the unnecessary slack here--but see
       # https://github.com/NixOS/nixpkgs/issues/125121 for caveats.
 
+      # shrink to fit
+      resize2fs -M $img
+
+      # Add 16 MebiByte to the current_size
+      new_size=$(dumpe2fs -h $img | awk -F: \
+        '/Block count/{count=$2} /Block size/{size=$2} END{print (count*size+16*2**20)/size}')
+
+      resize2fs $img $new_size
+
       if [ ${builtins.toString compressImage} ]; then
         echo "Compressing image"
         zstd -v --no-progress ./$img -o $out
diff --git a/nixos/lib/make-multi-disk-zfs-image.nix b/nixos/lib/make-multi-disk-zfs-image.nix
index a84732aa11712..0a894c8b9888f 100644
--- a/nixos/lib/make-multi-disk-zfs-image.nix
+++ b/nixos/lib/make-multi-disk-zfs-image.nix
@@ -128,7 +128,7 @@ let
       gptfdisk
       nix
       parted
-      utillinux
+      util-linux
       zfs
     ]
   );
diff --git a/nixos/lib/make-options-doc/default.nix b/nixos/lib/make-options-doc/default.nix
index 3324ef7fcd6f0..6649fc41d41aa 100644
--- a/nixos/lib/make-options-doc/default.nix
+++ b/nixos/lib/make-options-doc/default.nix
@@ -22,6 +22,10 @@
 , transformOptions ? lib.id  # function for additional tranformations of the options
 , documentType ? "appendix" # TODO deprecate "appendix" in favor of "none"
                             #      and/or rename function to moduleOptionDoc for clean slate
+
+  # If you include more than one option list into a document, you need to
+  # provide different ids.
+, variablelistId ? "configuration-variable-list"
 , revision ? "" # Specify revision for the options
 # a set of options the docs we are generating will be merged into, as if by recursiveUpdate.
 # used to split the options doc build into a static part (nixos/modules) and a dynamic part
@@ -112,7 +116,15 @@ in rec {
 
   optionsJSON = pkgs.runCommand "options.json"
     { meta.description = "List of NixOS options in JSON format";
-      buildInputs = [ pkgs.brotli ];
+      buildInputs = [
+        pkgs.brotli
+        (let
+          self = (pkgs.python3Minimal.override {
+            inherit self;
+            includeSiteCustomize = true;
+           });
+         in self.withPackages (p: [ p.mistune_2_0 ]))
+      ];
       options = builtins.toFile "options.json"
         (builtins.unsafeDiscardStringContext (builtins.toJSON optionsNix));
     }
@@ -123,9 +135,13 @@ in rec {
 
       ${
         if baseOptionsJSON == null
-          then "cp $options $dst/options.json"
+          then ''
+            # `cp $options $dst/options.json`, but with temporary
+            # markdown processing
+            python ${./mergeJSON.py} $options <(echo '{}') > $dst/options.json
+          ''
           else ''
-            ${pkgs.python3Minimal}/bin/python ${./mergeJSON.py} \
+            python ${./mergeJSON.py} \
               ${lib.optionalString warningsAreErrors "--warnings-are-errors"} \
               ${baseOptionsJSON} $options \
               > $dst/options.json
@@ -165,6 +181,7 @@ in rec {
     ${pkgs.libxslt.bin}/bin/xsltproc \
       --stringparam documentType '${documentType}' \
       --stringparam revision '${revision}' \
+      --stringparam variablelistId '${variablelistId}' \
       -o intermediate.xml ${./options-to-docbook.xsl} sorted.xml
     ${pkgs.libxslt.bin}/bin/xsltproc \
       -o "$out" ${./postprocess-option-descriptions.xsl} intermediate.xml
diff --git a/nixos/lib/make-options-doc/mergeJSON.py b/nixos/lib/make-options-doc/mergeJSON.py
index 44a188a08c999..33e5172270b58 100644
--- a/nixos/lib/make-options-doc/mergeJSON.py
+++ b/nixos/lib/make-options-doc/mergeJSON.py
@@ -41,6 +41,154 @@ def unpivot(options: Dict[Key, Option]) -> Dict[str, JSON]:
         result[opt.name] = opt.value
     return result
 
+# converts in-place!
+def convertMD(options: Dict[str, Any]) -> str:
+    import mistune
+    import re
+    from xml.sax.saxutils import escape, quoteattr
+
+    admonitions = {
+        '.warning': 'warning',
+        '.important': 'important',
+        '.note': 'note'
+    }
+    class Renderer(mistune.renderers.BaseRenderer):
+        def _get_method(self, name):
+            try:
+                return super(Renderer, self)._get_method(name)
+            except AttributeError:
+                def not_supported(*args, **kwargs):
+                    raise NotImplementedError("md node not supported yet", name, args, **kwargs)
+                return not_supported
+
+        def text(self, text):
+            return escape(text)
+        def paragraph(self, text):
+            return text + "\n\n"
+        def newline(self):
+            return "<literallayout>\n</literallayout>"
+        def codespan(self, text):
+            return f"<literal>{escape(text)}</literal>"
+        def block_code(self, text, info=None):
+            info = f" language={quoteattr(info)}" if info is not None else ""
+            return f"<programlisting{info}>\n{escape(text)}</programlisting>"
+        def link(self, link, text=None, title=None):
+            if link[0:1] == '#':
+                attr = "linkend"
+                link = quoteattr(link[1:])
+            else:
+                # try to faithfully reproduce links that were of the form <link href="..."/>
+                # in docbook format
+                if text == link:
+                    text = ""
+                attr = "xlink:href"
+                link = quoteattr(link)
+            return f"<link {attr}={link}>{text}</link>"
+        def list(self, text, ordered, level, start=None):
+            if ordered:
+                raise NotImplementedError("ordered lists not supported yet")
+            return f"<itemizedlist>\n{text}\n</itemizedlist>"
+        def list_item(self, text, level):
+            return f"<listitem><para>{text}</para></listitem>\n"
+        def block_text(self, text):
+            return text
+        def emphasis(self, text):
+            return f"<emphasis>{text}</emphasis>"
+        def strong(self, text):
+            return f"<emphasis role=\"strong\">{text}</emphasis>"
+        def admonition(self, text, kind):
+            if kind not in admonitions:
+                raise NotImplementedError(f"admonition {kind} not supported yet")
+            tag = admonitions[kind]
+            # we don't keep whitespace here because usually we'll contain only
+            # a single paragraph and the original docbook string is no longer
+            # available to restore the trailer.
+            return f"<{tag}><para>{text.rstrip()}</para></{tag}>"
+        def block_quote(self, text):
+            return f"<blockquote><para>{text}</para></blockquote>"
+        def command(self, text):
+            return f"<command>{escape(text)}</command>"
+        def option(self, text):
+            return f"<option>{escape(text)}</option>"
+        def file(self, text):
+            return f"<filename>{escape(text)}</filename>"
+        def manpage(self, page, section):
+            title = f"<refentrytitle>{escape(page)}</refentrytitle>"
+            vol = f"<manvolnum>{escape(section)}</manvolnum>"
+            return f"<citerefentry>{title}{vol}</citerefentry>"
+
+        def finalize(self, data):
+            return "".join(data)
+
+    plugins = []
+
+    COMMAND_PATTERN = r'\{command\}`(.*?)`'
+    def command(md):
+        def parse(self, m, state):
+            return ('command', m.group(1))
+        md.inline.register_rule('command', COMMAND_PATTERN, parse)
+        md.inline.rules.append('command')
+    plugins.append(command)
+
+    FILE_PATTERN = r'\{file\}`(.*?)`'
+    def file(md):
+        def parse(self, m, state):
+            return ('file', m.group(1))
+        md.inline.register_rule('file', FILE_PATTERN, parse)
+        md.inline.rules.append('file')
+    plugins.append(file)
+
+    OPTION_PATTERN = r'\{option\}`(.*?)`'
+    def option(md):
+        def parse(self, m, state):
+            return ('option', m.group(1))
+        md.inline.register_rule('option', OPTION_PATTERN, parse)
+        md.inline.rules.append('option')
+    plugins.append(option)
+
+    MANPAGE_PATTERN = r'\{manpage\}`(.*?)\((.+?)\)`'
+    def manpage(md):
+        def parse(self, m, state):
+            return ('manpage', m.group(1), m.group(2))
+        md.inline.register_rule('manpage', MANPAGE_PATTERN, parse)
+        md.inline.rules.append('manpage')
+    plugins.append(manpage)
+
+    ADMONITION_PATTERN = re.compile(r'^::: \{([^\n]*?)\}\n(.*?)^:::\n', flags=re.MULTILINE|re.DOTALL)
+    def admonition(md):
+        def parse(self, m, state):
+            return {
+                'type': 'admonition',
+                'children': self.parse(m.group(2), state),
+                'params': [ m.group(1) ],
+            }
+        md.block.register_rule('admonition', ADMONITION_PATTERN, parse)
+        md.block.rules.append('admonition')
+    plugins.append(admonition)
+
+    def convertString(text: str) -> str:
+        rendered = mistune.markdown(text, renderer=Renderer(), plugins=plugins)
+        # keep trailing spaces so we can diff the generated XML to check for conversion bugs.
+        return rendered.rstrip() + text[len(text.rstrip()):]
+
+    def optionIs(option: Dict[str, Any], key: str, typ: str) -> bool:
+        if key not in option: return False
+        if type(option[key]) != dict: return False
+        if '_type' not in option[key]: return False
+        return option[key]['_type'] == typ
+
+    for (name, option) in options.items():
+        if optionIs(option, 'description', 'mdDoc'):
+            option['description'] = convertString(option['description']['text'])
+        if optionIs(option, 'example', 'literalMD'):
+            docbook = convertString(option['example']['text'])
+            option['example'] = { '_type': 'literalDocBook', 'text': docbook }
+        if optionIs(option, 'default', 'literalMD'):
+            docbook = convertString(option['default']['text'])
+            option['default'] = { '_type': 'literalDocBook', 'text': docbook }
+
+    return options
+
 warningsAreErrors = sys.argv[1] == "--warnings-are-errors"
 optOffset = 1 if warningsAreErrors else 0
 options = pivot(json.load(open(sys.argv[1 + optOffset], 'r')))
@@ -50,7 +198,7 @@ overrides = pivot(json.load(open(sys.argv[2 + optOffset], 'r')))
 for (k, v) in options.items():
     # The _module options are not declared in nixos/modules
     if v.value['loc'][0] != "_module":
-        v.value['declarations'] = list(map(lambda s: f'nixos/modules/{s}', v.value['declarations']))
+        v.value['declarations'] = list(map(lambda s: f'nixos/modules/{s}' if isinstance(s, str) else s, v.value['declarations']))
 
 # merge both descriptions
 for (k, v) in overrides.items():
@@ -92,4 +240,4 @@ if hasWarnings and warningsAreErrors:
         file=sys.stderr)
     sys.exit(1)
 
-json.dump(unpivot(options), fp=sys.stdout)
+json.dump(convertMD(unpivot(options)), fp=sys.stdout)
diff --git a/nixos/lib/make-options-doc/options-to-docbook.xsl b/nixos/lib/make-options-doc/options-to-docbook.xsl
index 03e14365cda93..978d5e2468a83 100644
--- a/nixos/lib/make-options-doc/options-to-docbook.xsl
+++ b/nixos/lib/make-options-doc/options-to-docbook.xsl
@@ -14,6 +14,7 @@
   <xsl:param name="revision" />
   <xsl:param name="documentType" />
   <xsl:param name="program" />
+  <xsl:param name="variablelistId" />
 
 
   <xsl:template match="/expr/list">
@@ -31,7 +32,8 @@
   </xsl:template>
 
   <xsl:template name="variable-list">
-      <variablelist xml:id="configuration-variable-list">
+      <variablelist>
+      <xsl:attribute name="id" namespace="http://www.w3.org/XML/1998/namespace"><xsl:value-of select="$variablelistId"/></xsl:attribute>
         <xsl:for-each select="attrs">
           <xsl:variable name="id" select="
             concat('opt-',
@@ -213,6 +215,23 @@
 
   <xsl:template match="attr[@name = 'declarations' or @name = 'definitions']">
     <simplelist>
+      <!--
+        Example:
+          opt.declarations = [ { name = "foo/bar.nix"; url = "https://github.com/....."; } ];
+      -->
+      <xsl:for-each select="list/attrs[attr[@name = 'name']]">
+        <member><filename>
+          <xsl:if test="attr[@name = 'url']">
+            <xsl:attribute name="xlink:href"><xsl:value-of select="attr[@name = 'url']/string/@value"/></xsl:attribute>
+          </xsl:if>
+          <xsl:value-of select="attr[@name = 'name']/string/@value"/>
+        </filename></member>
+      </xsl:for-each>
+
+      <!--
+        When the declarations/definitions are raw strings,
+        fall back to hardcoded location logic, specific to Nixpkgs.
+      -->
       <xsl:for-each select="list/string">
         <member><filename>
           <!-- Hyperlink the filename either to the NixOS Subversion
diff --git a/nixos/lib/make-single-disk-zfs-image.nix b/nixos/lib/make-single-disk-zfs-image.nix
index 9310febd91792..98150584fb3e4 100644
--- a/nixos/lib/make-single-disk-zfs-image.nix
+++ b/nixos/lib/make-single-disk-zfs-image.nix
@@ -116,7 +116,7 @@ let
       gptfdisk
       nix
       parted
-      utillinux
+      util-linux
       zfs
     ]
   );
diff --git a/nixos/lib/systemd-unit-options.nix b/nixos/lib/systemd-unit-options.nix
index 02e91fdf1e624..cae82433d856b 100644
--- a/nixos/lib/systemd-unit-options.nix
+++ b/nixos/lib/systemd-unit-options.nix
@@ -20,10 +20,15 @@ in rec {
     merge = loc: defs:
       let
         defs' = filterOverrides defs;
-        defs'' = getValues defs';
       in
-        if isList (head defs'')
-        then concatLists defs''
+        if isList (head defs').value
+        then concatMap (def:
+          if builtins.typeOf def.value == "list"
+          then def.value
+          else
+            throw "The definitions for systemd unit options should be either all lists, representing repeatable options, or all non-lists, but for the option ${showOption loc}, the definitions are a mix of list and non-list ${lib.options.showDefs defs'}"
+        ) defs'
+
         else mergeEqualOption loc defs';
   };
 
diff --git a/nixos/lib/test-driver/setup.py b/nixos/lib/test-driver/setup.py
index 476c7b2dab2a4..1719b988db689 100644
--- a/nixos/lib/test-driver/setup.py
+++ b/nixos/lib/test-driver/setup.py
@@ -4,6 +4,7 @@ setup(
   name="nixos-test-driver",
   version='1.1',
   packages=find_packages(),
+  package_data={"test_driver": ["py.typed"]},
   entry_points={
     "console_scripts": [
       "nixos-test-driver=test_driver:main",
diff --git a/nixos/lib/test-driver/test_driver/vlan.py b/nixos/lib/test-driver/test_driver/vlan.py
index e5c8f07b4edf4..f2a7f250d1d2f 100644
--- a/nixos/lib/test-driver/test_driver/vlan.py
+++ b/nixos/lib/test-driver/test_driver/vlan.py
@@ -32,8 +32,12 @@ class VLan:
         rootlog.info("start vlan")
         pty_master, pty_slave = pty.openpty()
 
+        # The --hub is required for the scenario determined by
+        # nixos/tests/networking.nix vlan-ping.
+        # VLAN Tagged traffic (802.1Q) seams to be blocked if a vde_switch is
+        # used without the hub mode (flood packets to all ports).
         self.process = subprocess.Popen(
-            ["vde_switch", "-s", self.socket_dir, "--dirmode", "0700"],
+            ["vde_switch", "-s", self.socket_dir, "--dirmode", "0700", "--hub"],
             stdin=pty_slave,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE,
@@ -50,7 +54,7 @@ class VLan:
         if not (self.socket_dir / "ctl").exists():
             rootlog.error("cannot start vde_switch")
 
-        rootlog.info(f"running vlan (pid {self.pid})")
+        rootlog.info(f"running vlan (pid {self.pid}; ctl {self.socket_dir})")
 
     def __del__(self) -> None:
         rootlog.info(f"kill vlan (pid {self.pid})")
diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix
index a6868a708aaf3..4bb1689ffd789 100644
--- a/nixos/lib/testing-python.nix
+++ b/nixos/lib/testing-python.nix
@@ -119,6 +119,7 @@ rec {
       {
         inherit testName;
         nativeBuildInputs = [ makeWrapper mypy ];
+        buildInputs = [ testDriver ];
         testScript = testScript';
         preferLocalBuild = true;
         passthru = passthru // {
@@ -138,13 +139,10 @@ rec {
           echo "${builtins.toString vlanNames}" >> testScriptWithTypes
           echo -n "$testScript" >> testScriptWithTypes
 
-          # set pythonpath so mypy knows where to find the imports. this requires the py.typed file.
-          export PYTHONPATH='${./test-driver}'
           mypy  --no-implicit-optional \
                 --pretty \
                 --no-color-output \
                 testScriptWithTypes
-          unset PYTHONPATH
         ''}
 
         echo -n "$testScript" >> $out/test-script
diff --git a/nixos/modules/config/appstream.nix b/nixos/modules/config/appstream.nix
index a72215c2f5613..5b48f6e1705d9 100644
--- a/nixos/modules/config/appstream.nix
+++ b/nixos/modules/config/appstream.nix
@@ -6,9 +6,9 @@ with lib;
     appstream.enable = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = lib.mdDoc ''
         Whether to install files to support the
-        <link xlink:href="https://www.freedesktop.org/software/appstream/docs/index.html">AppStream metadata specification</link>.
+        [AppStream metadata specification](https://www.freedesktop.org/software/appstream/docs/index.html).
       '';
     };
   };
diff --git a/nixos/modules/config/console.nix b/nixos/modules/config/console.nix
index b60fc55851daf..97e6405db91e1 100644
--- a/nixos/modules/config/console.nix
+++ b/nixos/modules/config/console.nix
@@ -46,9 +46,9 @@ in
       type = with types; either str path;
       default = "Lat2-Terminus16";
       example = "LatArCyrHeb-16";
-      description = ''
+      description = mdDoc ''
         The font used for the virtual consoles.  Leave empty to use
-        whatever the <command>setfont</command> program considers the
+        whatever the {command}`setfont` program considers the
         default font.
         Can be either a font name or a path to a PSF font file.
       '';
diff --git a/nixos/modules/config/debug-info.nix b/nixos/modules/config/debug-info.nix
index 2942ae5905d10..78de26fda4403 100644
--- a/nixos/modules/config/debug-info.nix
+++ b/nixos/modules/config/debug-info.nix
@@ -9,21 +9,20 @@ with lib;
     environment.enableDebugInfo = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = mdDoc ''
         Some NixOS packages provide debug symbols. However, these are
         not included in the system closure by default to save disk
         space. Enabling this option causes the debug symbols to appear
-        in <filename>/run/current-system/sw/lib/debug/.build-id</filename>,
-        where tools such as <command>gdb</command> can find them.
+        in {file}`/run/current-system/sw/lib/debug/.build-id`,
+        where tools such as {command}`gdb` can find them.
         If you need debug symbols for a package that doesn't
         provide them by default, you can enable them as follows:
-        <programlisting>
-        nixpkgs.config.packageOverrides = pkgs: {
-          hello = pkgs.hello.overrideAttrs (oldAttrs: {
-            separateDebugInfo = true;
-          });
-        };
-        </programlisting>
+
+            nixpkgs.config.packageOverrides = pkgs: {
+              hello = pkgs.hello.overrideAttrs (oldAttrs: {
+                separateDebugInfo = true;
+              });
+            };
       '';
     };
 
diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
index 1e68fef7ce74c..a10a8c6428a10 100644
--- a/nixos/modules/config/fonts/fontconfig.nix
+++ b/nixos/modules/config/fonts/fontconfig.nix
@@ -65,7 +65,7 @@ let
           ${fcBool cfg.hinting.autohint}
         </edit>
         <edit mode="append" name="hintstyle">
-          <const>hintslight</const>
+          <const>${cfg.hinting.style}</const>
         </edit>
         <edit mode="append" name="antialias">
           ${fcBool cfg.antialias}
@@ -226,7 +226,6 @@ in
     (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "useEmbeddedBitmaps" ] [ "fonts" "fontconfig" "useEmbeddedBitmaps" ])
     (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "forceAutohint" ] [ "fonts" "fontconfig" "forceAutohint" ])
     (mkRenamedOptionModule [ "fonts" "fontconfig" "ultimate" "renderMonoTTFAsBitmap" ] [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ])
-    (mkRemovedOptionModule [ "fonts" "fontconfig" "hinting" "style" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "forceAutohint" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "renderMonoTTFAsBitmap" ] "")
     (mkRemovedOptionModule [ "fonts" "fontconfig" "dpi" ] "Use display server-specific options")
@@ -349,6 +348,20 @@ in
               fonts, but better than unhinted fonts.
             '';
           };
+
+          style = mkOption {
+            type = types.enum [ "hintnone" "hintslight" "hintmedium" "hintfull" ];
+            default = "hintslight";
+            description = ''
+              Hintstyle is the amount of font reshaping done to line up
+              to the grid.
+
+              hintslight will make the font more fuzzy to line up to the grid
+              but will be better in retaining font shape, while hintfull will
+              be a crisp font that aligns well to the pixel grid but will lose
+              a greater amount of font shape.
+            '';
+          };
         };
 
         includeUserConf = mkOption {
diff --git a/nixos/modules/config/fonts/fontdir.nix b/nixos/modules/config/fonts/fontdir.nix
index 560918302ca66..30e0dfe2566a8 100644
--- a/nixos/modules/config/fonts/fontdir.nix
+++ b/nixos/modules/config/fonts/fontdir.nix
@@ -30,9 +30,9 @@ in
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to create a directory with links to all fonts in
-          <filename>/run/current-system/sw/share/X11/fonts</filename>.
+          {file}`/run/current-system/sw/share/X11/fonts`.
         '';
       };
 
@@ -40,9 +40,9 @@ in
         type = types.bool;
         default = config.programs.xwayland.enable;
         defaultText = literalExpression "config.programs.xwayland.enable";
-        description = ''
+        description = lib.mdDoc ''
           Whether to decompress fonts in
-          <filename>/run/current-system/sw/share/X11/fonts</filename>.
+          {file}`/run/current-system/sw/share/X11/fonts`.
         '';
       };
 
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 5b8d5b214496b..b78e678488530 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -53,7 +53,23 @@ with lib;
 
       supportedLocales = mkOption {
         type = types.listOf types.str;
-        default = ["all"];
+        default = unique
+          (builtins.map (l: (replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
+            [
+              "C.UTF-8"
+              "en_US.UTF-8"
+              config.i18n.defaultLocale
+            ] ++ (attrValues (filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
+          ));
+        defaultText = literalExpression ''
+          unique
+            (builtins.map (l: (replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") (
+              [
+                "C.UTF-8"
+                config.i18n.defaultLocale
+              ] ++ (attrValues (filterAttrs (n: v: n != "LANGUAGE") config.i18n.extraLocaleSettings))
+            ))
+        '';
         example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
         description = ''
           List of locales that the system should support.  The value
diff --git a/nixos/modules/config/locale.nix b/nixos/modules/config/locale.nix
index 6f05658818774..7716e121c7129 100644
--- a/nixos/modules/config/locale.nix
+++ b/nixos/modules/config/locale.nix
@@ -22,9 +22,8 @@ in
         default = null;
         type = timezone;
         example = "America/New_York";
-        description = ''
-          The time zone used when displaying times and dates. See <link
-          xlink:href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"/>
+        description = lib.mdDoc ''
+          The time zone used when displaying times and dates. See <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>
           for a comprehensive list of possible values for this setting.
 
           If null, the timezone will default to UTC and can be set imperatively
@@ -35,7 +34,7 @@ in
       hardwareClockInLocalTime = mkOption {
         default = false;
         type = types.bool;
-        description = "If set, keep the hardware clock in local time instead of UTC.";
+        description = lib.mdDoc "If set, keep the hardware clock in local time instead of UTC.";
       };
 
     };
@@ -44,18 +43,18 @@ in
 
       latitude = mkOption {
         type = types.float;
-        description = ''
+        description = lib.mdDoc ''
           Your current latitude, between
-          <literal>-90.0</literal> and <literal>90.0</literal>. Must be provided
+          `-90.0` and `90.0`. Must be provided
           along with longitude.
         '';
       };
 
       longitude = mkOption {
         type = types.float;
-        description = ''
+        description = lib.mdDoc ''
           Your current longitude, between
-          between <literal>-180.0</literal> and <literal>180.0</literal>. Must be
+          between `-180.0` and `180.0`. Must be
           provided along with latitude.
         '';
       };
@@ -63,9 +62,9 @@ in
       provider = mkOption {
         type = types.enum [ "manual" "geoclue2" ];
         default = "manual";
-        description = ''
+        description = lib.mdDoc ''
           The location provider to use for determining your location. If set to
-          <literal>manual</literal> you must also provide latitude/longitude.
+          `manual` you must also provide latitude/longitude.
         '';
       };
 
diff --git a/nixos/modules/config/nsswitch.nix b/nixos/modules/config/nsswitch.nix
index e494ff5f74d5a..b004072813bdf 100644
--- a/nixos/modules/config/nsswitch.nix
+++ b/nixos/modules/config/nsswitch.nix
@@ -13,10 +13,10 @@ with lib;
       type = types.listOf types.path;
       internal = true;
       default = [];
-      description = ''
+      description = lib.mdDoc ''
         Search path for NSS (Name Service Switch) modules.  This allows
         several DNS resolution methods to be specified via
-        <filename>/etc/nsswitch.conf</filename>.
+        {file}`/etc/nsswitch.conf`.
       '';
       apply = list:
         {
@@ -28,8 +28,8 @@ with lib;
     system.nssDatabases = {
       passwd = mkOption {
         type = types.listOf types.str;
-        description = ''
-          List of passwd entries to configure in <filename>/etc/nsswitch.conf</filename>.
+        description = lib.mdDoc ''
+          List of passwd entries to configure in {file}`/etc/nsswitch.conf`.
 
           Note that "files" is always prepended while "systemd" is appended if nscd is enabled.
 
@@ -40,8 +40,8 @@ with lib;
 
       group = mkOption {
         type = types.listOf types.str;
-        description = ''
-          List of group entries to configure in <filename>/etc/nsswitch.conf</filename>.
+        description = lib.mdDoc ''
+          List of group entries to configure in {file}`/etc/nsswitch.conf`.
 
           Note that "files" is always prepended while "systemd" is appended if nscd is enabled.
 
@@ -52,8 +52,8 @@ with lib;
 
       shadow = mkOption {
         type = types.listOf types.str;
-        description = ''
-          List of shadow entries to configure in <filename>/etc/nsswitch.conf</filename>.
+        description = lib.mdDoc ''
+          List of shadow entries to configure in {file}`/etc/nsswitch.conf`.
 
           Note that "files" is always prepended.
 
@@ -64,8 +64,8 @@ with lib;
 
       hosts = mkOption {
         type = types.listOf types.str;
-        description = ''
-          List of hosts entries to configure in <filename>/etc/nsswitch.conf</filename>.
+        description = lib.mdDoc ''
+          List of hosts entries to configure in {file}`/etc/nsswitch.conf`.
 
           Note that "files" is always prepended, and "dns" and "myhostname" are always appended.
 
@@ -76,8 +76,8 @@ with lib;
 
       services = mkOption {
         type = types.listOf types.str;
-        description = ''
-          List of services entries to configure in <filename>/etc/nsswitch.conf</filename>.
+        description = lib.mdDoc ''
+          List of services entries to configure in {file}`/etc/nsswitch.conf`.
 
           Note that "files" is always prepended.
 
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index 01555d28b73fb..aa3ca549f09a9 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -89,7 +89,7 @@ in {
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to enable the PulseAudio sound server.
         '';
       };
@@ -97,7 +97,7 @@ in {
       systemWide = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           If false, a PulseAudio server is launched automatically for
           each user that tries to use the sound system. The server runs
           with user privileges. If true, one system-wide PulseAudio
@@ -112,7 +112,7 @@ in {
       support32Bit = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to include the 32-bit pulseaudio libraries in the system or not.
           This is only useful on 64-bit systems and currently limited to x86_64-linux.
         '';
@@ -120,7 +120,7 @@ in {
 
       configFile = mkOption {
         type = types.nullOr types.path;
-        description = ''
+        description = lib.mdDoc ''
           The path to the default configuration options the PulseAudio server
           should use. By default, the "default.pa" configuration
           from the PulseAudio distribution is used.
@@ -130,8 +130,8 @@ in {
       extraConfig = mkOption {
         type = types.lines;
         default = "";
-        description = ''
-          Literal string to append to <literal>configFile</literal>
+        description = lib.mdDoc ''
+          Literal string to append to `configFile`
           and the config file generated by the pulseaudio module.
         '';
       };
@@ -139,7 +139,7 @@ in {
       extraClientConf = mkOption {
         type = types.lines;
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Extra configuration appended to pulse/client.conf file.
         '';
       };
@@ -151,10 +151,10 @@ in {
                   else pkgs.pulseaudio;
         defaultText = literalExpression "pkgs.pulseaudio";
         example = literalExpression "pkgs.pulseaudioFull";
-        description = ''
+        description = lib.mdDoc ''
           The PulseAudio derivation to use.  This can be used to enable
           features (such as JACK support, Bluetooth) via the
-          <literal>pulseaudioFull</literal> package.
+          `pulseaudioFull` package.
         '';
       };
 
@@ -162,7 +162,7 @@ in {
         type = types.listOf types.package;
         default = [];
         example = literalExpression "[ pkgs.pulseaudio-modules-bt ]";
-        description = ''
+        description = lib.mdDoc ''
           Extra pulseaudio modules to use. This is intended for out-of-tree
           pulseaudio modules like extra bluetooth codecs.
 
@@ -174,7 +174,7 @@ in {
         logLevel = mkOption {
           type = types.str;
           default = "notice";
-          description = ''
+          description = lib.mdDoc ''
             The log level that the system-wide pulseaudio daemon should use,
             if activated.
           '';
@@ -183,7 +183,7 @@ in {
         config = mkOption {
           type = types.attrsOf types.unspecified;
           default = {};
-          description = "Config of the pulse daemon. See <literal>man pulse-daemon.conf</literal>.";
+          description = lib.mdDoc "Config of the pulse daemon. See `man pulse-daemon.conf`.";
           example = literalExpression ''{ realtime-scheduling = "yes"; }'';
         };
       };
@@ -205,7 +205,7 @@ in {
             type = types.listOf types.str;
             default = [];
             example = literalExpression ''[ "127.0.0.1" "192.168.1.0/24" ]'';
-            description = ''
+            description = lib.mdDoc ''
               A list of IP subnets that are allowed to stream to the server.
             '';
           };
diff --git a/nixos/modules/config/qt5.nix b/nixos/modules/config/qt5.nix
index eabba9ad95f00..9e19774b582fb 100644
--- a/nixos/modules/config/qt5.nix
+++ b/nixos/modules/config/qt5.nix
@@ -8,14 +8,21 @@ let
 
   isQGnome = cfg.platformTheme == "gnome" && builtins.elem cfg.style ["adwaita" "adwaita-dark"];
   isQtStyle = cfg.platformTheme == "gtk2" && !(builtins.elem cfg.style ["adwaita" "adwaita-dark"]);
+  isQt5ct = cfg.platformTheme == "qt5ct";
+  isLxqt = cfg.platformTheme == "lxqt";
+  isKde = cfg.platformTheme == "kde";
 
   packages = if isQGnome then [ pkgs.qgnomeplatform pkgs.adwaita-qt ]
     else if isQtStyle then [ pkgs.libsForQt5.qtstyleplugins ]
+    else if isQt5ct then [ pkgs.libsForQt5.qt5ct ]
+    else if isLxqt then [ pkgs.lxqt.lxqt-qtplugin pkgs.lxqt.lxqt-config ]
+    else if isKde then [ pkgs.libsForQt5.plasma-integration pkgs.libsForQt5.systemsettings ]
     else throw "`qt5.platformTheme` ${cfg.platformTheme} and `qt5.style` ${cfg.style} are not compatible.";
 
 in
 
 {
+  meta.maintainers = [ maintainers.romildo ];
 
   options = {
     qt5 = {
@@ -26,29 +33,29 @@ in
         type = types.enum [
           "gtk2"
           "gnome"
+          "lxqt"
+          "qt5ct"
+          "kde"
         ];
         example = "gnome";
         relatedPackages = [
           "qgnomeplatform"
           ["libsForQt5" "qtstyleplugins"]
+          ["libsForQt5" "qt5ct"]
+          ["lxqt" "lxqt-qtplugin"]
+          ["libsForQt5" "plasma-integration"]
         ];
-        description = ''
-          Selects the platform theme to use for Qt5 applications.</para>
-          <para>The options are
-          <variablelist>
-            <varlistentry>
-              <term><literal>gtk</literal></term>
-              <listitem><para>Use GTK theme with
-                <link xlink:href="https://github.com/qt/qtstyleplugins">qtstyleplugins</link>
-              </para></listitem>
-            </varlistentry>
-            <varlistentry>
-              <term><literal>gnome</literal></term>
-              <listitem><para>Use GNOME theme with
-                <link xlink:href="https://github.com/FedoraQt/QGnomePlatform">qgnomeplatform</link>
-              </para></listitem>
-            </varlistentry>
-          </variablelist>
+        description = lib.mdDoc ''
+          Selects the platform theme to use for Qt5 applications.
+
+          The options are
+          - `gtk`: Use GTK theme with [qtstyleplugins](https://github.com/qt/qtstyleplugins)
+          - `gnome`: Use GNOME theme with [qgnomeplatform](https://github.com/FedoraQt/QGnomePlatform)
+          - `lxqt`: Use LXQt style set using the [lxqt-config-appearance](https://github.com/lxqt/lxqt-config)
+             application.
+          - `qt5ct`: Use Qt style set using the [qt5ct](https://sourceforge.net/projects/qt5ct/)
+             application.
+          - `kde`: Use Qt settings from Plasma.
         '';
       };
 
@@ -66,27 +73,14 @@ in
           "adwaita-qt"
           ["libsForQt5" "qtstyleplugins"]
         ];
-        description = ''
-          Selects the style to use for Qt5 applications.</para>
-          <para>The options are
-          <variablelist>
-            <varlistentry>
-              <term><literal>adwaita</literal></term>
-              <term><literal>adwaita-dark</literal></term>
-              <listitem><para>Use Adwaita Qt style with
-                <link xlink:href="https://github.com/FedoraQt/adwaita-qt">adwaita</link>
-              </para></listitem>
-            </varlistentry>
-            <varlistentry>
-              <term><literal>cleanlooks</literal></term>
-              <term><literal>gtk2</literal></term>
-              <term><literal>motif</literal></term>
-              <term><literal>plastique</literal></term>
-              <listitem><para>Use styles from
-                <link xlink:href="https://github.com/qt/qtstyleplugins">qtstyleplugins</link>
-              </para></listitem>
-            </varlistentry>
-          </variablelist>
+        description = lib.mdDoc ''
+          Selects the style to use for Qt5 applications.
+
+          The options are
+          - `adwaita`, `adwaita-dark`: Use Adwaita Qt style with
+            [adwaita](https://github.com/FedoraQt/adwaita-qt)
+          - `cleanlooks`, `gtk2`, `motif`, `plastique`: Use styles from
+            [qtstyleplugins](https://github.com/qt/qtstyleplugins)
         '';
       };
     };
@@ -96,7 +90,7 @@ in
 
     environment.variables.QT_QPA_PLATFORMTHEME = cfg.platformTheme;
 
-    environment.variables.QT_STYLE_OVERRIDE = cfg.style;
+    environment.variables.QT_STYLE_OVERRIDE = mkIf (! (isQt5ct || isLxqt || isKde)) cfg.style;
 
     environment.systemPackages = packages;
 
diff --git a/nixos/modules/config/resolvconf.nix b/nixos/modules/config/resolvconf.nix
index 4499481811fd8..3e14884bb2bff 100644
--- a/nixos/modules/config/resolvconf.nix
+++ b/nixos/modules/config/resolvconf.nix
@@ -50,7 +50,20 @@ in
         default = !(config.environment.etc ? "resolv.conf");
         defaultText = literalExpression ''!(config.environment.etc ? "resolv.conf")'';
         description = ''
-          DNS configuration is managed by resolvconf.
+          Whether DNS configuration is managed by resolvconf.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.openresolv;
+        defaultText = literalExpression "pkgs.openresolv";
+        description = ''
+          The package that provides the system-wide resolvconf command. Defaults to <literal>openresolv</literal>
+          if this module is enabled. Otherwise, can be used by other modules (for example <option>services.resolved</option>) to
+          provide a compatibility layer.
+
+          This option generally shouldn't be set by the user.
         '';
       };
 
@@ -119,10 +132,12 @@ in
             exit 1
           ''
         else configText;
+
+      environment.systemPackages = [ cfg.package ];
     }
 
     (mkIf cfg.enable {
-      environment.systemPackages = [ pkgs.openresolv ];
+      networking.resolvconf.package = pkgs.openresolv;
 
       systemd.services.resolvconf = {
         description = "resolvconf update";
@@ -134,7 +149,7 @@ in
 
         serviceConfig = {
           Type = "oneshot";
-          ExecStart = "${pkgs.openresolv}/bin/resolvconf -u";
+          ExecStart = "${cfg.package}/bin/resolvconf -u";
           RemainAfterExit = true;
         };
       };
diff --git a/nixos/modules/config/unix-odbc-drivers.nix b/nixos/modules/config/unix-odbc-drivers.nix
index 055c3b2364e6e..7bd3fa1600b09 100644
--- a/nixos/modules/config/unix-odbc-drivers.nix
+++ b/nixos/modules/config/unix-odbc-drivers.nix
@@ -20,10 +20,10 @@ in {
       type = types.listOf types.package;
       default = [];
       example = literalExpression "with pkgs.unixODBCDrivers; [ sqlite psql ]";
-      description = ''
+      description = lib.mdDoc ''
         Specifies Unix ODBC drivers to be registered in
-        <filename>/etc/odbcinst.ini</filename>.  You may also want to
-        add <literal>pkgs.unixODBC</literal> to the system path to get
+        {file}`/etc/odbcinst.ini`.  You may also want to
+        add `pkgs.unixODBC` to the system path to get
         a command line client to connect to ODBC databases.
       '';
     };
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index d3bdf218c3392..85dfb93656195 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -6,12 +6,6 @@ let
   ids = config.ids;
   cfg = config.users;
 
-  isPasswdCompatible = str: !(hasInfix ":" str || hasInfix "\n" str);
-  passwdEntry = type: lib.types.addCheck type isPasswdCompatible // {
-    name = "passwdEntry ${type.name}";
-    description = "${type.description}, not containing newlines or colons";
-  };
-
   # Check whether a password hash will allow login.
   allowsLogin = hash:
     hash == "" # login without password
@@ -60,7 +54,7 @@ let
     options = {
 
       name = mkOption {
-        type = passwdEntry types.str;
+        type = types.passwdEntry types.str;
         apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
         description = ''
           The name of the user account. If undefined, the name of the
@@ -69,7 +63,7 @@ let
       };
 
       description = mkOption {
-        type = passwdEntry types.str;
+        type = types.passwdEntry types.str;
         default = "";
         example = "Alice Q. User";
         description = ''
@@ -134,7 +128,7 @@ let
       };
 
       home = mkOption {
-        type = passwdEntry types.path;
+        type = types.passwdEntry types.path;
         default = "/var/empty";
         description = "The user's home directory.";
       };
@@ -169,7 +163,7 @@ let
       };
 
       shell = mkOption {
-        type = types.nullOr (types.either types.shellPackage (passwdEntry types.path));
+        type = types.nullOr (types.either types.shellPackage (types.passwdEntry types.path));
         default = pkgs.shadow;
         defaultText = literalExpression "pkgs.shadow";
         example = literalExpression "pkgs.bashInteractive";
@@ -349,7 +343,7 @@ let
     options = {
 
       name = mkOption {
-        type = passwdEntry types.str;
+        type = types.passwdEntry types.str;
         description = ''
           The name of the group. If undefined, the name of the attribute set
           will be used.
diff --git a/nixos/modules/config/xdg/autostart.nix b/nixos/modules/config/xdg/autostart.nix
index 40984cb5ec53f..a4fdbda911a28 100644
--- a/nixos/modules/config/xdg/autostart.nix
+++ b/nixos/modules/config/xdg/autostart.nix
@@ -10,9 +10,9 @@ with lib;
     xdg.autostart.enable = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = lib.mdDoc ''
         Whether to install files to support the
-        <link xlink:href="https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html">XDG Autostart specification</link>.
+        [XDG Autostart specification](https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html).
       '';
     };
   };
diff --git a/nixos/modules/config/xdg/icons.nix b/nixos/modules/config/xdg/icons.nix
index 1e91670cf03bf..8d44a431445bc 100644
--- a/nixos/modules/config/xdg/icons.nix
+++ b/nixos/modules/config/xdg/icons.nix
@@ -10,9 +10,9 @@ with lib;
     xdg.icons.enable = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = lib.mdDoc ''
         Whether to install files to support the
-        <link xlink:href="https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html">XDG Icon Theme specification</link>.
+        [XDG Icon Theme specification](https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html).
       '';
     };
   };
diff --git a/nixos/modules/config/xdg/menus.nix b/nixos/modules/config/xdg/menus.nix
index 6735a7a5c430f..b8f829e81547b 100644
--- a/nixos/modules/config/xdg/menus.nix
+++ b/nixos/modules/config/xdg/menus.nix
@@ -10,9 +10,9 @@ with lib;
     xdg.menus.enable = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = lib.mdDoc ''
         Whether to install files to support the
-        <link xlink:href="https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html">XDG Desktop Menu specification</link>.
+        [XDG Desktop Menu specification](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html).
       '';
     };
   };
diff --git a/nixos/modules/config/xdg/portal.nix b/nixos/modules/config/xdg/portal.nix
index 088f2af59e22a..1e6ddd7c4a26b 100644
--- a/nixos/modules/config/xdg/portal.nix
+++ b/nixos/modules/config/xdg/portal.nix
@@ -1,10 +1,30 @@
 { config, pkgs, lib, ... }:
 
-with lib;
+let
+  inherit (lib)
+    mkEnableOption
+    mkIf
+    mkOption
+    mkRenamedOptionModule
+    teams
+    types;
+in
 
 {
   imports = [
     (mkRenamedOptionModule [ "services" "flatpak" "extraPortals" ] [ "xdg" "portal" "extraPortals" ])
+
+    ({ config, lib, options, ... }:
+      let
+        from = [ "xdg" "portal" "gtkUsePortal" ];
+        fromOpt = lib.getAttrFromPath from options;
+      in
+      {
+        warnings = lib.mkIf config.xdg.portal.gtkUsePortal [
+          "The option `${lib.showOption from}' defined in ${lib.showFiles fromOpt.files} has been deprecated. Setting the variable globally with `environment.sessionVariables' NixOS option can have unforseen side-effects."
+        ];
+      }
+    )
   ];
 
   meta = {
@@ -32,11 +52,12 @@ with lib;
 
     gtkUsePortal = mkOption {
       type = types.bool;
+      visible = false;
       default = false;
       description = ''
         Sets environment variable <literal>GTK_USE_PORTAL</literal> to <literal>1</literal>.
-        This is needed for packages ran outside Flatpak to respect and use XDG Desktop Portals.
-        For example, you'd need to set this for non-flatpak Firefox to use native filechoosers.
+        This will force GTK-based programs ran outside Flatpak to respect and use XDG Desktop Portals
+        for features like file chooser but it is an unsupported hack that can easily break things.
         Defaults to <literal>false</literal> to respect its opt-in nature.
       '';
     };
diff --git a/nixos/modules/config/xdg/portals/lxqt.nix b/nixos/modules/config/xdg/portals/lxqt.nix
new file mode 100644
index 0000000000000..e85e2cc326933
--- /dev/null
+++ b/nixos/modules/config/xdg/portals/lxqt.nix
@@ -0,0 +1,49 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.xdg.portal.lxqt;
+
+in
+{
+  meta = {
+    maintainers = teams.lxqt.members;
+  };
+
+  options.xdg.portal.lxqt = {
+    enable = mkEnableOption ''
+      the desktop portal for the LXQt desktop environment.
+
+      This will add the <package>lxqt.xdg-desktop-portal-lxqt</package>
+      package (with the extra Qt styles) into the
+      <option>xdg.portal.extraPortals</option> option
+    '';
+
+    styles = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression ''[
+        pkgs.libsForQt5.qtstyleplugin-kvantum
+        pkgs.breeze-qt5
+        pkgs.qtcurve
+      ];
+      '';
+      description = ''
+        Extra Qt styles that will be available to the
+        <package>lxqt.xdg-desktop-portal-lxqt</package>.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    xdg.portal = {
+      enable = true;
+      extraPortals = [
+        (pkgs.lxqt.xdg-desktop-portal-lxqt.override { extraQtStyles = cfg.styles; })
+      ];
+    };
+
+    environment.systemPackages = cfg.styles;
+  };
+}
diff --git a/nixos/modules/config/xdg/sounds.nix b/nixos/modules/config/xdg/sounds.nix
index 0b94f550929b4..713d68131fc0b 100644
--- a/nixos/modules/config/xdg/sounds.nix
+++ b/nixos/modules/config/xdg/sounds.nix
@@ -10,9 +10,9 @@ with lib;
     xdg.sounds.enable = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = lib.mdDoc ''
         Whether to install files to support the
-        <link xlink:href="https://www.freedesktop.org/wiki/Specifications/sound-theme-spec/">XDG Sound Theme specification</link>.
+        [XDG Sound Theme specification](https://www.freedesktop.org/wiki/Specifications/sound-theme-spec/).
       '';
     };
   };
diff --git a/nixos/modules/hardware/ckb-next.nix b/nixos/modules/hardware/ckb-next.nix
index b2bbd77c9d7fa..751ed663aab57 100644
--- a/nixos/modules/hardware/ckb-next.nix
+++ b/nixos/modules/hardware/ckb-next.nix
@@ -48,6 +48,6 @@ in
     };
 
     meta = {
-      maintainers = with lib.maintainers; [ kierdavis ];
+      maintainers = with lib.maintainers; [ superherointj ];
     };
   }
diff --git a/nixos/modules/hardware/cpu/intel-sgx.nix b/nixos/modules/hardware/cpu/intel-sgx.nix
index 1355ee753f0d3..76664133a08ff 100644
--- a/nixos/modules/hardware/cpu/intel-sgx.nix
+++ b/nixos/modules/hardware/cpu/intel-sgx.nix
@@ -6,13 +6,13 @@ let
 in
 {
   options.hardware.cpu.intel.sgx.enableDcapCompat = mkOption {
-    description = ''
+    description = lib.mdDoc ''
       Whether to enable backward compatibility for SGX software build for the
       out-of-tree Intel SGX DCAP driver.
 
-      Creates symbolic links for the SGX devices <literal>/dev/sgx_enclave</literal>
-      and <literal>/dev/sgx_provision</literal> to make them available as
-      <literal>/dev/sgx/enclave</literal>  and <literal>/dev/sgx/provision</literal>,
+      Creates symbolic links for the SGX devices `/dev/sgx_enclave`
+      and `/dev/sgx_provision` to make them available as
+      `/dev/sgx/enclave`  and `/dev/sgx/provision`,
       respectively.
     '';
     type = types.bool;
@@ -22,17 +22,17 @@ in
   options.hardware.cpu.intel.sgx.provision = {
     enable = mkEnableOption "access to the Intel SGX provisioning device";
     user = mkOption {
-      description = "Owner to assign to the SGX provisioning device.";
+      description = lib.mdDoc "Owner to assign to the SGX provisioning device.";
       type = types.str;
       default = "root";
     };
     group = mkOption {
-      description = "Group to assign to the SGX provisioning device.";
+      description = lib.mdDoc "Group to assign to the SGX provisioning device.";
       type = types.str;
       default = defaultPrvGroup;
     };
     mode = mkOption {
-      description = "Mode to set for the SGX provisioning device.";
+      description = lib.mdDoc "Mode to set for the SGX provisioning device.";
       type = types.str;
       default = "0660";
     };
diff --git a/nixos/modules/hardware/device-tree.nix b/nixos/modules/hardware/device-tree.nix
index be67116ad507d..5a8a8e27bee1b 100644
--- a/nixos/modules/hardware/device-tree.nix
+++ b/nixos/modules/hardware/device-tree.nix
@@ -36,14 +36,11 @@ let
           /plugin/;
           / {
                   compatible = "raspberrypi";
-                  fragment@0 {
-                          target-path = "/soc";
-                          __overlay__ {
-                                  pps {
-                                          compatible = "pps-gpio";
-                                          status = "okay";
-                                  };
-                          };
+          };
+          &{/soc} {
+                  pps {
+                          compatible = "pps-gpio";
+                          status = "okay";
                   };
           };
         '';
@@ -88,13 +85,14 @@ let
 
   # Compile single Device Tree overlay source
   # file (.dts) into its compiled variant (.dtbo)
-  compileDTS = name: f: pkgs.callPackage({ dtc }: pkgs.stdenv.mkDerivation {
+  compileDTS = name: f: pkgs.callPackage({ stdenv, dtc }: stdenv.mkDerivation {
     name = "${name}-dtbo";
 
     nativeBuildInputs = [ dtc ];
 
     buildCommand = ''
-      dtc -I dts ${f} -O dtb -@ -o $out
+      $CC -E -nostdinc -I${getDev cfg.kernelPackage}/lib/modules/${cfg.kernelPackage.modDirVersion}/source/scripts/dtc/include-prefixes -undef -D__DTS__ -x assembler-with-cpp ${f} | \
+        dtc -I dts -O dtb -@ -o $out
     '';
   }) {};
 
diff --git a/nixos/modules/hardware/ksm.nix b/nixos/modules/hardware/ksm.nix
index 829c3532c4597..ba7a1c12169f2 100644
--- a/nixos/modules/hardware/ksm.nix
+++ b/nixos/modules/hardware/ksm.nix
@@ -15,9 +15,9 @@ in {
     sleep = mkOption {
       type = types.nullOr types.int;
       default = null;
-      description = ''
+      description = lib.mdDoc ''
         How many milliseconds ksmd should sleep between scans.
-        Setting it to <literal>null</literal> uses the kernel's default time.
+        Setting it to `null` uses the kernel's default time.
       '';
     };
   };
diff --git a/nixos/modules/hardware/raid/hpsa.nix b/nixos/modules/hardware/raid/hpsa.nix
index fa6f0b8fc84a3..120348a74bfbe 100644
--- a/nixos/modules/hardware/raid/hpsa.nix
+++ b/nixos/modules/hardware/raid/hpsa.nix
@@ -40,7 +40,7 @@ let
       homepage = "https://downloads.linux.hpe.com/SDR/downloads/MCP/Ubuntu/pool/non-free/";
       license = licenses.unfreeRedistributable;
       platforms = [ "x86_64-linux" ];
-      maintainers = with maintainers; [ volth ];
+      maintainers = with maintainers; [ ];
     };
   };
 in {
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index a9b04bcc85959..f0f7b8e449a27 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -24,7 +24,7 @@ let
   primeEnabled = syncCfg.enable || offloadCfg.enable;
   nvidiaPersistencedEnabled =  cfg.nvidiaPersistenced;
   nvidiaSettings = cfg.nvidiaSettings;
-  busIDType = types.strMatching "([[:print:]]+\:[0-9]{1,3}\:[0-9]{1,2}\:[0-9])?";
+  busIDType = types.strMatching "([[:print:]]+[\:\@][0-9]{1,3}\:[0-9]{1,2}\:[0-9])?";
 in
 
 {
@@ -183,6 +183,14 @@ in
       '';
       example = literalExpression "config.boot.kernelPackages.nvidiaPackages.legacy_340";
     };
+
+    hardware.nvidia.open = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Whether to use the open source kernel module
+      '';
+    };
   };
 
   config = let
@@ -231,6 +239,11 @@ in
         );
         message = "Required files for driver based power management don't exist.";
       }
+
+      {
+        assertion = cfg.open -> (cfg.package ? open && cfg.package ? firmware);
+        message = "This version of NVIDIA driver does not provide a corresponding opensource kernel driver";
+      }
     ];
 
     # If Optimus/PRIME is enabled, we:
@@ -364,7 +377,8 @@ in
       ++ optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
         "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
 
-    boot.extraModulePackages = [ nvidia_x11.bin ];
+    boot.extraModulePackages = if cfg.open then [ nvidia_x11.open ] else [ nvidia_x11.bin ];
+    hardware.firmware = lib.optional cfg.open nvidia_x11.firmware;
 
     # nvidia-uvm is required by CUDA applications.
     boot.kernelModules = [ "nvidia-uvm" ] ++
@@ -372,15 +386,15 @@ in
 
     # If requested enable modesetting via kernel parameter.
     boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
-      ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1";
+      ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1"
+      ++ optional cfg.open "nvidia.NVreg_OpenRmEnableUnsupportedGpus=1";
 
     services.udev.extraRules =
       ''
         # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
-        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c 195 255'"
-        KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c 195 254'"
-        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", PROGRAM="${pkgs.gnugrep}/bin/grep 'Device Minor:' /proc/driver/nvidia/gpus/%b/information", \
-          RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%c{3} c 195 %c{3}"
+        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $$(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
+        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'for i in $$(cat /proc/driver/nvidia/gpus/*/information | grep Minor | cut -d \  -f 4); do mknod -m 666 /dev/nvidia$${i} c $$(grep nvidia-frontend /proc/devices | cut -d \  -f 1) $${i}; done'"
+        KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $$(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
         KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
         KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 1'"
       '' + optionalString cfg.powerManagement.finegrained (
diff --git a/nixos/modules/i18n/input-method/fcitx.nix b/nixos/modules/i18n/input-method/fcitx.nix
index 7738581b893a2..043ec3d55c1f6 100644
--- a/nixos/modules/i18n/input-method/fcitx.nix
+++ b/nixos/modules/i18n/input-method/fcitx.nix
@@ -22,9 +22,9 @@ in
           let
             enginesDrv = filterAttrs (const isDerivation) pkgs.fcitx-engines;
             engines = concatStringsSep ", "
-              (map (name: "<literal>${name}</literal>") (attrNames enginesDrv));
+              (map (name: "`${name}`") (attrNames enginesDrv));
           in
-            "Enabled Fcitx engines. Available engines are: ${engines}.";
+            lib.mdDoc "Enabled Fcitx engines. Available engines are: ${engines}.";
       };
     };
 
diff --git a/nixos/modules/i18n/input-method/ibus.nix b/nixos/modules/i18n/input-method/ibus.nix
index 907f6451fce11..520db128acd9f 100644
--- a/nixos/modules/i18n/input-method/ibus.nix
+++ b/nixos/modules/i18n/input-method/ibus.nix
@@ -43,15 +43,15 @@ in
           let
             enginesDrv = filterAttrs (const isDerivation) pkgs.ibus-engines;
             engines = concatStringsSep ", "
-              (map (name: "<literal>${name}</literal>") (attrNames enginesDrv));
+              (map (name: "`${name}`") (attrNames enginesDrv));
           in
-            "Enabled IBus engines. Available engines are: ${engines}.";
+            lib.mdDoc "Enabled IBus engines. Available engines are: ${engines}.";
       };
       panel = mkOption {
         type = with types; nullOr path;
         default = null;
         example = literalExpression ''"''${pkgs.plasma5Packages.plasma-desktop}/lib/libexec/kimpanel-ibus-panel"'';
-        description = "Replace the IBus panel with another panel.";
+        description = lib.mdDoc "Replace the IBus panel with another panel.";
       };
     };
   };
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 95aeca1a928a6..d015e10c11d81 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
@@ -18,7 +18,8 @@
     extraGSettingsOverrides = ''
       [org.gnome.shell]
       welcome-dialog-last-shown-version='9999999999'
-
+      [org.gnome.desktop.session]
+      idle-delay=0
       [org.gnome.settings-daemon.plugins.power]
       sleep-inactive-ac-type='nothing'
       sleep-inactive-battery-type='nothing'
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix b/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
deleted file mode 100644
index 054c8c74a76b1..0000000000000
--- a/nixos/modules/installer/cd-dvd/system-tarball-fuloong2f.nix
+++ /dev/null
@@ -1,160 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  # A dummy /etc/nixos/configuration.nix in the booted CD that
-  # rebuilds the CD's configuration (and allows the configuration to
-  # be modified, of course, providing a true live CD).  Problem is
-  # that we don't really know how the CD was built - the Nix
-  # expression language doesn't allow us to query the expression being
-  # evaluated.  So we'll just hope for the best.
-  dummyConfiguration = pkgs.writeText "configuration.nix"
-    ''
-      { config, pkgs, ... }:
-
-      { # Add your own options below, e.g.:
-        #   services.openssh.enable = true;
-        nixpkgs.config.platform = pkgs.platforms.fuloong2f_n32;
-      }
-    '';
-
-
-  pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
-
-  # A clue for the kernel loading
-  kernelParams = pkgs.writeText "kernel-params.txt" ''
-    Kernel Parameters:
-      init=/boot/init ${toString config.boot.kernelParams}
-  '';
-
-  # System wide nixpkgs config
-  nixpkgsUserConfig = pkgs.writeText "config.nix" ''
-    pkgs:
-    {
-      platform = pkgs.platforms.fuloong2f_n32;
-    }
-  '';
-
-in
-
-{
-  imports = [ ./system-tarball.nix ];
-
-  # Disable some other stuff we don't need.
-  security.sudo.enable = false;
-
-  # Include only the en_US locale.  This saves 75 MiB or so compared to
-  # the full glibcLocales package.
-  i18n.supportedLocales = ["en_US.UTF-8/UTF-8" "en_US/ISO-8859-1"];
-
-  # Include some utilities that are useful for installing or repairing
-  # the system.
-  environment.systemPackages =
-    [ pkgs.w3m # needed for the manual anyway
-      pkgs.testdisk # useful for repairing boot problems
-      pkgs.ms-sys # for writing Microsoft boot sectors / MBRs
-      pkgs.parted
-      pkgs.ddrescue
-      pkgs.ccrypt
-      pkgs.cryptsetup # needed for dm-crypt volumes
-
-      # Some networking tools.
-      pkgs.sshfs-fuse
-      pkgs.socat
-      pkgs.screen
-      pkgs.wpa_supplicant # !!! should use the wpa module
-
-      # Hardware-related tools.
-      pkgs.sdparm
-      pkgs.hdparm
-      pkgs.dmraid
-
-      # Tools to create / manipulate filesystems.
-      pkgs.ntfsprogs # for resizing NTFS partitions
-      pkgs.btrfs-progs
-      pkgs.jfsutils
-
-      # Some compression/archiver tools.
-      pkgs.unzip
-      pkgs.zip
-      pkgs.xz
-      pkgs.dar # disk archiver
-
-      # Some editors.
-      pkgs.nvi
-      pkgs.bvi # binary editor
-      pkgs.joe
-    ];
-
-  # The initrd has to contain any module that might be necessary for
-  # mounting the CD/DVD.
-  boot.initrd.availableKernelModules =
-    [ "vfat" "reiserfs" ];
-
-  boot.kernelPackages = pkgs.linuxKernel.packages.linux_3_10;
-  boot.kernelParams = [ "console=tty1" ];
-
-  boot.postBootCommands =
-    ''
-      mkdir -p /mnt
-
-      cp ${dummyConfiguration} /etc/nixos/configuration.nix
-    '';
-
-  # Some more help text.
-  services.getty.helpLine =
-    ''
-
-      Log in as "root" with an empty password.  ${
-        if config.services.xserver.enable then
-          "Type `start xserver' to start\nthe graphical user interface."
-        else ""
-      }
-    '';
-
-  # Include the firmware for various wireless cards.
-  networking.enableRalinkFirmware = true;
-  networking.enableIntel2200BGFirmware = true;
-
-  # To speed up further installation of packages, include the complete stdenv
-  # in the Nix store of the tarball.
-  tarball.storeContents = pkgs2storeContents [ pkgs.stdenv ]
-    ++ [
-      {
-        object = config.system.build.bootStage2;
-        symlink = "/boot/init";
-      }
-      {
-        object = config.system.build.toplevel;
-        symlink = "/boot/system";
-      }
-    ];
-
-  tarball.contents = [
-    { source = kernelParams;
-      target = "/kernelparams.txt";
-    }
-    { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
-      target = "/boot/" + config.system.boot.loader.kernelFile;
-    }
-    { source = nixpkgsUserConfig;
-      target = "/root/.nixpkgs/config.nix";
-    }
-  ];
-
-  # Allow sshd to be started manually through "start sshd".  It should
-  # not be started by default on the installation CD because the
-  # default root password is empty.
-  services.openssh.enable = true;
-  systemd.services.openssh.wantedBy = lib.mkOverride 50 [];
-
-  boot.loader.grub.enable = false;
-  boot.loader.generationsDir.enable = false;
-  system.boot.loader.kernelFile = "vmlinux";
-
-  nixpkgs.config = {
-    platform = pkgs.platforms.fuloong2f_n32;
-  };
-}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt b/nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt
deleted file mode 100644
index 887bf60d0fbe4..0000000000000
--- a/nixos/modules/installer/cd-dvd/system-tarball-pc-readme.txt
+++ /dev/null
@@ -1,89 +0,0 @@
-Let all the files in the system tarball sit in a directory served by NFS (the
-NFS root) like this in exportfs:
-  /home/pcroot    192.168.1.0/24(rw,no_root_squash,no_all_squash)
-
-Run "exportfs -a" after editing /etc/exportfs, for the nfs server to be aware
-of the changes.
-
-Use a tftp server serving the root of boot/ (from the system tarball).
-
-In order to have PXE boot, use the boot/dhcpd.conf-example file for your dhcpd
-server, as it will point your PXE clients to pxelinux.0 from the tftp server.
-Adapt the configuration to your network.
-
-Adapt the pxelinux configuration (boot/pxelinux.cfg/default) to set the path to
-your nfrroot. If you use ip=dhcp in the kernel, the nfs server ip will be taken
-from dhcp and so you don't have to specify it.
-
-The linux in bzImage includes network drivers for some usual cards.
-
-
-QEMU Testing
----------------
-
-You can test qemu pxe boot without having a DHCP server adapted, but having
-nfsroot, like this:
-  qemu-system-x86_64 -tftp /home/pcroot/boot -net nic -net user,bootfile=pxelinux.0 -boot n
-
-I don't know how to use NFS through the qemu '-net user' though.
-
-
-QEMU Testing with NFS root and bridged network
--------------------------------------------------
-
-This allows testing with qemu as any other host in your LAN.
-
-Testing with the real dhcpd server requires setting up a bridge and having a
-tap device.
-  tunctl -t tap0
-  brctl addbr br0
-  brctl addif br0 eth0
-  brctl addif tap0 eth0
-  ifconfig eth0 0.0.0.0 up
-  ifconfig tap0 0.0.0.0 up
-  ifconfig br0 up # With your ip configuration
-
-Then you can run qemu:
-  qemu-system-x86_64 -boot n -net tap,ifname=tap0,script=no -net nic,model=e1000
-
-
-Using the system-tarball-pc in a chroot
---------------------------------------------------
-
-Installation:
-  mkdir nixos-chroot && cd nixos-chroot
-  tar xf your-system-tarball.tar.xz
-  mkdir sys dev proc tmp root var run
-  mount --bind /sys sys
-  mount --bind /dev dev
-  mount --bind /proc proc
-
-Activate the system: look for a directory in nix/store similar to:
-    "/nix/store/y0d1lcj9fppli0hl3x0m0ba5g1ndjv2j-nixos-feb97bx-53f008"
-Having found it, activate that nixos system *twice*:
-  chroot . /nix/store/SOMETHING-nixos-SOMETHING/activate
-  chroot . /nix/store/SOMETHING-nixos-SOMETHING/activate
-
-This runs a 'hostname' command. Restore your old hostname with:
-  hostname OLDHOSTNAME
-
-Copy your system resolv.conf to the /etc/resolv.conf inside the chroot:
-  cp /etc/resolv.conf etc
-
-Then you can get an interactive shell in the nixos chroot. '*' means
-to run inside the chroot interactive shell
-  chroot . /bin/sh
-*  source /etc/profile
-
-Populate the nix database: that should be done in the init script if you
-had booted this nixos. Run:
-*  `grep local-cmds run/current-system/init`
-
-Then you can proceed normally subscribing to a nixos channel:
-  nix-channel --add https://nixos.org/channels/nixos-unstable
-  nix-channel --update
-
-Testing:
-  nix-env -i hello
-  which hello
-  hello
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-pc.nix b/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
deleted file mode 100644
index 674fb6c8a33c5..0000000000000
--- a/nixos/modules/installer/cd-dvd/system-tarball-pc.nix
+++ /dev/null
@@ -1,163 +0,0 @@
-# This module contains the basic configuration for building a NixOS
-# tarball, that can directly boot, maybe using PXE or unpacking on a fs.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
-
-  # For PXE kernel loading
-  pxeconfig = pkgs.writeText "pxeconfig-default" ''
-    default menu.c32
-    prompt 0
-
-    label bootlocal
-      menu default
-      localboot 0
-      timeout 80
-      TOTALTIMEOUT 9000
-
-    label nixos
-      MENU LABEL ^NixOS using nfsroot
-      KERNEL bzImage
-      append ip=dhcp nfsroot=/home/pcroot init=${config.system.build.toplevel}/init rw
-
-    # I don't know how to make this boot with nfsroot (using the initrd)
-    label nixos_initrd
-      MENU LABEL NixOS booting the poor ^initrd.
-      KERNEL bzImage
-      append initrd=initrd ip=dhcp nfsroot=/home/pcroot init=${config.system.build.toplevel}/init rw
-
-    label memtest
-      MENU LABEL ^${pkgs.memtest86.name}
-      KERNEL memtest
-  '';
-
-  dhcpdExampleConfig = pkgs.writeText "dhcpd.conf-example" ''
-    # Example configuration for booting PXE.
-    allow booting;
-    allow bootp;
-
-    # Adapt this to your network configuration.
-    option domain-name "local";
-    option subnet-mask 255.255.255.0;
-    option broadcast-address 192.168.1.255;
-    option domain-name-servers 192.168.1.1;
-    option routers 192.168.1.1;
-
-    # PXE-specific configuration directives...
-    # Some BIOS don't accept slashes for paths inside the tftp servers,
-    # and will report Access Violation if they see slashes.
-    filename "pxelinux.0";
-    # For the TFTP and NFS root server. Set the IP of your server.
-    next-server 192.168.1.34;
-
-    subnet 192.168.1.0 netmask 255.255.255.0 {
-      range 192.168.1.50 192.168.1.55;
-    }
-  '';
-
-  readme = ./system-tarball-pc-readme.txt;
-
-in
-
-{
-  imports =
-    [ ./system-tarball.nix
-
-      # Profiles of this basic installation.
-      ../../profiles/all-hardware.nix
-      ../../profiles/base.nix
-      ../../profiles/installation-device.nix
-    ];
-
-  # To speed up further installation of packages, include the complete stdenv
-  # in the Nix store of the tarball.
-  tarball.storeContents = pkgs2storeContents [ pkgs.stdenv ];
-
-  tarball.contents =
-    [ { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
-        target = "/boot/" + config.system.boot.loader.kernelFile;
-      }
-      { source = "${pkgs.syslinux}/share/syslinux/pxelinux.0";
-        target = "/boot/pxelinux.0";
-      }
-      { source = "${pkgs.syslinux}/share/syslinux/menu.c32";
-        target = "/boot/menu.c32";
-      }
-      { source = pxeconfig;
-        target = "/boot/pxelinux.cfg/default";
-      }
-      { source = readme;
-        target = "/readme.txt";
-      }
-      { source = dhcpdExampleConfig;
-        target = "/boot/dhcpd.conf-example";
-      }
-      { source = "${pkgs.memtest86}/memtest.bin";
-        # We can't leave '.bin', because pxelinux interprets this specially,
-        # and it would not load the image fine.
-        # http://forum.canardpc.com/threads/46464-0104-when-launched-via-pxe
-        target = "/boot/memtest";
-      }
-    ];
-
-  # Allow sshd to be started manually through "start sshd".  It should
-  # not be started by default on the installation CD because the
-  # default root password is empty.
-  services.openssh.enable = true;
-  systemd.services.openssh.wantedBy = lib.mkOverride 50 [];
-
-  # To be able to use the systemTarball to catch troubles.
-  boot.crashDump = {
-    enable = true;
-    kernelPackages = pkgs.linuxKernel.packages.linux_3_4;
-  };
-
-  # No grub for the tarball.
-  boot.loader.grub.enable = false;
-
-  /* fake entry, just to have a happy stage-1. Users
-     may boot without having stage-1 though */
-  fileSystems.fake =
-    { mountPoint = "/";
-      device = "/dev/something";
-    };
-
-  nixpkgs.config = {
-    packageOverrides = p: {
-      linux_3_4 = p.linux_3_4.override {
-        extraConfig = ''
-          # Enable drivers in kernel for most NICs.
-          E1000 y
-          # E1000E y
-          # ATH5K y
-          8139TOO y
-          NE2K_PCI y
-          ATL1 y
-          ATL1E y
-          ATL1C y
-          VORTEX y
-          VIA_RHINE y
-          R8169 y
-
-          # Enable nfs root boot
-          UNIX y # http://www.linux-mips.org/archives/linux-mips/2006-11/msg00113.html
-          IP_PNP y
-          IP_PNP_DHCP y
-          FSCACHE y
-          NFS_FS y
-          NFS_FSCACHE y
-          ROOT_NFS y
-
-          # Enable devtmpfs
-          DEVTMPFS y
-          DEVTMPFS_MOUNT y
-        '';
-      };
-    };
-  };
-}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
deleted file mode 100644
index 329bd329dc15f..0000000000000
--- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
+++ /dev/null
@@ -1,172 +0,0 @@
-# This module contains the basic configuration for building a NixOS
-# tarball for the sheevaplug.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  # A dummy /etc/nixos/configuration.nix in the booted CD that
-  # rebuilds the CD's configuration (and allows the configuration to
-  # be modified, of course, providing a true live CD).  Problem is
-  # that we don't really know how the CD was built - the Nix
-  # expression language doesn't allow us to query the expression being
-  # evaluated.  So we'll just hope for the best.
-  dummyConfiguration = pkgs.writeText "configuration.nix"
-    ''
-      { config, pkgs, ... }:
-
-      {
-        # Add your own options below and run "nixos-rebuild switch".
-        # E.g.,
-        #   services.openssh.enable = true;
-      }
-    '';
-
-
-  pkgs2storeContents = l : map (x: { object = x; symlink = "none"; }) l;
-
-  # A clue for the kernel loading
-  kernelParams = pkgs.writeText "kernel-params.txt" ''
-    Kernel Parameters:
-      init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
-  '';
-
-
-in
-
-{
-  imports = [ ./system-tarball.nix ];
-
-  # Disable some other stuff we don't need.
-  security.sudo.enable = false;
-
-  # Include only the en_US locale.  This saves 75 MiB or so compared to
-  # the full glibcLocales package.
-  i18n.supportedLocales = ["en_US.UTF-8/UTF-8" "en_US/ISO-8859-1"];
-
-  # Include some utilities that are useful for installing or repairing
-  # the system.
-  environment.systemPackages =
-    [ pkgs.w3m # needed for the manual anyway
-      pkgs.ddrescue
-      pkgs.ccrypt
-      pkgs.cryptsetup # needed for dm-crypt volumes
-
-      # Some networking tools.
-      pkgs.sshfs-fuse
-      pkgs.socat
-      pkgs.screen
-      pkgs.wpa_supplicant # !!! should use the wpa module
-
-      # Hardware-related tools.
-      pkgs.sdparm
-      pkgs.hdparm
-      pkgs.dmraid
-
-      # Tools to create / manipulate filesystems.
-      pkgs.btrfs-progs
-
-      # Some compression/archiver tools.
-      pkgs.unzip
-      pkgs.zip
-      pkgs.xz
-      pkgs.dar # disk archiver
-
-      # Some editors.
-      pkgs.nvi
-      pkgs.bvi # binary editor
-      pkgs.joe
-    ];
-
-  boot.loader.grub.enable = false;
-  boot.loader.generationsDir.enable = false;
-  system.boot.loader.kernelFile = "uImage";
-
-  boot.initrd.availableKernelModules =
-    [ "mvsdio" "reiserfs" "ext3" "ums-cypress" "rtc_mv" "ext4" ];
-
-  boot.postBootCommands = lib.mkIf (!boot.initrd.systemd.enable)
-    ''
-      mkdir -p /mnt
-
-      cp ${dummyConfiguration} /etc/nixos/configuration.nix
-    '';
-
-  boot.initrd.extraUtilsCommands = lib.mkIf (!boot.initrd.systemd.enable)
-    ''
-      copy_bin_and_libs ${pkgs.util-linux}/sbin/hwclock
-    '';
-
-  boot.initrd.postDeviceCommands = lib.mkIf (!boot.initrd.systemd.enable)
-    ''
-      hwclock -s
-    '';
-
-  boot.kernelParams =
-    [
-      "selinux=0"
-      "console=tty1"
-      # "console=ttyS0,115200n8"  # serial console
-    ];
-
-  boot.kernelPackages = pkgs.linuxKernel.packages.linux_3_4;
-
-  boot.supportedFilesystems = [ "reiserfs" ];
-
-  /* fake entry, just to have a happy stage-1. Users
-     may boot without having stage-1 though */
-  fileSystems.fake =
-    { mountPoint = "/";
-      device = "/dev/something";
-    };
-
-  services.getty = {
-    # Some more help text.
-    helpLine = ''
-      Log in as "root" with an empty password.  ${
-        if config.services.xserver.enable then
-          "Type `start xserver' to start\nthe graphical user interface."
-        else ""
-      }
-    '';
-  };
-
-  # Setting vesa, we don't get the nvidia driver, which can't work in arm.
-  services.xserver.videoDrivers = [ "vesa" ];
-
-  documentation.nixos.enable = false;
-
-  # Include the firmware for various wireless cards.
-  networking.enableRalinkFirmware = true;
-  networking.enableIntel2200BGFirmware = true;
-
-  # To speed up further installation of packages, include the complete stdenv
-  # in the Nix store of the tarball.
-  tarball.storeContents = pkgs2storeContents [ pkgs.stdenv ];
-  tarball.contents = [
-    { source = kernelParams;
-      target = "/kernelparams.txt";
-    }
-    { source = config.boot.kernelPackages.kernel + "/" + config.system.boot.loader.kernelFile;
-      target = "/boot/" + config.system.boot.loader.kernelFile;
-    }
-    { source = pkgs.ubootSheevaplug;
-      target = "/boot/uboot";
-    }
-  ];
-
-  # Allow sshd to be started manually through "start sshd".  It should
-  # not be started by default on the installation CD because the
-  # default root password is empty.
-  services.openssh.enable = true;
-  systemd.services.openssh.wantedBy = lib.mkOverride 50 [];
-
-  # cpufrequtils fails to build on non-pc
-  powerManagement.enable = false;
-
-  nixpkgs.config = {
-    platform = pkgs.platforms.sheevaplug;
-  };
-}
diff --git a/nixos/modules/installer/cd-dvd/system-tarball.nix b/nixos/modules/installer/cd-dvd/system-tarball.nix
deleted file mode 100644
index 362c555cc53e8..0000000000000
--- a/nixos/modules/installer/cd-dvd/system-tarball.nix
+++ /dev/null
@@ -1,93 +0,0 @@
-# This module creates a bootable ISO image containing the given NixOS
-# configuration.  The derivation for the ISO image will be placed in
-# config.system.build.tarball.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  versionFile = pkgs.writeText "nixos-label" config.system.nixos.label;
-
-in
-
-{
-  options = {
-    tarball.contents = mkOption {
-      example = literalExpression ''
-        [ { source = pkgs.memtest86 + "/memtest.bin";
-            target = "boot/memtest.bin";
-          }
-        ]
-      '';
-      description = ''
-        This option lists files to be copied to fixed locations in the
-        generated ISO image.
-      '';
-    };
-
-    tarball.storeContents = mkOption {
-      example = literalExpression "[ pkgs.stdenv ]";
-      description = ''
-        This option lists additional derivations to be included in the
-        Nix store in the generated ISO image.
-      '';
-    };
-
-  };
-
-  config = {
-
-    # In stage 1 of the boot, mount the CD/DVD as the root FS by label
-    # so that we don't need to know its device.
-    fileSystems = { };
-
-    # boot.initrd.availableKernelModules = [ "mvsdio" "reiserfs" "ext3" "ext4" ];
-
-    # boot.initrd.kernelModules = [ "rtc_mv" ];
-
-    # Closures to be copied to the Nix store on the CD, namely the init
-    # script and the top-level system configuration directory.
-    tarball.storeContents =
-      [ { object = config.system.build.toplevel;
-          symlink = "/run/current-system";
-        }
-      ];
-
-    # Individual files to be included on the CD, outside of the Nix
-    # store on the CD.
-    tarball.contents =
-      [ { source = config.system.build.initialRamdisk + "/" + config.system.boot.loader.initrdFile;
-          target = "/boot/" + config.system.boot.loader.initrdFile;
-        }
-        { source = versionFile;
-          target = "/nixos-version.txt";
-        }
-      ];
-
-    # Create the tarball
-    system.build.tarball = import ../../../lib/make-system-tarball.nix {
-      inherit (pkgs) stdenv closureInfo pixz;
-
-      inherit (config.tarball) contents storeContents;
-    };
-
-    boot.postBootCommands =
-      ''
-        # After booting, register the contents of the Nix store on the
-        # CD in the Nix database in the tmpfs.
-        if [ -f /nix-path-registration ]; then
-          ${config.nix.package.out}/bin/nix-store --load-db < /nix-path-registration &&
-          rm /nix-path-registration
-        fi
-
-        # nixos-rebuild also requires a "system" profile and an
-        # /etc/NIXOS tag.
-        touch /etc/NIXOS
-        ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system
-      '';
-
-  };
-
-}
diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix
index 3127bdc436f92..fed6a7c372879 100644
--- a/nixos/modules/installer/netboot/netboot.nix
+++ b/nixos/modules/installer/netboot/netboot.nix
@@ -81,7 +81,7 @@ with lib;
 
 
     # Create the initrd
-    system.build.netbootRamdisk = pkgs.makeInitrd {
+    system.build.netbootRamdisk = pkgs.makeInitrdNG {
       inherit (config.boot.initrd) compressor;
       prepend = [ "${config.system.build.initialRamdisk}/initrd" ];
 
diff --git a/nixos/modules/installer/sd-card/sd-image.nix b/nixos/modules/installer/sd-card/sd-image.nix
index 7560c682517aa..c0335ea487592 100644
--- a/nixos/modules/installer/sd-card/sd-image.nix
+++ b/nixos/modules/installer/sd-card/sd-image.nix
@@ -18,7 +18,7 @@ with lib;
 let
   rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({
     inherit (config.sdImage) storePaths;
-    compressImage = true;
+    compressImage = config.sdImage.compressImage;
     populateImageCommands = config.sdImage.populateRootCommands;
     volumeLabel = "NIXOS_SD";
   } // optionalAttrs (config.sdImage.rootPartitionUUID != null) {
@@ -174,7 +174,8 @@ in
     mtools, libfaketime, util-linux, zstd }: stdenv.mkDerivation {
       name = config.sdImage.imageName;
 
-      nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime util-linux zstd ];
+      nativeBuildInputs = [ dosfstools e2fsprogs libfaketime mtools util-linux ]
+      ++ lib.optional config.sdImage.compressImage zstd;
 
       inherit (config.sdImage) imageName compressImage;
 
@@ -189,14 +190,18 @@ in
           echo "file sd-image $img" >> $out/nix-support/hydra-build-products
         fi
 
+        root_fs=${rootfsImage}
+        ${lib.optionalString config.sdImage.compressImage ''
+        root_fs=./root-fs.img
         echo "Decompressing rootfs image"
-        zstd -d --no-progress "${rootfsImage}" -o ./root-fs.img
+        zstd -d --no-progress "${rootfsImage}" -o $root_fs
+        ''}
 
         # Gap in front of the first partition, in MiB
         gap=${toString config.sdImage.firmwarePartitionOffset}
 
         # Create the image file sized to fit /boot/firmware and /, plus slack for the gap.
-        rootSizeBlocks=$(du -B 512 --apparent-size ./root-fs.img | awk '{ print $1 }')
+        rootSizeBlocks=$(du -B 512 --apparent-size $root_fs | awk '{ print $1 }')
         firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512))
         imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024))
         truncate -s $imageSize $img
@@ -214,7 +219,7 @@ in
 
         # Copy the rootfs into the SD image
         eval $(partx $img -o START,SECTORS --nr 2 --pairs)
-        dd conv=notrunc if=./root-fs.img of=$img seek=$START count=$SECTORS
+        dd conv=notrunc if=$root_fs of=$img seek=$START count=$SECTORS
 
         # Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img
         eval $(partx $img -o START,SECTORS --nr 1 --pairs)
diff --git a/nixos/modules/installer/tools/get-version-suffix b/nixos/modules/installer/tools/get-version-suffix
index b8972cd57d22b..8d72905cdcb47 100644
--- a/nixos/modules/installer/tools/get-version-suffix
+++ b/nixos/modules/installer/tools/get-version-suffix
@@ -1,14 +1,15 @@
 getVersion() {
     local dir="$1"
     rev=
-    if [ -e "$dir/.git" ]; then
+    gitDir="$dir/.git"
+    if [ -e "$gitDir" ]; then
         if [ -z "$(type -P git)" ]; then
             echo "warning: Git not found; cannot figure out revision of $dir" >&2
             return
         fi
         cd "$dir"
-        rev=$(git rev-parse --short HEAD)
-        if git describe --always --dirty | grep -q dirty; then
+        rev=$(git --git-dir="$gitDir" rev-parse --short HEAD)
+        if git --git-dir="$gitDir" describe --always --dirty | grep -q dirty; then
             rev+=M
         fi
     fi
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index bd09724ebfa29..0035ceca6fc9c 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/6g4fla3vkcxihph282a0v3cd10709y7c-nix-2.9.1";
-  i686-linux = "/nix/store/j143221z44469zx21f5m9a47x7y1jpr5-nix-2.9.1";
-  aarch64-linux = "/nix/store/c4z3vy1sgm49la8bvmdrrpssgk4iw2nk-nix-2.9.1";
-  x86_64-darwin = "/nix/store/cqdwb7khf6zg94bz7lnvfjqx6z775qaw-nix-2.9.1";
-  aarch64-darwin = "/nix/store/1brkxcs287n1px2i4fq39l7h51hjv0f8-nix-2.9.1";
+  x86_64-linux = "/nix/store/3af6g226v4hsv6x7xzh23d6wqyq0nzjp-nix-2.10.3";
+  i686-linux = "/nix/store/43xxh2jip6rpdhylc5z9a5fxx54dw206-nix-2.10.3";
+  aarch64-linux = "/nix/store/6qw3r57nra08ars8j8zyj3fl8lz4cvnd-nix-2.10.3";
+  x86_64-darwin = "/nix/store/3b7qrm0qjw57fmznrsvm0ai568i89hc2-nix-2.10.3";
+  aarch64-darwin = "/nix/store/gp7k17iy1n7hgf97qwnxw28c6v9nhb1i-nix-2.10.3";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index b74ec838df42f..212b2b3cd23a2 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -84,6 +84,15 @@ sub debug {
 }
 
 
+# nixpkgs.system
+my ($status, @systemLines) = runCommand("@nixInstantiate@ --impure --eval --expr builtins.currentSystem");
+if ($status != 0 || join("", @systemLines) =~ /error/) {
+    die "Failed to retrieve current system type from nix.\n";
+}
+chomp(my $system = @systemLines[0]);
+push @attrs, "nixpkgs.hostPlatform = lib.mkDefault $system;";
+
+
 my $cpuinfo = read_file "/proc/cpuinfo";
 
 
@@ -291,6 +300,12 @@ if ($virt eq "oracle") {
     push @attrs, "virtualisation.virtualbox.guest.enable = true;"
 }
 
+# Check if we're a Parallels guest. If so, enable the guest additions.
+# It is blocked by https://github.com/systemd/systemd/pull/23859
+if ($virt eq "parallels") {
+    push @attrs, "hardware.parallels.enable = true;";
+    push @attrs, "nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ \"prl-tools\" ];";
+}
 
 # Likewise for QEMU.
 if ($virt eq "qemu" || $virt eq "kvm" || $virt eq "bochs") {
diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix
index 04be272742ce1..4490ad84e1447 100644
--- a/nixos/modules/installer/tools/tools.nix
+++ b/nixos/modules/installer/tools/tools.nix
@@ -34,6 +34,7 @@ let
     name = "nixos-generate-config";
     src = ./nixos-generate-config.pl;
     perl = "${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl";
+    nixInstantiate = "${pkgs.nix}/bin/nix-instantiate";
     detectvirt = "${config.systemd.package}/bin/systemd-detect-virt";
     btrfs = "${pkgs.btrfs-progs}/bin/btrfs";
     inherit (config.system.nixos-generate-config) configuration desktopConfiguration;
@@ -74,15 +75,15 @@ in
     configuration = mkOption {
       internal = true;
       type = types.str;
-      description = ''
-        The NixOS module that <literal>nixos-generate-config</literal>
-        saves to <literal>/etc/nixos/configuration.nix</literal>.
+      description = lib.mdDoc ''
+        The NixOS module that `nixos-generate-config`
+        saves to `/etc/nixos/configuration.nix`.
 
         This is an internal option. No backward compatibility is guaranteed.
         Use at your own risk!
 
         Note that this string gets spliced into a Perl script. The perl
-        variable <literal>$bootLoaderConfig</literal> can be used to
+        variable `$bootLoaderConfig` can be used to
         splice in the boot loader configuration.
       '';
     };
@@ -91,15 +92,15 @@ in
       internal = true;
       type = types.listOf types.lines;
       default = [];
-      description = ''
-        Text to preseed the desktop configuration that <literal>nixos-generate-config</literal>
-        saves to <literal>/etc/nixos/configuration.nix</literal>.
+      description = lib.mdDoc ''
+        Text to preseed the desktop configuration that `nixos-generate-config`
+        saves to `/etc/nixos/configuration.nix`.
 
         This is an internal option. No backward compatibility is guaranteed.
         Use at your own risk!
 
         Note that this string gets spliced into a Perl script. The perl
-        variable <literal>$bootLoaderConfig</literal> can be used to
+        variable `$bootLoaderConfig` can be used to
         splice in the boot loader configuration.
       '';
     };
@@ -109,7 +110,7 @@ in
     internal = true;
     type = types.bool;
     default = false;
-    description = ''
+    description = lib.mdDoc ''
       Disable nixos-rebuild, nixos-generate-config, nixos-installer
       and other NixOS tools. This is useful to shrink embedded,
       read-only systems which are not expected to be rebuild or
diff --git a/nixos/modules/misc/documentation.nix b/nixos/modules/misc/documentation.nix
index 8e28d3336fa42..b031ff2f2be29 100644
--- a/nixos/modules/misc/documentation.nix
+++ b/nixos/modules/misc/documentation.nix
@@ -178,19 +178,12 @@ in
       man.generateCaches = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = mdDoc ''
           Whether to generate the manual page index caches.
           This allows searching for a page or
-          keyword using utilities like
-          <citerefentry>
-            <refentrytitle>apropos</refentrytitle>
-            <manvolnum>1</manvolnum>
-          </citerefentry>
-          and the <literal>-k</literal> option of
-          <citerefentry>
-            <refentrytitle>man</refentrytitle>
-            <manvolnum>1</manvolnum>
-          </citerefentry>.
+          keyword using utilities like {manpage}`apropos(1)`
+          and the `-k` option of
+          {manpage}`man(1)`.
         '';
       };
 
@@ -216,16 +209,14 @@ in
       dev.enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = mdDoc ''
           Whether to install documentation targeted at developers.
-          <itemizedlist>
-          <listitem><para>This includes man pages targeted at developers if <option>documentation.man.enable</option> is
-                    set (this also includes "devman" outputs).</para></listitem>
-          <listitem><para>This includes info pages targeted at developers if <option>documentation.info.enable</option>
-                    is set (this also includes "devinfo" outputs).</para></listitem>
-          <listitem><para>This includes other pages targeted at developers if <option>documentation.doc.enable</option>
-                    is set (this also includes "devdoc" outputs).</para></listitem>
-          </itemizedlist>
+          * This includes man pages targeted at developers if {option}`documentation.man.enable` is
+            set (this also includes "devman" outputs).
+          * This includes info pages targeted at developers if {option}`documentation.info.enable`
+            is set (this also includes "devinfo" outputs).
+          * This includes other pages targeted at developers if {option}`documentation.doc.enable`
+            is set (this also includes "devdoc" outputs).
         '';
       };
 
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 7d1faa50f4bfa..e3d7866cabb51 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -3,7 +3,7 @@
 
 # IMPORTANT!
 # We only add static uids and gids for services where it is not feasible
-# to change uids/gids on service start, in example a service with a lot of
+# to change uids/gids on service start, for example a service with a lot of
 # files. Please also check if the service is applicable for systemd's
 # DynamicUser option and does not need a uid/gid allocation at all.
 # Systemd can also change ownership of service directories using the
@@ -236,7 +236,7 @@ in
       gitit = 202;
       riemanntools = 203;
       subsonic = 204;
-      riak = 205;
+      # riak = 205; # unused, remove 2022-07-22
       #shout = 206; # dynamically allocated as of 2021-09-18
       gateone = 207;
       namecoin = 208;
@@ -553,7 +553,7 @@ in
       gitit = 202;
       riemanntools = 203;
       subsonic = 204;
-      riak = 205;
+      # riak = 205;#unused, removed 2022-06-22
       #shout = 206; #unused
       gateone = 207;
       namecoin = 208;
@@ -667,6 +667,27 @@ in
       # uid. Users and groups with the same name should have equal
       # uids and gids. Also, don't use gids above 399!
 
+      # For exceptional cases where you really need a gid above 399, leave a
+      # comment stating why.
+      #
+      # Also, avoid the following GID ranges:
+      #
+      #  1000 - 29999: user accounts (see ../config/update-users-groups.pl)
+      # 30000 - 31000: nixbld users (the upper limit is arbitrarily chosen)
+      # 61184 - 65519: systemd DynamicUser (see systemd.exec(5))
+      #         65535: the error return sentinel value when uid_t was 16 bits
+      #
+      # 100000 - 6653600: subgid allocated for user namespaces
+      #                   (see ../config/update-users-groups.pl)
+      #       4294967294: unauthenticated user in some NFS implementations
+      #       4294967295: error return sentinel value
+      #
+      # References:
+      # https://www.debian.org/doc/debian-policy/ch-opersys.html#uid-and-gid-classes
+
+      onepassword = 31001; # 1Password requires that its GID be larger than 1000
+      onepassword-cli = 31002; # 1Password requires that its GID be larger than 1000
+
       users = 100;
       nixbld = 30000;
       nogroup = 65534;
diff --git a/nixos/modules/misc/label.nix b/nixos/modules/misc/label.nix
index 02b91555b3c21..b97cbaa26304e 100644
--- a/nixos/modules/misc/label.nix
+++ b/nixos/modules/misc/label.nix
@@ -11,7 +11,7 @@ in
   options.system = {
 
     nixos.label = mkOption {
-      type = types.str;
+      type = types.strMatching "[a-zA-Z0-9:_\\.-]*";
       description = ''
         NixOS version name to be used in the names of generated
         outputs and boot labels.
@@ -19,6 +19,9 @@ in
         If you ever wanted to influence the labels in your GRUB menu,
         this is the option for you.
 
+        It can only contain letters, numbers and the following symbols:
+        <literal>:</literal>, <literal>_</literal>, <literal>.</literal> and <literal>-</literal>.
+
         The default is <option>system.nixos.tags</option> separated by
         "-" + "-" + <envar>NIXOS_LABEL_VERSION</envar> environment
         variable (defaults to the value of
diff --git a/nixos/modules/misc/man-db.nix b/nixos/modules/misc/man-db.nix
index 8bd329bc4e0c3..d267ad125646e 100644
--- a/nixos/modules/misc/man-db.nix
+++ b/nixos/modules/misc/man-db.nix
@@ -23,11 +23,11 @@ in
             ++ lib.optionals config.documentation.dev.enable [ "devman" ];
           ignoreCollisions = true;
         };
-        defaultText = lib.literalDocBook "all man pages in <option>config.environment.systemPackages</option>";
-        description = ''
-          The manual pages to generate caches for if <option>documentation.man.generateCaches</option>
+        defaultText = lib.literalMD "all man pages in {option}`config.environment.systemPackages`";
+        description = lib.mdDoc ''
+          The manual pages to generate caches for if {option}`documentation.man.generateCaches`
           is enabled. Must be a path to a directory with man pages under
-          <literal>/share/man</literal>; see the source for an example.
+          `/share/man`; see the source for an example.
           Advanced users can make this a content-addressed derivation to save a few rebuilds.
         '';
       };
@@ -36,8 +36,8 @@ in
         type = lib.types.package;
         default = pkgs.man-db;
         defaultText = lib.literalExpression "pkgs.man-db";
-        description = ''
-          The <literal>man-db</literal> derivation to use. Useful to override
+        description = lib.mdDoc ''
+          The `man-db` derivation to use. Useful to override
           configuration options used for the package.
         '';
       };
diff --git a/nixos/modules/misc/mandoc.nix b/nixos/modules/misc/mandoc.nix
index 838f20876563a..d67c42bff6a09 100644
--- a/nixos/modules/misc/mandoc.nix
+++ b/nixos/modules/misc/mandoc.nix
@@ -16,9 +16,9 @@ in {
         type = with lib.types; listOf str;
         default = [ "share/man" ];
         example = lib.literalExpression "[ \"share/man\" \"share/man/fr\" ]";
-        description = ''
+        description = lib.mdDoc ''
           Change the manpath, i. e. the directories where
-          <citerefentry><refentrytitle>man</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+          {manpage}`man(1)`
           looks for section-specific directories of man pages.
           You only need to change this setting if you want extra man pages
           (e. g. in non-english languages). All values must be strings that
@@ -31,8 +31,8 @@ in {
         type = lib.types.package;
         default = pkgs.mandoc;
         defaultText = lib.literalExpression "pkgs.mandoc";
-        description = ''
-          The <literal>mandoc</literal> derivation to use. Useful to override
+        description = lib.mdDoc ''
+          The `mandoc` derivation to use. Useful to override
           configuration options used for the package.
         '';
       };
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 866bb35160091..ad017aff816c7 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -55,9 +55,46 @@ let
     check = builtins.isAttrs;
   };
 
-  defaultPkgs = import ../../.. {
-    inherit (cfg) config overlays localSystem crossSystem;
-  };
+  hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority;
+  hasHostPlatform = opt.hostPlatform.isDefined;
+  hasPlatform = hasHostPlatform || hasBuildPlatform;
+
+  # Context for messages
+  hostPlatformLine = optionalString hasHostPlatform "${showOptionWithDefLocs opt.hostPlatform}";
+  buildPlatformLine = optionalString hasBuildPlatform "${showOptionWithDefLocs opt.buildPlatform}";
+  platformLines = optionalString hasPlatform ''
+    Your system configuration configures nixpkgs with platform parameters:
+    ${hostPlatformLine
+    }${buildPlatformLine
+    }'';
+
+  legacyOptionsDefined =
+    optional (opt.localSystem.highestPrio < (mkDefault {}).priority) opt.system
+    ++ optional (opt.localSystem.highestPrio < (mkOptionDefault {}).priority) opt.localSystem
+    ++ optional (opt.crossSystem.highestPrio < (mkOptionDefault {}).priority) opt.crossSystem
+    ;
+
+  defaultPkgs =
+    if opt.hostPlatform.isDefined
+    then
+      let isCross = cfg.buildPlatform != cfg.hostPlatform;
+          systemArgs =
+            if isCross
+            then {
+              localSystem = cfg.buildPlatform;
+              crossSystem = cfg.hostPlatform;
+            }
+            else {
+              localSystem = cfg.hostPlatform;
+            };
+      in
+      import ../../.. ({
+        inherit (cfg) config overlays;
+      } // systemArgs)
+    else
+      import ../../.. {
+        inherit (cfg) config overlays localSystem crossSystem;
+      };
 
   finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else defaultPkgs;
 
@@ -157,6 +194,46 @@ in
       '';
     };
 
+    hostPlatform = mkOption {
+      type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
+      example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
+      # Make sure that the final value has all fields for sake of other modules
+      # referring to this. TODO make `lib.systems` itself use the module system.
+      apply = lib.systems.elaborate;
+      defaultText = literalExpression
+        ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
+      description = ''
+        Specifies the platform where the NixOS configuration will run.
+
+        To cross-compile, set also <code>nixpkgs.buildPlatform</code>.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
+    buildPlatform = mkOption {
+      type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
+      default = cfg.hostPlatform;
+      example = { system = "x86_64-linux"; config = "x86_64-unknown-linux-gnu"; };
+      # Make sure that the final value has all fields for sake of other modules
+      # referring to this.
+      apply = lib.systems.elaborate;
+      defaultText = literalExpression
+        ''config.nixpkgs.hostPlatform'';
+      description = ''
+        Specifies the platform on which NixOS should be built.
+        By default, NixOS is built on the system where it runs, but you can
+        change where it's built. Setting this option will cause NixOS to be
+        cross-compiled.
+
+        For instance, if you're doing distributed multi-platform deployment,
+        or if you're building machines, you can set this to match your
+        development system and/or build farm.
+
+        Ignored when <code>nixpkgs.pkgs</code> is set.
+      '';
+    };
+
     localSystem = mkOption {
       type = types.attrs; # TODO utilize lib.systems.parsedPlatform
       default = { inherit (cfg) system; };
@@ -176,10 +253,13 @@ in
         deployment, or when building virtual machines. See its
         description in the Nixpkgs manual for more details.
 
-        Ignored when <code>nixpkgs.pkgs</code> is set.
+        Ignored when <code>nixpkgs.pkgs</code> or <code>hostPlatform</code> is set.
       '';
     };
 
+    # TODO deprecate. "crossSystem" is a nonsense identifier, because "cross"
+    #      is a relation between at least 2 systems in the context of a
+    #      specific build step, not a single system.
     crossSystem = mkOption {
       type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
       default = null;
@@ -193,7 +273,7 @@ in
         should be set as null, the default. See its description in the
         Nixpkgs manual for more details.
 
-        Ignored when <code>nixpkgs.pkgs</code> is set.
+        Ignored when <code>nixpkgs.pkgs</code> or <code>hostPlatform</code> is set.
       '';
     };
 
@@ -216,8 +296,7 @@ in
         </programlisting>
         See <code>nixpkgs.localSystem</code> for more information.
 
-        Ignored when <code>nixpkgs.localSystem</code> is set.
-        Ignored when <code>nixpkgs.pkgs</code> is set.
+        Ignored when <code>nixpkgs.pkgs</code>, <code>nixpkgs.localSystem</code> or <code>nixpkgs.hostPlatform</code> is set.
       '';
     };
   };
@@ -240,10 +319,23 @@ in
             else "nixpkgs.localSystem";
           pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
         in {
-          assertion = nixosExpectedSystem == pkgsSystem;
+          assertion = !hasPlatform -> nixosExpectedSystem == pkgsSystem;
           message = "The NixOS nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but NixOS was configured for system ${nixosExpectedSystem} via NixOS option ${nixosOption}. The NixOS system settings must match the Nixpkgs target system.";
         }
       )
+      {
+        assertion = hasPlatform -> legacyOptionsDefined == [];
+        message = ''
+          Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
+          ${hostPlatformLine
+          }${buildPlatformLine
+          }
+          However, it also defines the legacy options:
+          ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
+          For a future proof system configuration, we recommend to remove
+          the legacy definitions.
+        '';
+      }
     ];
   };
 
diff --git a/nixos/modules/misc/nixpkgs/test.nix b/nixos/modules/misc/nixpkgs/test.nix
index ec5fab9fb4a5e..9e8851707f8fc 100644
--- a/nixos/modules/misc/nixpkgs/test.nix
+++ b/nixos/modules/misc/nixpkgs/test.nix
@@ -1,8 +1,63 @@
 { evalMinimalConfig, pkgs, lib, stdenv }:
+let
+  eval = mod: evalMinimalConfig {
+    imports = [ ../nixpkgs.nix mod ];
+  };
+  withHost = eval {
+    nixpkgs.hostPlatform = "aarch64-linux";
+  };
+  withHostAndBuild = eval {
+    nixpkgs.hostPlatform = "aarch64-linux";
+    nixpkgs.buildPlatform = "aarch64-darwin";
+  };
+  ambiguous = {
+    _file = "ambiguous.nix";
+    nixpkgs.hostPlatform = "aarch64-linux";
+    nixpkgs.buildPlatform = "aarch64-darwin";
+    nixpkgs.system = "x86_64-linux";
+    nixpkgs.localSystem.system = "x86_64-darwin";
+    nixpkgs.crossSystem.system = "i686-linux";
+    imports = [
+      { _file = "repeat.nix";
+        nixpkgs.hostPlatform = "aarch64-linux";
+      }
+    ];
+  };
+  getErrors = module:
+    let
+      uncheckedEval = lib.evalModules { modules = [ ../nixpkgs.nix module ]; };
+    in map (ass: ass.message) (lib.filter (ass: !ass.assertion) uncheckedEval.config.assertions);
+in
 lib.recurseIntoAttrs {
   invokeNixpkgsSimple =
-    (evalMinimalConfig ({ config, modulesPath, ... }: {
-      imports = [ (modulesPath + "/misc/nixpkgs.nix") ];
+    (eval {
       nixpkgs.system = stdenv.hostPlatform.system;
-    }))._module.args.pkgs.hello;
+    })._module.args.pkgs.hello;
+  assertions =
+    assert withHost._module.args.pkgs.stdenv.hostPlatform.system == "aarch64-linux";
+    assert withHost._module.args.pkgs.stdenv.buildPlatform.system == "aarch64-linux";
+    assert withHostAndBuild._module.args.pkgs.stdenv.hostPlatform.system == "aarch64-linux";
+    assert withHostAndBuild._module.args.pkgs.stdenv.buildPlatform.system == "aarch64-darwin";
+    assert builtins.trace (lib.head (getErrors ambiguous))
+      getErrors ambiguous ==
+        [''
+          Your system configures nixpkgs with the platform parameters:
+          nixpkgs.hostPlatform, with values defined in:
+            - repeat.nix
+            - ambiguous.nix
+          nixpkgs.buildPlatform, with values defined in:
+            - ambiguous.nix
+
+          However, it also defines the legacy options:
+          nixpkgs.system, with values defined in:
+            - ambiguous.nix
+          nixpkgs.localSystem, with values defined in:
+            - ambiguous.nix
+          nixpkgs.crossSystem, with values defined in:
+            - ambiguous.nix
+
+          For a future proof system configuration, we recommend to remove
+          the legacy definitions.
+        ''];
+    pkgs.emptyFile;
 }
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index da458a5748401..bdc3e5623be68 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -49,42 +49,42 @@ in
     nixos.version = mkOption {
       internal = true;
       type = types.str;
-      description = "The full NixOS version (e.g. <literal>16.03.1160.f2d4ee1</literal>).";
+      description = lib.mdDoc "The full NixOS version (e.g. `16.03.1160.f2d4ee1`).";
     };
 
     nixos.release = mkOption {
       readOnly = true;
       type = types.str;
       default = trivial.release;
-      description = "The NixOS release (e.g. <literal>16.03</literal>).";
+      description = lib.mdDoc "The NixOS release (e.g. `16.03`).";
     };
 
     nixos.versionSuffix = mkOption {
       internal = true;
       type = types.str;
       default = trivial.versionSuffix;
-      description = "The NixOS version suffix (e.g. <literal>1160.f2d4ee1</literal>).";
+      description = lib.mdDoc "The NixOS version suffix (e.g. `1160.f2d4ee1`).";
     };
 
     nixos.revision = mkOption {
       internal = true;
       type = types.nullOr types.str;
       default = trivial.revisionWithDefault null;
-      description = "The Git revision from which this NixOS configuration was built.";
+      description = lib.mdDoc "The Git revision from which this NixOS configuration was built.";
     };
 
     nixos.codeName = mkOption {
       readOnly = true;
       type = types.str;
       default = trivial.codeName;
-      description = "The NixOS release code name (e.g. <literal>Emu</literal>).";
+      description = lib.mdDoc "The NixOS release code name (e.g. `Emu`).";
     };
 
     stateVersion = mkOption {
       type = types.str;
       default = cfg.release;
       defaultText = literalExpression "config.${opt.release}";
-      description = ''
+      description = lib.mdDoc ''
         Every once in a while, a new NixOS release may change
         configuration defaults in a way incompatible with stateful
         data. For instance, if the default version of PostgreSQL
@@ -108,13 +108,13 @@ in
       internal = true;
       type = types.str;
       default = "https://nixos.org/channels/nixos-unstable";
-      description = "Default NixOS channel to which the root user is subscribed.";
+      description = lib.mdDoc "Default NixOS channel to which the root user is subscribed.";
     };
 
     configurationRevision = mkOption {
       type = types.nullOr types.str;
       default = null;
-      description = "The Git revision of the top-level flake from which this configuration was built.";
+      description = lib.mdDoc "The Git revision of the top-level flake from which this configuration was built.";
     };
 
   };
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 43ae28ac02c5f..3010a213705b2 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -10,6 +10,7 @@
   ./config/xdg/mime.nix
   ./config/xdg/portal.nix
   ./config/xdg/portals/wlr.nix
+  ./config/xdg/portals/lxqt.nix
   ./config/appstream.nix
   ./config/console.nix
   ./config/xdg/sounds.nix
@@ -141,7 +142,6 @@
   ./programs/cdemu.nix
   ./programs/cfs-zen-tweaks.nix
   ./programs/chromium.nix
-  ./programs/clickshare.nix
   ./programs/cnping.nix
   ./programs/command-not-found/command-not-found.nix
   ./programs/criu.nix
@@ -215,9 +215,11 @@
   ./programs/systemtap.nix
   ./programs/starship.nix
   ./programs/steam.nix
+  ./programs/streamdeck-ui.nix
   ./programs/sway.nix
   ./programs/system-config-printer.nix
   ./programs/thefuck.nix
+  ./programs/thunar.nix
   ./programs/tmux.nix
   ./programs/traceroute.nix
   ./programs/tsm-client.nix
@@ -230,6 +232,7 @@
   ./programs/weylus.nix
   ./programs/wireshark.nix
   ./programs/wshowkeys.nix
+  ./programs/xfconf.nix
   ./programs/xfs_quota.nix
   ./programs/xonsh.nix
   ./programs/xss-lock.nix
@@ -351,6 +354,7 @@
   ./services/databases/cockroachdb.nix
   ./services/databases/couchdb.nix
   ./services/databases/dragonflydb.nix
+  ./services/databases/dgraph.nix
   ./services/databases/firebird.nix
   ./services/databases/foundationdb.nix
   ./services/databases/hbase.nix
@@ -366,7 +370,6 @@
   ./services/databases/pgmanage.nix
   ./services/databases/postgresql.nix
   ./services/databases/redis.nix
-  ./services/databases/riak.nix
   ./services/databases/victoriametrics.nix
   ./services/desktops/accountsservice.nix
   ./services/desktops/bamf.nix
@@ -431,6 +434,7 @@
   ./services/games/terraria.nix
   ./services/hardware/acpid.nix
   ./services/hardware/actkbd.nix
+  ./services/hardware/argonone.nix
   ./services/hardware/auto-cpufreq.nix
   ./services/hardware/bluetooth.nix
   ./services/hardware/bolt.nix
@@ -516,6 +520,7 @@
   ./services/mail/rspamd.nix
   ./services/mail/rss2email.nix
   ./services/mail/roundcube.nix
+  ./services/mail/schleuder.nix
   ./services/mail/sympa.nix
   ./services/mail/nullmailer.nix
   ./services/matrix/appservice-discord.nix
@@ -612,6 +617,7 @@
   ./services/misc/plex.nix
   ./services/misc/plikd.nix
   ./services/misc/podgrab.nix
+  ./services/misc/polaris.nix
   ./services/misc/prowlarr.nix
   ./services/misc/tautulli.nix
   ./services/misc/pinnwand.nix
@@ -660,6 +666,7 @@
   ./services/monitoring/do-agent.nix
   ./services/monitoring/fusion-inventory.nix
   ./services/monitoring/grafana.nix
+  ./services/monitoring/grafana-agent.nix
   ./services/monitoring/grafana-image-renderer.nix
   ./services/monitoring/grafana-reporter.nix
   ./services/monitoring/graphite.nix
@@ -823,6 +830,7 @@
   ./services/networking/libreswan.nix
   ./services/networking/lldpd.nix
   ./services/networking/logmein-hamachi.nix
+  ./services/networking/lokinet.nix
   ./services/networking/lxd-image-server.nix
   ./services/networking/magic-wormhole-mailbox-server.nix
   ./services/networking/matterbridge.nix
@@ -893,6 +901,7 @@
   ./services/networking/redsocks.nix
   ./services/networking/resilio.nix
   ./services/networking/robustirc-bridge.nix
+  ./services/networking/routedns.nix
   ./services/networking/rpcbind.nix
   ./services/networking/rxe.nix
   ./services/networking/sabnzbd.nix
@@ -994,6 +1003,7 @@
   ./services/security/oauth2_proxy.nix
   ./services/security/oauth2_proxy_nginx.nix
   ./services/security/opensnitch.nix
+  ./services/security/pass-secret-service.nix
   ./services/security/privacyidea.nix
   ./services/security/physlock.nix
   ./services/security/shibboleth-sp.nix
@@ -1026,6 +1036,7 @@
   ./services/torrent/peerflix.nix
   ./services/torrent/rtorrent.nix
   ./services/torrent/transmission.nix
+  ./services/tracing/tempo.nix
   ./services/ttys/getty.nix
   ./services/ttys/gpm.nix
   ./services/ttys/kmscon.nix
@@ -1041,7 +1052,6 @@
   ./services/web-apps/code-server.nix
   ./services/web-apps/baget.nix
   ./services/web-apps/convos.nix
-  ./services/web-apps/cryptpad.nix
   ./services/web-apps/dex.nix
   ./services/web-apps/discourse.nix
   ./services/web-apps/documize.nix
@@ -1053,6 +1063,7 @@
   ./services/web-apps/gerrit.nix
   ./services/web-apps/gotify-server.nix
   ./services/web-apps/grocy.nix
+  ./services/web-apps/healthchecks.nix
   ./services/web-apps/hedgedoc.nix
   ./services/web-apps/hledger-web.nix
   ./services/web-apps/icingaweb2/icingaweb2.nix
@@ -1076,6 +1087,8 @@
   ./services/web-apps/nexus.nix
   ./services/web-apps/nifi.nix
   ./services/web-apps/node-red.nix
+  ./services/web-apps/phylactery.nix
+  ./services/web-apps/onlyoffice.nix
   ./services/web-apps/pict-rs.nix
   ./services/web-apps/peertube.nix
   ./services/web-apps/plantuml-server.nix
@@ -1267,7 +1280,6 @@
   ./virtualisation/parallels-guest.nix
   ./virtualisation/podman/default.nix
   ./virtualisation/qemu-guest-agent.nix
-  ./virtualisation/railcar.nix
   ./virtualisation/spice-usb-redirection.nix
   ./virtualisation/virtualbox-guest.nix
   ./virtualisation/virtualbox-host.nix
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index e79b927238419..0e65989214a18 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -8,9 +8,6 @@ with lib;
 {
   environment.noXlibs = mkDefault true;
 
-  # This isn't perfect, but let's expect the user specifies an UTF-8 defaultLocale
-  i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ];
-
   documentation.enable = mkDefault false;
 
   documentation.nixos.enable = mkDefault false;
diff --git a/nixos/modules/programs/_1password-gui.nix b/nixos/modules/programs/_1password-gui.nix
index 42f6a0b52252c..657116c267d92 100644
--- a/nixos/modules/programs/_1password-gui.nix
+++ b/nixos/modules/programs/_1password-gui.nix
@@ -8,19 +8,16 @@ let
 
 in
 {
+  imports = [
+    (mkRemovedOptionModule [ "programs" "_1password-gui" "gid" ] ''
+      A preallocated GID will be used instead.
+    '')
+  ];
+
   options = {
     programs._1password-gui = {
       enable = mkEnableOption "the 1Password GUI application";
 
-      gid = mkOption {
-        type = types.addCheck types.int (x: x >= 1000);
-        example = literalExpression "5000";
-        description = ''
-          The gid to assign to the onepassword group, which is needed for browser integration.
-          It must be 1000 or greater.
-        '';
-      };
-
       polkitPolicyOwners = mkOption {
         type = types.listOf types.str;
         default = [ ];
@@ -44,7 +41,7 @@ in
     in
     mkIf cfg.enable {
       environment.systemPackages = [ package ];
-      users.groups.onepassword.gid = cfg.gid;
+      users.groups.onepassword.gid = config.ids.gids.onepassword;
 
       security.wrappers = {
         "1Password-BrowserSupport" = {
diff --git a/nixos/modules/programs/_1password.nix b/nixos/modules/programs/_1password.nix
index 547c12867a916..b87e9b776e85b 100644
--- a/nixos/modules/programs/_1password.nix
+++ b/nixos/modules/programs/_1password.nix
@@ -8,19 +8,16 @@ let
 
 in
 {
+  imports = [
+    (mkRemovedOptionModule [ "programs" "_1password" "gid" ] ''
+      A preallocated GID will be used instead.
+    '')
+  ];
+
   options = {
     programs._1password = {
       enable = mkEnableOption "the 1Password CLI tool";
 
-      gid = mkOption {
-        type = types.addCheck types.int (x: x >= 1000);
-        example = literalExpression "5001";
-        description = ''
-          The gid to assign to the onepassword-cli group, which is needed for integration with the 1Password GUI.
-          It must be 1000 or greater.
-        '';
-      };
-
       package = mkPackageOption pkgs "1Password CLI" {
         default = [ "_1password" ];
       };
@@ -29,7 +26,7 @@ in
 
   config = mkIf cfg.enable {
     environment.systemPackages = [ cfg.package ];
-    users.groups.onepassword-cli.gid = cfg.gid;
+    users.groups.onepassword-cli.gid = config.ids.gids.onepassword-cli;
 
     security.wrappers = {
       "op" = {
diff --git a/nixos/modules/programs/atop.nix b/nixos/modules/programs/atop.nix
index a31078a891a0c..a0763d2dcf6b0 100644
--- a/nixos/modules/programs/atop.nix
+++ b/nixos/modules/programs/atop.nix
@@ -20,7 +20,7 @@ in
         type = types.package;
         default = pkgs.atop;
         defaultText = literalExpression "pkgs.atop";
-        description = ''
+        description = lib.mdDoc ''
           Which package to use for Atop.
         '';
       };
@@ -29,7 +29,7 @@ in
         enable = mkOption {
           type = types.bool;
           default = false;
-          description = ''
+          description = lib.mdDoc ''
             Whether to install and enable the netatop kernel module.
             Note: this sets the kernel taint flag "O" for loading out-of-tree modules.
           '';
@@ -38,7 +38,7 @@ in
           type = types.package;
           default = config.boot.kernelPackages.netatop;
           defaultText = literalExpression "config.boot.kernelPackages.netatop";
-          description = ''
+          description = lib.mdDoc ''
             Which package to use for netatop.
           '';
         };
@@ -47,7 +47,7 @@ in
       atopgpu.enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to install and enable the atopgpud daemon to get information about
           NVIDIA gpus.
         '';
@@ -56,7 +56,7 @@ in
       setuidWrapper.enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to install a setuid wrapper for Atop. This is required to use some of
           the features as non-root user (e.g.: ipc information, netatop, atopgpu).
           Atop tries to drop the root privileges shortly after starting.
@@ -66,7 +66,7 @@ in
       atopService.enable = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whether to enable the atop service responsible for storing statistics for
           long-term analysis.
         '';
@@ -74,7 +74,7 @@ in
       atopRotateTimer.enable = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whether to enable the atop-rotate timer, which restarts the atop service
           daily to make sure the data files are rotate.
         '';
@@ -82,7 +82,7 @@ in
       atopacctService.enable = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whether to enable the atopacct service which manages process accounting.
           This allows Atop to gather data about processes that disappeared in between
           two refresh intervals.
@@ -95,8 +95,8 @@ in
           flags = "a1f";
           interval = 5;
         };
-        description = ''
-          Parameters to be written to <filename>/etc/atoprc</filename>.
+        description = lib.mdDoc ''
+          Parameters to be written to {file}`/etc/atoprc`.
         '';
       };
     };
diff --git a/nixos/modules/programs/bash/bash.nix b/nixos/modules/programs/bash/bash.nix
index 7281126979e50..249e99ddc4721 100644
--- a/nixos/modules/programs/bash/bash.nix
+++ b/nixos/modules/programs/bash/bash.nix
@@ -30,10 +30,10 @@ in
       /*
       enable = mkOption {
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whenever to configure Bash as an interactive shell.
           Note that this tries to make Bash the default
-          <option>users.defaultUserShell</option>,
+          {option}`users.defaultUserShell`,
           which in turn means that you might need to explicitly
           set this variable if you have another shell configured
           with NixOS.
@@ -44,16 +44,16 @@ in
 
       shellAliases = mkOption {
         default = {};
-        description = ''
-          Set of aliases for bash shell, which overrides <option>environment.shellAliases</option>.
-          See <option>environment.shellAliases</option> for an option format description.
+        description = lib.mdDoc ''
+          Set of aliases for bash shell, which overrides {option}`environment.shellAliases`.
+          See {option}`environment.shellAliases` for an option format description.
         '';
         type = with types; attrsOf (nullOr (either str path));
       };
 
       shellInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code called during bash shell initialisation.
         '';
         type = types.lines;
@@ -61,7 +61,7 @@ in
 
       loginShellInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code called during login bash shell initialisation.
         '';
         type = types.lines;
@@ -69,7 +69,7 @@ in
 
       interactiveShellInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code called during interactive bash shell initialisation.
         '';
         type = types.lines;
@@ -92,7 +92,7 @@ in
             fi
           fi
         '';
-        description = ''
+        description = lib.mdDoc ''
           Shell script code used to initialise the bash prompt.
         '';
         type = types.lines;
@@ -100,7 +100,7 @@ in
 
       promptPluginInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code used to initialise bash prompt plugins.
         '';
         type = types.lines;
diff --git a/nixos/modules/programs/clickshare.nix b/nixos/modules/programs/clickshare.nix
deleted file mode 100644
index 9980a7daf5254..0000000000000
--- a/nixos/modules/programs/clickshare.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-
-  options.programs.clickshare-csc1.enable =
-    lib.options.mkEnableOption ''
-      Barco ClickShare CSC-1 driver/client.
-      This allows users in the <literal>clickshare</literal>
-      group to access and use a ClickShare USB dongle
-      that is connected to the machine
-    '';
-
-  config = lib.modules.mkIf config.programs.clickshare-csc1.enable {
-    environment.systemPackages = [ pkgs.clickshare-csc1 ];
-    services.udev.packages = [ pkgs.clickshare-csc1 ];
-    users.groups.clickshare = {};
-  };
-
-  meta.maintainers = [ lib.maintainers.yarny ];
-
-}
diff --git a/nixos/modules/programs/dconf.nix b/nixos/modules/programs/dconf.nix
index 265c41cbbbc99..b5ef42a3b72bc 100644
--- a/nixos/modules/programs/dconf.nix
+++ b/nixos/modules/programs/dconf.nix
@@ -33,14 +33,14 @@ in
       profiles = mkOption {
         type = types.attrsOf types.path;
         default = {};
-        description = "Set of dconf profile files, installed at <filename>/etc/dconf/profiles/<replaceable>name</replaceable></filename>.";
+        description = lib.mdDoc "Set of dconf profile files, installed at {file}`/etc/dconf/profiles/«name»`.";
         internal = true;
       };
 
       packages = mkOption {
         type = types.listOf types.package;
         default = [];
-        description = "A list of packages which provide dconf profiles and databases in <filename>/etc/dconf</filename>.";
+        description = lib.mdDoc "A list of packages which provide dconf profiles and databases in {file}`/etc/dconf`.";
       };
     };
   };
diff --git a/nixos/modules/programs/dmrconfig.nix b/nixos/modules/programs/dmrconfig.nix
index d2a5117c48ef2..89ac3443a1f33 100644
--- a/nixos/modules/programs/dmrconfig.nix
+++ b/nixos/modules/programs/dmrconfig.nix
@@ -6,7 +6,7 @@ let
   cfg = config.programs.dmrconfig;
 
 in {
-  meta.maintainers = [ maintainers.etu ];
+  meta.maintainers = with maintainers; [ ];
 
   ###### interface
   options = {
diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix
index 8dd7101947fae..357105c3e79b3 100644
--- a/nixos/modules/programs/fish.nix
+++ b/nixos/modules/programs/fish.nix
@@ -49,7 +49,7 @@ in
 
       enable = mkOption {
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to configure fish as an interactive shell.
         '';
         type = types.bool;
@@ -58,16 +58,16 @@ in
       useBabelfish = mkOption {
         type = types.bool;
         default = false;
-        description = ''
-          If enabled, the configured environment will be translated to native fish using <link xlink:href="https://github.com/bouk/babelfish">babelfish</link>.
-          Otherwise, <link xlink:href="https://github.com/oh-my-fish/plugin-foreign-env">foreign-env</link> will be used.
+        description = lib.mdDoc ''
+          If enabled, the configured environment will be translated to native fish using [babelfish](https://github.com/bouk/babelfish).
+          Otherwise, [foreign-env](https://github.com/oh-my-fish/plugin-foreign-env) will be used.
         '';
       };
 
       vendor.config.enable = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whether fish should source configuration snippets provided by other packages.
         '';
       };
@@ -75,7 +75,7 @@ in
       vendor.completions.enable = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whether fish should use completion files provided by other packages.
         '';
       };
@@ -83,7 +83,7 @@ in
       vendor.functions.enable = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = lib.mdDoc ''
           Whether fish should autoload fish functions provided by other packages.
         '';
       };
@@ -94,7 +94,7 @@ in
           gco = "git checkout";
           npu = "nix-prefetch-url";
         };
-        description = ''
+        description = lib.mdDoc ''
           Set of fish abbreviations.
         '';
         type = with types; attrsOf str;
@@ -102,16 +102,16 @@ in
 
       shellAliases = mkOption {
         default = {};
-        description = ''
-          Set of aliases for fish shell, which overrides <option>environment.shellAliases</option>.
-          See <option>environment.shellAliases</option> for an option format description.
+        description = lib.mdDoc ''
+          Set of aliases for fish shell, which overrides {option}`environment.shellAliases`.
+          See {option}`environment.shellAliases` for an option format description.
         '';
         type = with types; attrsOf (nullOr (either str path));
       };
 
       shellInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code called during fish shell initialisation.
         '';
         type = types.lines;
@@ -119,7 +119,7 @@ in
 
       loginShellInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code called during fish login shell initialisation.
         '';
         type = types.lines;
@@ -127,7 +127,7 @@ in
 
       interactiveShellInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code called during interactive fish shell initialisation.
         '';
         type = types.lines;
@@ -135,7 +135,7 @@ in
 
       promptInit = mkOption {
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           Shell script code used to initialise fish prompt.
         '';
         type = types.lines;
diff --git a/nixos/modules/programs/less.nix b/nixos/modules/programs/less.nix
index 794146b19faf5..9f2d5d9158159 100644
--- a/nixos/modules/programs/less.nix
+++ b/nixos/modules/programs/less.nix
@@ -41,12 +41,12 @@ in
         type = types.nullOr types.path;
         default = null;
         example = literalExpression ''"''${pkgs.my-configs}/lesskey"'';
-        description = ''
+        description = lib.mdDoc ''
           Path to lesskey configuration file.
 
-          <option>configFile</option> takes precedence over <option>commands</option>,
-          <option>clearDefaultCommands</option>, <option>lineEditingKeys</option>, and
-          <option>envVariables</option>.
+          {option}`configFile` takes precedence over {option}`commands`,
+          {option}`clearDefaultCommands`, {option}`lineEditingKeys`, and
+          {option}`envVariables`.
         '';
       };
 
@@ -57,13 +57,13 @@ in
           h = "noaction 5\\e(";
           l = "noaction 5\\e)";
         };
-        description = "Defines new command keys.";
+        description = lib.mdDoc "Defines new command keys.";
       };
 
       clearDefaultCommands = mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Clear all default commands.
           You should remember to set the quit key.
           Otherwise you will not be able to leave less without killing it.
@@ -76,7 +76,7 @@ in
         example = {
           e = "abort";
         };
-        description = "Defines new line-editing keys.";
+        description = lib.mdDoc "Defines new line-editing keys.";
       };
 
       envVariables = mkOption {
@@ -87,14 +87,14 @@ in
         example = {
           LESS = "--quit-if-one-screen";
         };
-        description = "Defines environment variables.";
+        description = lib.mdDoc "Defines environment variables.";
       };
 
       lessopen = mkOption {
         type = types.nullOr types.str;
         default = "|${pkgs.lesspipe}/bin/lesspipe.sh %s";
         defaultText = literalExpression ''"|''${pkgs.lesspipe}/bin/lesspipe.sh %s"'';
-        description = ''
+        description = lib.mdDoc ''
           Before less opens a file, it first gives your input preprocessor a chance to modify the way the contents of the file are displayed.
         '';
       };
@@ -102,7 +102,7 @@ in
       lessclose = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = ''
+        description = lib.mdDoc ''
           When less closes a file opened in such a way, it will call another program, called the input postprocessor, which may  perform  any  desired  clean-up  action (such  as deleting the replacement file created by LESSOPEN).
         '';
       };
diff --git a/nixos/modules/programs/nano.nix b/nixos/modules/programs/nano.nix
index 5837dd46d7cd7..16bab620d6e2a 100644
--- a/nixos/modules/programs/nano.nix
+++ b/nixos/modules/programs/nano.nix
@@ -14,9 +14,9 @@ in
       nanorc = lib.mkOption {
         type = lib.types.lines;
         default = "";
-        description = ''
+        description = lib.mdDoc ''
           The system-wide nano configuration.
-          See <citerefentry><refentrytitle>nanorc</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          See {manpage}`nanorc(5)`.
         '';
         example = ''
           set nowrap
@@ -27,7 +27,7 @@ in
       syntaxHighlight = lib.mkOption {
         type = lib.types.bool;
         default = true;
-        description = "Whether to enable syntax highlight for various languages.";
+        description = lib.mdDoc "Whether to enable syntax highlight for various languages.";
       };
     };
   };
diff --git a/nixos/modules/programs/proxychains.nix b/nixos/modules/programs/proxychains.nix
index 3f44e23a93efe..5d932b2d84238 100644
--- a/nixos/modules/programs/proxychains.nix
+++ b/nixos/modules/programs/proxychains.nix
@@ -26,17 +26,17 @@ let
 
       type = mkOption {
         type = types.enum [ "http" "socks4" "socks5" ];
-        description = "Proxy type.";
+        description = lib.mdDoc "Proxy type.";
       };
 
       host = mkOption {
         type = types.str;
-        description = "Proxy host or IP address.";
+        description = lib.mdDoc "Proxy host or IP address.";
       };
 
       port = mkOption {
         type = types.port;
-        description = "Proxy port";
+        description = lib.mdDoc "Proxy port";
       };
     };
   };
@@ -55,26 +55,26 @@ in {
         type = mkOption {
           type = types.enum [ "dynamic" "strict" "random" ];
           default = "strict";
-          description = ''
-            <literal>dynamic</literal> - Each connection will be done via chained proxies
+          description = lib.mdDoc ''
+            `dynamic` - Each connection will be done via chained proxies
             all proxies chained in the order as they appear in the list
             at least one proxy must be online to play in chain
             (dead proxies are skipped)
-            otherwise <literal>EINTR</literal> is returned to the app.
+            otherwise `EINTR` is returned to the app.
 
-            <literal>strict</literal> - Each connection will be done via chained proxies
+            `strict` - Each connection will be done via chained proxies
             all proxies chained in the order as they appear in the list
             all proxies must be online to play in chain
-            otherwise <literal>EINTR</literal> is returned to the app.
+            otherwise `EINTR` is returned to the app.
 
-            <literal>random</literal> - Each connection will be done via random proxy
-            (or proxy chain, see <option>programs.proxychains.chain.length</option>) from the list.
+            `random` - Each connection will be done via random proxy
+            (or proxy chain, see {option}`programs.proxychains.chain.length`) from the list.
           '';
         };
         length = mkOption {
           type = types.nullOr types.int;
           default = null;
-          description = ''
+          description = lib.mdDoc ''
             Chain length for random chain.
           '';
         };
@@ -83,7 +83,7 @@ in {
       proxyDNS = mkOption {
         type = types.bool;
         default = true;
-        description = "Proxy DNS requests - no leak for DNS data.";
+        description = lib.mdDoc "Proxy DNS requests - no leak for DNS data.";
       };
 
       quietMode = mkEnableOption "Quiet mode (no output from the library).";
@@ -91,7 +91,7 @@ in {
       remoteDNSSubnet = mkOption {
         type = types.enum [ 10 127 224 ];
         default = 224;
-        description = ''
+        description = lib.mdDoc ''
           Set the class A subnet number to use for the internal remote DNS mapping, uses the reserved 224.x.x.x range by default.
         '';
       };
@@ -99,24 +99,24 @@ in {
       tcpReadTimeOut = mkOption {
         type = types.int;
         default = 15000;
-        description = "Connection read time-out in milliseconds.";
+        description = lib.mdDoc "Connection read time-out in milliseconds.";
       };
 
       tcpConnectTimeOut = mkOption {
         type = types.int;
         default = 8000;
-        description = "Connection time-out in milliseconds.";
+        description = lib.mdDoc "Connection time-out in milliseconds.";
       };
 
       localnet = mkOption {
         type = types.str;
         default = "127.0.0.0/255.0.0.0";
-        description = "By default enable localnet for loopback address ranges.";
+        description = lib.mdDoc "By default enable localnet for loopback address ranges.";
       };
 
       proxies = mkOption {
         type = types.attrsOf (types.submodule proxyOptions);
-        description = ''
+        description = lib.mdDoc ''
           Proxies to be used by proxychains.
         '';
 
diff --git a/nixos/modules/programs/qt5ct.nix b/nixos/modules/programs/qt5ct.nix
index 88e861bf4031a..3ff47b355915b 100644
--- a/nixos/modules/programs/qt5ct.nix
+++ b/nixos/modules/programs/qt5ct.nix
@@ -1,31 +1,9 @@
-{ config, lib, pkgs, ... }:
+{ lib, ... }:
 
 with lib;
 
 {
-  meta.maintainers = [ maintainers.romildo ];
-
-  ###### interface
-  options = {
-    programs.qt5ct = {
-      enable = mkOption {
-        default = false;
-        type = types.bool;
-        description = ''
-          Whether to enable the Qt5 Configuration Tool (qt5ct), a
-          program that allows users to configure Qt5 settings (theme,
-          font, icons, etc.) under desktop environments or window
-          manager without Qt integration.
-
-          Official home page: <link xlink:href="https://sourceforge.net/projects/qt5ct/">https://sourceforge.net/projects/qt5ct/</link>
-        '';
-      };
-    };
-  };
-
-  ###### implementation
-  config = mkIf config.programs.qt5ct.enable {
-    environment.variables.QT_QPA_PLATFORMTHEME = "qt5ct";
-    environment.systemPackages = with pkgs; [ libsForQt5.qt5ct ];
-  };
+  imports = [
+    (mkRemovedOptionModule [ "programs" "qt5ct" "enable" ] "Use qt5.platformTheme = \"qt5ct\" instead.")
+  ];
 }
diff --git a/nixos/modules/programs/spacefm.nix b/nixos/modules/programs/spacefm.nix
index f71abcaa33253..b4ba9dcdea567 100644
--- a/nixos/modules/programs/spacefm.nix
+++ b/nixos/modules/programs/spacefm.nix
@@ -17,8 +17,8 @@ in
       enable = mkOption {
         type = types.bool;
         default = false;
-        description = ''
-          Whether to install SpaceFM and create <filename>/etc/spacefm/spacefm.conf</filename>.
+        description = lib.mdDoc ''
+          Whether to install SpaceFM and create {file}`/etc/spacefm/spacefm.conf`.
         '';
       };
 
@@ -34,10 +34,10 @@ in
             terminal_su = "''${pkgs.sudo}/bin/sudo";
           }
         '';
-        description = ''
+        description = lib.mdDoc ''
           The system-wide spacefm configuration.
-          Parameters to be written to <filename>/etc/spacefm/spacefm.conf</filename>.
-          Refer to the <link xlink:href="https://ignorantguru.github.io/spacefm/spacefm-manual-en.html#programfiles-etc">relevant entry</link> in the SpaceFM manual.
+          Parameters to be written to {file}`/etc/spacefm/spacefm.conf`.
+          Refer to the [relevant entry](https://ignorantguru.github.io/spacefm/spacefm-manual-en.html#programfiles-etc) in the SpaceFM manual.
         '';
       };
 
diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix
index 83d2272003c63..ade80b9999e18 100644
--- a/nixos/modules/programs/starship.nix
+++ b/nixos/modules/programs/starship.nix
@@ -16,8 +16,8 @@ in {
     settings = mkOption {
       inherit (settingsFormat) type;
       default = { };
-      description = ''
-        Configuration included in <literal>starship.toml</literal>.
+      description = lib.mdDoc ''
+        Configuration included in `starship.toml`.
 
         See https://starship.rs/config/#prompt for documentation.
       '';
diff --git a/nixos/modules/programs/streamdeck-ui.nix b/nixos/modules/programs/streamdeck-ui.nix
new file mode 100644
index 0000000000000..1434f82660de3
--- /dev/null
+++ b/nixos/modules/programs/streamdeck-ui.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.streamdeck-ui;
+in {
+  options.programs.streamdeck-ui = {
+    enable = mkEnableOption "streamdeck-ui";
+
+    autoStart = mkOption {
+      default = true;
+      type = types.bool;
+      description = "Whether streamdeck-ui should be started automatically.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [
+      streamdeck-ui
+      (mkIf cfg.autoStart (makeAutostartItem { name = "streamdeck-ui"; package = streamdeck-ui; }))
+    ];
+
+    services.udev.packages = with pkgs; [ streamdeck-ui ];
+  };
+
+  meta.maintainers = with maintainers; [ majiir ];
+}
diff --git a/nixos/modules/programs/thunar.nix b/nixos/modules/programs/thunar.nix
new file mode 100644
index 0000000000000..5ea2982dd93cf
--- /dev/null
+++ b/nixos/modules/programs/thunar.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.thunar;
+
+in {
+  meta = {
+    maintainers = teams.xfce.members;
+  };
+
+  options = {
+    programs.thunar = {
+      enable = mkEnableOption "Thunar, the Xfce file manager";
+
+      plugins = mkOption {
+        default = [];
+        type = types.listOf types.package;
+        description = "List of thunar plugins to install.";
+        example = literalExpression "with pkgs.xfce; [ thunar-archive-plugin thunar-volman ]";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable (
+    let package = pkgs.xfce.thunar.override { thunarPlugins = cfg.plugins; };
+
+    in {
+      environment.systemPackages = [
+        package
+      ];
+
+      services.dbus.packages = [
+        package
+      ];
+
+      systemd.packages = [
+        package
+      ];
+
+      programs.xfconf.enable = true;
+    }
+  );
+}
diff --git a/nixos/modules/programs/xfconf.nix b/nixos/modules/programs/xfconf.nix
new file mode 100644
index 0000000000000..8e854b40e513d
--- /dev/null
+++ b/nixos/modules/programs/xfconf.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.programs.xfconf;
+
+in {
+  meta = {
+    maintainers = teams.xfce.members;
+  };
+
+  options = {
+    programs.xfconf = {
+      enable = mkEnableOption "Xfconf, the Xfce configuration storage system";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [
+      pkgs.xfce.xfconf
+    ];
+
+    services.dbus.packages = [
+      pkgs.xfce.xfconf
+    ];
+  };
+}
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 1d22627649304..22fcb72e9ff40 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -68,6 +68,7 @@ with lib;
       prey-bash-client is deprecated upstream
     '')
     (mkRemovedOptionModule [ "services" "quagga" ] "the corresponding package has been removed from nixpkgs")
+    (mkRemovedOptionModule [ "services" "railcar" ] "the corresponding package has been removed from nixpkgs")
     (mkRemovedOptionModule [ "services" "seeks" ] "")
     (mkRemovedOptionModule [ "services" "ssmtp" ] ''
       The ssmtp package and the corresponding module have been removed due to
@@ -97,6 +98,8 @@ with lib;
     (mkRemovedOptionModule [ "services" "gogoclient" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "virtuoso" ] "The corresponding package was removed from nixpkgs.")
     (mkRemovedOptionModule [ "services" "openfire" ] "The corresponding package was removed from nixpkgs.")
+    (mkRemovedOptionModule [ "services" "riak" ] "The corresponding package was removed from nixpkgs.")
+    (mkRemovedOptionModule [ "services" "cryptpad" ] "The corresponding package was removed from nixpkgs.")
 
     # Do NOT add any option renames here, see top of the file
   ];
diff --git a/nixos/modules/security/lock-kernel-modules.nix b/nixos/modules/security/lock-kernel-modules.nix
index 065587bc286e6..674ba857818ca 100644
--- a/nixos/modules/security/lock-kernel-modules.nix
+++ b/nixos/modules/security/lock-kernel-modules.nix
@@ -11,11 +11,11 @@ with lib;
     security.lockKernelModules = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = lib.mdDoc ''
         Disable kernel module loading once the system is fully initialised.
         Module loading is disabled until the next reboot. Problems caused
         by delayed module loading can be fixed by adding the module(s) in
-        question to <option>boot.kernelModules</option>.
+        question to {option}`boot.kernelModules`.
       '';
     };
   };
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 23d1344a57ac5..d9d072b36e6e6 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -482,9 +482,10 @@ let
           (let p11 = config.security.pam.p11; in optionalString cfg.p11Auth ''
             auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so
           '') +
-          (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth ''
-            auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"}
-          '') +
+          (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth (''
+              auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ''
+                + ''${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"} ${optionalString (u2f.origin != null) "origin=${u2f.origin}"}
+          '')) +
           optionalString cfg.usbAuth ''
             auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so
           '' +
@@ -610,7 +611,6 @@ let
             session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
           '' +
           optionalString cfg.pamMount ''
-            session [success=1 default=ignore] ${pkgs.pam}/lib/security/pam_succeed_if.so service = systemd-user quiet
             session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
           '' +
           optionalString use_ldap ''
@@ -895,6 +895,24 @@ in
         '';
       };
 
+      origin = mkOption {
+        default = null;
+        type = with types; nullOr str;
+        description = ''
+            By default <literal>pam-u2f</literal> module sets the origin
+            to <literal>pam://$HOSTNAME</literal>.
+            Setting origin to an host independent value will allow you to
+            reuse credentials across machines
+
+            When using <command>pamu2fcfg</command>, you can specify your
+            application ID with the <literal>-o</literal> flag.
+
+            More information can be found <link
+            xlink:href="https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html">
+            here</link>
+        '';
+      };
+
       control = mkOption {
         default = "sufficient";
         type = types.enum [ "required" "requisite" "sufficient" "optional" ];
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
index 1ba149745c654..0a2d81445ba53 100644
--- a/nixos/modules/security/polkit.nix
+++ b/nixos/modules/security/polkit.nix
@@ -29,7 +29,7 @@ in
             if (subject.local) return "yes";
           });
         '';
-      description =
+      description = lib.mdDoc
         ''
           Any polkit rules to be added to config (in JavaScript ;-). See:
           http://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html#polkit-rules
@@ -40,12 +40,12 @@ in
       type = types.listOf types.str;
       default = [ "unix-group:wheel" ];
       example = [ "unix-user:alice" "unix-group:admin" ];
-      description =
+      description = lib.mdDoc
         ''
           Specifies which users are considered “administrators”, for those
           actions that require the user to authenticate as an
-          administrator (i.e. have an <literal>auth_admin</literal>
-          value).  By default, this is all users in the <literal>wheel</literal> group.
+          administrator (i.e. have an `auth_admin`
+          value).  By default, this is all users in the `wheel` group.
         '';
     };
 
diff --git a/nixos/modules/security/systemd-confinement.nix b/nixos/modules/security/systemd-confinement.nix
index f3a2de3bf87a0..07b725effb7d1 100644
--- a/nixos/modules/security/systemd-confinement.nix
+++ b/nixos/modules/security/systemd-confinement.nix
@@ -22,16 +22,17 @@ in {
       options.confinement.fullUnit = lib.mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           Whether to include the full closure of the systemd unit file into the
           chroot, instead of just the dependencies for the executables.
 
-          <warning><para>While it may be tempting to just enable this option to
+          ::: {.warning}
+          While it may be tempting to just enable this option to
           make things work quickly, please be aware that this might add paths
           to the closure of the chroot that you didn't anticipate. It's better
-          to use <option>confinement.packages</option> to <emphasis
-          role="strong">explicitly</emphasis> add additional store paths to the
-          chroot.</para></warning>
+          to use {option}`confinement.packages` to **explicitly** add additional store paths to the
+          chroot.
+          :::
         '';
       };
 
diff --git a/nixos/modules/services/audio/hqplayerd.nix b/nixos/modules/services/audio/hqplayerd.nix
index 416d12ce21724..822a8abef2439 100644
--- a/nixos/modules/services/audio/hqplayerd.nix
+++ b/nixos/modules/services/audio/hqplayerd.nix
@@ -133,7 +133,7 @@ in
     users.users = {
       hqplayer = {
         description = "hqplayer daemon user";
-        extraGroups = [ "audio" ];
+        extraGroups = [ "audio" "video" ];
         group = "hqplayer";
         uid = config.ids.uids.hqplayer;
       };
diff --git a/nixos/modules/services/audio/mpd.nix b/nixos/modules/services/audio/mpd.nix
index 586b9ffa68888..11733d99fca65 100644
--- a/nixos/modules/services/audio/mpd.nix
+++ b/nixos/modules/services/audio/mpd.nix
@@ -215,6 +215,7 @@ in {
     systemd.sockets.mpd = mkIf cfg.startWhenNeeded {
       wantedBy = [ "sockets.target" ];
       listenStreams = [
+        ""  # Note: this is needed to override the upstream unit
         (if pkgs.lib.hasPrefix "/" cfg.network.listenAddress
           then cfg.network.listenAddress
           else "${optionalString (cfg.network.listenAddress != "any") "${cfg.network.listenAddress}:"}${toString cfg.network.port}")
diff --git a/nixos/modules/services/audio/navidrome.nix b/nixos/modules/services/audio/navidrome.nix
index 3660e05310be1..319212c020777 100644
--- a/nixos/modules/services/audio/navidrome.nix
+++ b/nixos/modules/services/audio/navidrome.nix
@@ -45,7 +45,10 @@ in {
         RootDirectory = "/run/navidrome";
         ReadWritePaths = "";
         BindReadOnlyPaths = [
+          # navidrome uses online services to download additional album metadata / covers
+          "${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt"
           builtins.storeDir
+          "/etc"
         ] ++ lib.optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder;
         CapabilityBoundingSet = "";
         RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
diff --git a/nixos/modules/services/audio/roon-server.nix b/nixos/modules/services/audio/roon-server.nix
index de1f61c8e73b7..c8a2071c15592 100644
--- a/nixos/modules/services/audio/roon-server.nix
+++ b/nixos/modules/services/audio/roon-server.nix
@@ -53,10 +53,12 @@ in {
     networking.firewall = mkIf cfg.openFirewall {
       allowedTCPPortRanges = [
         { from = 9100; to = 9200; }
-        { from = 9330; to = 9332; }
+        { from = 9330; to = 9339; }
+        { from = 30000; to = 30010; }
       ];
       allowedUDPPorts = [ 9003 ];
       extraCommands = ''
+        ## IGMP / Broadcast ##
         iptables -A INPUT -s 224.0.0.0/4 -j ACCEPT
         iptables -A INPUT -d 224.0.0.0/4 -j ACCEPT
         iptables -A INPUT -s 240.0.0.0/5 -j ACCEPT
diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix
index 562458eb45710..f22b613382734 100644
--- a/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixos/modules/services/backup/postgresql-backup.nix
@@ -17,8 +17,8 @@ let
 
       compressCmd = getAttr cfg.compression {
         "none" = "cat";
-        "gzip" = "${pkgs.gzip}/bin/gzip -c";
-        "zstd" = "${pkgs.zstd}/bin/zstd -c";
+        "gzip" = "${pkgs.gzip}/bin/gzip -c -${toString cfg.compressionLevel}";
+        "zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel}";
       };
 
       mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
@@ -130,16 +130,33 @@ in {
           The type of compression to use on the generated database dump.
         '';
       };
+
+      compressionLevel = mkOption {
+        type = types.ints.between 1 19;
+        default = 6;
+        description = ''
+          The compression level used when compression is enabled.
+          gzip accepts levels 1 to 9. zstd accepts levels 1 to 19.
+        '';
+      };
     };
 
   };
 
   config = mkMerge [
     {
-      assertions = [{
-        assertion = cfg.backupAll -> cfg.databases == [];
-        message = "config.services.postgresqlBackup.backupAll cannot be used together with config.services.postgresqlBackup.databases";
-      }];
+      assertions = [
+        {
+          assertion = cfg.backupAll -> cfg.databases == [];
+          message = "config.services.postgresqlBackup.backupAll cannot be used together with config.services.postgresqlBackup.databases";
+        }
+        {
+          assertion = cfg.compression == "none" ||
+            (cfg.compression == "gzip" && cfg.compressionLevel >= 1 && cfg.compressionLevel <= 9) ||
+            (cfg.compression == "zstd" && cfg.compressionLevel >= 1 && cfg.compressionLevel <= 19);
+          message = "config.services.postgresqlBackup.compressionLevel must be set between 1 and 9 for gzip and 1 and 19 for zstd";
+        }
+      ];
     }
     (mkIf cfg.enable {
       systemd.tmpfiles.rules = [
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index 333fdd494e3b9..a0de1124c661c 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -222,6 +222,15 @@ in
             A script that must run after finishing the backup process.
           '';
         };
+
+        package = mkOption {
+          type = types.package;
+          default = pkgs.restic;
+          defaultText = literalExpression "pkgs.restic";
+          description = ''
+            Restic package to use.
+          '';
+        };
       };
     }));
     default = { };
@@ -254,7 +263,7 @@ in
         (name: backup:
           let
             extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
-            resticCmd = "${pkgs.restic}/bin/restic${extraOptions}";
+            resticCmd = "${backup.package}/bin/restic${extraOptions}";
             filesFromTmpFile = "/run/restic-backups-${name}/includes";
             backupPaths =
               if (backup.dynamicFilesFrom == null)
diff --git a/nixos/modules/services/backup/zrepl.nix b/nixos/modules/services/backup/zrepl.nix
index 73f5e4d9f6d76..1b4216ed1714a 100644
--- a/nixos/modules/services/backup/zrepl.nix
+++ b/nixos/modules/services/backup/zrepl.nix
@@ -13,6 +13,13 @@ in
     services.zrepl = {
       enable = mkEnableOption "zrepl";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.zrepl;
+        defaultText = literalExpression "pkgs.zrepl";
+        description = "Which package to use for zrepl";
+      };
+
       settings = mkOption {
         default = { };
         description = ''
@@ -30,14 +37,14 @@ in
   ### Implementation ###
 
   config = mkIf cfg.enable {
-    environment.systemPackages = [ pkgs.zrepl ];
+    environment.systemPackages = [ cfg.package ];
 
     # zrepl looks for its config in this location by default. This
     # allows the use of e.g. `zrepl signal wakeup <job>` without having
     # to specify the storepath of the config.
     environment.etc."zrepl/zrepl.yml".source = configFile;
 
-    systemd.packages = [ pkgs.zrepl ];
+    systemd.packages = [ cfg.package ];
 
     # Note that pkgs.zrepl copies and adapts the upstream systemd unit, and
     # the fields defined here only override certain fields from that unit.
diff --git a/nixos/modules/services/cluster/k3s/default.nix b/nixos/modules/services/cluster/k3s/default.nix
index 3a36cfa3f37b8..bc5e8c66e2a5f 100644
--- a/nixos/modules/services/cluster/k3s/default.nix
+++ b/nixos/modules/services/cluster/k3s/default.nix
@@ -3,8 +3,14 @@
 with lib;
 let
   cfg = config.services.k3s;
+  removeOption = config: instruction:
+    lib.mkRemovedOptionModule ([ "services" "k3s" ] ++ config) instruction;
 in
 {
+  imports = [
+    (removeOption [ "docker" ] "k3s docker option is no longer supported.")
+  ];
+
   # interface
   options.services.k3s = {
     enable = mkEnableOption "k3s";
@@ -48,12 +54,6 @@ in
       default = null;
     };
 
-    docker = mkOption {
-      type = types.bool;
-      default = false;
-      description = "Use docker to run containers rather than the built-in containerd.";
-    };
-
     extraFlags = mkOption {
       description = "Extra flags to pass to the k3s command.";
       type = types.str;
@@ -88,14 +88,11 @@ in
       }
     ];
 
-    virtualisation.docker = mkIf cfg.docker {
-      enable = mkDefault true;
-    };
     environment.systemPackages = [ config.services.k3s.package ];
 
     systemd.services.k3s = {
       description = "k3s service";
-      after = [ "network.service" "firewall.service" ] ++ (optional cfg.docker "docker.service");
+      after = [ "network.service" "firewall.service" ];
       wants = [ "network.service" "firewall.service" ];
       wantedBy = [ "multi-user.target" ];
       path = optional config.boot.zfs.enabled config.boot.zfs.package;
@@ -113,8 +110,7 @@ in
         ExecStart = concatStringsSep " \\\n " (
           [
             "${cfg.package}/bin/k3s ${cfg.role}"
-          ] ++ (optional cfg.docker "--docker")
-          ++ (optional (cfg.docker && config.systemd.enableUnifiedCgroupHierarchy) "--kubelet-arg=cgroup-driver=systemd")
+          ]
           ++ (optional cfg.disableAgent "--disable-agent")
           ++ (optional (cfg.serverAddr != "") "--server ${cfg.serverAddr}")
           ++ (optional (cfg.token != "") "--token ${cfg.token}")
diff --git a/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixos/modules/services/cluster/kubernetes/kubelet.nix
index af3a5062febc9..4363ed35d3416 100644
--- a/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -23,7 +23,11 @@ let
   infraContainer = pkgs.dockerTools.buildImage {
     name = "pause";
     tag = "latest";
-    contents = top.package.pause;
+    copyToRoot = pkgs.buildEnv {
+      name = "image-root";
+      pathsToLink = [ "/bin" ];
+      paths = [ top.package.pause ];
+    };
     config.Cmd = ["/bin/pause"];
   };
 
diff --git a/nixos/modules/services/continuous-integration/github-runner.nix b/nixos/modules/services/continuous-integration/github-runner.nix
index 30dd919b81a38..19e73582a0904 100644
--- a/nixos/modules/services/continuous-integration/github-runner.nix
+++ b/nixos/modules/services/continuous-integration/github-runner.nix
@@ -280,7 +280,6 @@ in
         CapabilityBoundingSet = "";
         # ProtectClock= adds DeviceAllow=char-rtc r
         DeviceAllow = "";
-        LockPersonality = true;
         NoNewPrivileges = true;
         PrivateDevices = true;
         PrivateMounts = true;
@@ -300,13 +299,17 @@ in
         RestrictSUIDSGID = true;
         UMask = "0066";
         ProtectProc = "invisible";
-        ProcSubset = "pid";
         SystemCallFilter = [
-          "~@debug"
-          "~@mount"
-          "~@privileged"
+          "~@clock"
           "~@cpu-emulation"
+          "~@module"
+          "~@mount"
           "~@obsolete"
+          "~@raw-io"
+          "~@reboot"
+          "~capset"
+          "~setdomainname"
+          "~sethostname"
         ];
         RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ];
 
@@ -314,6 +317,17 @@ in
         PrivateNetwork = false;
         # Cannot be true due to Node
         MemoryDenyWriteExecute = false;
+
+        # The more restrictive "pid" option makes `nix` commands in CI emit
+        # "GC Warning: Couldn't read /proc/stat"
+        # You may want to set this to "pid" if not using `nix` commands
+        ProcSubset = "all";
+        # Coverage programs for compiled code such as `cargo-tarpaulin` disable
+        # ASLR (address space layout randomization) which requires the
+        # `personality` syscall
+        # You may want to set this to `true` if not using coverage tooling on
+        # compiled code
+        LockPersonality = false;
       };
     };
   };
diff --git a/nixos/modules/services/continuous-integration/hydra/default.nix b/nixos/modules/services/continuous-integration/hydra/default.nix
index 87806d48e89f3..d3945f2370d71 100644
--- a/nixos/modules/services/continuous-integration/hydra/default.nix
+++ b/nixos/modules/services/continuous-integration/hydra/default.nix
@@ -298,6 +298,7 @@ in
         environment = env // {
           HYDRA_DBI = "${env.HYDRA_DBI};application_name=hydra-init";
         };
+        path = [ pkgs.util-linux ];
         preStart = ''
           mkdir -p ${baseDir}
           chown hydra:hydra ${baseDir}
@@ -318,11 +319,11 @@ in
 
           ${optionalString haveLocalDB ''
             if ! [ -e ${baseDir}/.db-created ]; then
-              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra
-              ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O hydra hydra
+              runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createuser hydra
+              runuser -u ${config.services.postgresql.superUser} ${config.services.postgresql.package}/bin/createdb -O hydra hydra
               touch ${baseDir}/.db-created
             fi
-            echo "create extension if not exists pg_trgm" | ${pkgs.sudo}/bin/sudo -u ${config.services.postgresql.superUser} -- ${config.services.postgresql.package}/bin/psql hydra
+            echo "create extension if not exists pg_trgm" | runuser -u ${config.services.postgresql.superUser} -- ${config.services.postgresql.package}/bin/psql hydra
           ''}
 
           if [ ! -e ${cfg.gcRootsDir} ]; then
diff --git a/nixos/modules/services/continuous-integration/jenkins/job-builder.nix b/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
index 3ca1542c18f2a..49b39b03d4758 100644
--- a/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/job-builder.nix
@@ -156,12 +156,22 @@ in {
           reloadScript = ''
             echo "Asking Jenkins to reload config"
             curl_opts="--silent --fail --show-error"
-            access_token=${if cfg.accessTokenFile != ""
-                           then "$(cat '${cfg.accessTokenFile}')"
-                           else cfg.accessToken}
-            jenkins_url="http://${cfg.accessUser}:$access_token@${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}"
-            crumb=$(curl $curl_opts "$jenkins_url"'/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
-            curl $curl_opts -X POST -H "$crumb" "$jenkins_url"/reload
+            access_token_file=${if cfg.accessTokenFile != ""
+                           then cfg.accessTokenFile
+                           else "$RUNTIME_DIRECTORY/jenkins_access_token.txt"}
+            if [ "${cfg.accessToken}" != "" ]; then
+               (umask 0077; printf "${cfg.accessToken}" >"$access_token_file")
+            fi
+            jenkins_url="http://${jenkinsCfg.listenAddress}:${toString jenkinsCfg.port}${jenkinsCfg.prefix}"
+            auth_file="$RUNTIME_DIRECTORY/jenkins_auth_file.txt"
+            trap 'rm -f "$auth_file"' EXIT
+            (umask 0077; printf "${cfg.accessUser}:@password_placeholder@" >"$auth_file")
+            "${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "$access_token_file" "$auth_file"
+
+            if ! "${pkgs.jenkins}/bin/jenkins-cli" -s "$jenkins_url" -auth "@$auth_file" reload-configuration; then
+                echo "error: failed to reload configuration"
+                exit 1
+            fi
           '';
         in
           ''
@@ -233,6 +243,7 @@ in {
             done
           '' + (if cfg.accessUser != "" then reloadScript else "");
       serviceConfig = {
+        Type = "oneshot";
         User = jenkinsCfg.user;
         RuntimeDirectory = "jenkins-job-builder";
       };
diff --git a/nixos/modules/services/databases/cassandra.nix b/nixos/modules/services/databases/cassandra.nix
index b36cac35e7c29..b457e69babaad 100644
--- a/nixos/modules/services/databases/cassandra.nix
+++ b/nixos/modules/services/databases/cassandra.nix
@@ -4,11 +4,12 @@ let
   inherit (lib)
     concatStringsSep
     flip
-    literalDocBook
+    literalMD
     literalExpression
     optionalAttrs
     optionals
     recursiveUpdate
+    mdDoc
     mkEnableOption
     mkIf
     mkOption
@@ -107,7 +108,7 @@ in
     clusterName = mkOption {
       type = types.str;
       default = "Test Cluster";
-      description = ''
+      description = mdDoc ''
         The name of the cluster.
         This setting prevents nodes in one logical cluster from joining
         another. All nodes in a cluster must have the same value.
@@ -117,19 +118,19 @@ in
     user = mkOption {
       type = types.str;
       default = defaultUser;
-      description = "Run Apache Cassandra under this user.";
+      description = mdDoc "Run Apache Cassandra under this user.";
     };
 
     group = mkOption {
       type = types.str;
       default = defaultUser;
-      description = "Run Apache Cassandra under this group.";
+      description = mdDoc "Run Apache Cassandra under this group.";
     };
 
     homeDir = mkOption {
       type = types.path;
       default = "/var/lib/cassandra";
-      description = ''
+      description = mdDoc ''
         Home directory for Apache Cassandra.
       '';
     };
@@ -139,7 +140,7 @@ in
       default = pkgs.cassandra;
       defaultText = literalExpression "pkgs.cassandra";
       example = literalExpression "pkgs.cassandra_3_11";
-      description = ''
+      description = mdDoc ''
         The Apache Cassandra package to use.
       '';
     };
@@ -147,8 +148,8 @@ in
     jvmOpts = mkOption {
       type = types.listOf types.str;
       default = [ ];
-      description = ''
-        Populate the JVM_OPT environment variable.
+      description = mdDoc ''
+        Populate the `JVM_OPT` environment variable.
       '';
     };
 
@@ -156,20 +157,20 @@ in
       type = types.nullOr types.str;
       default = "127.0.0.1";
       example = null;
-      description = ''
+      description = mdDoc ''
         Address or interface to bind to and tell other Cassandra nodes
         to connect to. You _must_ change this if you want multiple
         nodes to be able to communicate!
 
-        Set listenAddress OR listenInterface, not both.
+        Set {option}`listenAddress` OR {option}`listenInterface`, not both.
 
         Leaving it blank leaves it up to
-        InetAddress.getLocalHost(). This will always do the Right
-        Thing _if_ the node is properly configured (hostname, name
+        `InetAddress.getLocalHost()`. This will always do the "Right
+        Thing" _if_ the node is properly configured (hostname, name
         resolution, etc), and the Right Thing is to use the address
         associated with the hostname (it might not be).
 
-        Setting listen_address to 0.0.0.0 is always wrong.
+        Setting {option}`listenAddress` to `0.0.0.0` is always wrong.
       '';
     };
 
@@ -177,8 +178,8 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "eth1";
-      description = ''
-        Set listenAddress OR listenInterface, not both. Interfaces
+      description = mdDoc ''
+        Set `listenAddress` OR `listenInterface`, not both. Interfaces
         must correspond to a single address, IP aliasing is not
         supported.
       '';
@@ -188,18 +189,18 @@ in
       type = types.nullOr types.str;
       default = "127.0.0.1";
       example = null;
-      description = ''
+      description = mdDoc ''
         The address or interface to bind the native transport server to.
 
-        Set rpcAddress OR rpcInterface, not both.
+        Set {option}`rpcAddress` OR {option}`rpcInterface`, not both.
 
-        Leaving rpcAddress blank has the same effect as on
-        listenAddress (i.e. it will be based on the configured hostname
+        Leaving {option}`rpcAddress` blank has the same effect as on
+        {option}`listenAddress` (i.e. it will be based on the configured hostname
         of the node).
 
-        Note that unlike listenAddress, you can specify 0.0.0.0, but you
-        must also set extraConfig.broadcast_rpc_address to a value other
-        than 0.0.0.0.
+        Note that unlike {option}`listenAddress`, you can specify `"0.0.0.0"`, but you
+        must also set `extraConfig.broadcast_rpc_address` to a value other
+        than `"0.0.0.0"`.
 
         For security reasons, you should not expose this port to the
         internet. Firewall it if needed.
@@ -210,8 +211,8 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "eth1";
-      description = ''
-        Set rpcAddress OR rpcInterface, not both. Interfaces must
+      description = mdDoc ''
+        Set {option}`rpcAddress` OR {option}`rpcInterface`, not both. Interfaces must
         correspond to a single address, IP aliasing is not supported.
       '';
     };
@@ -233,7 +234,7 @@ in
           <logger name="com.thinkaurelius.thrift" level="ERROR"/>
         </configuration>
       '';
-      description = ''
+      description = mdDoc ''
         XML logback configuration for cassandra
       '';
     };
@@ -241,24 +242,24 @@ in
     seedAddresses = mkOption {
       type = types.listOf types.str;
       default = [ "127.0.0.1" ];
-      description = ''
+      description = mdDoc ''
         The addresses of hosts designated as contact points in the cluster. A
         joining node contacts one of the nodes in the seeds list to learn the
         topology of the ring.
-        Set to 127.0.0.1 for a single node cluster.
+        Set to `[ "127.0.0.1" ]` for a single node cluster.
       '';
     };
 
     allowClients = mkOption {
       type = types.bool;
       default = true;
-      description = ''
+      description = mdDoc ''
         Enables or disables the native transport server (CQL binary protocol).
-        This server uses the same address as the <literal>rpcAddress</literal>,
-        but the port it uses is not <literal>rpc_port</literal> but
-        <literal>native_transport_port</literal>. See the official Cassandra
+        This server uses the same address as the {option}`rpcAddress`,
+        but the port it uses is not `rpc_port` but
+        `native_transport_port`. See the official Cassandra
         docs for more information on these variables and set them using
-        <literal>extraConfig</literal>.
+        {option}`extraConfig`.
       '';
     };
 
@@ -269,8 +270,8 @@ in
         {
           commitlog_sync_batch_window_in_ms = 3;
         };
-      description = ''
-        Extra options to be merged into cassandra.yaml as nix attribute set.
+      description = mdDoc ''
+        Extra options to be merged into {file}`cassandra.yaml` as nix attribute set.
       '';
     };
 
@@ -278,8 +279,8 @@ in
       type = types.lines;
       default = "";
       example = literalExpression ''"CLASSPATH=$CLASSPATH:''${extraJar}"'';
-      description = ''
-        Extra shell lines to be appended onto cassandra-env.sh.
+      description = mdDoc ''
+        Extra shell lines to be appended onto {file}`cassandra-env.sh`.
       '';
     };
 
@@ -287,13 +288,13 @@ in
       type = types.nullOr types.str;
       default = "3w";
       example = null;
-      description = ''
+      description = mdDoc ''
         Set the interval how often full repairs are run, i.e.
-        <literal>nodetool repair --full</literal> is executed. See
-        https://cassandra.apache.org/doc/latest/operating/repair.html
+        {command}`nodetool repair --full` is executed. See
+        <https://cassandra.apache.org/doc/latest/operating/repair.html>
         for more information.
 
-        Set to <literal>null</literal> to disable full repairs.
+        Set to `null` to disable full repairs.
       '';
     };
 
@@ -301,7 +302,7 @@ in
       type = types.listOf types.str;
       default = [ ];
       example = [ "--partitioner-range" ];
-      description = ''
+      description = mdDoc ''
         Options passed through to the full repair command.
       '';
     };
@@ -310,13 +311,13 @@ in
       type = types.nullOr types.str;
       default = "3d";
       example = null;
-      description = ''
+      description = mdDoc ''
         Set the interval how often incremental repairs are run, i.e.
-        <literal>nodetool repair</literal> is executed. See
-        https://cassandra.apache.org/doc/latest/operating/repair.html
+        {command}`nodetool repair` is executed. See
+        <https://cassandra.apache.org/doc/latest/operating/repair.html>
         for more information.
 
-        Set to <literal>null</literal> to disable incremental repairs.
+        Set to `null` to disable incremental repairs.
       '';
     };
 
@@ -324,7 +325,7 @@ in
       type = types.listOf types.str;
       default = [ ];
       example = [ "--partitioner-range" ];
-      description = ''
+      description = mdDoc ''
         Options passed through to the incremental repair command.
       '';
     };
@@ -333,15 +334,15 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "4G";
-      description = ''
-        Must be left blank or set together with heapNewSize.
+      description = mdDoc ''
+        Must be left blank or set together with {option}`heapNewSize`.
         If left blank a sensible value for the available amount of RAM and CPU
         cores is calculated.
 
         Override to set the amount of memory to allocate to the JVM at
         start-up. For production use you may wish to adjust this for your
-        environment. MAX_HEAP_SIZE is the total amount of memory dedicated
-        to the Java heap. HEAP_NEWSIZE refers to the size of the young
+        environment. `MAX_HEAP_SIZE` is the total amount of memory dedicated
+        to the Java heap. `HEAP_NEWSIZE` refers to the size of the young
         generation.
 
         The main trade-off for the young generation is that the larger it
@@ -354,21 +355,21 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "800M";
-      description = ''
-        Must be left blank or set together with heapNewSize.
+      description = mdDoc ''
+        Must be left blank or set together with {option}`heapNewSize`.
         If left blank a sensible value for the available amount of RAM and CPU
         cores is calculated.
 
         Override to set the amount of memory to allocate to the JVM at
         start-up. For production use you may wish to adjust this for your
-        environment. HEAP_NEWSIZE refers to the size of the young
+        environment. `HEAP_NEWSIZE` refers to the size of the young
         generation.
 
         The main trade-off for the young generation is that the larger it
         is, the longer GC pause times will be. The shorter it is, the more
         expensive GC will be (usually).
 
-        The example HEAP_NEWSIZE assumes a modern 8-core+ machine for decent pause
+        The example `HEAP_NEWSIZE` assumes a modern 8-core+ machine for decent pause
         times. If in doubt, and if you do not particularly want to tweak, go with
         100 MB per physical CPU core.
       '';
@@ -378,7 +379,7 @@ in
       type = types.nullOr types.int;
       default = null;
       example = 4;
-      description = ''
+      description = mdDoc ''
         Set this to control the amount of arenas per-thread in glibc.
       '';
     };
@@ -386,19 +387,19 @@ in
     remoteJmx = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = mdDoc ''
         Cassandra ships with JMX accessible *only* from localhost.
         To enable remote JMX connections set to true.
 
         Be sure to also enable authentication and/or TLS.
-        See: https://wiki.apache.org/cassandra/JmxSecurity
+        See: <https://wiki.apache.org/cassandra/JmxSecurity>
       '';
     };
 
     jmxPort = mkOption {
       type = types.int;
       default = 7199;
-      description = ''
+      description = mdDoc ''
         Specifies the default port over which Cassandra will be available for
         JMX connections.
         For security reasons, you should not expose this port to the internet.
@@ -408,11 +409,11 @@ in
 
     jmxRoles = mkOption {
       default = [ ];
-      description = ''
-        Roles that are allowed to access the JMX (e.g. nodetool)
-        BEWARE: The passwords will be stored world readable in the nix-store.
+      description = mdDoc ''
+        Roles that are allowed to access the JMX (e.g. {command}`nodetool`)
+        BEWARE: The passwords will be stored world readable in the nix store.
                 It's recommended to use your own protected file using
-                <literal>jmxRolesFile</literal>
+                {option}`jmxRolesFile`
 
         Doesn't work in versions older than 3.11 because they don't like that
         it's world readable.
@@ -437,7 +438,7 @@ in
         if versionAtLeast cfg.package.version "3.11"
         then pkgs.writeText "jmx-roles-file" defaultJmxRolesFile
         else null;
-      defaultText = literalDocBook ''generated configuration file if version is at least 3.11, otherwise <literal>null</literal>'';
+      defaultText = literalMD ''generated configuration file if version is at least 3.11, otherwise `null`'';
       example = "/var/lib/cassandra/jmx.password";
       description = ''
         Specify your own jmx roles file.
diff --git a/nixos/modules/services/databases/dgraph.nix b/nixos/modules/services/databases/dgraph.nix
new file mode 100644
index 0000000000000..5c1ae536051c1
--- /dev/null
+++ b/nixos/modules/services/databases/dgraph.nix
@@ -0,0 +1,148 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.dgraph;
+  settingsFormat = pkgs.formats.json {};
+  configFile = settingsFormat.generate "config.json" cfg.settings;
+  dgraphWithNode = pkgs.runCommand "dgraph" {
+    nativeBuildInputs = [ pkgs.makeWrapper ];
+  }
+  ''
+    mkdir -p $out/bin
+    makeWrapper ${cfg.package}/bin/dgraph $out/bin/dgraph \
+      --set PATH '${lib.makeBinPath [ pkgs.nodejs ]}:$PATH' \
+  '';
+  securityOptions = {
+      NoNewPrivileges = true;
+
+      AmbientCapabilities = "";
+      CapabilityBoundingSet = "";
+
+      DeviceAllow = "";
+
+      LockPersonality = true;
+
+      PrivateTmp = true;
+      PrivateDevices = true;
+      PrivateUsers = true;
+
+      ProtectClock = true;
+      ProtectControlGroups = true;
+      ProtectHostname = true;
+      ProtectKernelLogs = true;
+      ProtectKernelModules = true;
+      ProtectKernelTunables = true;
+
+      RemoveIPC = true;
+
+      RestrictNamespaces = true;
+      RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+      RestrictRealtime = true;
+      RestrictSUIDSGID = true;
+
+      SystemCallArchitectures = "native";
+      SystemCallErrorNumber = "EPERM";
+      SystemCallFilter = [
+        "@system-service"
+        "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"
+      ];
+  };
+in
+{
+  options = {
+    services.dgraph = {
+      enable = mkEnableOption "Dgraph native GraphQL database with a graph backend";
+
+      package = lib.mkPackageOption pkgs "dgraph" { };
+
+      settings = mkOption {
+        type = settingsFormat.type;
+        default = {};
+        description = ''
+          Contents of the dgraph config. For more details see https://dgraph.io/docs/deploy/config
+        '';
+      };
+
+      alpha = {
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = ''
+            The host which dgraph alpha will be run on.
+          '';
+        };
+        port = mkOption {
+          type = types.port;
+          default = 7080;
+          description = ''
+            The port which to run dgraph alpha on.
+          '';
+        };
+
+      };
+
+      zero = {
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = ''
+            The host which dgraph zero will be run on.
+          '';
+        };
+        port = mkOption {
+          type = types.port;
+          default = 5080;
+          description = ''
+            The port which to run dgraph zero on.
+          '';
+        };
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.dgraph.settings = {
+      badger.compression = mkDefault "zstd:3";
+    };
+
+    systemd.services.dgraph-zero = {
+      description = "Dgraph native GraphQL database with a graph backend. Zero controls node clustering";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        StateDirectory = "dgraph-zero";
+        WorkingDirectory = "/var/lib/dgraph-zero";
+        DynamicUser = true;
+        ExecStart = "${cfg.package}/bin/dgraph zero --my ${cfg.zero.host}:${toString cfg.zero.port}";
+        Restart = "on-failure";
+      } // securityOptions;
+    };
+
+    systemd.services.dgraph-alpha = {
+      description = "Dgraph native GraphQL database with a graph backend. Alpha serves data";
+      after = [ "network.target" "dgraph-zero.service" ];
+      requires = [ "dgraph-zero.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        StateDirectory = "dgraph-alpha";
+        WorkingDirectory = "/var/lib/dgraph-alpha";
+        DynamicUser = true;
+        ExecStart = "${dgraphWithNode}/bin/dgraph alpha --config ${configFile} --my ${cfg.alpha.host}:${toString cfg.alpha.port} --zero ${cfg.zero.host}:${toString cfg.zero.port}";
+        ExecStop = ''
+          ${pkgs.curl}/bin/curl --data "mutation { shutdown { response { message code } } }" \
+              --header 'Content-Type: application/graphql' \
+              -X POST \
+              http://localhost:8080/admin
+        '';
+        Restart = "on-failure";
+      } // securityOptions;
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ happysalada ];
+}
diff --git a/nixos/modules/services/databases/openldap.nix b/nixos/modules/services/databases/openldap.nix
index 1967a2371bdd7..7c7340094e2c5 100644
--- a/nixos/modules/services/databases/openldap.nix
+++ b/nixos/modules/services/databases/openldap.nix
@@ -245,7 +245,7 @@ in {
     };
   };
 
-  meta.maintainers = with lib.maintainers; [ mic92 kwohlfahrt ];
+  meta.maintainers = with lib.maintainers; [ kwohlfahrt ];
 
   config = mkIf cfg.enable {
     assertions = map (opt: {
@@ -312,6 +312,7 @@ in {
           "-h" (lib.concatStringsSep " " cfg.urlList)
         ]);
         Type = "notify";
+        NotifyAccess = "all";
         PIDFile = cfg.settings.attrs.olcPidFile;
       };
     };
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index a1bd73c9e371d..5532c54019706 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -166,7 +166,11 @@ in {
             save = mkOption {
               type = with types; listOf (listOf int);
               default = [ [900 1] [300 10] [60 10000] ];
-              description = "The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.";
+              description = mdDoc ''
+                The schedule in which data is persisted to disk, represented as a list of lists where the first element represent the amount of seconds and the second the number of changes.
+
+                If set to the empty list (`[]`) then RDB persistence will be disabled (useful if you are using AOF or don't want any persistence).
+              '';
             };
 
             slaveOf = mkOption {
@@ -268,7 +272,11 @@ in {
               syslog-enabled = config.syslog;
               databases = config.databases;
               maxclients = config.maxclients;
-              save = map (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}") config.save;
+              save = if config.save == []
+                then ''""'' # Disable saving with `save = ""`
+                else map
+                  (d: "${toString (builtins.elemAt d 0)} ${toString (builtins.elemAt d 1)}")
+                  config.save;
               dbfilename = "dump.rdb";
               dir = "/var/lib/${redisName name}";
               appendOnly = config.appendOnly;
diff --git a/nixos/modules/services/databases/riak.nix b/nixos/modules/services/databases/riak.nix
deleted file mode 100644
index cc4237d038cdb..0000000000000
--- a/nixos/modules/services/databases/riak.nix
+++ /dev/null
@@ -1,162 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-
-  cfg = config.services.riak;
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.riak = {
-
-      enable = mkEnableOption "riak";
-
-      package = mkOption {
-        type = types.package;
-        default = pkgs.riak;
-        defaultText = literalExpression "pkgs.riak";
-        description = ''
-          Riak package to use.
-        '';
-      };
-
-      nodeName = mkOption {
-        type = types.str;
-        default = "riak@127.0.0.1";
-        description = ''
-          Name of the Erlang node.
-        '';
-      };
-
-      distributedCookie = mkOption {
-        type = types.str;
-        default = "riak";
-        description = ''
-          Cookie for distributed node communication.  All nodes in the
-          same cluster should use the same cookie or they will not be able to
-          communicate.
-        '';
-      };
-
-      dataDir = mkOption {
-        type = types.path;
-        default = "/var/db/riak";
-        description = ''
-          Data directory for Riak.
-        '';
-      };
-
-      logDir = mkOption {
-        type = types.path;
-        default = "/var/log/riak";
-        description = ''
-          Log directory for Riak.
-        '';
-      };
-
-      extraConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = ''
-          Additional text to be appended to <filename>riak.conf</filename>.
-        '';
-      };
-
-      extraAdvancedConfig = mkOption {
-        type = types.lines;
-        default = "";
-        description = ''
-          Additional text to be appended to <filename>advanced.config</filename>.
-        '';
-      };
-
-    };
-
-  };
-
-  ###### implementation
-
-  config = mkIf cfg.enable {
-
-    environment.systemPackages = [ cfg.package ];
-    environment.etc."riak/riak.conf".text = ''
-      nodename = ${cfg.nodeName}
-      distributed_cookie = ${cfg.distributedCookie}
-
-      platform_log_dir = ${cfg.logDir}
-      platform_etc_dir = /etc/riak
-      platform_data_dir = ${cfg.dataDir}
-
-      ${cfg.extraConfig}
-    '';
-
-    environment.etc."riak/advanced.config".text = ''
-      ${cfg.extraAdvancedConfig}
-    '';
-
-    users.users.riak = {
-      name = "riak";
-      uid = config.ids.uids.riak;
-      group = "riak";
-      description = "Riak server user";
-    };
-
-    users.groups.riak.gid = config.ids.gids.riak;
-
-    systemd.services.riak = {
-      description = "Riak Server";
-
-      wantedBy = [ "multi-user.target" ];
-      after = [ "network.target" ];
-
-      path = [
-        pkgs.util-linux # for `logger`
-        pkgs.bash
-      ];
-
-      environment.HOME = "${cfg.dataDir}";
-      environment.RIAK_DATA_DIR = "${cfg.dataDir}";
-      environment.RIAK_LOG_DIR = "${cfg.logDir}";
-      environment.RIAK_ETC_DIR = "/etc/riak";
-
-      preStart = ''
-        if ! test -e ${cfg.logDir}; then
-          mkdir -m 0755 -p ${cfg.logDir}
-          chown -R riak ${cfg.logDir}
-        fi
-
-        if ! test -e ${cfg.dataDir}; then
-          mkdir -m 0700 -p ${cfg.dataDir}
-          chown -R riak ${cfg.dataDir}
-        fi
-      '';
-
-      serviceConfig = {
-        ExecStart = "${cfg.package}/bin/riak console";
-        ExecStop = "${cfg.package}/bin/riak stop";
-        StandardInput = "tty";
-        User = "riak";
-        Group = "riak";
-        PermissionsStartOnly = true;
-        # Give Riak a decent amount of time to clean up.
-        TimeoutStopSec = 120;
-        LimitNOFILE = 65536;
-      };
-
-      unitConfig.RequiresMountsFor = [
-        "${cfg.dataDir}"
-        "${cfg.logDir}"
-        "/etc/riak"
-      ];
-    };
-
-  };
-
-}
diff --git a/nixos/modules/services/desktops/pipewire/daemon/minimal.conf.json b/nixos/modules/services/desktops/pipewire/daemon/minimal.conf.json
index c7f58fd5799ad..0f1ebe5749c67 100644
--- a/nixos/modules/services/desktops/pipewire/daemon/minimal.conf.json
+++ b/nixos/modules/services/desktops/pipewire/daemon/minimal.conf.json
@@ -91,6 +91,7 @@
         "adapter.auto-port-config": {
           "mode": "dsp",
           "monitor": false,
+          "control": false,
           "position": "unknown"
         }
       }
@@ -109,6 +110,7 @@
         "adapter.auto-port-config": {
           "mode": "dsp",
           "monitor": false,
+          "control": false,
           "position": "unknown"
         }
       }
diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix
index 07b5dd12ffc54..dd1f5e3a018db 100644
--- a/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -234,7 +234,7 @@ in {
     environment.etc."pipewire/pipewire.conf" = {
       source = json.generate "pipewire.conf" configs.pipewire;
     };
-    environment.etc."pipewire/pipewire-pulse.conf" = {
+    environment.etc."pipewire/pipewire-pulse.conf" = mkIf cfg.pulse.enable {
       source = json.generate "pipewire-pulse.conf" configs.pipewire-pulse;
     };
 
@@ -260,5 +260,8 @@ in {
     # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/464#note_723554
     systemd.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1";
     systemd.user.services.pipewire.environment."PIPEWIRE_LINK_PASSIVE" = "1";
+
+    # pipewire-pulse default config expects pactl to be in PATH
+    systemd.user.services.pipewire-pulse.path = lib.mkIf cfg.pulse.enable [ pkgs.pulseaudio ];
   };
 }
diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix
index 1dbdd842c4a1e..004392c7873c4 100644
--- a/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -37,11 +37,25 @@ in
     environment.systemPackages = [ cfg.package ];
 
     environment.etc."wireplumber/main.lua.d/80-nixos.lua" = lib.mkIf (!pwUsedForAudio) {
-     text = ''
+      text = ''
         -- Pipewire is not used for audio, so prevent it from grabbing audio devices
         alsa_monitor.enable = function() end
       '';
     };
+    environment.etc."wireplumber/main.lua.d/80-systemwide.lua" = lib.mkIf config.services.pipewire.systemWide {
+      text = ''
+        -- 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
+      '';
+    };
+    environment.etc."wireplumber/bluetooth.lua.d/80-systemwide.lua" = lib.mkIf config.services.pipewire.systemWide {
+      text = ''
+        -- When running system-wide, logind-integration needs to be disabled.
+        bluez_monitor.properties["with-logind"] = false
+      '';
+    };
 
     systemd.packages = [ cfg.package ];
 
@@ -50,5 +64,10 @@ in
 
     systemd.services.wireplumber.wantedBy = [ "pipewire.service" ];
     systemd.user.services.wireplumber.wantedBy = [ "pipewire.service" ];
+
+    systemd.services.wireplumber.environment = lib.mkIf config.services.pipewire.systemWide {
+      # Force wireplumber to use system dbus.
+      DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/dbus/system_bus_socket";
+    };
   };
 }
diff --git a/nixos/modules/services/development/jupyter/default.nix b/nixos/modules/services/development/jupyter/default.nix
index 4eacc4782a9a4..7c86e8b647854 100644
--- a/nixos/modules/services/development/jupyter/default.nix
+++ b/nixos/modules/services/development/jupyter/default.nix
@@ -143,6 +143,9 @@ in {
             language = "python";
             logo32 = "''${env.sitePackages}/ipykernel/resources/logo-32x32.png";
             logo64 = "''${env.sitePackages}/ipykernel/resources/logo-64x64.png";
+            extraPaths = {
+              "cool.txt" = pkgs.writeText "cool" "cool content";
+            };
           };
         }
       '';
diff --git a/nixos/modules/services/development/jupyter/kernel-options.nix b/nixos/modules/services/development/jupyter/kernel-options.nix
index 348a8b44b382b..0a9eaafa31856 100644
--- a/nixos/modules/services/development/jupyter/kernel-options.nix
+++ b/nixos/modules/services/development/jupyter/kernel-options.nix
@@ -56,5 +56,14 @@ with lib;
         Path to 64x64 logo png.
       '';
     };
+
+    extraPaths = mkOption {
+      type = types.attrsOf types.path;
+      default = { };
+      example = literalExpression ''"{ examples = ''${env.sitePack}/IRkernel/kernelspec/kernel.js"; }'';
+      description = ''
+        Extra paths to link in kernel directory
+      '';
+    };
   };
 }
diff --git a/nixos/modules/services/display-managers/greetd.nix b/nixos/modules/services/display-managers/greetd.nix
index 895961707d363..4fc6ca3674915 100644
--- a/nixos/modules/services/display-managers/greetd.nix
+++ b/nixos/modules/services/display-managers/greetd.nix
@@ -54,7 +54,7 @@ in
   config = mkIf cfg.enable {
 
     services.greetd.settings.terminal.vt = mkDefault cfg.vt;
-    services.greetd.settings.default_session = mkDefault "greeter";
+    services.greetd.settings.default_session.user = mkDefault "greeter";
 
     security.pam.services.greetd = {
       allowNullPassword = true;
diff --git a/nixos/modules/services/games/minecraft-server.nix b/nixos/modules/services/games/minecraft-server.nix
index 8233962c1a2cc..309fe43eaf4c4 100644
--- a/nixos/modules/services/games/minecraft-server.nix
+++ b/nixos/modules/services/games/minecraft-server.nix
@@ -22,6 +22,15 @@ let
   '' + concatStringsSep "\n" (mapAttrsToList
     (n: v: "${n}=${cfgToString v}") cfg.serverProperties));
 
+  stopScript = pkgs.writeShellScript "minecraft-server-stop" ''
+    echo stop > ${config.systemd.sockets.minecraft-server.socketConfig.ListenFIFO}
+
+    # Wait for the PID of the minecraft server to disappear before
+    # returning, so systemd doesn't attempt to SIGKILL it.
+    while kill -0 "$1" 2> /dev/null; do
+      sleep 1s
+    done
+  '';
 
   # To be able to open the firewall, we need to read out port values in the
   # server properties, but fall back to the defaults when those don't exist.
@@ -172,16 +181,35 @@ in {
     };
     users.groups.minecraft = {};
 
+    systemd.sockets.minecraft-server = {
+      bindsTo = [ "minecraft-server.service" ];
+      socketConfig = {
+        ListenFIFO = "/run/minecraft-server.stdin";
+        SocketMode = "0660";
+        SocketUser = "minecraft";
+        SocketGroup = "minecraft";
+        RemoveOnStop = true;
+        FlushPending = true;
+      };
+    };
+
     systemd.services.minecraft-server = {
       description   = "Minecraft Server Service";
       wantedBy      = [ "multi-user.target" ];
-      after         = [ "network.target" ];
+      requires      = [ "minecraft-server.socket" ];
+      after         = [ "network.target" "minecraft-server.socket" ];
 
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/minecraft-server ${cfg.jvmOpts}";
+        ExecStop = "${stopScript} $MAINPID";
         Restart = "always";
         User = "minecraft";
         WorkingDirectory = cfg.dataDir;
+
+        StandardInput = "socket";
+        StandardOutput = "journal";
+        StandardError = "journal";
+
         # Hardening
         CapabilityBoundingSet = [ "" ];
         DeviceAllow = [ "" ];
diff --git a/nixos/modules/services/hardware/argonone.nix b/nixos/modules/services/hardware/argonone.nix
new file mode 100644
index 0000000000000..638181b1b12e2
--- /dev/null
+++ b/nixos/modules/services/hardware/argonone.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.hardware.argonone;
+in
+{
+  options.services.hardware.argonone = {
+    enable = lib.mkEnableOption "the driver for Argon One Raspberry Pi case fan and power button";
+    package = lib.mkOption {
+      type = lib.types.package;
+      default = pkgs.argononed;
+      defaultText = "pkgs.argononed";
+      description = ''
+        The package implementing the Argon One driver
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    hardware.i2c.enable = true;
+    hardware.deviceTree.overlays = [
+      {
+        name = "argononed";
+        dtboFile = "${cfg.package}/boot/overlays/argonone.dtbo";
+      }
+      {
+        name = "i2c1-okay-overlay";
+        dtsText = ''
+          /dts-v1/;
+          /plugin/;
+          / {
+            compatible = "brcm,bcm2711";
+            fragment@0 {
+              target = <&i2c1>;
+              __overlay__ {
+                status = "okay";
+              };
+            };
+          };
+        '';
+      }
+    ];
+    environment.systemPackages = [ cfg.package ];
+    systemd.services.argononed = {
+      description = "Argon One Raspberry Pi case Daemon Service";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${cfg.package}/bin/argononed";
+        PIDFile = "/run/argononed.pid";
+        Restart = "on-failure";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ misterio77 ];
+
+}
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index e255e5d22188b..2cff5051c757f 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -369,6 +369,17 @@ in {
 
     networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.config.http.server_port ];
 
+    # symlink the configuration to /etc/home-assistant
+    environment.etc = lib.mkMerge [
+      (lib.mkIf (cfg.config != null && !cfg.configWritable) {
+        "home-assistant/configuration.yaml".source = configFile;
+      })
+
+      (lib.mkIf (cfg.lovelaceConfig != null && !cfg.lovelaceConfigWritable) {
+        "home-assistant/ui-lovelace.yaml".source = lovelaceConfigFile;
+      })
+    ];
+
     systemd.services.home-assistant = {
       description = "Home Assistant";
       after = [
@@ -378,18 +389,21 @@ in {
         "mysql.service"
         "postgresql.service"
       ];
+      reloadTriggers = lib.optional (cfg.config != null) configFile
+      ++ lib.optional (cfg.lovelaceConfig != null) lovelaceConfigFile;
+
       preStart = let
         copyConfig = if cfg.configWritable then ''
           cp --no-preserve=mode ${configFile} "${cfg.configDir}/configuration.yaml"
         '' else ''
           rm -f "${cfg.configDir}/configuration.yaml"
-          ln -s ${configFile} "${cfg.configDir}/configuration.yaml"
+          ln -s /etc/home-assistant/configuration.yaml "${cfg.configDir}/configuration.yaml"
         '';
         copyLovelaceConfig = if cfg.lovelaceConfigWritable then ''
           cp --no-preserve=mode ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml"
         '' else ''
           rm -f "${cfg.configDir}/ui-lovelace.yaml"
-          ln -s ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml"
+          ln -s /etc/home-assistant/ui-lovelace.yaml "${cfg.configDir}/ui-lovelace.yaml"
         '';
       in
         (optionalString (cfg.config != null) copyConfig) +
diff --git a/nixos/modules/services/logging/heartbeat.nix b/nixos/modules/services/logging/heartbeat.nix
index 56fb4deabda52..843ec033911f1 100644
--- a/nixos/modules/services/logging/heartbeat.nix
+++ b/nixos/modules/services/logging/heartbeat.nix
@@ -20,6 +20,16 @@ in
 
       enable = mkEnableOption "heartbeat";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.heartbeat;
+        defaultText = literalExpression "pkgs.heartbeat";
+        example = literalExpression "pkgs.heartbeat7";
+        description = ''
+          The heartbeat package to use.
+        '';
+      };
+
       name = mkOption {
         type = types.str;
         default = "heartbeat";
@@ -67,7 +77,7 @@ in
       serviceConfig = {
         User = "nobody";
         AmbientCapabilities = "cap_net_raw";
-        ExecStart = "${pkgs.heartbeat}/bin/heartbeat -c \"${heartbeatYml}\" -path.data \"${cfg.stateDir}/data\" -path.logs \"${cfg.stateDir}/logs\"";
+        ExecStart = "${cfg.package}/bin/heartbeat -c \"${heartbeatYml}\" -path.data \"${cfg.stateDir}/data\" -path.logs \"${cfg.stateDir}/logs\"";
       };
     };
   };
diff --git a/nixos/modules/services/logging/logrotate.nix b/nixos/modules/services/logging/logrotate.nix
index e6eb0552c9e95..893d951ee2348 100644
--- a/nixos/modules/services/logging/logrotate.nix
+++ b/nixos/modules/services/logging/logrotate.nix
@@ -167,22 +167,23 @@ let
       sed -e "s/\bsu\s.*/su $user $group/" \
           -e "s/\b\(create\s\+[0-9]*\s*\|createolddir\s\+[0-9]*\s\+\).*/\1$user $group/" \
           -e "1imissingok" -e "s/\bnomissingok\b//" \
-          $out > /tmp/logrotate.conf
+          $out > logrotate.conf
       # Since this makes for very verbose builds only show real error.
       # There is no way to control log level, but logrotate hardcodes
       # 'error:' at common log level, so we can use grep, taking care
       # to keep error codes
       set -o pipefail
-      if ! ${pkgs.buildPackages.logrotate}/sbin/logrotate --debug /tmp/logrotate.conf 2>&1 \
-          | ( ! grep "error:" ) > /tmp/logrotate-error; then
+      if ! ${pkgs.buildPackages.logrotate}/sbin/logrotate -s logrotate.status \
+                      --debug logrotate.conf 2>&1 \
+                  | ( ! grep "error:" ) > logrotate-error; then
               echo "Logrotate configuration check failed."
               echo "The failing configuration (after adjustments to pass tests in sandbox) was:"
               printf "%s\n" "-------"
-              cat /tmp/logrotate.conf
+              cat logrotate.conf
               printf "%s\n" "-------"
               echo "The error reported by logrotate was as follow:"
               printf "%s\n" "-------"
-              cat /tmp/logrotate-error
+              cat logrotate-error
               printf "%s\n" "-------"
               echo "You can disable this check with services.logrotate.checkConfig = false,"
               echo "but if you think it should work please report this failure along with"
@@ -193,7 +194,7 @@ let
   };
 
   mailOption =
-    if foldr (n: a: a || n ? mail) false (attrValues cfg.settings)
+    if foldr (n: a: a || (n.mail or false) != false) false (attrValues cfg.settings)
     then "--mail=${pkgs.mailutils}/bin/mail"
     else "";
 in
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 5b714c384de3f..f08605218a535 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -6,7 +6,7 @@ let
 
   cfg = config.services.mailman;
 
-  inherit (pkgs.mailmanPackages.buildEnvs { withHyperkitty = cfg.hyperkitty.enable; })
+  inherit (pkgs.mailmanPackages.buildEnvs { withHyperkitty = cfg.hyperkitty.enable; withLDAP = cfg.ldap.enable; })
     mailmanEnv webEnv;
 
   withPostgresql = config.services.postgresql.enable;
@@ -44,7 +44,13 @@ let
     transport_file_type: hash
   '';
 
-  mailmanCfg = lib.generators.toINI {} cfg.settings;
+  mailmanCfg = lib.generators.toINI {}
+    (recursiveUpdate cfg.settings
+      ((optionalAttrs (cfg.restApiPassFile != null) {
+        webservice.admin_pass = "#NIXOS_MAILMAN_REST_API_PASS_SECRET#";
+      })));
+
+  mailmanCfgFile = pkgs.writeText "mailman-raw.cfg" mailmanCfg;
 
   mailmanHyperkittyCfg = pkgs.writeText "mailman-hyperkitty.cfg" ''
     [general]
@@ -87,6 +93,114 @@ in {
         description = "Enable Mailman on this host. Requires an active MTA on the host (e.g. Postfix).";
       };
 
+      ldap = {
+        enable = mkEnableOption "LDAP auth";
+        serverUri = mkOption {
+          type = types.str;
+          example = "ldaps://ldap.host";
+          description = ''
+            LDAP host to connect against.
+          '';
+        };
+        bindDn = mkOption {
+          type = types.str;
+          example = "cn=root,dc=nixos,dc=org";
+          description = ''
+            Service account to bind against.
+          '';
+        };
+        bindPasswordFile = mkOption {
+          type = types.str;
+          example = "/run/secrets/ldap-bind";
+          description = ''
+            Path to the file containing the bind password of the servie account
+            defined by <xref linkend="opt-services.mailman.ldap.bindDn" />.
+          '';
+        };
+        superUserGroup = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "cn=admin,ou=groups,dc=nixos,dc=org";
+          description = ''
+            Group where a user must be a member of to gain superuser rights.
+          '';
+        };
+        userSearch = {
+          query = mkOption {
+            type = types.str;
+            example = "(&(objectClass=inetOrgPerson)(|(uid=%(user)s)(mail=%(user)s)))";
+            description = ''
+              Query to find a user in the LDAP database.
+            '';
+          };
+          ou = mkOption {
+            type = types.str;
+            example = "ou=users,dc=nixos,dc=org";
+            description = ''
+              Organizational unit to look up a user.
+            '';
+          };
+        };
+        groupSearch = {
+          type = mkOption {
+            type = types.enum [
+              "posixGroup" "groupOfNames" "memberDNGroup" "nestedMemberDNGroup" "nestedGroupOfNames"
+              "groupOfUniqueNames" "nestedGroupOfUniqueNames" "activeDirectoryGroup" "nestedActiveDirectoryGroup"
+              "organizationalRoleGroup" "nestedOrganizationalRoleGroup"
+            ];
+            default = "posixGroup";
+            apply = v: "${toUpper (substring 0 1 v)}${substring 1 (stringLength v) v}Type";
+            description = ''
+              Type of group to perform a group search against.
+            '';
+          };
+          query = mkOption {
+            type = types.str;
+            example = "(objectClass=groupOfNames)";
+            description = ''
+              Query to find a group associated to a user in the LDAP database.
+            '';
+          };
+          ou = mkOption {
+            type = types.str;
+            example = "ou=groups,dc=nixos,dc=org";
+            description = ''
+              Organizational unit to look up a group.
+            '';
+          };
+        };
+        attrMap = {
+          username = mkOption {
+            default = "uid";
+            type = types.str;
+            description = ''
+              LDAP-attribute that corresponds to the <literal>username</literal>-attribute in mailman.
+            '';
+          };
+          firstName = mkOption {
+            default = "givenName";
+            type = types.str;
+            description = ''
+              LDAP-attribute that corresponds to the <literal>firstName</literal>-attribute in mailman.
+            '';
+          };
+          lastName = mkOption {
+            default = "sn";
+            type = types.str;
+            description = ''
+              LDAP-attribute that corresponds to the <literal>lastName</literal>-attribute in mailman.
+            '';
+          };
+          email = mkOption {
+            default = "mail";
+            type = types.str;
+            description = ''
+              LDAP-attribute that corresponds to the <literal>email</literal>-attribute in mailman.
+            '';
+          };
+        };
+      };
+
       enablePostfix = mkOption {
         type = types.bool;
         default = true;
@@ -139,6 +253,14 @@ in {
         '';
       };
 
+      restApiPassFile = mkOption {
+        default = null;
+        type = types.nullOr types.str;
+        description = ''
+          Path to the file containing the value for <literal>MAILMAN_REST_API_PASS</literal>.
+        '';
+      };
+
       serve = {
         enable = mkEnableOption "Automatic nginx and uwsgi setup for mailman-web";
       };
@@ -255,8 +377,6 @@ in {
     };
     users.groups.mailman = {};
 
-    environment.etc."mailman.cfg".text = mailmanCfg;
-
     environment.etc."mailman3/settings.py".text = ''
       import os
 
@@ -274,6 +394,39 @@ in {
 
       with open('/var/lib/mailman-web/settings_local.json') as f:
           globals().update(json.load(f))
+
+      ${optionalString (cfg.restApiPassFile != null) ''
+        with open('${cfg.restApiPassFile}') as f:
+            MAILMAN_REST_API_PASS = f.read().rstrip('\n')
+      ''}
+
+      ${optionalString (cfg.ldap.enable) ''
+        import ldap
+        from django_auth_ldap.config import LDAPSearch, ${cfg.ldap.groupSearch.type}
+        AUTH_LDAP_SERVER_URI = "${cfg.ldap.serverUri}"
+        AUTH_LDAP_BIND_DN = "${cfg.ldap.bindDn}"
+        with open("${cfg.ldap.bindPasswordFile}") as f:
+            AUTH_LDAP_BIND_PASSWORD = f.read().rstrip('\n')
+        AUTH_LDAP_USER_SEARCH = LDAPSearch("${cfg.ldap.userSearch.ou}",
+            ldap.SCOPE_SUBTREE, "${cfg.ldap.userSearch.query}")
+        AUTH_LDAP_GROUP_TYPE = ${cfg.ldap.groupSearch.type}()
+        AUTH_LDAP_GROUP_SEARCH = LDAPSearch("${cfg.ldap.groupSearch.ou}",
+            ldap.SCOPE_SUBTREE, "${cfg.ldap.groupSearch.query}")
+        AUTH_LDAP_USER_ATTR_MAP = {
+          ${concatStrings (flip mapAttrsToList cfg.ldap.attrMap (key: value: ''
+            "${key}": "${value}",
+          ''))}
+        }
+        ${optionalString (cfg.ldap.superUserGroup != null) ''
+          AUTH_LDAP_USER_FLAGS_BY_GROUP = {
+            "is_superuser": "${cfg.ldap.superUserGroup}"
+          }
+        ''}
+        AUTHENTICATION_BACKENDS = (
+            "django_auth_ldap.backend.LDAPBackend",
+            "django.contrib.auth.backends.ModelBackend"
+        )
+      ''}
     '';
 
     services.nginx = mkIf (cfg.serve.enable && cfg.webHosts != []) {
@@ -320,7 +473,7 @@ in {
         after = [ "network.target" ]
           ++ lib.optional cfg.enablePostfix "postfix-setup.service"
           ++ lib.optional withPostgresql "postgresql.service";
-        restartTriggers = [ config.environment.etc."mailman.cfg".source ];
+        restartTriggers = [ mailmanCfgFile ];
         requires = optional withPostgresql "postgresql.service";
         wantedBy = [ "multi-user.target" ];
         serviceConfig = {
@@ -344,6 +497,14 @@ in {
         requires = optional withPostgresql "postgresql.service";
         serviceConfig.Type = "oneshot";
         script = ''
+          install -m0750 -o mailman -g mailman ${mailmanCfgFile} /etc/mailman.cfg
+          ${optionalString (cfg.restApiPassFile != null) ''
+            ${pkgs.replace-secret}/bin/replace-secret \
+              '#NIXOS_MAILMAN_REST_API_PASS_SECRET#' \
+              ${cfg.restApiPassFile} \
+              /etc/mailman.cfg
+          ''}
+
           mailmanDir=/var/lib/mailman
           mailmanWebDir=/var/lib/mailman-web
 
@@ -424,7 +585,7 @@ in {
       mailman-daily = {
         description = "Trigger daily Mailman events";
         startAt = "daily";
-        restartTriggers = [ config.environment.etc."mailman.cfg".source ];
+        restartTriggers = [ mailmanCfgFile ];
         serviceConfig = {
           ExecStart = "${mailmanEnv}/bin/mailman digests --send";
           User = "mailman";
diff --git a/nixos/modules/services/mail/schleuder.nix b/nixos/modules/services/mail/schleuder.nix
new file mode 100644
index 0000000000000..7ba15f1070bde
--- /dev/null
+++ b/nixos/modules/services/mail/schleuder.nix
@@ -0,0 +1,162 @@
+{ config, pkgs, lib, ... }:
+let
+  cfg = config.services.schleuder;
+  settingsFormat = pkgs.formats.yaml { };
+  postfixMap = entries: lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: "${name} ${value}") entries);
+  writePostfixMap = name: entries: pkgs.writeText name (postfixMap entries);
+  configScript = pkgs.writeScript "schleuder-cfg" ''
+    #!${pkgs.runtimeShell}
+    set -exuo pipefail
+    umask 0077
+    ${pkgs.yq}/bin/yq \
+      --slurpfile overrides <(${pkgs.yq}/bin/yq . <${lib.escapeShellArg cfg.extraSettingsFile}) \
+      < ${settingsFormat.generate "schleuder.yml" cfg.settings} \
+      '. * $overrides[0]' \
+      > /etc/schleuder/schleuder.yml
+    chown schleuder: /etc/schleuder/schleuder.yml
+  '';
+in
+{
+  options.services.schleuder = {
+    enable = lib.mkEnableOption "Schleuder secure remailer";
+    enablePostfix = lib.mkEnableOption "automatic postfix integration" // { default = true; };
+    lists = lib.mkOption {
+      description = ''
+        List of list addresses that should be handled by Schleuder.
+
+        Note that this is only handled by the postfix integration, and
+        the setup of the lists, their members and their keys has to be
+        performed separately via schleuder's API, using a tool such as
+        schleuder-cli.
+      '';
+      type = lib.types.listOf lib.types.str;
+      default = [ ];
+      example = [ "widget-team@example.com" "security@example.com" ];
+    };
+    /* maybe one day....
+      domains = lib.mkOption {
+      description = "Domains for which all mail should be handled by Schleuder.";
+      type = lib.types.listOf lib.types.str;
+      default = [];
+      example = ["securelists.example.com"];
+      };
+    */
+    settings = lib.mkOption {
+      description = ''
+        Settings for schleuder.yml.
+
+        Check the <link xlink:href="https://0xacab.org/schleuder/schleuder/blob/master/etc/schleuder.yml">example configuration</link> for possible values.
+      '';
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+        options.keyserver = lib.mkOption {
+          type = lib.types.str;
+          description = ''
+            Key server from which to fetch and update keys.
+
+            Note that NixOS uses a different default from upstream, since the upstream default sks-keyservers.net is deprecated.
+          '';
+          default = "keys.openpgp.org";
+        };
+      };
+      default = { };
+    };
+    extraSettingsFile = lib.mkOption {
+      description = "YAML file to merge into the schleuder config at runtime. This can be used for secrets such as API keys.";
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+    };
+    listDefaults = lib.mkOption {
+      description = ''
+        Default settings for lists (list-defaults.yml).
+
+        Check the <link xlink:href="https://0xacab.org/schleuder/schleuder/-/blob/master/etc/list-defaults.yml">example configuration</link> for possible values.
+      '';
+      type = settingsFormat.type;
+      default = { };
+    };
+  };
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = !(cfg.settings.api ? valid_api_keys);
+        message = ''
+          services.schleuder.settings.api.valid_api_keys is set. Defining API keys via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store API keys in a non-public location.
+        '';
+      }
+      {
+        assertion = !(lib.any (db: db ? password) (lib.attrValues cfg.settings.database or {}));
+        message = ''
+          A password is defined for at least one database in services.schleuder.settings.database. Defining passwords via NixOS config results in them being copied to the world-readable Nix store. Please use the extraSettingsFile option to store database passwords in a non-public location.
+        '';
+      }
+    ];
+    users.users.schleuder.isSystemUser = true;
+    users.users.schleuder.group = "schleuder";
+    users.groups.schleuder = {};
+    environment.systemPackages = [
+      pkgs.schleuder-cli
+    ];
+    services.postfix = lib.mkIf cfg.enablePostfix {
+      extraMasterConf = ''
+        schleuder  unix  -       n       n       -       -       pipe
+          flags=DRhu user=schleuder argv=/${pkgs.schleuder}/bin/schleuder work ''${recipient}
+      '';
+      transport = lib.mkIf (cfg.lists != [ ]) (postfixMap (lib.genAttrs cfg.lists (_: "schleuder:")));
+      extraConfig = ''
+        schleuder_destination_recipient_limit = 1
+      '';
+      # review: does this make sense?
+      localRecipients = lib.mkIf (cfg.lists != [ ]) cfg.lists;
+    };
+    systemd.services = let commonServiceConfig = {
+      # We would have liked to use DynamicUser, but since the default
+      # database is SQLite and lives in StateDirectory, and that same
+      # database needs to be readable from the postfix service, this
+      # isn't trivial to do.
+      User = "schleuder";
+      StateDirectory = "schleuder";
+      StateDirectoryMode = "0700";
+    }; in
+      {
+        schleuder-init = {
+          serviceConfig = commonServiceConfig // {
+            ExecStartPre = lib.mkIf (cfg.extraSettingsFile != null) [
+              "+${configScript}"
+            ];
+            ExecStart = [ "${pkgs.schleuder}/bin/schleuder install" ];
+            Type = "oneshot";
+          };
+        };
+        schleuder-api-daemon = {
+          after = [ "local-fs.target" "network.target" "schleuder-init.service" ];
+          wantedBy = [ "multi-user.target" ];
+          requires = [ "schleuder-init.service" ];
+          serviceConfig = commonServiceConfig // {
+            ExecStart = [ "${pkgs.schleuder}/bin/schleuder-api-daemon" ];
+          };
+        };
+        schleuder-weekly-key-maintenance = {
+          after = [ "local-fs.target" "network.target" ];
+          startAt = "weekly";
+          serviceConfig = commonServiceConfig // {
+            ExecStart = [
+              "${pkgs.schleuder}/bin/schleuder refresh_keys"
+              "${pkgs.schleuder}/bin/schleuder check_keys"
+            ];
+          };
+        };
+      };
+
+    environment.etc."schleuder/schleuder.yml" = lib.mkIf (cfg.extraSettingsFile == null) {
+      source = settingsFormat.generate "schleuder.yml" cfg.settings;
+    };
+    environment.etc."schleuder/list-defaults.yml".source = settingsFormat.generate "list-defaults.yml" cfg.listDefaults;
+
+    services.schleuder = {
+      #lists_dir = "/var/lib/schleuder.lists";
+      settings.filters_dir = lib.mkDefault "/var/lib/schleuder/filters";
+      settings.keyword_handlers_dir = lib.mkDefault "/var/lib/schleuder/keyword_handlers";
+    };
+  };
+}
diff --git a/nixos/modules/services/matrix/appservice-irc.nix b/nixos/modules/services/matrix/appservice-irc.nix
index b041c9c82c56e..ff938527ed58a 100644
--- a/nixos/modules/services/matrix/appservice-irc.nix
+++ b/nixos/modules/services/matrix/appservice-irc.nix
@@ -153,6 +153,9 @@ in {
     systemd.services.matrix-appservice-irc = {
       description = "Matrix-IRC bridge";
       before = [ "matrix-synapse.service" ]; # So the registration can be used by Synapse
+      after = lib.optionals (cfg.settings.database.engine == "postgres") [
+        "postgresql.service"
+      ];
       wantedBy = [ "multi-user.target" ];
 
       preStart = ''
diff --git a/nixos/modules/services/matrix/mautrix-telegram.nix b/nixos/modules/services/matrix/mautrix-telegram.nix
index 794c4dd9ddcd7..88e5c38f71633 100644
--- a/nixos/modules/services/matrix/mautrix-telegram.nix
+++ b/nixos/modules/services/matrix/mautrix-telegram.nix
@@ -125,6 +125,7 @@ in {
       wantedBy = [ "multi-user.target" ];
       wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
       after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      path = [ pkgs.lottieconverter ];
 
       preStart = ''
         # Not all secrets can be passed as environment variable (yet)
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index b3108484fae12..3d5d10cdf070b 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -191,12 +191,12 @@ in {
 
       settings = mkOption {
         default = {};
-        description = ''
+        description = mdDoc ''
           The primary synapse configuration. See the
-          <link xlink:href="https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml">sample configuration</link>
+          [sample configuration](https://github.com/matrix-org/synapse/blob/v${cfg.package.version}/docs/sample_config.yaml)
           for possible values.
 
-          Secrets should be passed in by using the <literal>extraConfigFiles</literal> option.
+          Secrets should be passed in by using the `extraConfigFiles` option.
         '';
         type = with types; submodule {
           freeformType = format.type;
@@ -230,23 +230,23 @@ in {
             registration_shared_secret = mkOption {
               type = types.nullOr types.str;
               default = null;
-              description = ''
+              description = mdDoc ''
                 If set, allows registration by anyone who also has the shared
                 secret, even if registration is otherwise disabled.
 
-                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+                Secrets should be passed in via `extraConfigFiles`!
               '';
             };
 
             macaroon_secret_key = mkOption {
               type = types.nullOr types.str;
               default = null;
-              description = ''
+              description = mdDoc ''
                 Secret key for authentication tokens. If none is specified,
                 the registration_shared_secret is used, if one is given; otherwise,
                 a secret key is derived from the signing key.
 
-                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+                Secrets should be passed in via `extraConfigFiles`!
               '';
             };
 
@@ -620,10 +620,10 @@ in {
               example = literalExpression ''
                 config.services.coturn.static-auth-secret
               '';
-              description = ''
+              description = mdDoc ''
                 The shared secret used to compute passwords for the TURN server.
 
-                Secrets should be passed in via <literal>extraConfigFiles</literal>!
+                Secrets should be passed in via `extraConfigFiles`!
               '';
             };
 
diff --git a/nixos/modules/services/matrix/synapse.xml b/nixos/modules/services/matrix/synapse.xml
index cf33957d58ece..65bc53d33ac34 100644
--- a/nixos/modules/services/matrix/synapse.xml
+++ b/nixos/modules/services/matrix/synapse.xml
@@ -33,21 +33,26 @@
    <link xlink:href="https://github.com/matrix-org/synapse#synapse-installation">
    installation instructions of Synapse </link>.
 <programlisting>
-{ pkgs, lib, ... }:
+{ pkgs, lib, config, ... }:
 let
-  fqdn =
-    let
-      join = hostName: domain: hostName + lib.optionalString (domain != null) ".${domain}";
-    in join config.networking.hostName config.networking.domain;
-in {
-  networking = {
-    <link linkend="opt-networking.hostName">hostName</link> = "myhostname";
-    <link linkend="opt-networking.domain">domain</link> = "example.org";
+  fqdn = "${config.networking.hostName}.${config.networking.domain}";
+  clientConfig = {
+    "m.homeserver".base_url = "https://${fqdn}";
+    "m.identity_server" = {};
   };
-  <link linkend="opt-networking.firewall.allowedTCPPorts">networking.firewall.allowedTCPPorts</link> = [ 80 443 ];
+  serverConfig."m.server" = "${config.services.matrix-synapse.settings.server_name}:443";
+  mkWellKnown = data: ''
+    add_header Content-Type application/json;
+    add_header Access-Control-Allow-Origin *;
+    return 200 '${builtins.toJSON data}';
+  '';
+in {
+  <xref linkend="opt-networking.hostName" /> = "myhostname";
+  <xref linkend="opt-networking.domain" /> = "example.org";
+  <xref linkend="opt-networking.firewall.allowedTCPPorts" /> = [ 80 443 ];
 
-  <link linkend="opt-services.postgresql.enable">services.postgresql.enable</link> = true;
-  <link linkend="opt-services.postgresql.initialScript">services.postgresql.initialScript</link> = pkgs.writeText "synapse-init.sql" ''
+  <xref linkend="opt-services.postgresql.enable" /> = true;
+  <xref linkend="opt-services.postgresql.initialScript" /> = pkgs.writeText "synapse-init.sql" ''
     CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
     CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
       TEMPLATE template0
@@ -57,78 +62,41 @@ in {
 
   services.nginx = {
     <link linkend="opt-services.nginx.enable">enable</link> = true;
-    # only recommendedProxySettings and recommendedGzipSettings are strictly required,
-    # but the rest make sense as well
     <link linkend="opt-services.nginx.recommendedTlsSettings">recommendedTlsSettings</link> = true;
     <link linkend="opt-services.nginx.recommendedOptimisation">recommendedOptimisation</link> = true;
     <link linkend="opt-services.nginx.recommendedGzipSettings">recommendedGzipSettings</link> = true;
     <link linkend="opt-services.nginx.recommendedProxySettings">recommendedProxySettings</link> = true;
-
     <link linkend="opt-services.nginx.virtualHosts">virtualHosts</link> = {
-      # This host section can be placed on a different host than the rest,
-      # i.e. to delegate from the host being accessible as ${config.networking.domain}
-      # to another host actually running the Matrix homeserver.
-      "${config.networking.domain}" = {
+      "${config.networking.domain}" = { <co xml:id='ex-matrix-synapse-dns' />
         <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
         <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
-
-        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/server".extraConfig</link> =
-          let
-            # use 443 instead of the default 8448 port to unite
-            # the client-server and server-server port for simplicity
-            server = { "m.server" = "${fqdn}:443"; };
-          in ''
-            add_header Content-Type application/json;
-            return 200 '${builtins.toJSON server}';
-          '';
-        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/client".extraConfig</link> =
-          let
-            client = {
-              "m.homeserver" =  { "base_url" = "https://${fqdn}"; };
-              "m.identity_server" =  { "base_url" = "https://vector.im"; };
-            };
-          # ACAO required to allow element-web on any URL to request this json file
-          in ''
-            add_header Content-Type application/json;
-            add_header Access-Control-Allow-Origin *;
-            return 200 '${builtins.toJSON client}';
-          '';
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/server".extraConfig</link> = mkWellKnown serverConfig; <co xml:id='ex-matrix-synapse-well-known-server' />
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."= /.well-known/matrix/client".extraConfig</link> = mkWellKnown clientConfig; <co xml:id='ex-matrix-synapse-well-known-client' />
       };
-
-      # Reverse proxy for Matrix client-server and server-server communication
-      ${fqdn} = {
+      "${fqdn}" = {
         <link linkend="opt-services.nginx.virtualHosts._name_.enableACME">enableACME</link> = true;
         <link linkend="opt-services.nginx.virtualHosts._name_.forceSSL">forceSSL</link> = true;
-
-        # Or do a redirect instead of the 404, or whatever is appropriate for you.
-        # But do not put a Matrix Web client here! See the Element web section below.
-        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."/".extraConfig</link> = ''
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.extraConfig">locations."/".extraConfig</link> = '' <co xml:id='ex-matrix-synapse-rev-default' />
           return 404;
         '';
-
-        # forward all Matrix API calls to the synapse Matrix homeserver
-        locations."/_matrix" = {
-          <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.proxyPass">proxyPass</link> = "http://[::1]:8008"; # without a trailing /
-        };
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.proxyPass">locations."/_matrix".proxyPass</link> = "http://[::1]:8008"; <co xml:id='ex-matrix-synapse-rev-proxy-pass' />
+        <link linkend="opt-services.nginx.virtualHosts._name_.locations._name_.proxyPass">locations."/_synapse/client".proxyPass</link> = "http://[::1]:8008"; <co xml:id='ex-matrix-synapse-rev-client' />
       };
     };
   };
+
   services.matrix-synapse = {
     <link linkend="opt-services.matrix-synapse.enable">enable</link> = true;
-    <link linkend="opt-services.matrix-synapse.settings.server_name">server_name</link> = config.networking.domain;
-    <link linkend="opt-services.matrix-synapse.settings.listeners">listeners</link> = [
-      {
-        <link linkend="opt-services.matrix-synapse.settings.listeners._.port">port</link> = 8008;
+    <link linkend="opt-services.matrix-synapse.settings.server_name">settings.server_name</link> = config.networking.domain;
+    <link linkend="opt-services.matrix-synapse.settings.listeners">settings.listeners</link> = [
+      { <link linkend="opt-services.matrix-synapse.settings.listeners._.port">port</link> = 8008;
         <link linkend="opt-services.matrix-synapse.settings.listeners._.bind_addresses">bind_addresses</link> = [ "::1" ];
         <link linkend="opt-services.matrix-synapse.settings.listeners._.type">type</link> = "http";
         <link linkend="opt-services.matrix-synapse.settings.listeners._.tls">tls</link> = false;
         <link linkend="opt-services.matrix-synapse.settings.listeners._.x_forwarded">x_forwarded</link> = true;
         <link linkend="opt-services.matrix-synapse.settings.listeners._.resources">resources</link> = [ {
-          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "client" ];
+          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "client" "federation" ];
           <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = true;
-        } {
-          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.names">names</link> = [ "federation" ];
-          <link linkend="opt-services.matrix-synapse.settings.listeners._.resources._.compress">compress</link> = false;
         } ];
       }
     ];
@@ -136,20 +104,59 @@ in {
 }
 </programlisting>
   </para>
-
-  <para>
-   If the <code>A</code> and <code>AAAA</code> DNS records on
-   <literal>example.org</literal> do not point on the same host as the records
-   for <code>myhostname.example.org</code>, you can easily move the
-   <code>/.well-known</code> virtualHost section of the code to the host that
-   is serving <literal>example.org</literal>, while the rest stays on
-   <literal>myhostname.example.org</literal> with no other changes required.
-   This pattern also allows to seamlessly move the homeserver from
-   <literal>myhostname.example.org</literal> to
-   <literal>myotherhost.example.org</literal> by only changing the
-   <code>/.well-known</code> redirection target.
-  </para>
-
+  <calloutlist>
+   <callout arearefs='ex-matrix-synapse-dns'>
+    <para>
+     If the <code>A</code> and <code>AAAA</code> DNS records on
+     <literal>example.org</literal> do not point on the same host as the records
+     for <code>myhostname.example.org</code>, you can easily move the
+     <code>/.well-known</code> virtualHost section of the code to the host that
+     is serving <literal>example.org</literal>, while the rest stays on
+     <literal>myhostname.example.org</literal> with no other changes required.
+     This pattern also allows to seamlessly move the homeserver from
+     <literal>myhostname.example.org</literal> to
+     <literal>myotherhost.example.org</literal> by only changing the
+     <code>/.well-known</code> redirection target.
+    </para>
+   </callout>
+   <callout arearefs='ex-matrix-synapse-well-known-server'>
+    <para>
+     This section is not needed if the <link linkend="opt-services.matrix-synapse.settings.server_name">server_name</link>
+     of <package>matrix-synapse</package> is equal to the domain (i.e.
+     <literal>example.org</literal> from <literal>@foo:example.org</literal>)
+     and the federation port is 8448.
+     Further reference can be found in the <link xlink:href="https://matrix-org.github.io/synapse/latest/delegate.html">docs
+     about delegation</link>.
+    </para>
+   </callout>
+   <callout arearefs='ex-matrix-synapse-well-known-client'>
+    <para>
+     This is usually needed for homeserver discovery (from e.g. other Matrix clients).
+     Further reference can be found in the <link xlink:href="https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixclient">upstream docs</link>
+    </para>
+   </callout>
+   <callout arearefs='ex-matrix-synapse-rev-default'>
+    <para>
+     It's also possible to do a redirect here or something else, this vhost is not
+     needed for Matrix. It's recommended though to <emphasis>not put</emphasis> element
+     here, see also the <link linkend='ex-matrix-synapse-rev-default'>section about Element</link>.
+    </para>
+   </callout>
+   <callout arearefs='ex-matrix-synapse-rev-proxy-pass'>
+    <para>
+     Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash
+     <emphasis>must not</emphasis> be used here.
+    </para>
+   </callout>
+   <callout arearefs='ex-matrix-synapse-rev-client'>
+    <para>
+     Forward requests for e.g. SSO and password-resets.
+    </para>
+   </callout>
+  </calloutlist>
+ </section>
+ <section xml:id="module-services-matrix-register-users">
+  <title>Registering Matrix users</title>
   <para>
    If you want to run a server with public registration by anybody, you can
    then enable <literal><link linkend="opt-services.matrix-synapse.settings.enable_registration">services.matrix-synapse.settings.enable_registration</link> =
@@ -159,7 +166,7 @@ in {
    To create a new user or admin, run the following after you have set the secret
    and have rebuilt NixOS:
 <screen>
-<prompt>$ </prompt>nix run nixpkgs.matrix-synapse
+<prompt>$ </prompt>nix-shell -p matrix-synapse
 <prompt>$ </prompt>register_new_matrix_user -k <replaceable>your-registration-shared-secret</replaceable> http://localhost:8008
 <prompt>New user localpart: </prompt><replaceable>your-username</replaceable>
 <prompt>Password:</prompt>
@@ -168,12 +175,51 @@ in {
 Success!
 </screen>
    In the example, this would create a user with the Matrix Identifier
-   <literal>@your-username:example.org</literal>. Note that the registration
-   secret ends up in the nix store and therefore is world-readable by any user
-   on your machine, so it makes sense to only temporarily activate the
-   <link linkend="opt-services.matrix-synapse.settings.registration_shared_secret">registration_shared_secret</link>
-   option until a better solution for NixOS is in place.
+   <literal>@your-username:example.org</literal>.
+   <warning>
+    <para>
+     When using <xref linkend="opt-services.matrix-synapse.settings.registration_shared_secret" />, the secret
+     will end up in the world-readable store. Instead it's recommended to deploy the secret
+     in an additional file like this:
+     <itemizedlist>
+      <listitem>
+       <para>
+        Create a file with the following contents:
+<programlisting>registration_shared_secret: your-very-secret-secret</programlisting>
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+        Deploy the file with a secret-manager such as <link xlink:href="https://nixops.readthedocs.io/en/latest/overview.html#managing-keys"><option>deployment.keys</option></link>
+        from <citerefentry><refentrytitle>nixops</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        or <link xlink:href="https://github.com/Mic92/sops-nix/">sops-nix</link> to
+        e.g. <filename>/run/secrets/matrix-shared-secret</filename> and ensure that it's readable
+        by <package>matrix-synapse</package>.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+        Include the file like this in your configuration:
+<programlisting>
+{
+  <xref linkend="opt-services.matrix-synapse.extraConfigFiles" /> = [
+    "/run/secrets/matrix-shared-secret"
+  ];
+}
+</programlisting>
+       </para>
+      </listitem>
+     </itemizedlist>
+    </para>
+   </warning>
   </para>
+  <note>
+   <para>
+    It's also possible to user alternative authentication mechanism such as
+    <link xlink:href="https://github.com/matrix-org/matrix-synapse-ldap3">LDAP (via <literal>matrix-synapse-ldap3</literal>)</link>
+    or <link xlink:href="https://matrix-org.github.io/synapse/latest/openid.html">OpenID</link>.
+   </para>
+  </note>
  </section>
  <section xml:id="module-services-matrix-element-web">
   <title>Element (formerly known as Riot) Web Client</title>
@@ -206,10 +252,7 @@ Success!
 
     <link linkend="opt-services.nginx.virtualHosts._name_.root">root</link> = pkgs.element-web.override {
       conf = {
-        default_server_config."m.homeserver" = {
-          "base_url" = "https://${fqdn}";
-          "server_name" = "${fqdn}";
-        };
+        default_server_config = clientConfig; # see `clientConfig` from the snippet above.
       };
     };
   };
@@ -217,15 +260,17 @@ Success!
 </programlisting>
   </para>
 
-  <para>
-   Note that the Element developers do not recommend running Element and your Matrix
-   homeserver on the same fully-qualified domain name for security reasons. In
-   the example, this means that you should not reuse the
-   <literal>myhostname.example.org</literal> virtualHost to also serve Element,
-   but instead serve it on a different subdomain, like
-   <literal>element.example.org</literal> in the example. See the
-   <link xlink:href="https://github.com/vector-im/riot-web#important-security-note">Element
-   Important Security Notes</link> for more information on this subject.
-  </para>
+  <note>
+   <para>
+    The Element developers do not recommend running Element and your Matrix
+    homeserver on the same fully-qualified domain name for security reasons. In
+    the example, this means that you should not reuse the
+    <literal>myhostname.example.org</literal> virtualHost to also serve Element,
+    but instead serve it on a different subdomain, like
+    <literal>element.example.org</literal> in the example. See the
+    <link xlink:href="https://github.com/vector-im/element-web/tree/v1.10.0#important-security-notes">Element
+    Important Security Notes</link> for more information on this subject.
+   </para>
+  </note>
  </section>
 </chapter>
diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix
index 96e2a4e7c2602..8cb51bb0b7a7f 100644
--- a/nixos/modules/services/misc/dictd.nix
+++ b/nixos/modules/services/misc/dictd.nix
@@ -45,6 +45,10 @@ in
     # get the command line client on system path to make some use of the service
     environment.systemPackages = [ pkgs.dict ];
 
+    environment.etc."dict.conf".text = ''
+      server localhost
+    '';
+
     users.users.dictd =
       { group = "dictd";
         description = "DICT.org dictd server";
diff --git a/nixos/modules/services/misc/geoipupdate.nix b/nixos/modules/services/misc/geoipupdate.nix
index 3211d4d88e4d9..db643c3d84795 100644
--- a/nixos/modules/services/misc/geoipupdate.nix
+++ b/nixos/modules/services/misc/geoipupdate.nix
@@ -2,6 +2,7 @@
 
 let
   cfg = config.services.geoipupdate;
+  inherit (builtins) isAttrs isString isInt isList typeOf hashString;
 in
 {
   imports = [
@@ -27,11 +28,30 @@ in
       };
 
       settings = lib.mkOption {
+        example = lib.literalExpression ''
+          {
+            AccountID = 200001;
+            DatabaseDirectory = "/var/lib/GeoIP";
+            LicenseKey = { _secret = "/run/keys/maxmind_license_key"; };
+            Proxy = "10.0.0.10:8888";
+            ProxyUserPassword = { _secret = "/run/keys/proxy_pass"; };
+          }
+        '';
         description = ''
           <productname>geoipupdate</productname> configuration
           options. See
           <link xlink:href="https://github.com/maxmind/geoipupdate/blob/main/doc/GeoIP.conf.md" />
           for a full list of available options.
+
+          Settings containing secret data should be set to an
+          attribute set containing the attribute
+          <literal>_secret</literal> - a string pointing to a file
+          containing the value the option should be set to. See the
+          example to get a better picture of this: in the resulting
+          <filename>GeoIP.conf</filename> file, the
+          <literal>ProxyUserPassword</literal> key will be set to the
+          contents of the
+          <filename>/run/keys/proxy_pass</filename> file.
         '';
         type = lib.types.submodule {
           freeformType =
@@ -65,11 +85,18 @@ in
             };
 
             LicenseKey = lib.mkOption {
-              type = lib.types.path;
+              type = with lib.types; either path (attrsOf path);
               description = ''
-                A file containing the <productname>MaxMind</productname>
-                license key.
+                A file containing the
+                <productname>MaxMind</productname> license key.
+
+                Always handled as a secret whether the value is
+                wrapped in a <literal>{ _secret = ...; }</literal>
+                attrset or not (refer to <xref
+                linkend="opt-services.geoipupdate.settings" /> for
+                details).
               '';
+              apply = x: if isAttrs x then x else { _secret = x; };
             };
 
             DatabaseDirectory = lib.mkOption {
@@ -102,6 +129,9 @@ in
     systemd.services.geoipupdate-create-db-dir = {
       serviceConfig.Type = "oneshot";
       script = ''
+        set -o errexit -o pipefail -o nounset -o errtrace
+        shopt -s inherit_errexit
+
         mkdir -p ${cfg.settings.DatabaseDirectory}
         chmod 0755 ${cfg.settings.DatabaseDirectory}
       '';
@@ -115,32 +145,41 @@ in
         "network-online.target"
         "nss-lookup.target"
       ];
+      path = [ pkgs.replace-secret ];
       wants = [ "network-online.target" ];
       startAt = cfg.interval;
       serviceConfig = {
         ExecStartPre =
           let
+            isSecret = v: isAttrs v && v ? _secret && isString v._secret;
             geoipupdateKeyValue = lib.generators.toKeyValue {
               mkKeyValue = lib.flip lib.generators.mkKeyValueDefault " " rec {
-                mkValueString = v: with builtins;
+                mkValueString = v:
                   if isInt           v then toString v
                   else if isString   v then v
                   else if true  ==   v then "1"
                   else if false ==   v then "0"
                   else if isList     v then lib.concatMapStringsSep " " mkValueString v
+                  else if isSecret   v then hashString "sha256" v._secret
                   else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
               };
             };
+            secretPaths = lib.catAttrs "_secret" (lib.collect isSecret cfg.settings);
+            mkSecretReplacement = file: ''
+              replace-secret ${lib.escapeShellArgs [ (hashString "sha256" file) file "/run/geoipupdate/GeoIP.conf" ]}
+            '';
+            secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
 
             geoipupdateConf = pkgs.writeText "geoipupdate.conf" (geoipupdateKeyValue cfg.settings);
 
             script = ''
+              set -o errexit -o pipefail -o nounset -o errtrace
+              shopt -s inherit_errexit
+
               chown geoip "${cfg.settings.DatabaseDirectory}"
 
               cp ${geoipupdateConf} /run/geoipupdate/GeoIP.conf
-              ${pkgs.replace-secret}/bin/replace-secret '${cfg.settings.LicenseKey}' \
-                                                        '${cfg.settings.LicenseKey}' \
-                                                        /run/geoipupdate/GeoIP.conf
+              ${secretReplacements}
             '';
           in
             "+${pkgs.writeShellScript "start-pre-full-privileges" script}";
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 24eefb7bf302c..77556bfe00707 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -13,6 +13,22 @@ let
                       else
                         pkgs.postgresql_12;
 
+  # Git 2.36.1 seemingly contains a commit-graph related bug which is
+  # easily triggered through GitLab, so we downgrade it to 2.35.x
+  # until this issue is solved. See
+  # https://gitlab.com/gitlab-org/gitlab/-/issues/360783#note_992870101.
+  gitPackage =
+    let
+      version = "2.35.4";
+    in
+      pkgs.git.overrideAttrs (oldAttrs: rec {
+        inherit version;
+        src = pkgs.fetchurl {
+          url = "https://www.kernel.org/pub/software/scm/git/git-${version}.tar.xz";
+          sha256 = "sha256-mv13OdNkXggeKQkJ+47QcJ6lYmcw6Qjri1ZJ2ETCTOk=";
+        };
+      });
+
   gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket";
   gitalySocket = "${cfg.statePath}/tmp/sockets/gitaly.socket";
   pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
@@ -41,7 +57,7 @@ let
     prometheus_listen_addr = "localhost:9236"
 
     [git]
-    bin_path = "${pkgs.git}/bin/git"
+    bin_path = "${gitPackage}/bin/git"
 
     [gitaly-ruby]
     dir = "${cfg.packages.gitaly.ruby}"
@@ -137,7 +153,7 @@ let
       };
       workhorse.secret_file = "${cfg.statePath}/.gitlab_workhorse_secret";
       gitlab_kas.secret_file = "${cfg.statePath}/.gitlab_kas_secret";
-      git.bin_path = "git";
+      git.bin_path = "${gitPackage}/bin/git";
       monitoring = {
         ip_whitelist = [ "127.0.0.0/8" "::1/128" ];
         sidekiq_exporter = {
@@ -1047,7 +1063,7 @@ in {
         chown ${cfg.user}:${cfg.group} ${cfg.registry.certFile}
       '';
 
-      serviceConfig = {
+      unitConfig = {
         ConditionPathExists = "!${cfg.registry.certFile}";
       };
     };
@@ -1275,7 +1291,7 @@ in {
       });
       path = with pkgs; [
         postgresqlPackage
-        git
+        gitPackage
         ruby
         openssh
         nodejs
@@ -1306,7 +1322,7 @@ in {
       path = with pkgs; [
         openssh
         procps  # See https://gitlab.com/gitlab-org/gitaly/issues/1562
-        git
+        gitPackage
         cfg.packages.gitaly.rubyEnv
         cfg.packages.gitaly.rubyEnv.wrappedRuby
         gzip
@@ -1351,7 +1367,7 @@ in {
       partOf = [ "gitlab.target" ];
       path = with pkgs; [
         exiftool
-        git
+        gitPackage
         gnutar
         gzip
         openssh
@@ -1412,7 +1428,7 @@ in {
       environment = gitlabEnv;
       path = with pkgs; [
         postgresqlPackage
-        git
+        gitPackage
         openssh
         nodejs
         procps
diff --git a/nixos/modules/services/misc/gollum.nix b/nixos/modules/services/misc/gollum.nix
index 354278fad226b..5a5f488dc565b 100644
--- a/nixos/modules/services/misc/gollum.nix
+++ b/nixos/modules/services/misc/gollum.nix
@@ -47,7 +47,7 @@ in
     user-icons = mkOption {
       type = types.nullOr (types.enum [ "gravatar" "identicon" ]);
       default = null;
-      description = "User icons for history view";
+      description = "Enable specific user icons for history view";
     };
 
     emoji = mkOption {
@@ -68,6 +68,12 @@ in
       description = "Disable editing pages";
     };
 
+    local-time = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Use the browser's local timezone instead of the server's for displaying dates.";
+    };
+
     branch = mkOption {
       type = types.str;
       default = "master";
@@ -123,6 +129,7 @@ in
             ${optionalString cfg.emoji "--emoji"} \
             ${optionalString cfg.h1-title "--h1-title"} \
             ${optionalString cfg.no-edit "--no-edit"} \
+            ${optionalString cfg.local-time "--local-time"} \
             ${optionalString (cfg.allowUploads != null) "--allow-uploads ${cfg.allowUploads}"} \
             ${optionalString (cfg.user-icons != null) "--user-icons ${cfg.user-icons}"} \
             ${cfg.stateDir}
diff --git a/nixos/modules/services/misc/jellyfin.nix b/nixos/modules/services/misc/jellyfin.nix
index 789b78702e9bc..e2f227b06196a 100644
--- a/nixos/modules/services/misc/jellyfin.nix
+++ b/nixos/modules/services/misc/jellyfin.nix
@@ -49,7 +49,10 @@ in
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
 
+      # This is mostly follows: https://github.com/jellyfin/jellyfin/blob/master/fedora/jellyfin.service
+      # Upstream also disable some hardenings when running in LXC, we do the same with the isContainer option
       serviceConfig = rec {
+        Type = "simple";
         User = cfg.user;
         Group = cfg.group;
         StateDirectory = "jellyfin";
@@ -57,48 +60,50 @@ in
         CacheDirectory = "jellyfin";
         CacheDirectoryMode = "0700";
         UMask = "0077";
+        WorkingDirectory = "/var/lib/jellyfin";
         ExecStart = "${cfg.package}/bin/jellyfin --datadir '/var/lib/${StateDirectory}' --cachedir '/var/cache/${CacheDirectory}'";
         Restart = "on-failure";
+        TimeoutSec = 15;
+        SuccessExitStatus = ["0" "143"];
 
         # Security options:
-
         NoNewPrivileges = true;
-
-        AmbientCapabilities = "";
-        CapabilityBoundingSet = "";
-
-        # ProtectClock= adds DeviceAllow=char-rtc r
-        DeviceAllow = "";
-
-        LockPersonality = true;
-
-        PrivateTmp = true;
-        # Disabled to allow Jellyfin to access hw accel devices endpoints
-        # PrivateDevices = true;
-        PrivateUsers = true;
-
-        # Disabled as it does not allow Jellyfin to interface with CUDA devices
-        # ProtectClock = true;
-        ProtectControlGroups = true;
-        ProtectHostname = true;
-        ProtectKernelLogs = true;
-        ProtectKernelModules = true;
-        ProtectKernelTunables = true;
-
-        RemoveIPC = true;
-
-        RestrictNamespaces = true;
+        SystemCallArchitectures = "native";
         # AF_NETLINK needed because Jellyfin monitors the network connection
-        RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" "AF_UNIX" ];
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
+        RestrictNamespaces = !config.boot.isContainer;
         RestrictRealtime = true;
         RestrictSUIDSGID = true;
+        ProtectControlGroups = !config.boot.isContainer;
+        ProtectHostname = true;
+        ProtectKernelLogs = !config.boot.isContainer;
+        ProtectKernelModules = !config.boot.isContainer;
+        ProtectKernelTunables = !config.boot.isContainer;
+        LockPersonality = true;
+        PrivateTmp = !config.boot.isContainer;
+        # needed for hardware accelaration
+        PrivateDevices = false;
+        PrivateUsers = true;
+        RemoveIPC = true;
 
-        SystemCallArchitectures = "native";
-        SystemCallErrorNumber = "EPERM";
         SystemCallFilter = [
-          "@system-service"
-          "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"
+          "~@clock"
+          "~@aio"
+          "~@chown"
+          "~@cpu-emulation"
+          "~@debug"
+          "~@keyring"
+          "~@memlock"
+          "~@module"
+          "~@mount"
+          "~@obsolete"
+          "~@privileged"
+          "~@raw-io"
+          "~@reboot"
+          "~@setuid"
+          "~@swap"
         ];
+        SystemCallErrorNumber = "EPERM";
       };
     };
 
diff --git a/nixos/modules/services/misc/paperless.nix b/nixos/modules/services/misc/paperless.nix
index bfaf842fb464f..17cd555d7e1bd 100644
--- a/nixos/modules/services/misc/paperless.nix
+++ b/nixos/modules/services/misc/paperless.nix
@@ -83,7 +83,7 @@ let
   };
 in
 {
-  meta.maintainers = with maintainers; [ earvstedt Flakebi ];
+  meta.maintainers = with maintainers; [ erikarvstedt Flakebi ];
 
   imports = [
     (mkRenamedOptionModule [ "services" "paperless-ng" ] [ "services" "paperless" ])
diff --git a/nixos/modules/services/misc/polaris.nix b/nixos/modules/services/misc/polaris.nix
new file mode 100644
index 0000000000000..68045af1528b5
--- /dev/null
+++ b/nixos/modules/services/misc/polaris.nix
@@ -0,0 +1,151 @@
+{ config
+, pkgs
+, lib
+, ...}:
+
+with lib;
+let
+  cfg = config.services.polaris;
+  settingsFormat = pkgs.formats.toml {};
+in
+{
+  options = {
+    services.polaris = {
+      enable = mkEnableOption "Polaris Music Server";
+
+      package = mkPackageOption pkgs "polaris" { };
+
+      user = mkOption {
+        type = types.str;
+        default = "polaris";
+        description = "User account under which Polaris runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "polaris";
+        description = "Group under which Polaris is run.";
+      };
+
+      extraGroups = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = "Polaris' auxiliary groups.";
+        example = literalExpression ''["media" "music"]'';
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 5050;
+        description = ''
+          The port which the Polaris REST api and web UI should listen to.
+          Note: polaris is hardcoded to listen to the hostname "0.0.0.0".
+        '';
+      };
+
+      settings = mkOption {
+        type = settingsFormat.type;
+        default = {};
+        description = ''
+          Contents for the TOML Polaris config, applied each start.
+          Although poorly documented, an example may be found here:
+          <link xlink:href="https://github.com/agersant/polaris/blob/374d0ca56fc0a466d797a4b252e2078607476797/test-data/config.toml">test-config.toml</link>
+        '';
+        example = literalExpression ''
+          {
+            settings.reindex_every_n_seconds = 7*24*60*60; # weekly, default is 1800
+            settings.album_art_pattern =
+              "(cover|front|folder)\.(jpeg|jpg|png|bmp|gif)";
+            mount_dirs = [
+              {
+                name = "NAS";
+                source = "/mnt/nas/music";
+              }
+              {
+                name = "Local";
+                source = "/home/my_user/Music";
+              }
+            ];
+          }
+        '';
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open the configured port in the firewall.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.polaris = {
+      description = "Polaris Music Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = rec {
+        User = cfg.user;
+        Group = cfg.group;
+        DynamicUser = true;
+        SupplementaryGroups = cfg.extraGroups;
+        StateDirectory = "polaris";
+        CacheDirectory = "polaris";
+        ExecStart = escapeShellArgs ([
+          "${cfg.package}/bin/polaris"
+          "--foreground"
+          "--port" cfg.port
+          "--database" "/var/lib/${StateDirectory}/db.sqlite"
+          "--cache" "/var/cache/${CacheDirectory}"
+        ] ++ optionals (cfg.settings != {}) [
+          "--config" (settingsFormat.generate "polaris-config.toml" cfg.settings)
+        ]);
+        Restart = "on-failure";
+
+        # Security options:
+
+        #NoNewPrivileges = true; # implied by DynamicUser
+        #RemoveIPC = true; # implied by DynamicUser
+
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+
+        DeviceAllow = "";
+
+        LockPersonality = true;
+
+        #PrivateTmp = true; # implied by DynamicUser
+        PrivateDevices = true;
+        PrivateUsers = true;
+
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+
+        RestrictNamespaces = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
+        RestrictRealtime = true;
+        #RestrictSUIDSGID = true; # implied by DynamicUser
+
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [
+          "@system-service"
+          "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"
+        ];
+      };
+    };
+
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+    };
+
+  };
+
+  meta.maintainers = with maintainers; [ pbsds ];
+}
diff --git a/nixos/modules/services/misc/radarr.nix b/nixos/modules/services/misc/radarr.nix
index 74444e24043f9..826d59da0af14 100644
--- a/nixos/modules/services/misc/radarr.nix
+++ b/nixos/modules/services/misc/radarr.nix
@@ -11,6 +11,14 @@ in
     services.radarr = {
       enable = mkEnableOption "Radarr";
 
+      package = mkOption {
+        description = "Radarr package to use";
+        default = pkgs.radarr;
+        defaultText = literalExpression "pkgs.radarr";
+        example = literalExpression "pkgs.radarr";
+        type = types.package;
+      };
+
       dataDir = mkOption {
         type = types.str;
         default = "/var/lib/radarr/.config/Radarr";
@@ -51,7 +59,7 @@ in
         Type = "simple";
         User = cfg.user;
         Group = cfg.group;
-        ExecStart = "${pkgs.radarr}/bin/Radarr -nobrowser -data='${cfg.dataDir}'";
+        ExecStart = "${cfg.package}/bin/Radarr -nobrowser -data='${cfg.dataDir}'";
         Restart = "on-failure";
       };
     };
diff --git a/nixos/modules/services/monitoring/grafana-agent.nix b/nixos/modules/services/monitoring/grafana-agent.nix
new file mode 100644
index 0000000000000..bbeda18464707
--- /dev/null
+++ b/nixos/modules/services/monitoring/grafana-agent.nix
@@ -0,0 +1,143 @@
+{ lib, pkgs, config, generators, ... }:
+with lib;
+let
+  cfg = config.services.grafana-agent;
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "grafana-agent.yaml" cfg.settings;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ flokli zimbatm ];
+  };
+
+  options.services.grafana-agent = {
+    enable = mkEnableOption "grafana-agent";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.grafana-agent;
+      defaultText = "pkgs.grafana-agent";
+      description = "The grafana-agent package to use.";
+    };
+
+    credentials = mkOption {
+      description = ''
+        Credentials to load at service startup. Keys that are UPPER_SNAKE will be loaded as env vars. Values are absolute paths to the credentials.
+      '';
+      type = types.attrsOf types.str;
+      default = { };
+
+      example = {
+        logs_remote_write_password = "/run/keys/grafana_agent_logs_remote_write_password";
+        LOGS_REMOTE_WRITE_URL = "/run/keys/grafana_agent_logs_remote_write_url";
+        LOGS_REMOTE_WRITE_USERNAME = "/run/keys/grafana_agent_logs_remote_write_username";
+        metrics_remote_write_password = "/run/keys/grafana_agent_metrics_remote_write_password";
+        METRICS_REMOTE_WRITE_URL = "/run/keys/grafana_agent_metrics_remote_write_url";
+        METRICS_REMOTE_WRITE_USERNAME = "/run/keys/grafana_agent_metrics_remote_write_username";
+      };
+    };
+
+    settings = mkOption {
+      description = ''
+        Configuration for <package>grafana-agent</package>.
+
+        See https://grafana.com/docs/agent/latest/configuration/
+      '';
+
+      type = types.submodule {
+        freeformType = settingsFormat.type;
+      };
+
+      default = {
+        metrics = {
+          wal_directory = "\${STATE_DIRECTORY}";
+          global.scrape_interval = "5s";
+        };
+        integrations = {
+          agent.enabled = true;
+          agent.scrape_integration = true;
+          node_exporter.enabled = true;
+          replace_instance_label = true;
+        };
+      };
+
+      example = {
+        metrics.global.remote_write = [{
+          url = "\${METRICS_REMOTE_WRITE_URL}";
+          basic_auth.username = "\${METRICS_REMOTE_WRITE_USERNAME}";
+          basic_auth.password_file = "\${CREDENTIALS_DIRECTORY}/metrics_remote_write_password";
+        }];
+        logs.configs = [{
+          name = "default";
+          scrape_configs = [
+            {
+              job_name = "journal";
+              journal = {
+                max_age = "12h";
+                labels.job = "systemd-journal";
+              };
+              relabel_configs = [
+                {
+                  source_labels = [ "__journal__systemd_unit" ];
+                  target_label = "systemd_unit";
+                }
+                {
+                  source_labels = [ "__journal__hostname" ];
+                  target_label = "nodename";
+                }
+                {
+                  source_labels = [ "__journal_syslog_identifier" ];
+                  target_label = "syslog_identifier";
+                }
+              ];
+            }
+          ];
+          positions.filename = "\${STATE_DIRECTORY}/loki_positions.yaml";
+          clients = [{
+            url = "\${LOGS_REMOTE_WRITE_URL}";
+            basic_auth.username = "\${LOGS_REMOTE_WRITE_USERNAME}";
+            basic_auth.password_file = "\${CREDENTIALS_DIRECTORY}/logs_remote_write_password";
+          }];
+        }];
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.grafana-agent = {
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        set -euo pipefail
+        shopt -u nullglob
+
+        # Load all credentials into env if they are in UPPER_SNAKE form.
+        if [[ -n "''${CREDENTIALS_DIRECTORY:-}" ]]; then
+          for file in "$CREDENTIALS_DIRECTORY"/*; do
+            key=$(basename "$file")
+            if [[ $key =~ ^[A-Z0-9_]+$ ]]; then
+              echo "Environ $key"
+              export "$key=$(< "$file")"
+            fi
+          done
+        fi
+
+        # We can't use Environment=HOSTNAME=%H, as it doesn't include the domain part.
+        export HOSTNAME=$(< /proc/sys/kernel/hostname)
+
+        exec ${cfg.package}/bin/agent -config.expand-env -config.file ${configFile}
+      '';
+      serviceConfig = {
+        Restart = "always";
+        DynamicUser = true;
+        RestartSec = 2;
+        SupplementaryGroups = [
+          # allow to read the systemd journal for loki log forwarding
+          "systemd-journal"
+        ];
+        StateDirectory = "grafana-agent";
+        LoadCredential = lib.mapAttrsToList (key: value: "${key}:${value}") cfg.credentials;
+        Type = "simple";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 497d46741381b..68b4796f4f4ec 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -124,6 +124,11 @@ let
         default = 1;
         description = "Org id. will default to orgId 1 if not specified.";
       };
+      uid = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "Custom UID which can be used to reference this datasource in other parts of the configuration, if not specified will be generated automatically.";
+      };
       url = mkOption {
         type = types.str;
         description = "Url of the datasource.";
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index baa943302a00f..1dc2cee479ac3 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -24,16 +24,6 @@ let
     + cfg.web.extraConfig
   );
 
-  graphiteApiConfig = pkgs.writeText "graphite-api.yaml" ''
-    search_index: ${dataDir}/index
-    ${optionalString (config.time.timeZone != null) "time_zone: ${config.time.timeZone}"}
-    ${optionalString (cfg.api.finders != []) "finders:"}
-    ${concatMapStringsSep "\n" (f: "  - " + f.moduleName) cfg.api.finders}
-    ${optionalString (cfg.api.functions != []) "functions:"}
-    ${concatMapStringsSep "\n" (f: "  - " + f) cfg.api.functions}
-    ${cfg.api.extraConfig}
-  '';
-
   seyrenConfig = {
     SEYREN_URL = cfg.seyren.seyrenUrl;
     MONGO_URL = cfg.seyren.mongoUrl;
@@ -72,6 +62,8 @@ let
 in {
 
   imports = [
+    (mkRemovedOptionModule ["services" "graphite" "api"] "")
+    (mkRemovedOptionModule ["services" "graphite" "beacon"] "")
     (mkRemovedOptionModule ["services" "graphite" "pager"] "")
   ];
 
@@ -115,88 +107,6 @@ in {
       };
     };
 
-    api = {
-      enable = mkOption {
-        description = ''
-          Whether to enable graphite api. Graphite api is lightweight alternative
-          to graphite web, with api and without dashboard. It's advised to use
-          grafana as alternative dashboard and influxdb as alternative to
-          graphite carbon.
-
-          For more information visit
-          <link xlink:href="https://graphite-api.readthedocs.org/en/latest/"/>
-        '';
-        default = false;
-        type = types.bool;
-      };
-
-      finders = mkOption {
-        description = "List of finder plugins to load.";
-        default = [];
-        example = literalExpression "[ pkgs.python3Packages.influxgraph ]";
-        type = types.listOf types.package;
-      };
-
-      functions = mkOption {
-        description = "List of functions to load.";
-        default = [
-          "graphite_api.functions.SeriesFunctions"
-          "graphite_api.functions.PieFunctions"
-        ];
-        type = types.listOf types.str;
-      };
-
-      listenAddress = mkOption {
-        description = "Graphite web service listen address.";
-        default = "127.0.0.1";
-        type = types.str;
-      };
-
-      port = mkOption {
-        description = "Graphite api service port.";
-        default = 8080;
-        type = types.int;
-      };
-
-      package = mkOption {
-        description = "Package to use for graphite api.";
-        default = pkgs.python3Packages.graphite_api;
-        defaultText = literalExpression "pkgs.python3Packages.graphite_api";
-        type = types.package;
-      };
-
-      extraConfig = mkOption {
-        description = "Extra configuration for graphite api.";
-        default = ''
-          whisper:
-            directories:
-                - ${dataDir}/whisper
-        '';
-        defaultText = literalExpression ''
-          '''
-            whisper:
-              directories:
-                - ''${config.${opt.dataDir}}/whisper
-          '''
-        '';
-        example = ''
-          allowed_origins:
-            - dashboard.example.com
-          cheat_times: true
-          influxdb:
-            host: localhost
-            port: 8086
-            user: influxdb
-            pass: influxdb
-            db: metrics
-          cache:
-            CACHE_TYPE: 'filesystem'
-            CACHE_DIR: '/tmp/graphite-api-cache'
-        '';
-        type = types.lines;
-      };
-    };
-
     carbon = {
       config = mkOption {
         description = "Content of carbon configuration file.";
@@ -354,16 +264,6 @@ in {
         '';
       };
     };
-
-    beacon = {
-      enable = mkEnableOption "graphite beacon";
-
-      config = mkOption {
-        description = "Graphite beacon configuration.";
-        default = {};
-        type = types.attrs;
-      };
-    };
   };
 
   ###### implementation
@@ -489,44 +389,6 @@ in {
       environment.systemPackages = [ pkgs.python3Packages.graphite-web ];
     }))
 
-    (mkIf cfg.api.enable {
-      systemd.services.graphiteApi = {
-        description = "Graphite Api Interface";
-        wantedBy = [ "multi-user.target" ];
-        after = [ "network.target" ];
-        environment = {
-          PYTHONPATH = let
-              aenv = pkgs.python3.buildEnv.override {
-                extraLibs = [ cfg.api.package pkgs.cairo pkgs.python3Packages.cffi ] ++ cfg.api.finders;
-              };
-            in "${aenv}/${pkgs.python3.sitePackages}";
-          GRAPHITE_API_CONFIG = graphiteApiConfig;
-          LD_LIBRARY_PATH = "${pkgs.cairo.out}/lib";
-        };
-        serviceConfig = {
-          ExecStart = ''
-            ${pkgs.python3Packages.waitress}/bin/waitress-serve \
-            --host=${cfg.api.listenAddress} --port=${toString cfg.api.port} \
-            graphite_api.app:app
-          '';
-          User = "graphite";
-          Group = "graphite";
-          PermissionsStartOnly = true;
-        };
-        preStart = ''
-          if ! test -e ${dataDir}/db-created; then
-            mkdir -p ${dataDir}/cache/
-            chmod 0700 ${dataDir}/cache/
-
-            chown graphite:graphite ${cfg.dataDir}
-            chown -R graphite:graphite ${cfg.dataDir}/cache
-
-            touch ${dataDir}/db-created
-          fi
-        '';
-      };
-    })
-
     (mkIf cfg.seyren.enable {
       systemd.services.seyren = {
         description = "Graphite Alerting Dashboard";
@@ -550,25 +412,9 @@ in {
       services.mongodb.enable = mkDefault true;
     })
 
-    (mkIf cfg.beacon.enable {
-      systemd.services.graphite-beacon = {
-        description = "Grpahite Beacon Alerting Daemon";
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-          ExecStart = ''
-            ${pkgs.python3Packages.graphite_beacon}/bin/graphite-beacon \
-              --config=${pkgs.writeText "graphite-beacon.json" (builtins.toJSON cfg.beacon.config)}
-          '';
-          User = "graphite";
-          Group = "graphite";
-        };
-      };
-    })
-
     (mkIf (
       cfg.carbon.enableCache || cfg.carbon.enableAggregator || cfg.carbon.enableRelay ||
-      cfg.web.enable || cfg.api.enable ||
-      cfg.seyren.enable || cfg.beacon.enable
+      cfg.web.enable || cfg.seyren.enable
      ) {
       users.users.graphite = {
         uid = config.ids.uids.graphite;
diff --git a/nixos/modules/services/monitoring/netdata.nix b/nixos/modules/services/monitoring/netdata.nix
index 489dd337bb7cc..baf869af1c428 100644
--- a/nixos/modules/services/monitoring/netdata.nix
+++ b/nixos/modules/services/monitoring/netdata.nix
@@ -186,7 +186,7 @@ in {
       description = "Real time performance monitoring";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
-      path = (with pkgs; [ curl gawk iproute2 which procps ])
+      path = (with pkgs; [ curl gawk iproute2 which procps bash ])
         ++ lib.optional cfg.python.enable (pkgs.python3.withPackages cfg.python.extraPackages)
         ++ lib.optional config.virtualisation.libvirtd.enable (config.virtualisation.libvirtd.package);
       environment = {
@@ -201,7 +201,9 @@ in {
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/netdata -P /run/netdata/netdata.pid -D -c /etc/netdata/netdata.conf";
         ExecReload = "${pkgs.util-linux}/bin/kill -s HUP -s USR1 -s USR2 $MAINPID";
-        ExecPostStart = ''while [ "$(netdatacli ping)" != pong ]; do sleep 0.5; done'';
+        ExecStartPost = pkgs.writeShellScript "wait-for-netdata-up" ''
+          while [ "$(${pkgs.netdata}/bin/netdatacli ping)" != pong ]; do sleep 0.5; done
+        '';
 
         TimeoutStopSec = 60;
         Restart = "on-failure";
diff --git a/nixos/modules/services/monitoring/parsedmarc.nix b/nixos/modules/services/monitoring/parsedmarc.nix
index ec71365ba3c1c..efc7f69be7d64 100644
--- a/nixos/modules/services/monitoring/parsedmarc.nix
+++ b/nixos/modules/services/monitoring/parsedmarc.nix
@@ -3,7 +3,19 @@
 let
   cfg = config.services.parsedmarc;
   opt = options.services.parsedmarc;
-  ini = pkgs.formats.ini {};
+  isSecret = v: isAttrs v && v ? _secret && isString v._secret;
+  ini = pkgs.formats.ini {
+    mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" rec {
+      mkValueString = v:
+        if isInt           v then toString v
+        else if isString   v then v
+        else if true  ==   v then "True"
+        else if false ==   v then "False"
+        else if isSecret   v then hashString "sha256" v._secret
+        else throw "unsupported type ${typeOf v}: ${(lib.generators.toPretty {}) v}";
+    };
+  };
+  inherit (builtins) elem isAttrs isString isInt isList typeOf hashString;
 in
 {
   options.services.parsedmarc = {
@@ -107,11 +119,35 @@ in
     };
 
     settings = lib.mkOption {
+      example = lib.literalExpression ''
+        {
+          imap = {
+            host = "imap.example.com";
+            user = "alice@example.com";
+            password = { _secret = "/run/keys/imap_password" };
+            watch = true;
+          };
+          splunk_hec = {
+            url = "https://splunkhec.example.com";
+            token = { _secret = "/run/keys/splunk_token" };
+            index = "email";
+          };
+        }
+      '';
       description = ''
         Configuration parameters to set in
         <filename>parsedmarc.ini</filename>. For a full list of
         available parameters, see
         <link xlink:href="https://domainaware.github.io/parsedmarc/#configuration-file" />.
+
+        Settings containing secret data should be set to an attribute
+        set containing the attribute <literal>_secret</literal> - a
+        string pointing to a file containing the value the option
+        should be set to. See the example to get a better picture of
+        this: in the resulting <filename>parsedmarc.ini</filename>
+        file, the <literal>splunk_hec.token</literal> key will be set
+        to the contents of the
+        <filename>/run/keys/splunk_token</filename> file.
       '';
 
       type = lib.types.submodule {
@@ -170,11 +206,18 @@ in
             };
 
             password = lib.mkOption {
-              type = with lib.types; nullOr path;
+              type = with lib.types; nullOr (either path (attrsOf path));
               default = null;
               description = ''
-                The path to a file containing the IMAP server password.
+                The IMAP server password.
+
+                Always handled as a secret whether the value is
+                wrapped in a <literal>{ _secret = ...; }</literal>
+                attrset or not (refer to <xref
+                linkend="opt-services.parsedmarc.settings" /> for
+                details).
               '';
+              apply = x: if isAttrs x || x == null then x else { _secret = x; };
             };
 
             watch = lib.mkOption {
@@ -228,11 +271,18 @@ in
             };
 
             password = lib.mkOption {
-              type = with lib.types; nullOr path;
+              type = with lib.types; nullOr (either path (attrsOf path));
               default = null;
               description = ''
-                The path to a file containing the SMTP server password.
+                The SMTP server password.
+
+                Always handled as a secret whether the value is
+                wrapped in a <literal>{ _secret = ...; }</literal>
+                attrset or not (refer to <xref
+                linkend="opt-services.parsedmarc.settings" /> for
+                details).
               '';
+              apply = x: if isAttrs x || x == null then x else { _secret = x; };
             };
 
             from = lib.mkOption {
@@ -274,12 +324,19 @@ in
             };
 
             password = lib.mkOption {
-              type = with lib.types; nullOr path;
+              type = with lib.types; nullOr (either path (attrsOf path));
               default = null;
               description = ''
-                The path to a file containing the password to use when
-                connecting to Elasticsearch, if required.
+                The password to use when connecting to Elasticsearch,
+                if required.
+
+                Always handled as a secret whether the value is
+                wrapped in a <literal>{ _secret = ...; }</literal>
+                attrset or not (refer to <xref
+                linkend="opt-services.parsedmarc.settings" /> for
+                details).
               '';
+              apply = x: if isAttrs x || x == null then x else { _secret = x; };
             };
 
             ssl = lib.mkOption {
@@ -299,63 +356,6 @@ in
               '';
             };
           };
-
-          kafka = {
-            hosts = lib.mkOption {
-              default = [];
-              type = with lib.types; listOf str;
-              apply = x: if x == [] then null else lib.concatStringsSep "," x;
-              description = ''
-                A list of Apache Kafka hosts to publish parsed reports
-                to.
-              '';
-            };
-
-            user = lib.mkOption {
-              type = with lib.types; nullOr str;
-              default = null;
-              description = ''
-                Username to use when connecting to Kafka, if
-                required.
-              '';
-            };
-
-            password = lib.mkOption {
-              type = with lib.types; nullOr path;
-              default = null;
-              description = ''
-                The path to a file containing the password to use when
-                connecting to Kafka, if required.
-              '';
-            };
-
-            ssl = lib.mkOption {
-              type = with lib.types; nullOr bool;
-              default = null;
-              description = ''
-                Whether to use an encrypted SSL/TLS connection.
-              '';
-            };
-
-            aggregate_topic = lib.mkOption {
-              type = with lib.types; nullOr str;
-              default = null;
-              example = "aggregate";
-              description = ''
-                The Kafka topic to publish aggregate reports on.
-              '';
-            };
-
-            forensic_topic = lib.mkOption {
-              type = with lib.types; nullOr str;
-              default = null;
-              example = "forensic";
-              description = ''
-                The Kafka topic to publish forensic reports on.
-              '';
-            };
-          };
-
         };
 
       };
@@ -404,21 +404,14 @@ in
         enable = cfg.provision.grafana.datasource || cfg.provision.grafana.dashboard;
         datasources =
           let
-            pkgVer = lib.getVersion config.services.elasticsearch.package;
-            esVersion =
-              if lib.versionOlder pkgVer "7" then
-                "60"
-              else if lib.versionOlder pkgVer "8" then
-                "70"
-              else
-                throw "When provisioning parsedmarc grafana datasources: unknown Elasticsearch version.";
+            esVersion = lib.getVersion config.services.elasticsearch.package;
           in
             lib.mkIf cfg.provision.grafana.datasource [
               {
                 name = "dmarc-ag";
                 type = "elasticsearch";
                 access = "proxy";
-                url = "localhost:9200";
+                url = "http://localhost:9200";
                 jsonData = {
                   timeField = "date_range";
                   inherit esVersion;
@@ -428,7 +421,7 @@ in
                 name = "dmarc-fo";
                 type = "elasticsearch";
                 access = "proxy";
-                url = "localhost:9200";
+                url = "http://localhost:9200";
                 jsonData = {
                   timeField = "date_range";
                   inherit esVersion;
@@ -467,12 +460,17 @@ in
         # lists, empty attrsets and null. This makes it possible to
         # list interesting options in `settings` without them always
         # ending up in the resulting config.
-        filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! builtins.elem v [ null [] {} ])) cfg.settings;
+        filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ null [] {} ])) cfg.settings;
+
+        # Extract secrets (attributes set to an attrset with a
+        # "_secret" key) from the settings and generate the commands
+        # to run to perform the secret replacements.
+        secretPaths = lib.catAttrs "_secret" (lib.collect isSecret filteredConfig);
         parsedmarcConfig = ini.generate "parsedmarc.ini" filteredConfig;
-        mkSecretReplacement = file:
-          lib.optionalString (file != null) ''
-            replace-secret '${file}' '${file}' /run/parsedmarc/parsedmarc.ini
-          '';
+        mkSecretReplacement = file: ''
+          replace-secret ${lib.escapeShellArgs [ (hashString "sha256" file) file "/run/parsedmarc/parsedmarc.ini" ]}
+        '';
+        secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
       in
         {
           wantedBy = [ "multi-user.target" ];
@@ -487,10 +485,7 @@ in
                 umask u=rwx,g=,o=
                 cp ${parsedmarcConfig} /run/parsedmarc/parsedmarc.ini
                 chown parsedmarc:parsedmarc /run/parsedmarc/parsedmarc.ini
-                ${mkSecretReplacement cfg.settings.smtp.password}
-                ${mkSecretReplacement cfg.settings.imap.password}
-                ${mkSecretReplacement cfg.settings.elasticsearch.password}
-                ${mkSecretReplacement cfg.settings.kafka.password}
+                ${secretReplacements}
               '' + lib.optionalString cfg.provision.localMail.enable ''
                 openssl rand -hex 64 >/run/parsedmarc/dmarc_user_passwd
                 replace-secret '@imap-password@' '/run/parsedmarc/dmarc_user_passwd' /run/parsedmarc/parsedmarc.ini
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/mail.nix b/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
index 956bd96aa4543..a60f47f63932a 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/mail.nix
@@ -5,6 +5,8 @@ with lib;
 let
   cfg = config.services.prometheus.exporters.mail;
 
+  configFile = if cfg.configuration != null then configurationFile else (escapeShellArg cfg.configFile);
+
   configurationFile = pkgs.writeText "prometheus-mail-exporter.conf" (builtins.toJSON (
     # removes the _module attribute, null values and converts attrNames to lowercase
     mapAttrs' (name: value:
@@ -137,6 +139,13 @@ in
 {
   port = 9225;
   extraOpts = {
+    environmentFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        File containing env-vars to be substituted into the exporter's config.
+      '';
+    };
     configFile = mkOption {
       type = types.nullOr types.path;
       default = null;
@@ -162,13 +171,19 @@ in
   serviceOpts = {
     serviceConfig = {
       DynamicUser = false;
+      EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+      RuntimeDirectory = "prometheus-mail-exporter";
+      ExecStartPre = [
+        "${pkgs.writeShellScript "subst-secrets-mail-exporter" ''
+          umask 0077
+          ${pkgs.envsubst}/bin/envsubst -i ${configFile} -o ''${RUNTIME_DIRECTORY}/mail-exporter.json
+        ''}"
+      ];
       ExecStart = ''
         ${pkgs.prometheus-mail-exporter}/bin/mailexporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
           --web.telemetry-path ${cfg.telemetryPath} \
-          --config.file ${
-            if cfg.configuration != null then configurationFile else (escapeShellArg cfg.configFile)
-          } \
+          --config.file ''${RUNTIME_DIRECTORY}/mail-exporter.json \
           ${concatStringsSep " \\\n  " cfg.extraFlags}
       '';
     };
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/node.nix b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
index 5e5fc7cd55245..417920402f341 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/node.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/node.nix
@@ -44,6 +44,8 @@ in
       ];
       # The timex collector needs to access clock APIs
       ProtectClock = any (collector: collector == "timex") cfg.disabledCollectors;
+      # Allow space monitoring under /home
+      ProtectHome = true;
     };
   };
 }
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
index 4d3c1fa267e5f..53509b7a385b4 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/postfix.nix
@@ -74,11 +74,13 @@ in
     };
   };
   serviceOpts = {
+    after = mkIf cfg.systemd.enable [ cfg.systemd.unit ];
     serviceConfig = {
       DynamicUser = false;
       # By default, each prometheus exporter only gets AF_INET & AF_INET6,
       # but AF_UNIX is needed to read from the `showq`-socket.
       RestrictAddressFamilies = [ "AF_UNIX" ];
+      SupplementaryGroups = mkIf cfg.systemd.enable [ "systemd-journal" ];
       ExecStart = ''
         ${pkgs.prometheus-postfix-exporter}/bin/postfix_exporter \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
index d4aa69629ec89..2d329a1af1cbc 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/wireguard.nix
@@ -57,9 +57,9 @@ in {
         ${pkgs.prometheus-wireguard-exporter}/bin/prometheus_wireguard_exporter \
           -p ${toString cfg.port} \
           -l ${cfg.listenAddress} \
-          ${optionalString cfg.verbose "-v"} \
-          ${optionalString cfg.singleSubnetPerField "-s"} \
-          ${optionalString cfg.withRemoteIp "-r"} \
+          ${optionalString cfg.verbose "-v true"} \
+          ${optionalString cfg.singleSubnetPerField "-s true"} \
+          ${optionalString cfg.withRemoteIp "-r true"} \
           ${optionalString (cfg.wireguardConfig != null) "-n ${escapeShellArg cfg.wireguardConfig}"}
       '';
       RestrictAddressFamilies = [
diff --git a/nixos/modules/services/network-filesystems/glusterfs.nix b/nixos/modules/services/network-filesystems/glusterfs.nix
index 38be098de5d90..aa5dd91d55370 100644
--- a/nixos/modules/services/network-filesystems/glusterfs.nix
+++ b/nixos/modules/services/network-filesystems/glusterfs.nix
@@ -159,9 +159,10 @@ in
         install -m 0755 -d /var/log/glusterfs
       ''
       # The copying of hooks is due to upstream bug https://bugzilla.redhat.com/show_bug.cgi?id=1452761
+      # Excludes one hook due to missing SELinux binaries.
       + ''
         mkdir -p /var/lib/glusterd/hooks/
-        ${rsync}/bin/rsync -a ${glusterfs}/var/lib/glusterd/hooks/ /var/lib/glusterd/hooks/
+        ${rsync}/bin/rsync -a --exclude="S10selinux-label-brick.sh" ${glusterfs}/var/lib/glusterd/hooks/ /var/lib/glusterd/hooks/
 
         ${tlsCmd}
       ''
diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix
index 395b9788855ff..b7e6a787cfb1b 100644
--- a/nixos/modules/services/network-filesystems/ipfs.nix
+++ b/nixos/modules/services/network-filesystems/ipfs.nix
@@ -257,7 +257,7 @@ in
       '' + optionalString cfg.autoMigrate ''
         ${pkgs.ipfs-migrator}/bin/fs-repo-migrations -to '${cfg.package.repoVersion}' -y
       '' + ''
-          ipfs --offline config profile apply ${profile}
+          ipfs --offline config profile apply ${profile} >/dev/null
         fi
       '' + optionalString cfg.autoMount ''
         ipfs --offline config Mounts.FuseAllowOther --json true
@@ -283,7 +283,7 @@ in
         User = cfg.user;
         Group = cfg.group;
         StateDirectory = "";
-        ReadWritePaths = [ "" cfg.dataDir ];
+        ReadWritePaths = optionals (!cfg.autoMount) [ "" cfg.dataDir ];
       } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; };
     } // optionalAttrs (!cfg.startWhenNeeded) {
       wantedBy = [ "default.target" ];
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index 8bf04e3a1a23c..f76cffc79bfa4 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -174,6 +174,7 @@ in
         serviceConfig = {
           DynamicUser = true;
           StateDirectory = "bitlbee";
+          ReadWritePaths = [ cfg.configDir ];
           ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}";
         };
       };
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index faee99b175eb4..43a50af8f9b59 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -13,7 +13,7 @@ let
     foreground=YES
     use=${cfg.use}
     login=${cfg.username}
-    password=${lib.optionalString (cfg.protocol == "nsupdate") "/run/${RuntimeDirectory}/ddclient.key"}
+    password=${if cfg.protocol == "nsupdate" then "/run/${RuntimeDirectory}/ddclient.key" else "@password_placeholder@"}
     protocol=${cfg.protocol}
     ${lib.optionalString (cfg.script != "") "script=${cfg.script}"}
     ${lib.optionalString (cfg.server != "") "server=${cfg.server}"}
@@ -33,10 +33,9 @@ let
     ${lib.optionalString (cfg.configFile == null) (if (cfg.protocol == "nsupdate") then ''
       install ${cfg.passwordFile} /run/${RuntimeDirectory}/ddclient.key
     '' else if (cfg.passwordFile != null) then ''
-      password=$(printf "%q" "$(head -n 1 "${cfg.passwordFile}")")
-      sed -i "s|^password=$|password=$password|" /run/${RuntimeDirectory}/ddclient.conf
+      "${pkgs.replace-secret}/bin/replace-secret" "@password_placeholder@" "${cfg.passwordFile}" "/run/${RuntimeDirectory}/ddclient.conf"
     '' else ''
-      sed -i '/^password=$/d' /run/${RuntimeDirectory}/ddclient.conf
+      sed -i '/^password=@password_placeholder@$/d' /run/${RuntimeDirectory}/ddclient.conf
     '')}
   '';
 
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 3eb7ca99eafd4..a4c8608c31db8 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -215,7 +215,7 @@ in
         # dhcpcd.  So do a "systemctl restart" instead.
         stopIfChanged = false;
 
-        path = [ dhcpcd pkgs.nettools pkgs.openresolv ];
+        path = [ dhcpcd pkgs.nettools config.networking.resolvconf.package ];
 
         unitConfig.ConditionCapability = "CAP_NET_ADMIN";
 
diff --git a/nixos/modules/services/networking/flannel.nix b/nixos/modules/services/networking/flannel.nix
index ac84b3d35a3d8..4698c1f682c99 100644
--- a/nixos/modules/services/networking/flannel.nix
+++ b/nixos/modules/services/networking/flannel.nix
@@ -155,10 +155,11 @@ in {
         FLANNELD_ETCD_KEYFILE = cfg.etcd.keyFile;
         FLANNELD_ETCD_CERTFILE = cfg.etcd.certFile;
         FLANNELD_ETCD_CAFILE = cfg.etcd.caFile;
-        ETCDCTL_CERT_FILE = cfg.etcd.certFile;
-        ETCDCTL_KEY_FILE = cfg.etcd.keyFile;
-        ETCDCTL_CA_FILE = cfg.etcd.caFile;
-        ETCDCTL_PEERS = concatStringsSep "," cfg.etcd.endpoints;
+        ETCDCTL_CERT = cfg.etcd.certFile;
+        ETCDCTL_KEY = cfg.etcd.keyFile;
+        ETCDCTL_CACERT = cfg.etcd.caFile;
+        ETCDCTL_ENDPOINTS = concatStringsSep "," cfg.etcd.endpoints;
+        ETCDCTL_API = "3";
       } // optionalAttrs (cfg.storageBackend == "kubernetes") {
         FLANNELD_KUBE_SUBNET_MGR = "true";
         FLANNELD_KUBECONFIG_FILE = cfg.kubeconfig;
@@ -167,7 +168,7 @@ in {
       path = [ pkgs.iptables ];
       preStart = optionalString (cfg.storageBackend == "etcd") ''
         echo "setting network configuration"
-        until ${pkgs.etcd}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}'
+        until ${pkgs.etcd}/bin/etcdctl put /coreos.com/network/config '${builtins.toJSON networkConfig}'
         do
           echo "setting network configuration, retry"
           sleep 1
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 34fda57b23d23..06f3420b8ff83 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -158,6 +158,10 @@ let
       (sec "addressbook")
       (strOpt "defaulturl" cfg.addressbook.defaulturl)
     ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions)
+      ++ [
+      (sec "meshnets")
+      (boolOpt "yggdrasil" cfg.yggdrasil.enable)
+    ] ++ (optionalNullString "yggaddress" cfg.yggdrasil.address)
       ++ (flip map
       (collect (proto: proto ? port && proto ? address) cfg.proto)
       (proto: let protoOpts = [
@@ -546,6 +550,17 @@ in
         '';
       };
 
+      yggdrasil.enable = mkEnableOption "Yggdrasil";
+
+      yggdrasil.address = mkOption {
+        type = with types; nullOr str;
+        default = null;
+        description = ''
+          Your local yggdrasil address. Specify it if you want to bind your router to a
+          particular address.
+        '';
+      };
+
       proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // {
 
         auth = mkEnableOption "Webconsole authentication";
diff --git a/nixos/modules/services/networking/lokinet.nix b/nixos/modules/services/networking/lokinet.nix
new file mode 100644
index 0000000000000..cf091341c8390
--- /dev/null
+++ b/nixos/modules/services/networking/lokinet.nix
@@ -0,0 +1,157 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.lokinet;
+  dataDir = "/var/lib/lokinet";
+  settingsFormat = pkgs.formats.ini { listsAsDuplicateKeys = true; };
+  configFile = settingsFormat.generate "lokinet.ini" (lib.filterAttrsRecursive (n: v: v != null) cfg.settings);
+in with lib; {
+  options.services.lokinet = {
+    enable = mkEnableOption "Lokinet daemon";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.lokinet;
+      defaultText = literalExpression "pkgs.lokinet";
+      description = "Lokinet package to use.";
+    };
+
+    useLocally = mkOption {
+      type = types.bool;
+      default = false;
+      example = true;
+      description = "Whether to use Lokinet locally.";
+    };
+
+    settings = mkOption {
+      type = with types;
+        submodule {
+          freeformType = settingsFormat.type;
+
+          options = {
+            dns = {
+              bind = mkOption {
+                type = str;
+                default = "127.3.2.1";
+                description = "Address to bind to for handling DNS requests.";
+              };
+
+              upstream = mkOption {
+                type = listOf str;
+                default = [ "9.9.9.10" ];
+                example = [ "1.1.1.1" "8.8.8.8" ];
+                description = ''
+                  Upstream resolver(s) to use as fallback for non-loki addresses.
+                  Multiple values accepted.
+                '';
+              };
+            };
+
+            network = {
+              exit = mkOption {
+                type = bool;
+                default = false;
+                description = ''
+                  Whether to act as an exit node. Beware that this
+                  increases demand on the server and may pose liability concerns.
+                  Enable at your own risk.
+                '';
+              };
+
+              exit-node = mkOption {
+                type = nullOr (listOf str);
+                default = null;
+                example = ''
+                  exit-node = [ "example.loki" ];              # maps all exit traffic to example.loki
+                  exit-node = [ "example.loki:100.0.0.0/24" ]; # maps 100.0.0.0/24 to example.loki
+                '';
+                description = ''
+                  Specify a `.loki` address and an optional ip range to use as an exit broker.
+                  See <link xlink:href="http://probably.loki/wiki/index.php?title=Exit_Nodes"/> for
+                  a list of exit nodes.
+                '';
+              };
+
+              keyfile = mkOption {
+                type = nullOr str;
+                default = null;
+                example = "snappkey.private";
+                description = ''
+                  The private key to persist address with. If not specified the address will be ephemeral.
+                  This keyfile is generated automatically if the specified file doesn't exist.
+                '';
+              };
+            };
+          };
+        };
+      default = { };
+      example = literalExpression ''
+        {
+          dns = {
+            bind = "127.3.2.1";
+            upstream = [ "1.1.1.1" "8.8.8.8" ];
+          };
+
+          network.exit-node = [ "example.loki" "example2.loki" ];
+        }
+      '';
+      description = ''
+        Configuration for Lokinet.
+        Currently, the best way to view the available settings is by
+        generating a config file using `lokinet -g`.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    networking.resolvconf.extraConfig = mkIf cfg.useLocally ''
+      name_servers="${cfg.settings.dns.bind}"
+    '';
+
+    systemd.services.lokinet = {
+      description = "Lokinet";
+      after = [ "network-online.target" "network.target" ];
+      wants = [ "network-online.target" "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      preStart = ''
+        ln -sf ${cfg.package}/share/bootstrap.signed ${dataDir}
+        ${pkgs.coreutils}/bin/install -m 600 ${configFile} ${dataDir}/lokinet.ini
+
+        ${optionalString (cfg.settings.network.keyfile != null) ''
+          ${pkgs.crudini}/bin/crudini --set ${dataDir}/lokinet.ini network keyfile "${dataDir}/${cfg.settings.network.keyfile}"
+        ''}
+      '';
+
+      serviceConfig = {
+        DynamicUser = true;
+        StateDirectory = "lokinet";
+        AmbientCapabilities = [ "CAP_NET_ADMIN" "CAP_NET_BIND_SERVICE" ];
+        ExecStart = "${cfg.package}/bin/lokinet ${dataDir}/lokinet.ini";
+        Restart = "always";
+        RestartSec = "5s";
+
+        # hardening
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateTmp = true;
+        PrivateMounts = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        ReadWritePaths = "/dev/net/tun";
+        RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+      };
+    };
+
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/services/networking/minidlna.nix b/nixos/modules/services/networking/minidlna.nix
index c860f63efa673..57c37718e7cb5 100644
--- a/nixos/modules/services/networking/minidlna.nix
+++ b/nixos/modules/services/networking/minidlna.nix
@@ -5,164 +5,136 @@ with lib;
 
 let
   cfg = config.services.minidlna;
-  port = 8200;
+  settingsFormat = pkgs.formats.keyValue { listsAsDuplicateKeys = true; };
+  settingsFile = settingsFormat.generate "minidlna.conf" cfg.settings;
 in
 
 {
   ###### interface
-  options = {
-    services.minidlna.enable = mkOption {
-      type = types.bool;
-      default = false;
-      description =
-        ''
-          Whether to enable MiniDLNA, a simple DLNA server.  It serves
-          media files such as video and music to DLNA client devices
-          such as televisions and media players.
-        '';
-    };
+  options.services.minidlna.enable = mkOption {
+    type = types.bool;
+    default = false;
+    description = ''
+      Whether to enable MiniDLNA, a simple DLNA server.
+      It serves media files such as video and music to DLNA client devices
+      such as televisions and media players. If you use the firewall consider
+      adding the following: <literal>services.minidlna.openFirewall = true;</literal>
+    '';
+  };
+
+  options.services.minidlna.openFirewall = mkOption {
+    type = types.bool;
+    default = false;
+    description = ''
+      Whether to open both HTTP (TCP) and SSDP (UDP) ports in the firewall.
+    '';
+  };
 
-    services.minidlna.mediaDirs = mkOption {
-      type = types.listOf types.str;
-      default = [];
-      example = [ "/data/media" "V,/home/alice/video" ];
-      description =
-        ''
-          Directories to be scanned for media files.  The prefixes
-          <literal>A,</literal>, <literal>V,</literal> and
+  options.services.minidlna.settings = mkOption {
+    default = {};
+    description = ''
+      The contents of MiniDLNA's configuration file.
+      When the service is activated, a basic template is generated
+      from the current options opened here.
+    '';
+    type = types.submodule {
+      freeformType = settingsFormat.type;
+
+      options.media_dir = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        example = [ "/data/media" "V,/home/alice/video" ];
+        description = ''
+          Directories to be scanned for media files.
+          The prefixes <literal>A,</literal>,<literal>V,</literal> and
           <literal>P,</literal> restrict a directory to audio, video
-          or image files.  The directories must be accessible to the
+          or image files. The directories must be accessible to the
           <literal>minidlna</literal> user account.
         '';
-    };
-
-    services.minidlna.friendlyName = mkOption {
-      type = types.str;
-      default = "${config.networking.hostName} MiniDLNA";
-      defaultText = literalExpression ''"''${config.networking.hostName} MiniDLNA"'';
-      example = "rpi3";
-      description =
-        ''
-          Name that the DLNA server presents to clients.
-        '';
-    };
-
-    services.minidlna.rootContainer = mkOption {
-      type = types.str;
-      default = ".";
-      example = "B";
-      description =
-        ''
-          Use a different container as the root of the directory tree presented
-          to clients. The possible values are:
-          - "." - standard container
-          - "B" - "Browse Directory"
-          - "M" - "Music"
-          - "P" - "Pictures"
-          - "V" - "Video"
-          - Or, you can specify the ObjectID of your desired root container
-            (eg. 1$F for Music/Playlists)
-          If you specify "B" and the client device is audio-only then
-          "Music/Folders" will be used as root.
-         '';
-    };
-
-    services.minidlna.loglevel = mkOption {
-      type = types.str;
-      default = "warn";
-      example = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn";
-      description =
-        ''
-          Defines the type of messages that should be logged, and down to
-          which level of importance they should be considered.
-
-          The possible types are “artwork”, “database”, “general”, “http”,
-          “inotify”, “metadata”, “scanner”, “ssdp” and “tivo”.
-
-          The levels are “off”, “fatal”, “error”, “warn”, “info” and
-          “debug”, listed here in order of decreasing importance.  “off”
-          turns off logging messages entirely, “fatal” logs the most
-          critical messages only, and so on down to “debug” that logs every
-          single messages.
-
-          The types are comma-separated, followed by an equal sign (‘=’),
-          followed by a level that applies to the preceding types. This can
-          be repeated, separating each of these constructs with a comma.
-
-          Defaults to “general,artwork,database,inotify,scanner,metadata,
-          http,ssdp,tivo=warn” which logs every type of message at the
-          “warn” level.
-        '';
-    };
-
-    services.minidlna.announceInterval = mkOption {
-      type = types.int;
-      default = 895;
-      description =
-        ''
+      };
+      options.notify_interval = mkOption {
+        type = types.int;
+        default = 90000;
+        description = ''
           The interval between announces (in seconds).
-
-          By default miniDLNA will announce its presence on the network
-          approximately every 15 minutes.
-
-          Many people prefer shorter announce intervals (e.g. 60 seconds)
-          on their home networks, especially when DLNA clients are
-          started on demand.
+          Instead of waiting on announces, one can open port UDP 1900 or
+          set <literal>openFirewall</literal> option to use SSDP discovery.
+          Furthermore announce interval has now been set as 90000 in order
+          to prevent disconnects with certain clients and to rely solely
+          on the SSDP method.
+
+          Lower values (e.g. 60 seconds) should be used if one does not
+          want to utilize SSDP. By default miniDLNA will announce its
+          presence on the network approximately every 15 minutes. Many
+          people prefer shorter announce intervals on their home networks,
+          especially when DLNA clients are started on demand.
+
+          Some relevant information can be found here:
+          https://sourceforge.net/p/minidlna/discussion/879957/thread/1389d197/
         '';
-    };
-
-    services.minidlna.config = mkOption {
-      type = types.lines;
-      description =
-      ''
-        The contents of MiniDLNA's configuration file.
-        When the service is activated, a basic template is generated
-        from the current options opened here.
-      '';
-    };
-
-    services.minidlna.extraConfig = mkOption {
-      type = types.lines;
-      default = "";
-      example = ''
-        # Not exhaustive example
-        # Support for streaming .jpg and .mp3 files to a TiVo supporting HMO.
-        enable_tivo=no
-        # SSDP notify interval, in seconds.
-        notify_interval=10
-        # maximum number of simultaneous connections
-        # note: many clients open several simultaneous connections while
-        # streaming
-        max_connections=50
-        # set this to yes to allow symlinks that point outside user-defined
-        # media_dirs.
-        wide_links=yes
-      '';
-      description =
-      ''
-        Extra minidlna options not yet opened for configuration here
-        (strict_dlna, model_number, model_name, etc...).  This is appended
-        to the current service already provided.
-      '';
+      };
+      options.port = mkOption {
+        type = types.port;
+        default = 8200;
+        description = "Port number for HTTP traffic (descriptions, SOAP, media transfer).";
+      };
+      options.db_dir = mkOption {
+        type = types.path;
+        default = "/var/cache/minidlna";
+        example = "/tmp/minidlna";
+        description = "Specify the directory where you want MiniDLNA to store its database and album art cache.";
+      };
+      options.friendly_name = mkOption {
+        type = types.str;
+        default = "${config.networking.hostName} MiniDLNA";
+        defaultText = literalExpression "config.networking.hostName";
+        example = "rpi3";
+        description = "Name that the DLNA server presents to clients.";
+      };
+      options.root_container = mkOption {
+        type = types.str;
+        default = ".";
+        example = "B";
+        description = "Use a different container as the root of the directory tree presented to clients.";
+      };
+      options.log_level = mkOption {
+        type = types.str;
+        default = "warn";
+        example = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn";
+        description = "Defines the type of messages that should be logged and down to which level of importance.";
+      };
+      options.inotify = mkOption {
+        type = types.enum [ "yes" "no" ];
+        default = "no";
+        description = "Whether to enable inotify monitoring to automatically discover new files.";
+      };
+      options.enable_tivo = mkOption {
+        type = types.enum [ "yes" "no" ];
+        default = "no";
+        description = "Support for streaming .jpg and .mp3 files to a TiVo supporting HMO.";
+      };
+      options.wide_links = mkOption {
+        type = types.enum [ "yes" "no" ];
+        default = "no";
+        description = "Set this to yes to allow symlinks that point outside user-defined media_dirs.";
+      };
     };
   };
 
+  imports = [
+    (mkRemovedOptionModule [ "services" "minidlna" "config" ] "")
+    (mkRemovedOptionModule [ "services" "minidlna" "extraConfig" ] "")
+    (mkRenamedOptionModule [ "services" "minidlna" "loglevel"] [ "services" "minidlna" "settings" "log_level" ])
+    (mkRenamedOptionModule [ "services" "minidlna" "rootContainer"] [ "services" "minidlna" "settings" "root_container" ])
+    (mkRenamedOptionModule [ "services" "minidlna" "mediaDirs"] [ "services" "minidlna" "settings" "media_dir" ])
+    (mkRenamedOptionModule [ "services" "minidlna" "friendlyName"] [ "services" "minidlna" "settings" "friendly_name" ])
+    (mkRenamedOptionModule [ "services" "minidlna" "announceInterval"] [ "services" "minidlna" "settings" "notify_interval" ])
+  ];
+
   ###### implementation
   config = mkIf cfg.enable {
-    services.minidlna.config =
-      ''
-        port=${toString port}
-        friendly_name=${cfg.friendlyName}
-        db_dir=/var/cache/minidlna
-        log_level=${cfg.loglevel}
-        inotify=yes
-        root_container=${cfg.rootContainer}
-        ${concatMapStrings (dir: ''
-          media_dir=${dir}
-        '') cfg.mediaDirs}
-        notify_interval=${toString cfg.announceInterval}
-        ${cfg.extraConfig}
-      '';
+    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ];
+    networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 1900 ];
 
     users.users.minidlna = {
       description = "MiniDLNA daemon user";
@@ -186,7 +158,7 @@ in
             PIDFile = "/run/minidlna/pid";
             ExecStart =
               "${pkgs.minidlna}/sbin/minidlnad -S -P /run/minidlna/pid" +
-              " -f ${pkgs.writeText "minidlna.conf" cfg.config}";
+              " -f ${settingsFile}";
           };
       };
   };
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 256d9457d3960..70c6725d1035c 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -54,10 +54,10 @@ let
       hashedPassword = mkOption {
         type = uniq (nullOr str);
         default = null;
-        description = ''
+        description = mdDoc ''
           Specifies the hashed password for the MQTT User.
-          To generate hashed password install <literal>mosquitto</literal>
-          package and use <literal>mosquitto_passwd</literal>.
+          To generate hashed password install `mosquitto`
+          package and use `mosquitto_passwd`.
         '';
       };
 
@@ -65,11 +65,11 @@ let
         type = uniq (nullOr types.path);
         example = "/path/to/file";
         default = null;
-        description = ''
+        description = mdDoc ''
           Specifies the path to a file containing the
           hashed password for the MQTT user.
-          To generate hashed password install <literal>mosquitto</literal>
-          package and use <literal>mosquitto_passwd</literal>.
+          To generate hashed password install `mosquitto`
+          package and use `mosquitto_passwd`.
         '';
       };
 
@@ -155,24 +155,24 @@ let
     options = {
       plugin = mkOption {
         type = path;
-        description = ''
-          Plugin path to load, should be a <literal>.so</literal> file.
+        description = mdDoc ''
+          Plugin path to load, should be a `.so` file.
         '';
       };
 
       denySpecialChars = mkOption {
         type = bool;
-        description = ''
-          Automatically disallow all clients using <literal>#</literal>
-          or <literal>+</literal> in their name/id.
+        description = mdDoc ''
+          Automatically disallow all clients using `#`
+          or `+` in their name/id.
         '';
         default = true;
       };
 
       options = mkOption {
         type = attrsOf optionType;
-        description = ''
-          Options for the auth plugin. Each key turns into a <literal>auth_opt_*</literal>
+        description = mdDoc ''
+          Options for the auth plugin. Each key turns into a `auth_opt_*`
            line in the config.
         '';
         default = {};
@@ -239,8 +239,8 @@ let
 
       address = mkOption {
         type = nullOr str;
-        description = ''
-          Address to listen on. Listen on <literal>0.0.0.0</literal>/<literal>::</literal>
+        description = mdDoc ''
+          Address to listen on. Listen on `0.0.0.0`/`::`
           when unset.
         '';
         default = null;
@@ -248,10 +248,10 @@ let
 
       authPlugins = mkOption {
         type = listOf authPluginOptions;
-        description = ''
+        description = mdDoc ''
           Authentication plugin to attach to this listener.
-          Refer to the <link xlink:href="https://mosquitto.org/man/mosquitto-conf-5.html">
-          mosquitto.conf documentation</link> for details on authentication plugins.
+          Refer to the [mosquitto.conf documentation](https://mosquitto.org/man/mosquitto-conf-5.html)
+          for details on authentication plugins.
         '';
         default = [];
       };
@@ -472,10 +472,10 @@ let
 
     includeDirs = mkOption {
       type = listOf path;
-      description = ''
+      description = mdDoc ''
         Directories to be scanned for further config files to include.
         Directories will processed in the order given,
-        <literal>*.conf</literal> files in the directory will be
+        `*.conf` files in the directory will be
         read in case-sensistive alphabetical order.
       '';
       default = [];
diff --git a/nixos/modules/services/networking/murmur.nix b/nixos/modules/services/networking/murmur.nix
index 06ec04dbbf16a..84b9936aa6235 100644
--- a/nixos/modules/services/networking/murmur.nix
+++ b/nixos/modules/services/networking/murmur.nix
@@ -59,6 +59,14 @@ in
         description = "If enabled, start the Murmur Mumble server.";
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Open ports in the firewall for the Murmur Mumble server.
+        '';
+      };
+
       autobanAttempts = mkOption {
         type = types.int;
         default = 10;
@@ -291,6 +299,11 @@ in
       gid             = config.ids.gids.murmur;
     };
 
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.port ];
+      allowedUDPPorts = [ cfg.port ];
+    };
+
     systemd.services.murmur = {
       description = "Murmur Chat Service";
       wantedBy    = [ "multi-user.target" ];
diff --git a/nixos/modules/services/networking/mxisd.nix b/nixos/modules/services/networking/mxisd.nix
index 803f0689d1fd1..1509671bc54ae 100644
--- a/nixos/modules/services/networking/mxisd.nix
+++ b/nixos/modules/services/networking/mxisd.nix
@@ -46,6 +46,15 @@ in {
         description = "The mxisd/ma1sd package to use";
       };
 
+      environmentFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Path to an environment-file which may contain secrets to be
+          substituted via <package>envsubst</package>.
+        '';
+      };
+
       dataDir = mkOption {
         type = types.str;
         default = "/var/lib/mxisd";
@@ -118,7 +127,13 @@ in {
         Type = "simple";
         User = "mxisd";
         Group = "mxisd";
-        ExecStart = "${cfg.package}/bin/${executable} -c ${configFile}";
+        EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+        ExecStart = "${cfg.package}/bin/${executable} -c ${cfg.dataDir}/mxisd-config.yaml";
+        ExecStartPre = "${pkgs.writeShellScript "mxisd-substitute-secrets" ''
+          umask 0077
+          ${pkgs.envsubst}/bin/envsubst -o ${cfg.dataDir}/mxisd-config.yaml \
+            -i ${configFile}
+        ''}";
         WorkingDirectory = cfg.dataDir;
         Restart = "on-failure";
       };
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index 7920e4b263457..9e8db04e62241 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -511,8 +511,13 @@ in
 
       dataDir = mkOption {
         type = types.path;
-        description = "Directory where Prosody stores its data";
         default = "/var/lib/prosody";
+        description = ''
+          The prosody home directory used to store all data. If left as the default value
+          this directory will automatically be created before the prosody server starts, otherwise
+          you are responsible for ensuring the directory exists with appropriate ownership
+          and permissions.
+        '';
       };
 
       disco_items = mkOption {
@@ -524,13 +529,29 @@ in
       user = mkOption {
         type = types.str;
         default = "prosody";
-        description = "User account under which prosody runs.";
+        description = ''
+          User account under which prosody runs.
+
+          <note><para>
+          If left as the default value this user will automatically be created
+          on system activation, otherwise you are responsible for
+          ensuring the user exists before the prosody service starts.
+          </para></note>
+        '';
       };
 
       group = mkOption {
         type = types.str;
         default = "prosody";
-        description = "Group account under which prosody runs.";
+        description = ''
+          Group account under which prosody runs.
+
+          <note><para>
+          If left as the default value this group will automatically be created
+          on system activation, otherwise you are responsible for
+          ensuring the group exists before the prosody service starts.
+          </para></note>
+        '';
       };
 
       allowRegistration = mkOption {
@@ -839,9 +860,8 @@ in
     users.users.prosody = mkIf (cfg.user == "prosody") {
       uid = config.ids.uids.prosody;
       description = "Prosody user";
-      createHome = true;
       inherit (cfg) group;
-      home = "${cfg.dataDir}";
+      home = cfg.dataDir;
     };
 
     users.groups.prosody = mkIf (cfg.group == "prosody") {
@@ -854,28 +874,33 @@ in
       wants = [ "network-online.target" ];
       wantedBy = [ "multi-user.target" ];
       restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ];
-      serviceConfig = {
-        User = cfg.user;
-        Group = cfg.group;
-        Type = "forking";
-        RuntimeDirectory = [ "prosody" ];
-        PIDFile = "/run/prosody/prosody.pid";
-        ExecStart = "${cfg.package}/bin/prosodyctl start";
-        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-
-        MemoryDenyWriteExecute = true;
-        PrivateDevices = true;
-        PrivateMounts = true;
-        PrivateTmp = true;
-        ProtectControlGroups = true;
-        ProtectHome = true;
-        ProtectHostname = true;
-        ProtectKernelModules = true;
-        ProtectKernelTunables = true;
-        RestrictNamespaces = true;
-        RestrictRealtime = true;
-        RestrictSUIDSGID = true;
-      };
+      serviceConfig = mkMerge [
+        {
+          User = cfg.user;
+          Group = cfg.group;
+          Type = "forking";
+          RuntimeDirectory = [ "prosody" ];
+          PIDFile = "/run/prosody/prosody.pid";
+          ExecStart = "${cfg.package}/bin/prosodyctl start";
+          ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+
+          MemoryDenyWriteExecute = true;
+          PrivateDevices = true;
+          PrivateMounts = true;
+          PrivateTmp = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectHostname = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+        }
+        (mkIf (cfg.dataDir == "/var/lib/prosody") {
+          StateDirectory = "prosody";
+        })
+      ];
     };
 
   };
diff --git a/nixos/modules/services/networking/radvd.nix b/nixos/modules/services/networking/radvd.nix
index 6e8db55bbf0d1..fb3eb71a8ce19 100644
--- a/nixos/modules/services/networking/radvd.nix
+++ b/nixos/modules/services/networking/radvd.nix
@@ -16,9 +16,9 @@ in
 
   ###### interface
 
-  options = {
+  options.services.radvd = {
 
-    services.radvd.enable = mkOption {
+    enable = mkOption {
       type = types.bool;
       default = false;
       description =
@@ -32,7 +32,16 @@ in
         '';
     };
 
-    services.radvd.config = mkOption {
+    package = mkOption {
+      type = types.package;
+      default = pkgs.radvd;
+      defaultText = literalExpression "pkgs.radvd";
+      description = ''
+        The RADVD package to use for the RADVD service.
+      '';
+    };
+
+    config = mkOption {
       type = types.lines;
       example =
         ''
@@ -67,7 +76,7 @@ in
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" ];
         serviceConfig =
-          { ExecStart = "@${pkgs.radvd}/bin/radvd radvd -n -u radvd -C ${confFile}";
+          { ExecStart = "@${cfg.package}/bin/radvd radvd -n -u radvd -C ${confFile}";
             Restart = "always";
           };
       };
diff --git a/nixos/modules/services/networking/routedns.nix b/nixos/modules/services/networking/routedns.nix
new file mode 100644
index 0000000000000..e0f5eedd2c8e5
--- /dev/null
+++ b/nixos/modules/services/networking/routedns.nix
@@ -0,0 +1,84 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+
+with lib;
+
+let
+  cfg = config.services.routedns;
+  settingsFormat = pkgs.formats.toml { };
+in
+{
+  options.services.routedns = {
+    enable = mkEnableOption "RouteDNS - DNS stub resolver, proxy and router";
+
+    settings = mkOption {
+      type = settingsFormat.type;
+      example = literalExpression ''
+        {
+          resolvers.cloudflare-dot = {
+            address = "1.1.1.1:853";
+            protocol = "dot";
+          };
+          groups.cloudflare-cached = {
+            type = "cache";
+            resolvers = ["cloudflare-dot"];
+          };
+          listeners.local-udp = {
+            address = "127.0.0.1:53";
+            protocol = "udp";
+            resolver = "cloudflare-cached";
+          };
+          listeners.local-tcp = {
+            address = "127.0.0.1:53";
+            protocol = "tcp";
+            resolver = "cloudflare-cached";
+          };
+        }
+      '';
+      description = ''
+        Configuration for RouteDNS, see <link xlink:href="https://github.com/folbricht/routedns/blob/master/doc/configuration.md"/>
+        for more information.
+      '';
+    };
+
+    configFile = mkOption {
+      default = settingsFormat.generate "routedns.toml" cfg.settings;
+      defaultText = "A RouteDNS configuration file automatically generated by values from services.routedns.*";
+      type = types.path;
+      example = literalExpression ''"''${pkgs.routedns}/cmd/routedns/example-config/use-case-1.toml"'';
+      description = "Path to RouteDNS TOML configuration file.";
+    };
+
+    package = mkOption {
+      default = pkgs.routedns;
+      defaultText = literalExpression "pkgs.routedns";
+      type = types.package;
+      description = "RouteDNS package to use.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.routedns = {
+      description = "RouteDNS - DNS stub resolver, proxy and router";
+      after = [ "network.target" ]; # in case a bootstrap resolver is used, this might fail a few times until the respective server is actually reachable
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network.target" ];
+      startLimitIntervalSec = 30;
+      startLimitBurst = 5;
+      serviceConfig = {
+        Restart = "on-failure";
+        RestartSec = "5s";
+        LimitNPROC = 512;
+        LimitNOFILE = 1048576;
+        DynamicUser = true;
+        AmbientCapabilities = "CAP_NET_BIND_SERVICE";
+        NoNewPrivileges = true;
+        ExecStart = "${getBin cfg.package}/bin/routedns -l 4 ${cfg.configFile}";
+      };
+    };
+  };
+  meta.maintainers = with maintainers; [ jsimonetti ];
+}
diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix
index b73f255eb9dd2..10dc27da70098 100644
--- a/nixos/modules/services/networking/searx.nix
+++ b/nixos/modules/services/networking/searx.nix
@@ -143,6 +143,7 @@ in
             disable-logging = true;
             http = ":8080";                   # serve via HTTP...
             socket = "/run/searx/searx.sock"; # ...or UNIX socket
+            chmod-socket = "660";             # allow the searx group to read/write to the socket
           }
         '';
         description = ''
@@ -220,7 +221,12 @@ in
         lazy-apps = true;
         enable-threads = true;
         module = "searx.webapp";
-        env = [ "SEARX_SETTINGS_PATH=${cfg.settingsFile}" ];
+        env = [
+          "SEARX_SETTINGS_PATH=${cfg.settingsFile}"
+          # searxng compatiblity https://github.com/searxng/searxng/issues/1519
+          "SEARXNG_SETTINGS_PATH=${cfg.settingsFile}"
+        ];
+        buffer-size = 32768;
         pythonPackages = self: [ cfg.package ];
       } // cfg.uwsgiConfig;
     };
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 6b69d559748c5..52a50b892ec61 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -442,7 +442,9 @@ in
 
                 ${flip concatMapStrings cfg.hostKeys (k: ''
                   if ! [ -s "${k.path}" ]; then
-                      rm -f "${k.path}"
+                      if ! [ -h "${k.path}" ]; then
+                          rm -f "${k.path}"
+                      fi
                       ssh-keygen \
                         -t "${k.type}" \
                         ${if k ? bits then "-b ${toString k.bits}" else ""} \
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
index df4908a0fff9e..55fac27f92ce9 100644
--- a/nixos/modules/services/networking/stunnel.nix
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -7,80 +7,27 @@ let
   cfg = config.services.stunnel;
   yesNo = val: if val then "yes" else "no";
 
+  verifyRequiredField = type: field: n: c: {
+    assertion = hasAttr field c;
+    message =  "stunnel: \"${n}\" ${type} configuration - Field ${field} is required.";
+  };
+
   verifyChainPathAssert = n: c: {
-    assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer);
+    assertion = (c.verifyHostname or null) == null || (c.verifyChain || c.verifyPeer);
     message =  "stunnel: \"${n}\" client configuration - hostname verification " +
       "is not possible without either verifyChain or verifyPeer enabled";
   };
 
-  serverConfig = {
-    options = {
-      accept = mkOption {
-        type = types.either types.str types.int;
-        description = ''
-          On which [host:]port stunnel should listen for incoming TLS connections.
-          Note that unlike other softwares stunnel ipv6 address need no brackets,
-          so to listen on all IPv6 addresses on port 1234 one would use ':::1234'.
-        '';
-      };
-
-      connect = mkOption {
-        type = types.either types.str types.int;
-        description = "Port or IP:Port to which the decrypted connection should be forwarded.";
-      };
-
-      cert = mkOption {
-        type = types.path;
-        description = "File containing both the private and public keys.";
-      };
-    };
-  };
-
-  clientConfig = {
-    options = {
-      accept = mkOption {
-        type = types.str;
-        description = "IP:Port on which connections should be accepted.";
-      };
-
-      connect = mkOption {
-        type = types.str;
-        description = "IP:Port destination to connect to.";
-      };
-
-      verifyChain = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Check if the provided certificate has a valid certificate chain (against CAPath).";
-      };
-
-      verifyPeer = mkOption {
-        type = types.bool;
-        default = false;
-        description = "Check if the provided certificate is contained in CAPath.";
-      };
-
-      CAPath = mkOption {
-        type = types.nullOr types.path;
-        default = null;
-        description = "Path to a directory containing certificates to validate against.";
-      };
-
-      CAFile = mkOption {
-        type = types.nullOr types.path;
-        default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
-        defaultText = literalExpression ''"''${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"'';
-        description = "Path to a file containing certificates to validate against.";
-      };
-
-      verifyHostname = mkOption {
-        type = with types; nullOr str;
-        default = null;
-        description = "If set, stunnel checks if the provided certificate is valid for the given hostname.";
-      };
-    };
-  };
-
+  removeNulls = mapAttrs (_: filterAttrs (_: v: v != null));
+  mkValueString = v:
+    if v == true then "yes"
+    else if v == false then "no"
+    else generators.mkValueStringDefault {} v;
+  generateConfig = c:
+    generators.toINI {
+      mkSectionName = id;
+      mkKeyValue = k: v: "${k} = ${mkValueString v}";
+    } (removeNulls c);
 
 in
 
@@ -130,8 +77,13 @@ in
 
 
       servers = mkOption {
-        description = "Define the server configuations.";
-        type = with types; attrsOf (submodule serverConfig);
+        description = ''
+          Define the server configuations.
+
+          See "SERVICE-LEVEL OPTIONS" in <citerefentry><refentrytitle>stunnel</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry>.
+        '';
+        type = with types; attrsOf (attrsOf (nullOr (oneOf [bool int str])));
         example = {
           fancyWebserver = {
             accept = 443;
@@ -143,8 +95,33 @@ in
       };
 
       clients = mkOption {
-        description = "Define the client configurations.";
-        type = with types; attrsOf (submodule clientConfig);
+        description = ''
+          Define the client configurations.
+
+          By default, verifyChain and OCSPaia are enabled and a CAFile is provided from pkgs.cacert.
+
+          See "SERVICE-LEVEL OPTIONS" in <citerefentry><refentrytitle>stunnel</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry>.
+        '';
+        type = with types; attrsOf (attrsOf (nullOr (oneOf [bool int str])));
+
+        apply = let
+          applyDefaults = c:
+            {
+              CAFile = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
+              OCSPaia = true;
+              verifyChain = true;
+            } // c;
+          setCheckHostFromVerifyHostname = c:
+            # To preserve backward-compatibility with the old NixOS stunnel module
+            # definition, allow "verifyHostname" as an alias for "checkHost".
+            c // {
+              checkHost = c.checkHost or c.verifyHostname or null;
+              verifyHostname = null; # Not a real stunnel configuration setting
+            };
+          forceClient = c: c // { client = true; };
+        in mapAttrs (_: c: forceClient (setCheckHostFromVerifyHostname (applyDefaults c)));
+
         example = {
           foobar = {
             accept = "0.0.0.0:8080";
@@ -169,6 +146,11 @@ in
       })
 
       (mapAttrsToList verifyChainPathAssert cfg.clients)
+      (mapAttrsToList (verifyRequiredField "client" "accept") cfg.clients)
+      (mapAttrsToList (verifyRequiredField "client" "connect") cfg.clients)
+      (mapAttrsToList (verifyRequiredField "server" "accept") cfg.servers)
+      (mapAttrsToList (verifyRequiredField "server" "cert") cfg.servers)
+      (mapAttrsToList (verifyRequiredField "server" "connect") cfg.servers)
     ];
 
     environment.systemPackages = [ pkgs.stunnel ];
@@ -183,36 +165,10 @@ in
       ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" }
 
       ; ----- SERVER CONFIGURATIONS -----
-      ${ lib.concatStringsSep "\n"
-           (lib.mapAttrsToList
-             (n: v: ''
-               [${n}]
-               accept = ${toString v.accept}
-               connect = ${toString v.connect}
-               cert = ${v.cert}
-
-             '')
-           cfg.servers)
-      }
+      ${ generateConfig cfg.servers }
 
       ; ----- CLIENT CONFIGURATIONS -----
-      ${ lib.concatStringsSep "\n"
-           (lib.mapAttrsToList
-             (n: v: ''
-               [${n}]
-               client = yes
-               accept = ${v.accept}
-               connect = ${v.connect}
-               verifyChain = ${yesNo v.verifyChain}
-               verifyPeer = ${yesNo v.verifyPeer}
-               ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"}
-               ${optionalString (v.CAFile != null) "CAFile = ${v.CAFile}"}
-               ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"}
-               OCSPaia = yes
-
-             '')
-           cfg.clients)
-      }
+      ${ generateConfig cfg.clients }
     '';
 
     systemd.services.stunnel = {
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index 3a3d4c80ecff4..66b85cd9d8a9f 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -30,15 +30,22 @@ let
   updateConfig = pkgs.writers.writeDash "merge-syncthing-config" ''
     set -efu
 
+    # be careful not to leak secrets in the filesystem or in process listings
+
+    umask 0077
+
     # get the api key by parsing the config.xml
     while
-        ! api_key=$(${pkgs.libxml2}/bin/xmllint \
+        ! ${pkgs.libxml2}/bin/xmllint \
             --xpath 'string(configuration/gui/apikey)' \
-            ${cfg.configDir}/config.xml)
+            ${cfg.configDir}/config.xml \
+            >"$RUNTIME_DIRECTORY/api_key"
     do sleep 1; done
 
+    (printf "X-API-Key: "; cat "$RUNTIME_DIRECTORY/api_key") >"$RUNTIME_DIRECTORY/headers"
+
     curl() {
-        ${pkgs.curl}/bin/curl -sSLk -H "X-API-Key: $api_key" \
+        ${pkgs.curl}/bin/curl -sSLk -H "@$RUNTIME_DIRECTORY/headers" \
             --retry 1000 --retry-delay 1 --retry-all-errors \
             "$@"
     }
@@ -72,39 +79,39 @@ in {
       cert = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = ''
-          Path to the <literal>cert.pem</literal> file, which will be copied into Syncthing's
-          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        description = mdDoc ''
+          Path to the `cert.pem` file, which will be copied into Syncthing's
+          [configDir](#opt-services.syncthing.configDir).
         '';
       };
 
       key = mkOption {
         type = types.nullOr types.str;
         default = null;
-        description = ''
-          Path to the <literal>key.pem</literal> file, which will be copied into Syncthing's
-          <link linkend="opt-services.syncthing.configDir">configDir</link>.
+        description = mdDoc ''
+          Path to the `key.pem` file, which will be copied into Syncthing's
+          [configDir](#opt-services.syncthing.configDir).
         '';
       };
 
       overrideDevices = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = mdDoc ''
           Whether to delete the devices which are not configured via the
-          <link linkend="opt-services.syncthing.devices">devices</link> option.
-          If set to <literal>false</literal>, devices added via the web
+          [devices](#opt-services.syncthing.devices) option.
+          If set to `false`, devices added via the web
           interface will persist and will have to be deleted manually.
         '';
       };
 
       devices = mkOption {
         default = {};
-        description = ''
+        description = mdDoc ''
           Peers/devices which Syncthing should communicate with.
 
           Note that you can still add devices manually, but those changes
-          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          will be reverted on restart if [overrideDevices](#opt-services.syncthing.overrideDevices)
           is enabled.
         '';
         example = {
@@ -135,27 +142,27 @@ in {
 
             id = mkOption {
               type = types.str;
-              description = ''
-                The device ID. See <link xlink:href="https://docs.syncthing.net/dev/device-ids.html"/>.
+              description = mdDoc ''
+                The device ID. See <https://docs.syncthing.net/dev/device-ids.html>.
               '';
             };
 
             introducer = mkOption {
               type = types.bool;
               default = false;
-              description = ''
+              description = mdDoc ''
                 Whether the device should act as an introducer and be allowed
                 to add folders on this computer.
-                See <link xlink:href="https://docs.syncthing.net/users/introducer.html"/>.
+                See <https://docs.syncthing.net/users/introducer.html>.
               '';
             };
 
             autoAcceptFolders = mkOption {
               type = types.bool;
               default = false;
-              description = ''
+              description = mdDoc ''
                 Automatically create or share folders that this device advertises at the default path.
-                See <link xlink:href="https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format"/>.
+                See <https://docs.syncthing.net/users/config.html?highlight=autoaccept#config-file-format>.
               '';
             };
 
@@ -166,21 +173,21 @@ in {
       overrideFolders = mkOption {
         type = types.bool;
         default = true;
-        description = ''
+        description = mdDoc ''
           Whether to delete the folders which are not configured via the
-          <link linkend="opt-services.syncthing.folders">folders</link> option.
-          If set to <literal>false</literal>, folders added via the web
+          [folders](#opt-services.syncthing.folders) option.
+          If set to `false`, folders added via the web
           interface will persist and will have to be deleted manually.
         '';
       };
 
       folders = mkOption {
         default = {};
-        description = ''
+        description = mdDoc ''
           Folders which should be shared by Syncthing.
 
-          Note that you can still add devices manually, but those changes
-          will be reverted on restart if <link linkend="opt-services.syncthing.overrideDevices">overrideDevices</link>
+          Note that you can still add folders manually, but those changes
+          will be reverted on restart if [overrideFolders](#opt-services.syncthing.overrideFolders)
           is enabled.
         '';
         example = literalExpression ''
@@ -231,18 +238,18 @@ in {
             devices = mkOption {
               type = types.listOf types.str;
               default = [];
-              description = ''
+              description = mdDoc ''
                 The devices this folder should be shared with. Each device must
-                be defined in the <link linkend="opt-services.syncthing.devices">devices</link> option.
+                be defined in the [devices](#opt-services.syncthing.devices) option.
               '';
             };
 
             versioning = mkOption {
               default = null;
-              description = ''
+              description = mdDoc ''
                 How to keep changed/deleted files with Syncthing.
                 There are 4 different types of versioning with different parameters.
-                See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                See <https://docs.syncthing.net/users/versioning.html>.
               '';
               example = literalExpression ''
                 [
@@ -284,17 +291,17 @@ in {
                 options = {
                   type = mkOption {
                     type = enum [ "external" "simple" "staggered" "trashcan" ];
-                    description = ''
+                    description = mdDoc ''
                       The type of versioning.
-                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                      See <https://docs.syncthing.net/users/versioning.html>.
                     '';
                   };
                   params = mkOption {
                     type = attrsOf (either str path);
-                    description = ''
+                    description = mdDoc ''
                       The parameters for versioning. Structure depends on
-                      <link linkend="opt-services.syncthing.folders._name_.versioning.type">versioning.type</link>.
-                      See <link xlink:href="https://docs.syncthing.net/users/versioning.html"/>.
+                      [versioning.type](#opt-services.syncthing.folders._name_.versioning.type).
+                      See <https://docs.syncthing.net/users/versioning.html>.
                     '';
                   };
                 };
@@ -345,9 +352,9 @@ in {
             ignoreDelete = mkOption {
               type = types.bool;
               default = false;
-              description = ''
+              description = mdDoc ''
                 Whether to skip deleting files that are deleted by peers.
-                See <link xlink:href="https://docs.syncthing.net/advanced/folder-ignoredelete.html"/>.
+                See <https://docs.syncthing.net/advanced/folder-ignoredelete.html>.
               '';
             };
           };
@@ -357,9 +364,9 @@ in {
       extraOptions = mkOption {
         type = types.addCheck (pkgs.formats.json {}).type isAttrs;
         default = {};
-        description = ''
+        description = mdDoc ''
           Extra configuration options for Syncthing.
-          See <link xlink:href="https://docs.syncthing.net/users/config.html"/>.
+          See <https://docs.syncthing.net/users/config.html>.
         '';
         example = {
           options.localAnnounceEnabled = false;
@@ -387,9 +394,9 @@ in {
         type = types.str;
         default = defaultUser;
         example = "yourUser";
-        description = ''
+        description = mdDoc ''
           The user to run Syncthing as.
-          By default, a user named <literal>${defaultUser}</literal> will be created.
+          By default, a user named `${defaultUser}` will be created.
         '';
       };
 
@@ -397,9 +404,9 @@ in {
         type = types.str;
         default = defaultGroup;
         example = "yourGroup";
-        description = ''
+        description = mdDoc ''
           The group to run Syncthing under.
-          By default, a group named <literal>${defaultGroup}</literal> will be created.
+          By default, a group named `${defaultGroup}` will be created.
         '';
       };
 
@@ -407,11 +414,11 @@ in {
         type = with types; nullOr str;
         default = null;
         example = "socks5://address.com:1234";
-        description = ''
+        description = mdDoc ''
           Overwrites the all_proxy environment variable for the Syncthing process to
           the given value. This is normally used to let Syncthing connect
           through a SOCKS5 proxy server.
-          See <link xlink:href="https://docs.syncthing.net/users/proxying.html"/>.
+          See <https://docs.syncthing.net/users/proxying.html>.
         '';
       };
 
@@ -432,25 +439,13 @@ in {
           The path where the settings and keys will exist.
         '';
         default = cfg.dataDir + optionalString cond "/.config/syncthing";
-        defaultText = literalDocBook ''
-          <variablelist>
-            <varlistentry>
-              <term><literal>stateVersion >= 19.03</literal></term>
-              <listitem>
-                <programlisting>
-                  config.${opt.dataDir} + "/.config/syncthing"
-                </programlisting>
-              </listitem>
-            </varlistentry>
-            <varlistentry>
-              <term>otherwise</term>
-              <listitem>
-                <programlisting>
-                  config.${opt.dataDir}
-                </programlisting>
-              </listitem>
-            </varlistentry>
-          </variablelist>
+        defaultText = literalMD ''
+          * if `stateVersion >= 19.03`:
+
+                config.${opt.dataDir} + "/.config/syncthing"
+          * otherwise:
+
+                config.${opt.dataDir}
         '';
       };
 
@@ -588,6 +583,7 @@ in {
         serviceConfig = {
           User = cfg.user;
           RemainAfterExit = true;
+          RuntimeDirectory = "syncthing-init";
           Type = "oneshot";
           ExecStart = updateConfig;
         };
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index 0133874d0e0d0..39c9c6fc5b0d1 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -6,6 +6,7 @@ let
   cfg = config.services.tailscale;
   firewallOn = config.networking.firewall.enable;
   rpfMode = config.networking.firewall.checkReversePath;
+  isNetworkd = config.networking.useNetworkd;
   rpfIsStrict = rpfMode == true || rpfMode == "strict";
 in {
   meta.maintainers = with maintainers; [ danderson mbaillie twitchyliquid64 ];
@@ -46,7 +47,7 @@ in {
     systemd.services.tailscaled = {
       wantedBy = [ "multi-user.target" ];
       path = [
-        pkgs.openresolv # for configuring DNS in some configs
+        config.networking.resolvconf.package # for configuring DNS in some configs
         pkgs.procps     # for collecting running services (opt-in feature)
         pkgs.glibc      # for `getent` to look up user shells
       ];
@@ -69,5 +70,17 @@ in {
       # linux distros.
       stopIfChanged = false;
     };
+
+    networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ];
+
+    systemd.network.networks."50-tailscale" = mkIf isNetworkd {
+      matchConfig = {
+        Name = cfg.interfaceName;
+      };
+      linkConfig = {
+        Unmanaged = true;
+        ActivationPolicy = "manual";
+      };
+    };
   };
 }
diff --git a/nixos/modules/services/networking/trickster.nix b/nixos/modules/services/networking/trickster.nix
index e48bba8fa587f..ac260a14d9a2d 100644
--- a/nixos/modules/services/networking/trickster.nix
+++ b/nixos/modules/services/networking/trickster.nix
@@ -6,6 +6,9 @@ let
   cfg = config.services.trickster;
 in
 {
+  imports = [
+    (mkRenamedOptionModule [ "services" "trickster" "origin" ] [ "services" "trickster" "origin-url" ])
+  ];
 
   options = {
     services.trickster = {
@@ -58,11 +61,19 @@ in
         '';
       };
 
-      origin = mkOption {
+      origin-type = mkOption {
+        type = types.enum [ "prometheus" "influxdb" ];
+        default = "prometheus";
+        description = ''
+          Type of origin (prometheus, influxdb)
+        '';
+      };
+
+      origin-url = mkOption {
         type = types.str;
         default = "http://prometheus:9090";
         description = ''
-          URL to the Prometheus Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090).
+          URL to the Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090).
         '';
       };
 
@@ -87,7 +98,7 @@ in
 
   config = mkIf cfg.enable {
     systemd.services.trickster = {
-      description = "Dashboard Accelerator for Prometheus";
+      description = "Reverse proxy cache and time series dashboard accelerator";
       after = [ "network.target" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
@@ -96,7 +107,8 @@ in
           ${cfg.package}/bin/trickster \
           -log-level ${cfg.log-level} \
           -metrics-port ${toString cfg.metrics-port} \
-          -origin ${cfg.origin} \
+          -origin-type ${cfg.origin-type} \
+          -origin-url ${cfg.origin-url} \
           -proxy-port ${toString cfg.proxy-port} \
           ${optionalString (cfg.configFile != null) "-config ${cfg.configFile}"} \
           ${optionalString (cfg.profiler-port != null) "-profiler-port ${cfg.profiler-port}"} \
diff --git a/nixos/modules/services/networking/wg-quick.nix b/nixos/modules/services/networking/wg-quick.nix
index 236d3f452e7e6..d44fad4202bb6 100644
--- a/nixos/modules/services/networking/wg-quick.nix
+++ b/nixos/modules/services/networking/wg-quick.nix
@@ -273,7 +273,7 @@ let
         after = [ "network.target" "network-online.target" ];
         wantedBy = optional values.autostart "multi-user.target";
         environment.DEVICE = name;
-        path = [ pkgs.kmod pkgs.wireguard-tools ];
+        path = [ pkgs.kmod pkgs.wireguard-tools config.networking.resolvconf.package ];
 
         serviceConfig = {
           Type = "oneshot";
@@ -332,5 +332,11 @@ in {
     # breaks the wg-quick routing because wireguard packets leave with a fwmark from wireguard.
     networking.firewall.checkReversePath = false;
     systemd.services = mapAttrs' generateUnit cfg.interfaces;
+
+    # Prevent networkd from clearing the rules set by wg-quick when restarted (e.g. when waking up from suspend).
+    systemd.network.config.networkConfig.ManageForeignRoutingPolicyRules = mkDefault false;
+
+    # WireGuard interfaces should be ignored in determining whether the network is online.
+    systemd.network.wait-online.ignoredInterfaces = builtins.attrNames cfg.interfaces;
   };
 }
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index c2e1d37e28bf4..5a7975ae17828 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -114,7 +114,7 @@ let
 
       script =
       ''
-        ${optionalString configIsGenerated ''
+        ${optionalString (configIsGenerated && !cfg.allowAuxiliaryImperativeNetworks) ''
           if [ -f /etc/wpa_supplicant.conf ]; then
             echo >&2 "<3>/etc/wpa_supplicant.conf present but ignored. Generated ${configFile} is used instead."
           fi
diff --git a/nixos/modules/services/networking/yggdrasil.nix b/nixos/modules/services/networking/yggdrasil.nix
index 99c18ae6919ea..ae681988ea119 100644
--- a/nixos/modules/services/networking/yggdrasil.nix
+++ b/nixos/modules/services/networking/yggdrasil.nix
@@ -61,10 +61,10 @@ in {
       };
 
       group = mkOption {
-        type = types.str;
-        default = "root";
+        type = types.nullOr types.str;
+        default = null;
         example = "wheel";
-        description = "Group to grant access to the Yggdrasil control socket.";
+        description = "Group to grant access to the Yggdrasil control socket. If <code>null</code>, only root can access the socket.";
       };
 
       openMulticastPort = mkOption {
@@ -154,27 +154,16 @@ in {
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         Restart = "always";
 
-        Group = cfg.group;
+        DynamicUser = true;
+        StateDirectory = "yggdrasil";
         RuntimeDirectory = "yggdrasil";
         RuntimeDirectoryMode = "0750";
         BindReadOnlyPaths = lib.optional configFileProvided cfg.configFile
           ++ lib.optional cfg.persistentKeys keysPath;
+        ReadWritePaths = "/run/yggdrasil";
 
-        # TODO: as of yggdrasil 0.3.8 and systemd 243, yggdrasil fails
-        # to set up the network adapter when DynamicUser is set.  See
-        # github.com/yggdrasil-network/yggdrasil-go/issues/557.  The
-        # following options are implied by DynamicUser according to
-        # the systemd.exec documentation, and can be removed if the
-        # upstream issue is fixed and DynamicUser is set to true:
-        PrivateTmp = true;
-        RemoveIPC = true;
-        NoNewPrivileges = true;
-        ProtectSystem = "strict";
-        RestrictSUIDSGID = true;
-        # End of list of options implied by DynamicUser.
-
-        AmbientCapabilities = "CAP_NET_ADMIN";
-        CapabilityBoundingSet = "CAP_NET_ADMIN";
+        AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
+        CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_BIND_SERVICE";
         MemoryDenyWriteExecute = true;
         ProtectControlGroups = true;
         ProtectHome = "tmpfs";
@@ -185,7 +174,9 @@ in {
         RestrictRealtime = true;
         SystemCallArchitectures = "native";
         SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @resources";
-      };
+      } // (if (cfg.group != null) then {
+        Group = cfg.group;
+      } else {});
     };
 
     networking.dhcpcd.denyInterfaces = cfg.denyDhcpcdInterfaces;
diff --git a/nixos/modules/services/security/pass-secret-service.nix b/nixos/modules/services/security/pass-secret-service.nix
new file mode 100644
index 0000000000000..6ae190eb95e6b
--- /dev/null
+++ b/nixos/modules/services/security/pass-secret-service.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.passSecretService;
+in
+{
+  options.services.passSecretService = {
+    enable = mkEnableOption "pass secret service";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.pass-secret-service;
+      defaultText = literalExpression "pkgs.pass-secret-service";
+      description = "Which pass-secret-service package to use.";
+      example = literalExpression "pkgs.pass-secret-service.override { python3 = pkgs.python310 }";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.packages = [ cfg.package ];
+    services.dbus.packages = [ cfg.package ];
+  };
+
+  meta.maintainers = with maintainers; [ aidalgol ];
+}
diff --git a/nixos/modules/services/security/privacyidea.nix b/nixos/modules/services/security/privacyidea.nix
index b8e2d9a8b0dfc..1f5639d475e85 100644
--- a/nixos/modules/services/security/privacyidea.nix
+++ b/nixos/modules/services/security/privacyidea.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.privacyidea;
   opt = options.services.privacyidea;
 
-  uwsgi = pkgs.uwsgi.override { plugins = [ "python3" ]; };
+  uwsgi = pkgs.uwsgi.override { plugins = [ "python3" ]; python3 = pkgs.python39; };
   python = uwsgi.python3;
   penv = python.withPackages (const [ pkgs.privacyidea ]);
   logCfg = pkgs.writeText "privacyidea-log.cfg" ''
@@ -51,6 +51,16 @@ let
     ${cfg.extraConfig}
   '';
 
+  renderValue = x:
+    if isList x then concatMapStringsSep "," (x: ''"${x}"'') x
+    else if isString x && hasInfix "," x then ''"${x}"''
+    else x;
+
+  ldapProxyConfig = pkgs.writeText "ldap-proxy.ini"
+    (generators.toINI {}
+      (flip mapAttrs cfg.ldap-proxy.settings
+        (const (mapAttrs (const renderValue)))));
+
 in
 
 {
@@ -172,7 +182,8 @@ in
         enable = mkEnableOption "PrivacyIDEA LDAP Proxy";
 
         configFile = mkOption {
-          type = types.path;
+          type = types.nullOr types.path;
+          default = null;
           description = ''
             Path to PrivacyIDEA LDAP Proxy configuration (proxy.ini).
           '';
@@ -189,6 +200,26 @@ in
           default = "pi-ldap-proxy";
           description = "Group account under which PrivacyIDEA LDAP proxy runs.";
         };
+
+        settings = mkOption {
+          type = with types; attrsOf (attrsOf (oneOf [ str bool int (listOf str) ]));
+          default = {};
+          description = ''
+            Attribute-set containing the settings for <package>privacyidea-ldap-proxy</package>.
+            It's possible to pass secrets using env-vars as substitutes and
+            use the option <xref linkend="opt-services.privacyidea.ldap-proxy.environmentFile" />
+            to inject them via <package>envsubst</package>.
+          '';
+        };
+
+        environmentFile = mkOption {
+          default = null;
+          type = types.nullOr types.str;
+          description = ''
+            Environment file containing secrets to be substituted into
+            <xref linkend="opt-services.privacyidea.ldap-proxy.settings" />.
+          '';
+        };
       };
     };
   };
@@ -276,6 +307,18 @@ in
 
     (mkIf cfg.ldap-proxy.enable {
 
+      assertions = [
+        { assertion = let
+            xor = a: b: a && !b || !a && b;
+          in xor (cfg.ldap-proxy.settings == {}) (cfg.ldap-proxy.configFile == null);
+          message = "configFile & settings are mutually exclusive for services.privacyidea.ldap-proxy!";
+        }
+      ];
+
+      warnings = mkIf (cfg.ldap-proxy.configFile != null) [
+        "Using services.privacyidea.ldap-proxy.configFile is deprecated! Use the RFC42-style settings option instead!"
+      ];
+
       systemd.services.privacyidea-ldap-proxy = let
         ldap-proxy-env = pkgs.python3.withPackages (ps: [ ps.privacyidea-ldap-proxy ]);
       in {
@@ -284,14 +327,28 @@ in
         serviceConfig = {
           User = cfg.ldap-proxy.user;
           Group = cfg.ldap-proxy.group;
-          ExecStart = ''
+          StateDirectory = "privacyidea-ldap-proxy";
+          EnvironmentFile = mkIf (cfg.ldap-proxy.environmentFile != null)
+            [ cfg.ldap-proxy.environmentFile ];
+          ExecStartPre =
+            "${pkgs.writeShellScript "substitute-secrets-ldap-proxy" ''
+              umask 0077
+              ${pkgs.envsubst}/bin/envsubst \
+                -i ${ldapProxyConfig} \
+                -o $STATE_DIRECTORY/ldap-proxy.ini
+            ''}";
+          ExecStart = let
+            configPath = if cfg.ldap-proxy.settings != {}
+              then "%S/privacyidea-ldap-proxy/ldap-proxy.ini"
+              else cfg.ldap-proxy.configFile;
+          in ''
             ${ldap-proxy-env}/bin/twistd \
               --nodaemon \
               --pidfile= \
               -u ${cfg.ldap-proxy.user} \
               -g ${cfg.ldap-proxy.group} \
               ldap-proxy \
-              -c ${cfg.ldap-proxy.configFile}
+              -c ${configPath}
           '';
           Restart = "always";
         };
diff --git a/nixos/modules/services/security/vault.nix b/nixos/modules/services/security/vault.nix
index d48bc472cb826..4942a05fe73b7 100644
--- a/nixos/modules/services/security/vault.nix
+++ b/nixos/modules/services/security/vault.nix
@@ -7,6 +7,8 @@ let
   opt = options.services.vault;
 
   configFile = pkgs.writeText "vault.hcl" ''
+    # vault in dev mode will refuse to start if its configuration sets listener
+    ${lib.optionalString (!cfg.dev) ''
     listener "tcp" {
       address = "${cfg.address}"
       ${if (cfg.tlsCertFile == null || cfg.tlsKeyFile == null) then ''
@@ -17,6 +19,7 @@ let
         ''}
       ${cfg.listenerExtraConfig}
     }
+    ''}
     storage "${cfg.storageBackend}" {
       ${optionalString (cfg.storagePath   != null) ''path = "${cfg.storagePath}"''}
       ${optionalString (cfg.storageConfig != null) cfg.storageConfig}
@@ -30,8 +33,10 @@ let
   '';
 
   allConfigPaths = [configFile] ++ cfg.extraSettingsPaths;
-
-  configOptions = escapeShellArgs (concatMap (p: ["-config" p]) allConfigPaths);
+  configOptions = escapeShellArgs
+    (lib.optional cfg.dev "-dev" ++
+     lib.optional (cfg.dev && cfg.devRootTokenID != null) "-dev-root-token-id=${cfg.devRootTokenID}"
+      ++ (concatMap (p: ["-config" p]) allConfigPaths));
 
 in
 
@@ -47,6 +52,22 @@ in
         description = "This option specifies the vault package to use.";
       };
 
+      dev = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          In this mode, Vault runs in-memory and starts unsealed. This option is not meant production but for development and testing i.e. for nixos tests.
+        '';
+      };
+
+      devRootTokenID = mkOption {
+        type = types.str;
+        default = false;
+        description = ''
+          Initial root token. This only applies when <option>services.vault.dev</option> is true
+        '';
+      };
+
       address = mkOption {
         type = types.str;
         default = "127.0.0.1:8200";
@@ -186,6 +207,9 @@ in
         Group = "vault";
         ExecStart = "${cfg.package}/bin/vault server ${configOptions}";
         ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
+        StateDirectory = "vault";
+        # In `dev` mode vault will put its token here
+        Environment = lib.optional (cfg.dev) "HOME=/var/lib/vault";
         PrivateDevices = true;
         PrivateTmp = true;
         ProtectSystem = "full";
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 8277f493639c2..756e0ee93b219 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -62,20 +62,52 @@ in {
       default = {};
       example = literalExpression ''
         {
-          domain = "https://bw.domain.tld:8443";
-          signupsAllowed = true;
-          rocketPort = 8222;
-          rocketLog = "critical";
+          DOMAIN = "https://bitwarden.example.com";
+          SIGNUPS_ALLOWED = false;
+
+          # Vaultwarden currently recommends running behind a reverse proxy
+          # (nginx or similar) for TLS termination, see
+          # https://github.com/dani-garcia/vaultwarden/wiki/Hardening-Guide#reverse-proxying
+          # > you should avoid enabling HTTPS via vaultwarden's built-in Rocket TLS support,
+          # > especially if your instance is publicly accessible.
+          #
+          # A suitable NixOS nginx reverse proxy example config might be:
+          #
+          #     services.nginx.virtualHosts."bitwarden.example.com" = {
+          #       enableACME = true;
+          #       forceSSL = true;
+          #       locations."/" = {
+          #         proxyPass = "http://127.0.0.1:''${toString config.services.vaultwarden.config.ROCKET_PORT}";
+          #       };
+          #     };
+          ROCKET_ADDRESS = "127.0.0.1";
+          ROCKET_PORT = 8222;
+
+          ROCKET_LOG = "critical";
+
+          # This example assumes a mailserver running on localhost,
+          # thus without transport encryption.
+          # If you use an external mail server, follow:
+          #   https://github.com/dani-garcia/vaultwarden/wiki/SMTP-configuration
+          SMTP_HOST = "127.0.0.1";
+          SMTP_PORT = 25;
+          SMTP_SSL = false;
+
+          SMTP_FROM = "admin@bitwarden.example.com";
+          SMTP_FROM_NAME = "example.com Bitwarden server";
         }
       '';
       description = ''
         The configuration of vaultwarden is done through environment variables,
-        therefore the names are converted from camel case (e.g. disable2FARemember)
-        to upper case snake case (e.g. DISABLE_2FA_REMEMBER).
+        therefore it is recommended to use upper snake case (e.g. <envar>DISABLE_2FA_REMEMBER</envar>).
+
+        However, camel case (e.g. <literal>disable2FARemember</literal>) is also supported:
+        The NixOS module will convert it automatically to
+        upper case snake case (e.g. <envar>DISABLE_2FA_REMEMBER</envar>).
         In this conversion digits (0-9) are handled just like upper case characters,
-        so foo2 would be converted to FOO_2.
-        Names already in this format remain unchanged, so FOO2 remains FOO2 if passed as such,
-        even though foo2 would have been converted to FOO_2.
+        so <literal>foo2</literal> would be converted to <envar>FOO_2</envar>.
+        Names already in this format remain unchanged, so <literal>FOO2</literal> remains <literal>FOO2</literal> if passed as such,
+        even though <literal>foo2</literal> would have been converted to <envar>FOO_2</envar>.
         This allows working around any potential future conflicting naming conventions.
 
         Based on the attributes passed to this config option an environment file will be generated
@@ -83,13 +115,16 @@ in {
 
         The available configuration options can be found in
         <link xlink:href="https://github.com/dani-garcia/vaultwarden/blob/${vaultwarden.version}/.env.template">the environment template file</link>.
+
+        See <xref linkend="opt-services.vaultwarden.environmentFile" /> for how
+        to set up access to the Admin UI to invite initial users.
       '';
     };
 
     environmentFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      example = "/root/vaultwarden.env";
+      example = "/var/lib/vaultwarden.env";
       description = ''
         Additional environment file as defined in <citerefentry>
         <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
@@ -100,6 +135,23 @@ in {
 
         Note that this file needs to be available on the host on which
         <literal>vaultwarden</literal> is running.
+
+        As a concrete example, to make the Admin UI available
+        (from which new users can be invited initially),
+        the secret <envar>ADMIN_TOKEN</envar> needs to be defined as described
+        <link xlink:href="https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page">here</link>.
+        Setting <literal>environmentFile</literal> to <literal>/var/lib/vaultwarden.env</literal>
+        and ensuring permissions with e.g.
+        <literal>chown vaultwarden:vaultwarden /var/lib/vaultwarden.env</literal>
+        (the <literal>vaultwarden</literal> user will only exist after activating with
+        <literal>enable = true;</literal> before this), we can set the contents of the file to have
+        contents such as:
+
+<programlisting>
+# Admin secret token, see
+# https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page
+ADMIN_TOKEN=...copy-paste a unique generated secret token here...
+</programlisting>
       '';
     };
 
diff --git a/nixos/modules/services/system/cachix-agent/default.nix b/nixos/modules/services/system/cachix-agent/default.nix
index f11d80d244d23..ed37c5784ce13 100644
--- a/nixos/modules/services/system/cachix-agent/default.nix
+++ b/nixos/modules/services/system/cachix-agent/default.nix
@@ -17,6 +17,12 @@ in {
       defaultText = "config.networking.hostName";
     };
 
+    verbose = mkOption {
+      type = types.bool;
+      description = "Enable verbose output";
+      default = false;
+    };
+
     profile = mkOption {
       type = types.nullOr types.str;
       default = null;
@@ -45,13 +51,19 @@ in {
       after = ["network-online.target"];
       path = [ config.nix.package ];
       wantedBy = [ "multi-user.target" ];
-      # don't restart while changing
-      reloadIfChanged = true;
+
+      # Cachix requires $USER to be set
       environment.USER = "root";
+
+      # don't stop the service if the unit disappears
+      unitConfig.X-StopOnRemoval = false;
+
       serviceConfig = {
+        # we don't want to kill children processes as those are deployments
+        KillMode = "process";
         Restart = "on-failure";
         EnvironmentFile = cfg.credentialsFile;
-        ExecStart = "${cfg.package}/bin/cachix deploy agent ${cfg.name} ${if cfg.profile != null then profile else ""}";
+        ExecStart = "${cfg.package}/bin/cachix ${lib.optionalString cfg.verbose "--verbose"} deploy agent ${cfg.name} ${if cfg.profile != null then profile else ""}";
       };
     };
   };
diff --git a/nixos/modules/services/tracing/tempo.nix b/nixos/modules/services/tracing/tempo.nix
new file mode 100644
index 0000000000000..15491a51c8e9e
--- /dev/null
+++ b/nixos/modules/services/tracing/tempo.nix
@@ -0,0 +1,68 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkEnableOption mkIf mkOption types;
+
+  cfg = config.services.tempo;
+
+  settingsFormat = pkgs.formats.yaml {};
+in {
+  options.services.tempo = {
+    enable = mkEnableOption "Grafana Tempo";
+
+    settings = mkOption {
+      type = settingsFormat.type;
+      default = {};
+      description = ''
+        Specify the configuration for Tempo in Nix.
+
+        See https://grafana.com/docs/tempo/latest/configuration/ for available options.
+      '';
+    };
+
+    configFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Specify a path to a configuration file that Tempo should use.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    # for tempo-cli and friends
+    environment.systemPackages = [ pkgs.tempo ];
+
+    assertions = [{
+      assertion = (
+        (cfg.settings == {}) != (cfg.configFile == null)
+      );
+      message  = ''
+        Please specify a configuration for Tempo with either
+        'services.tempo.settings' or
+        'services.tempo.configFile'.
+      '';
+    }];
+
+    systemd.services.tempo = {
+      description = "Grafana Tempo Service Daemon";
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = let
+        conf = if cfg.configFile == null
+               then settingsFormat.generate "config.yaml" cfg.settings
+               else cfg.configFile;
+      in
+      {
+        ExecStart = "${pkgs.tempo}/bin/tempo --config.file=${conf}";
+        DynamicUser = true;
+        Restart = "always";
+        ProtectSystem = "full";
+        DevicePolicy = "closed";
+        NoNewPrivileges = true;
+        WorkingDirectory = "/var/lib/tempo";
+        StateDirectory = "tempo";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/atlassian/confluence.nix b/nixos/modules/services/web-apps/atlassian/confluence.nix
index 28491fb3a4eed..4aad307731ab3 100644
--- a/nixos/modules/services/web-apps/atlassian/confluence.nix
+++ b/nixos/modules/services/web-apps/atlassian/confluence.nix
@@ -8,21 +8,22 @@ let
 
   pkg = cfg.package.override (optionalAttrs cfg.sso.enable {
     enableSSO = cfg.sso.enable;
-    crowdProperties = ''
-      application.name                        ${cfg.sso.applicationName}
-      application.password                    ${cfg.sso.applicationPassword}
-      application.login.url                   ${cfg.sso.crowd}/console/
-
-      crowd.server.url                        ${cfg.sso.crowd}/services/
-      crowd.base.url                          ${cfg.sso.crowd}/
-
-      session.isauthenticated                 session.isauthenticated
-      session.tokenkey                        session.tokenkey
-      session.validationinterval              ${toString cfg.sso.validationInterval}
-      session.lastvalidation                  session.lastvalidation
-    '';
   });
 
+  crowdProperties = pkgs.writeText "crowd.properties" ''
+    application.name                        ${cfg.sso.applicationName}
+    application.password                    ${if cfg.sso.applicationPassword != null then cfg.sso.applicationPassword else "@NIXOS_CONFLUENCE_CROWD_SSO_PWD@"}
+    application.login.url                   ${cfg.sso.crowd}/console/
+
+    crowd.server.url                        ${cfg.sso.crowd}/services/
+    crowd.base.url                          ${cfg.sso.crowd}/
+
+    session.isauthenticated                 session.isauthenticated
+    session.tokenkey                        session.tokenkey
+    session.validationinterval              ${toString cfg.sso.validationInterval}
+    session.lastvalidation                  session.lastvalidation
+  '';
+
 in
 
 {
@@ -107,10 +108,17 @@ in
         };
 
         applicationPassword = mkOption {
-          type = types.str;
+          type = types.nullOr types.str;
+          default = null;
           description = "Application password of this Confluence instance in Crowd";
         };
 
+        applicationPasswordFile = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = "Path to the application password for Crowd of Confluence.";
+        };
+
         validationInterval = mkOption {
           type = types.int;
           default = 2;
@@ -147,6 +155,16 @@ in
       group = cfg.group;
     };
 
+    assertions = [
+      { assertion = cfg.sso.enable -> ((cfg.sso.applicationPassword == null) != (cfg.sso.applicationPasswordFile));
+        message = "Please set either applicationPassword or applicationPasswordFile";
+      }
+    ];
+
+    warnings = mkIf (cfg.sso.enable && cfg.sso.applicationPassword != null) [
+      "Using `services.confluence.sso.applicationPassword` is deprecated! Use `applicationPasswordFile` instead!"
+    ];
+
     users.groups.${cfg.group} = {};
 
     systemd.tmpfiles.rules = [
@@ -173,6 +191,7 @@ in
         CONF_USER = cfg.user;
         JAVA_HOME = "${cfg.jrePackage}";
         CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions;
+        JAVA_OPTS = mkIf cfg.sso.enable "-Dcrowd.properties=${cfg.home}/crowd.properties";
       };
 
       preStart = ''
@@ -183,6 +202,16 @@ in
           -e 's,protocol="org.apache.coyote.http11.Http11NioProtocol",protocol="org.apache.coyote.http11.Http11NioProtocol" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}",' \
         '') + ''
           ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml
+
+        ${optionalString cfg.sso.enable ''
+          install -m660 ${crowdProperties} ${cfg.home}/crowd.properties
+          ${optionalString (cfg.sso.applicationPasswordFile != null) ''
+            ${pkgs.replace-secret}/bin/replace-secret \
+              '@NIXOS_CONFLUENCE_CROWD_SSO_PWD@' \
+              ${cfg.sso.applicationPasswordFile} \
+              ${cfg.home}/crowd.properties
+          ''}
+        ''}
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/services/web-apps/atlassian/crowd.nix b/nixos/modules/services/web-apps/atlassian/crowd.nix
index 79306541b855b..9418aff12adb3 100644
--- a/nixos/modules/services/web-apps/atlassian/crowd.nix
+++ b/nixos/modules/services/web-apps/atlassian/crowd.nix
@@ -14,6 +14,21 @@ let
     proxyUrl = "${cfg.proxy.scheme}://${cfg.proxy.name}:${toString cfg.proxy.port}";
   });
 
+  crowdPropertiesFile = pkgs.writeText "crowd.properties" ''
+    application.name                        crowd-openid-server
+    application.password @NIXOS_CROWD_OPENID_PW@
+    application.base.url                    http://localhost:${toString cfg.listenPort}/openidserver
+    application.login.url                   http://localhost:${toString cfg.listenPort}/openidserver
+    application.login.url.template          http://localhost:${toString cfg.listenPort}/openidserver?returnToUrl=''${RETURN_TO_URL}
+
+    crowd.server.url                        http://localhost:${toString cfg.listenPort}/crowd/services/
+
+    session.isauthenticated                 session.isauthenticated
+    session.tokenkey                        session.tokenkey
+    session.validationinterval              0
+    session.lastvalidation                  session.lastvalidation
+  '';
+
 in
 
 {
@@ -53,9 +68,16 @@ in
 
       openidPassword = mkOption {
         type = types.str;
+        default = "WILL_NEVER_BE_SET";
         description = "Application password for OpenID server.";
       };
 
+      openidPasswordFile = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = "Path to the file containing the application password for OpenID server.";
+      };
+
       catalinaOptions = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -140,6 +162,7 @@ in
         JAVA_HOME = "${cfg.jrePackage}";
         CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions;
         CATALINA_TMPDIR = "/tmp";
+        JAVA_OPTS = mkIf (cfg.openidPasswordFile != null) "-Dcrowd.properties=${cfg.home}/crowd.properties";
       };
 
       preStart = ''
@@ -151,6 +174,14 @@ in
           -e 's,compression="on",compression="off" protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${boolToString cfg.proxy.secure}",' \
         '') + ''
           ${pkg}/apache-tomcat/conf/server.xml.dist > ${cfg.home}/server.xml
+
+        ${optionalString (cfg.openidPasswordFile != null) ''
+          install -m660 ${crowdPropertiesFile} ${cfg.home}/crowd.properties
+          ${pkgs.replace-secret}/bin/replace-secret \
+            '@NIXOS_CROWD_OPENID_PW@' \
+            ${cfg.openidPasswordFile} \
+            ${cfg.home}/crowd.properties
+        ''}
       '';
 
       serviceConfig = {
diff --git a/nixos/modules/services/web-apps/atlassian/jira.nix b/nixos/modules/services/web-apps/atlassian/jira.nix
index bc0bf43522e0e..fd89d763c7fb4 100644
--- a/nixos/modules/services/web-apps/atlassian/jira.nix
+++ b/nixos/modules/services/web-apps/atlassian/jira.nix
@@ -8,21 +8,22 @@ let
 
   pkg = cfg.package.override (optionalAttrs cfg.sso.enable {
     enableSSO = cfg.sso.enable;
-    crowdProperties = ''
-      application.name                        ${cfg.sso.applicationName}
-      application.password                    ${cfg.sso.applicationPassword}
-      application.login.url                   ${cfg.sso.crowd}/console/
-
-      crowd.server.url                        ${cfg.sso.crowd}/services/
-      crowd.base.url                          ${cfg.sso.crowd}/
-
-      session.isauthenticated                 session.isauthenticated
-      session.tokenkey                        session.tokenkey
-      session.validationinterval              ${toString cfg.sso.validationInterval}
-      session.lastvalidation                  session.lastvalidation
-    '';
   });
 
+  crowdProperties = pkgs.writeText "crowd.properties" ''
+    application.name                        ${cfg.sso.applicationName}
+    application.password                    @NIXOS_JIRA_CROWD_SSO_PWD@
+    application.login.url                   ${cfg.sso.crowd}/console/
+
+    crowd.server.url                        ${cfg.sso.crowd}/services/
+    crowd.base.url                          ${cfg.sso.crowd}/
+
+    session.isauthenticated                 session.isauthenticated
+    session.tokenkey                        session.tokenkey
+    session.validationinterval              ${toString cfg.sso.validationInterval}
+    session.lastvalidation                  session.lastvalidation
+  '';
+
 in
 
 {
@@ -112,9 +113,9 @@ in
           description = "Exact name of this JIRA instance in Crowd";
         };
 
-        applicationPassword = mkOption {
+        applicationPasswordFile = mkOption {
           type = types.str;
-          description = "Application password of this JIRA instance in Crowd";
+          description = "Path to the file containing the application password of this JIRA instance in Crowd";
         };
 
         validationInterval = mkOption {
@@ -181,6 +182,7 @@ in
         JIRA_HOME = cfg.home;
         JAVA_HOME = "${cfg.jrePackage}";
         CATALINA_OPTS = concatStringsSep " " cfg.catalinaOptions;
+        JAVA_OPTS = mkIf cfg.sso.enable "-Dcrowd.properties=${cfg.home}/crowd.properties";
       };
 
       preStart = ''
@@ -191,6 +193,14 @@ in
           -e 's,protocol="HTTP/1.1",protocol="HTTP/1.1" proxyName="${cfg.proxy.name}" proxyPort="${toString cfg.proxy.port}" scheme="${cfg.proxy.scheme}" secure="${toString cfg.proxy.secure}",' \
         '') + ''
           ${pkg}/conf/server.xml.dist > ${cfg.home}/server.xml
+
+        ${optionalString cfg.sso.enable ''
+          install -m660 ${crowdProperties} ${cfg.home}/crowd.properties
+          ${pkgs.replace-secret}/bin/replace-secret \
+            '@NIXOS_JIRA_CROWD_SSO_PWD@' \
+            ${cfg.sso.applicationPasswordFile} \
+            ${cfg.home}/crowd.properties
+        ''}
       '';
 
       serviceConfig = {
@@ -204,4 +214,10 @@ in
       };
     };
   };
+
+  imports = [
+    (mkRemovedOptionModule [ "services" "jira" "sso" "applicationPassword" ] ''
+      Use `applicationPasswordFile` instead!
+    '')
+  ];
 }
diff --git a/nixos/modules/services/web-apps/calibre-web.nix b/nixos/modules/services/web-apps/calibre-web.nix
index 704cd2cfa8a7c..2bc817343a9b7 100644
--- a/nixos/modules/services/web-apps/calibre-web.nix
+++ b/nixos/modules/services/web-apps/calibre-web.nix
@@ -136,7 +136,7 @@ in
 
               ${pkgs.sqlite}/bin/sqlite3 ${appDb} "update settings set ${settings}"
             '' + optionalString (cfg.options.calibreLibrary != null) ''
-              test -f ${cfg.options.calibreLibrary}/metadata.db || { echo "Invalid Calibre library"; exit 1; }
+              test -f "${cfg.options.calibreLibrary}/metadata.db" || { echo "Invalid Calibre library"; exit 1; }
             ''
           );
 
diff --git a/nixos/modules/services/web-apps/cryptpad.nix b/nixos/modules/services/web-apps/cryptpad.nix
deleted file mode 100644
index e6772de768e0e..0000000000000
--- a/nixos/modules/services/web-apps/cryptpad.nix
+++ /dev/null
@@ -1,54 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.cryptpad;
-in
-{
-  options.services.cryptpad = {
-    enable = mkEnableOption "the Cryptpad service";
-
-    package = mkOption {
-      default = pkgs.cryptpad;
-      defaultText = literalExpression "pkgs.cryptpad";
-      type = types.package;
-      description = "
-        Cryptpad package to use.
-      ";
-    };
-
-    configFile = mkOption {
-      type = types.path;
-      default = "${cfg.package}/lib/node_modules/cryptpad/config/config.example.js";
-      defaultText = literalExpression ''"''${package}/lib/node_modules/cryptpad/config/config.example.js"'';
-      description = ''
-        Path to the JavaScript configuration file.
-
-        See <link
-        xlink:href="https://github.com/xwiki-labs/cryptpad/blob/master/config/config.example.js"/>
-        for a configuration example.
-      '';
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services.cryptpad = {
-      description = "Cryptpad Service";
-      wantedBy = [ "multi-user.target" ];
-      after = [ "networking.target" ];
-      serviceConfig = {
-        DynamicUser = true;
-        Environment = [
-          "CRYPTPAD_CONFIG=${cfg.configFile}"
-          "HOME=%S/cryptpad"
-        ];
-        ExecStart = "${cfg.package}/bin/cryptpad";
-        PrivateTmp = true;
-        Restart = "always";
-        StateDirectory = "cryptpad";
-        WorkingDirectory = "%S/cryptpad";
-      };
-    };
-  };
-}
diff --git a/nixos/modules/services/web-apps/healthchecks.nix b/nixos/modules/services/web-apps/healthchecks.nix
new file mode 100644
index 0000000000000..be025e7dd5518
--- /dev/null
+++ b/nixos/modules/services/web-apps/healthchecks.nix
@@ -0,0 +1,249 @@
+{ config, lib, pkgs, buildEnv, ... }:
+
+with lib;
+
+let
+  defaultUser = "healthchecks";
+  cfg = config.services.healthchecks;
+  pkg = cfg.package;
+  boolToPython = b: if b then "True" else "False";
+  environment = {
+    PYTHONPATH = pkg.pythonPath;
+    STATIC_ROOT = cfg.dataDir + "/static";
+    DB_NAME = "${cfg.dataDir}/healthchecks.sqlite";
+  } // cfg.settings;
+
+  environmentFile = pkgs.writeText "healthchecks-environment" (lib.generators.toKeyValue { } environment);
+
+  healthchecksManageScript = with pkgs; (writeShellScriptBin "healthchecks-manage" ''
+    if [[ "$USER" != "${cfg.user}" ]]; then
+        echo "please run as user 'healtchecks'." >/dev/stderr
+        exit 1
+    fi
+    export $(cat ${environmentFile} | xargs);
+    exec ${pkg}/opt/healthchecks/manage.py "$@"
+  '');
+in
+{
+  options.services.healthchecks = {
+    enable = mkEnableOption "healthchecks" // {
+      description = ''
+        Enable healthchecks.
+        It is expected to be run behind a HTTP reverse proxy.
+      '';
+    };
+
+    package = mkOption {
+      default = pkgs.healthchecks;
+      defaultText = literalExpression "pkgs.healthchecks";
+      type = types.package;
+      description = "healthchecks package to use.";
+    };
+
+    user = mkOption {
+      default = defaultUser;
+      type = types.str;
+      description = ''
+        User account under which healthchecks runs.
+
+        <note><para>
+        If left as the default value this user will automatically be created
+        on system activation, otherwise you are responsible for
+        ensuring the user exists before the healthchecks service starts.
+        </para></note>
+      '';
+    };
+
+    group = mkOption {
+      default = defaultUser;
+      type = types.str;
+      description = ''
+        Group account under which healthchecks runs.
+
+        <note><para>
+        If left as the default value this group will automatically be created
+        on system activation, otherwise you are responsible for
+        ensuring the group exists before the healthchecks service starts.
+        </para></note>
+      '';
+    };
+
+    listenAddress = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Address the server will listen on.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8000;
+      description = "Port the server will listen on.";
+    };
+
+    dataDir = mkOption {
+      type = types.str;
+      default = "/var/lib/healthchecks";
+      description = ''
+        The directory used to store all data for healthchecks.
+
+        <note><para>
+        If left as the default value this directory will automatically be created before
+        the healthchecks server starts, otherwise you are responsible for ensuring the
+        directory exists with appropriate ownership and permissions.
+        </para></note>
+      '';
+    };
+
+    settings = lib.mkOption {
+      description = ''
+        Environment variables which are read by healthchecks <literal>(local)_settings.py</literal>.
+
+        Settings which are explictly covered in options bewlow, are type-checked and/or transformed
+        before added to the environment, everything else is passed as a string.
+
+        See <link xlink:href="">https://healthchecks.io/docs/self_hosted_configuration/</link>
+        for a full documentation of settings.
+
+        We add two variables to this list inside the packages <literal>local_settings.py.</literal>
+        - STATIC_ROOT to set a state directory for dynamically generated static files.
+        - SECRET_KEY_FILE to read SECRET_KEY from a file at runtime and keep it out of /nix/store.
+      '';
+      type = types.submodule {
+        freeformType = types.attrsOf types.str;
+        options = {
+          ALLOWED_HOSTS = lib.mkOption {
+            type = types.listOf types.str;
+            default = [ "*" ];
+            description = "The host/domain names that this site can serve.";
+            apply = lib.concatStringsSep ",";
+          };
+
+          SECRET_KEY_FILE = mkOption {
+            type = types.path;
+            description = "Path to a file containing the secret key.";
+          };
+
+          DEBUG = mkOption {
+            type = types.bool;
+            default = false;
+            description = "Enable debug mode.";
+            apply = boolToPython;
+          };
+
+          REGISTRATION_OPEN = mkOption {
+            type = types.bool;
+            default = false;
+            description = ''
+              A boolean that controls whether site visitors can create new accounts.
+              Set it to false if you are setting up a private Healthchecks instance,
+              but it needs to be publicly accessible (so, for example, your cloud
+              services can send pings to it).
+              If you close new user registration, you can still selectively invite
+              users to your team account.
+            '';
+            apply = boolToPython;
+          };
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ healthchecksManageScript ];
+
+    systemd.targets.healthchecks = {
+      description = "Target for all Healthchecks services";
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "network-online.target" ];
+    };
+
+    systemd.services =
+      let
+        commonConfig = {
+          WorkingDirectory = cfg.dataDir;
+          User = cfg.user;
+          Group = cfg.group;
+          EnvironmentFile = environmentFile;
+          StateDirectory = mkIf (cfg.dataDir == "/var/lib/healthchecks") "healthchecks";
+          StateDirectoryMode = mkIf (cfg.dataDir == "/var/lib/healthchecks") "0750";
+        };
+      in
+        {
+        healthchecks-migration = {
+          description = "Healthchecks migrations";
+          wantedBy = [ "healthchecks.target" ];
+
+          serviceConfig = commonConfig // {
+            Restart = "on-failure";
+            Type = "oneshot";
+            ExecStart = ''
+              ${pkg}/opt/healthchecks/manage.py migrate
+            '';
+          };
+        };
+
+        healthchecks = {
+          description = "Healthchecks WSGI Service";
+          wantedBy = [ "healthchecks.target" ];
+          after = [ "healthchecks-migration.service" ];
+
+          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
+          '';
+
+          serviceConfig = commonConfig // {
+            Restart = "always";
+            ExecStart = ''
+              ${pkgs.python3Packages.gunicorn}/bin/gunicorn hc.wsgi \
+                --bind ${cfg.listenAddress}:${toString cfg.port} \
+                --pythonpath ${pkg}/opt/healthchecks
+            '';
+          };
+        };
+
+        healthchecks-sendalerts = {
+          description = "Healthchecks Alert Service";
+          wantedBy = [ "healthchecks.target" ];
+          after = [ "healthchecks.service" ];
+
+          serviceConfig = commonConfig // {
+            Restart = "always";
+            ExecStart = ''
+              ${pkg}/opt/healthchecks/manage.py sendalerts
+            '';
+          };
+        };
+
+        healthchecks-sendreports = {
+          description = "Healthchecks Reporting Service";
+          wantedBy = [ "healthchecks.target" ];
+          after = [ "healthchecks.service" ];
+
+          serviceConfig = commonConfig // {
+            Restart = "always";
+            ExecStart = ''
+              ${pkg}/opt/healthchecks/manage.py sendreports --loop
+            '';
+          };
+        };
+      };
+
+    users.users = optionalAttrs (cfg.user == defaultUser) {
+      ${defaultUser} =
+        {
+          description = "healthchecks service owner";
+          isSystemUser = true;
+          group = defaultUser;
+        };
+    };
+
+    users.groups = optionalAttrs (cfg.user == defaultUser) {
+      ${defaultUser} =
+        {
+          members = [ defaultUser ];
+        };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/hedgedoc.nix b/nixos/modules/services/web-apps/hedgedoc.nix
index 22270609dbccb..f0e4f753eb70d 100644
--- a/nixos/modules/services/web-apps/hedgedoc.nix
+++ b/nixos/modules/services/web-apps/hedgedoc.nix
@@ -13,17 +13,22 @@ let
     then "hedgedoc"
     else "codimd";
 
+  settingsFormat = pkgs.formats.json {};
+
   prettyJSON = conf:
     pkgs.runCommandLocal "hedgedoc-config.json" {
       nativeBuildInputs = [ pkgs.jq ];
     } ''
-      echo '${builtins.toJSON conf}' | jq \
-        '{production:del(.[]|nulls)|del(.[][]?|nulls)}' > $out
+      jq '{production:del(.[]|nulls)|del(.[][]?|nulls)}' \
+        < ${settingsFormat.generate "hedgedoc-ugly.json" cfg.settings} \
+        > $out
     '';
 in
 {
   imports = [
     (mkRenamedOptionModule [ "services" "codimd" ] [ "services" "hedgedoc" ])
+    (mkRenamedOptionModule
+      [ "services" "hedgedoc" "configuration" ] [ "services" "hedgedoc" "settings" ])
   ];
 
   options.services.hedgedoc = {
@@ -45,7 +50,7 @@ in
       '';
     };
 
-    configuration = {
+    settings = let options = {
       debug = mkEnableOption "debug mode";
       domain = mkOption {
         type = types.nullOr types.str;
@@ -838,7 +843,8 @@ in
               '';
             };
             searchAttributes = mkOption {
-              type = types.listOf types.str;
+              type = types.nullOr (types.listOf types.str);
+              default = null;
               example = [ "displayName" "mail" ];
               description = ''
                 LDAP attributes to search with.
@@ -861,6 +867,7 @@ in
             };
             tlsca = mkOption {
               type = types.str;
+              default = "/etc/ssl/certs/ca-certificates.crt";
               example = "server-cert.pem,root.pem";
               description = ''
                 Root CA for LDAP TLS in PEM format.
@@ -960,6 +967,16 @@ in
         default = null;
         description = "Configure the SAML integration.";
       };
+    }; in lib.mkOption {
+      type = lib.types.submodule {
+        freeformType = settingsFormat.type;
+        inherit options;
+      };
+      description = ''
+        HedgeDoc configuration, see
+        <link xlink:href="https://docs.hedgedoc.org/configuration/"/>
+        for documentation.
+      '';
     };
 
     environmentFile = mkOption {
@@ -1000,12 +1017,13 @@ in
         Package that provides HedgeDoc.
       '';
     };
+
   };
 
   config = mkIf cfg.enable {
     assertions = [
-      { assertion = cfg.configuration.db == {} -> (
-          cfg.configuration.dbURL != "" && cfg.configuration.dbURL != null
+      { assertion = cfg.settings.db == {} -> (
+          cfg.settings.dbURL != "" && cfg.settings.dbURL != null
         );
         message = "Database configuration for HedgeDoc missing."; }
     ];
@@ -1026,12 +1044,12 @@ in
       preStart = ''
         ${pkgs.envsubst}/bin/envsubst \
           -o ${cfg.workDir}/config.json \
-          -i ${prettyJSON cfg.configuration}
-        mkdir -p ${cfg.configuration.uploadsPath}
+          -i ${prettyJSON cfg.settings}
+        mkdir -p ${cfg.settings.uploadsPath}
       '';
       serviceConfig = {
         WorkingDirectory = cfg.workDir;
-        StateDirectory = [ cfg.workDir cfg.configuration.uploadsPath ];
+        StateDirectory = [ cfg.workDir cfg.settings.uploadsPath ];
         ExecStart = "${cfg.package}/bin/hedgedoc";
         EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
         Environment = [
diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix
index be0b5b94fb26c..8ad92706b0647 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -253,9 +253,20 @@ in
         '';
       };
     };
-    systemd.services.prosody.serviceConfig = mkIf cfg.prosody.enable {
-      EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ];
-      SupplementaryGroups = [ "jitsi-meet" ];
+    systemd.services.prosody = mkIf cfg.prosody.enable {
+      preStart = let
+        videobridgeSecret = if cfg.videobridge.passwordFile != null then cfg.videobridge.passwordFile else "/var/lib/jitsi-meet/videobridge-secret";
+      in ''
+        ${config.services.prosody.package}/bin/prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)"
+        ${config.services.prosody.package}/bin/prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})"
+        ${config.services.prosody.package}/bin/prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName}
+        ${config.services.prosody.package}/bin/prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)"
+        ${config.services.prosody.package}/bin/prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"
+      '';
+      serviceConfig = {
+        EnvironmentFile = [ "/var/lib/jitsi-meet/secrets-env" ];
+        SupplementaryGroups = [ "jitsi-meet" ];
+      };
     };
 
     users.groups.jitsi-meet = {};
@@ -266,14 +277,12 @@ in
     systemd.services.jitsi-meet-init-secrets = {
       wantedBy = [ "multi-user.target" ];
       before = [ "jicofo.service" "jitsi-videobridge2.service" ] ++ (optional cfg.prosody.enable "prosody.service");
-      path = [ config.services.prosody.package ];
       serviceConfig = {
         Type = "oneshot";
       };
 
       script = let
         secrets = [ "jicofo-component-secret" "jicofo-user-secret" "jibri-auth-secret" "jibri-recorder-secret" ] ++ (optional (cfg.videobridge.passwordFile == null) "videobridge-secret");
-        videobridgeSecret = if cfg.videobridge.passwordFile != null then cfg.videobridge.passwordFile else "/var/lib/jitsi-meet/videobridge-secret";
       in
       ''
         cd /var/lib/jitsi-meet
@@ -291,12 +300,6 @@ in
         chmod 640 secrets-env
       ''
       + optionalString cfg.prosody.enable ''
-        prosodyctl register focus auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jicofo-user-secret)"
-        prosodyctl register jvb auth.${cfg.hostName} "$(cat ${videobridgeSecret})"
-        prosodyctl mod_roster_command subscribe focus.${cfg.hostName} focus@auth.${cfg.hostName}
-        prosodyctl register jibri auth.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-auth-secret)"
-        prosodyctl register recorder recorder.${cfg.hostName} "$(cat /var/lib/jitsi-meet/jibri-recorder-secret)"
-
         # generate self-signed certificates
         if [ ! -f /var/lib/jitsi-meet.crt ]; then
           ${getBin pkgs.openssl}/bin/openssl req \
diff --git a/nixos/modules/services/web-apps/lemmy.nix b/nixos/modules/services/web-apps/lemmy.nix
index 7cd2357c45566..15e616c5d5ec1 100644
--- a/nixos/modules/services/web-apps/lemmy.nix
+++ b/nixos/modules/services/web-apps/lemmy.nix
@@ -164,7 +164,7 @@ in
 
         wantedBy = [ "multi-user.target" ];
 
-        after = [ "pict-rs.service " ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ];
+        after = [ "pict-rs.service" ] ++ lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ];
 
         requires = lib.optionals cfg.settings.database.createLocally [ "lemmy-postgresql.service" ];
 
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index e0aaafd463331..f8082d7544c12 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -6,6 +6,8 @@ let
   cfg = config.services.nextcloud;
   fpm = config.services.phpfpm.pools.nextcloud;
 
+  jsonFormat = pkgs.formats.json {};
+
   inherit (cfg) datadir;
 
   phpPackage = cfg.phpPackage.buildEnv {
@@ -89,7 +91,8 @@ in {
     };
     datadir = mkOption {
       type = types.str;
-      defaultText = "config.services.nextcloud.home";
+      default = config.services.nextcloud.home;
+      defaultText = literalExpression "config.services.nextcloud.home";
       description = ''
         Data storage path of nextcloud.  Will be <xref linkend="opt-services.nextcloud.home" /> by default.
         This folder will be populated with a config.php and data folder which contains the state of the instance (excl the database).";
@@ -546,6 +549,33 @@ in {
       '';
     };
 
+    extraOptions = mkOption {
+      type = jsonFormat.type;
+      default = {};
+      description = ''
+        Extra options which should be appended to nextcloud's config.php file.
+      '';
+      example = literalExpression '' {
+        redis = {
+          host = "/run/redis/redis.sock";
+          port = 0;
+          dbindex = 0;
+          password = "secret";
+          timeout = 1.5;
+        };
+      } '';
+    };
+
+    secretFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Secret options which will be appended to nextcloud's config.php file (written as JSON, in the same
+        form as the <xref linkend="opt-services.nextcloud.extraOptions"/> option), for example
+        <programlisting>{"redis":{"password":"secret"}}</programlisting>.
+      '';
+    };
+
     nginx = {
       recommendedHttpHeaders = mkOption {
         type = types.bool;
@@ -629,8 +659,6 @@ in {
           else nextcloud24
         );
 
-      services.nextcloud.datadir = mkOptionDefault config.services.nextcloud.home;
-
       services.nextcloud.phpPackage =
         if versionOlder cfg.package.version "24" then pkgs.php80
         # FIXME: Use PHP 8.1 with Nextcloud 24 and higher, once issues like this one are fixed:
@@ -650,6 +678,7 @@ in {
 
     { systemd.timers.nextcloud-cron = {
         wantedBy = [ "timers.target" ];
+        after = [ "nextcloud-setup.service" ];
         timerConfig.OnBootSec = "5m";
         timerConfig.OnUnitActiveSec = "5m";
         timerConfig.Unit = "nextcloud-cron.service";
@@ -706,10 +735,20 @@ in {
                     $file
                   ));
                 }
-
                 return trim(file_get_contents($file));
+              }''}
+            function nix_decode_json_file($file, $error) {
+              if (!file_exists($file)) {
+                throw new \RuntimeException(sprintf($error, $file));
               }
-            ''}
+              $decoded = json_decode(file_get_contents($file), true);
+
+              if (json_last_error() !== JSON_ERROR_NONE) {
+                throw new \RuntimeException(sprintf("Cannot decode %s, because: %s", $file, json_last_error_msg()));
+              }
+
+              return $decoded;
+            }
             $CONFIG = [
               'apps_paths' => [
                 ${optionalString (cfg.extraApps != { }) "[ 'path' => '${cfg.home}/nix-apps', 'url' => '/nix-apps', 'writable' => false ],"}
@@ -728,7 +767,12 @@ in {
               ${optionalString (c.dbport != null) "'dbport' => '${toString c.dbport}',"}
               ${optionalString (c.dbuser != null) "'dbuser' => '${c.dbuser}',"}
               ${optionalString (c.dbtableprefix != null) "'dbtableprefix' => '${toString c.dbtableprefix}',"}
-              ${optionalString (c.dbpassFile != null) "'dbpassword' => nix_read_secret('${c.dbpassFile}'),"}
+              ${optionalString (c.dbpassFile != null) ''
+                  'dbpassword' => nix_read_secret(
+                    "${c.dbpassFile}"
+                  ),
+                ''
+              }
               'dbtype' => '${c.dbtype}',
               'trusted_domains' => ${writePhpArrary ([ cfg.hostName ] ++ c.extraTrustedDomains)},
               'trusted_proxies' => ${writePhpArrary (c.trustedProxies)},
@@ -736,6 +780,18 @@ in {
               ${optionalString (nextcloudGreaterOrEqualThan "23") "'profile.enabled' => ${boolToString cfg.globalProfiles},"}
               ${objectstoreConfig}
             ];
+
+            $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file(
+              "${jsonFormat.generate "nextcloud-extraOptions.json" cfg.extraOptions}",
+              "impossible: this should never happen (decoding generated extraOptions file %s failed)"
+            ));
+
+            ${optionalString (cfg.secretFile != null) ''
+              $CONFIG = array_replace_recursive($CONFIG, nix_decode_json_file(
+                "${cfg.secretFile}",
+                "Cannot start Nextcloud, secrets file %s set by NixOS doesn't exist!"
+              ));
+            ''}
           '';
           occInstallCmd = let
             mkExport = { arg, value }: "export ${arg}=${value}";
@@ -840,12 +896,14 @@ in {
           serviceConfig.User = "nextcloud";
         };
         nextcloud-cron = {
+          after = [ "nextcloud-setup.service" ];
           environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config";
           serviceConfig.Type = "oneshot";
           serviceConfig.User = "nextcloud";
           serviceConfig.ExecStart = "${phpPackage}/bin/php -f ${cfg.package}/cron.php";
         };
         nextcloud-update-plugins = mkIf cfg.autoUpdateApps.enable {
+          after = [ "nextcloud-setup.service" ];
           serviceConfig.Type = "oneshot";
           serviceConfig.ExecStart = "${occ}/bin/nextcloud-occ app:update --all";
           serviceConfig.User = "nextcloud";
@@ -914,7 +972,6 @@ in {
             priority = 100;
             extraConfig = ''
               allow all;
-              log_not_found off;
               access_log off;
             '';
           };
diff --git a/nixos/modules/services/web-apps/onlyoffice.nix b/nixos/modules/services/web-apps/onlyoffice.nix
new file mode 100644
index 0000000000000..ce6a3e835e3b3
--- /dev/null
+++ b/nixos/modules/services/web-apps/onlyoffice.nix
@@ -0,0 +1,288 @@
+{ lib, config, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.onlyoffice;
+in
+{
+  options.services.onlyoffice = {
+    enable = mkEnableOption "OnlyOffice DocumentServer";
+
+    enableExampleServer = mkEnableOption "OnlyOffice example server";
+
+    hostname = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "FQDN for the onlyoffice instance.";
+    };
+
+    jwtSecretFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Path to a file that contains the secret to sign web requests using JSON Web Tokens.
+        If left at the default value null signing is disabled.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.onlyoffice-documentserver;
+      defaultText = "pkgs.onlyoffice-documentserver";
+      description = "Which package to use for the OnlyOffice instance.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8000;
+      description = "Port the OnlyOffice DocumentServer should listens on.";
+    };
+
+    examplePort = mkOption {
+      type = types.port;
+      default = null;
+      description = "Port the OnlyOffice Example server should listens on.";
+    };
+
+    postgresHost = mkOption {
+      type = types.str;
+      default = "/run/postgresql";
+      description = "The Postgresql hostname or socket path OnlyOffice should connect to.";
+    };
+
+    postgresName = mkOption {
+      type = types.str;
+      default = "onlyoffice";
+      description = "The name of databse OnlyOffice should user.";
+    };
+
+    postgresPasswordFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Path to a file that contains the password OnlyOffice should use to connect to Postgresql.
+        Unused when using socket authentication.
+      '';
+    };
+
+    postgresUser = mkOption {
+      type = types.str;
+      default = "onlyoffice";
+      description = ''
+        The username OnlyOffice should use to connect to Postgresql.
+        Unused when using socket authentication.
+      '';
+    };
+
+    rabbitmqUrl = mkOption {
+      type = types.str;
+      default = "amqp://guest:guest@localhost:5672";
+      description = "The Rabbitmq in amqp URI style OnlyOffice should connect to.";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services = {
+      nginx = {
+        enable = mkDefault true;
+        # misses text/csv, font/ttf, application/x-font-ttf, application/rtf, application/wasm
+        recommendedGzipSettings = mkDefault true;
+        recommendedProxySettings = mkDefault true;
+
+        upstreams = {
+          # /etc/nginx/includes/http-common.conf
+          onlyoffice-docservice = {
+            servers = { "localhost:${toString cfg.port}" = { }; };
+          };
+          onlyoffice-example = lib.mkIf cfg.enableExampleServer {
+            servers = { "localhost:${toString cfg.examplePort}" = { }; };
+          };
+        };
+
+        virtualHosts.${cfg.hostname} = {
+          locations = {
+            # /etc/nginx/includes/ds-docservice.conf
+            "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps\/apps\/api\/documents\/api\.js)$".extraConfig = ''
+              expires -1;
+              alias ${cfg.package}/var/www/onlyoffice/documentserver/$2;
+            '';
+            "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps)(\/.*\.json)$".extraConfig = ''
+              expires 365d;
+              error_log /dev/null crit;
+              alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
+            '';
+            "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(sdkjs-plugins)(\/.*\.json)$".extraConfig = ''
+              expires 365d;
+              error_log /dev/null crit;
+              alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
+            '';
+            "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps|sdkjs|sdkjs-plugins|fonts)(\/.*)$".extraConfig = ''
+              expires 365d;
+              alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
+            '';
+            "~* ^(\/cache\/files.*)(\/.*)".extraConfig = ''
+              alias /var/lib/onlyoffice/documentserver/App_Data$1;
+              add_header Content-Disposition "attachment; filename*=UTF-8''$arg_filename";
+
+              set $secret_string verysecretstring;
+              secure_link $arg_md5,$arg_expires;
+              secure_link_md5 "$secure_link_expires$uri$secret_string";
+
+              if ($secure_link = "") {
+                return 403;
+              }
+
+              if ($secure_link = "0") {
+                return 410;
+              }
+            '';
+            "~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(internal)(\/.*)$".extraConfig = ''
+              allow 127.0.0.1;
+              deny all;
+              proxy_pass http://onlyoffice-docservice/$2$3;
+            '';
+            "~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(info)(\/.*)$".extraConfig = ''
+              allow 127.0.0.1;
+              deny all;
+              proxy_pass http://onlyoffice-docservice/$2$3;
+            '';
+            "/".extraConfig = ''
+              proxy_pass http://onlyoffice-docservice;
+            '';
+            "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?(\/doc\/.*)".extraConfig = ''
+              proxy_pass http://onlyoffice-docservice$2;
+              proxy_http_version 1.1;
+            '';
+            "/${cfg.package.version}/".extraConfig = ''
+              proxy_pass http://onlyoffice-docservice/;
+            '';
+            "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(dictionaries)(\/.*)$".extraConfig = ''
+              expires 365d;
+              alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3;
+            '';
+            # /etc/nginx/includes/ds-example.conf
+            "~ ^(\/welcome\/.*)$".extraConfig = ''
+              expires 365d;
+              alias ${cfg.package}/var/www/onlyoffice/documentserver-example$1;
+              index docker.html;
+            '';
+            "/example/".extraConfig = lib.mkIf cfg.enableExampleServer ''
+              proxy_pass http://onlyoffice-example/;
+              proxy_set_header X-Forwarded-Path /example;
+            '';
+          };
+          extraConfig = ''
+            rewrite ^/$ /welcome/ redirect;
+            rewrite ^\/OfficeWeb(\/apps\/.*)$ /${cfg.package.version}/web-apps$1 redirect;
+            rewrite ^(\/web-apps\/apps\/(?!api\/).*)$ /${cfg.package.version}$1 redirect;
+
+            # based on https://github.com/ONLYOFFICE/document-server-package/blob/master/common/documentserver/nginx/includes/http-common.conf.m4#L29-L34
+            # without variable indirection and correct variable names
+            proxy_set_header Host $host;
+            proxy_set_header X-Forwarded-Host $host;
+            proxy_set_header X-Forwarded-Proto $scheme;
+            # required for CSP to take effect
+            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+            # required for websocket
+            proxy_set_header Upgrade $http_upgrade;
+            proxy_set_header Connection $connection_upgrade;
+          '';
+        };
+      };
+
+      rabbitmq.enable = lib.mkDefault true;
+
+      postgresql = {
+        enable = lib.mkDefault true;
+        ensureDatabases = [ "onlyoffice" ];
+        ensureUsers = [{
+          name = "onlyoffice";
+          ensurePermissions = { "DATABASE \"onlyoffice\"" = "ALL PRIVILEGES"; };
+        }];
+      };
+    };
+
+    systemd.services = {
+      onlyoffice-converter = {
+        description = "onlyoffice converter";
+        after = [ "network.target" "onlyoffice-docservice.service" "postgresql.service" ];
+        requires = [ "network.target" "onlyoffice-docservice.service" "postgresql.service" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper FileConverter/converter /run/onlyoffice/config";
+          Group = "onlyoffice";
+          Restart = "always";
+          RuntimeDirectory = "onlyoffice";
+          StateDirectory = "onlyoffice";
+          Type = "simple";
+          User = "onlyoffice";
+        };
+      };
+
+      onlyoffice-docservice =
+        let
+          onlyoffice-prestart = pkgs.writeShellScript "onlyoffice-prestart" ''
+            PATH=$PATH:${lib.makeBinPath (with pkgs; [ jq moreutils config.services.postgresql.package ])}
+            umask 077
+            mkdir -p /run/onlyoffice/config/ /var/lib/onlyoffice/documentserver/sdkjs/{slide/themes,common}/ /var/lib/onlyoffice/documentserver/{fonts,server/FileConverter/bin}/
+            cp -r ${cfg.package}/etc/onlyoffice/documentserver/* /run/onlyoffice/config/
+            chmod u+w /run/onlyoffice/config/default.json
+
+            cp /run/onlyoffice/config/default.json{,.orig}
+
+            # for a mapping of environment variables from the docker container to json options see
+            # https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/run-document-server.sh
+            jq '
+              .services.CoAuthoring.server.port = ${toString cfg.port} |
+              .services.CoAuthoring.sql.dbHost = "${cfg.postgresHost}" |
+              .services.CoAuthoring.sql.dbName = "${cfg.postgresName}" |
+            ${lib.optionalString (cfg.postgresPasswordFile != null) ''
+              .services.CoAuthoring.sql.dbPass = "'"$(cat ${cfg.postgresPasswordFile})"'" |
+            ''}
+              .services.CoAuthoring.sql.dbUser = "${cfg.postgresUser}" |
+            ${lib.optionalString (cfg.jwtSecretFile != null) ''
+              .services.CoAuthoring.token.enable.browser = true |
+              .services.CoAuthoring.token.enable.request.inbox = true |
+              .services.CoAuthoring.token.enable.request.outbox = true |
+              .services.CoAuthoring.secret.inbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" |
+              .services.CoAuthoring.secret.outbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" |
+              .services.CoAuthoring.secret.session.string = "'"$(cat ${cfg.jwtSecretFile})"'" |
+            ''}
+              .rabbitmq.url = "${cfg.rabbitmqUrl}"
+              ' /run/onlyoffice/config/default.json | sponge /run/onlyoffice/config/default.json
+
+            if ! psql -d onlyoffice -c "SELECT 'task_result'::regclass;" >/dev/null; then
+              psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql
+            fi
+          '';
+        in
+        {
+          description = "onlyoffice documentserver";
+          after = [ "network.target" "postgresql.service" ];
+          requires = [ "postgresql.service" ];
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = {
+            ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper DocService/docservice /run/onlyoffice/config";
+            ExecStartPre = onlyoffice-prestart;
+            Group = "onlyoffice";
+            Restart = "always";
+            RuntimeDirectory = "onlyoffice";
+            StateDirectory = "onlyoffice";
+            Type = "simple";
+            User = "onlyoffice";
+          };
+        };
+    };
+
+    users.users = {
+      onlyoffice = {
+        description = "OnlyOffice Service";
+        group = "onlyoffice";
+        isSystemUser = true;
+      };
+    };
+
+    users.groups.onlyoffice = { };
+  };
+}
diff --git a/nixos/modules/services/web-apps/openwebrx.nix b/nixos/modules/services/web-apps/openwebrx.nix
index 9e90c01e0bbb0..8a7972a3fc6ec 100644
--- a/nixos/modules/services/web-apps/openwebrx.nix
+++ b/nixos/modules/services/web-apps/openwebrx.nix
@@ -19,6 +19,10 @@ in
       wantedBy = [ "multi-user.target" ];
       path = with pkgs; [
         csdr
+        digiham
+        codec2
+        js8call
+        m17-cxx-demod
         alsaUtils
         netcat
       ];
diff --git a/nixos/modules/services/web-apps/phylactery.nix b/nixos/modules/services/web-apps/phylactery.nix
new file mode 100644
index 0000000000000..f0e97da1f2023
--- /dev/null
+++ b/nixos/modules/services/web-apps/phylactery.nix
@@ -0,0 +1,51 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let cfg = config.services.phylactery;
+in {
+  options.services.phylactery = {
+    enable = mkEnableOption "Whether to enable Phylactery server";
+
+    host = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = "Listen host for Phylactery";
+    };
+
+    port = mkOption {
+      type = types.port;
+      description = "Listen port for Phylactery";
+    };
+
+    library = mkOption {
+      type = types.path;
+      description = "Path to CBZ library";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.phylactery;
+      defaultText = literalExpression "pkgs.phylactery";
+      description = "The Phylactery package to use";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.phylactery = {
+      environment = {
+        PHYLACTERY_ADDRESS = "${cfg.host}:${toString cfg.port}";
+        PHYLACTERY_LIBRARY = "${cfg.library}";
+      };
+
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ConditionPathExists = cfg.library;
+        DynamicUser = true;
+        ExecStart = "${cfg.package}/bin/phylactery";
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ McSinyx ];
+}
diff --git a/nixos/modules/services/web-apps/tt-rss.nix b/nixos/modules/services/web-apps/tt-rss.nix
index 9aa38ab25c9a3..c441a2a7764e2 100644
--- a/nixos/modules/services/web-apps/tt-rss.nix
+++ b/nixos/modules/services/web-apps/tt-rss.nix
@@ -534,6 +534,7 @@ let
     services.phpfpm.pools = mkIf (cfg.pool == "${poolName}") {
       ${poolName} = {
         inherit (cfg) user;
+        phpPackage = pkgs.php80;
         settings = mapAttrs (name: mkDefault) {
           "listen.owner" = "nginx";
           "listen.group" = "nginx";
diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix
index 2b8c6f2e308bf..b262313577f10 100644
--- a/nixos/modules/services/web-servers/caddy/default.nix
+++ b/nixos/modules/services/web-servers/caddy/default.nix
@@ -299,7 +299,7 @@ in
         # https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
         # If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect.
         ExecStart = [ "" "${cfg.package}/bin/caddy run --config ${cfg.configFile} --adapter ${cfg.adapter} ${optionalString cfg.resume "--resume"}" ];
-        ExecReload = [ "" "${cfg.package}/bin/caddy reload --config ${cfg.configFile} --adapter ${cfg.adapter}" ];
+        ExecReload = [ "" "${cfg.package}/bin/caddy reload --config ${cfg.configFile} --adapter ${cfg.adapter} --force" ];
 
         ExecStartPre = "${cfg.package}/bin/caddy validate --config ${cfg.configFile} --adapter ${cfg.adapter}";
         User = cfg.user;
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 160d76597c889..5ccbaf77481b7 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -360,7 +360,7 @@ let
       ${optionalString (config.alias != null) "alias ${config.alias};"}
       ${optionalString (config.return != null) "return ${config.return};"}
       ${config.extraConfig}
-      ${optionalString (config.proxyPass != null && cfg.recommendedProxySettings) "include ${recommendedProxyConfig};"}
+      ${optionalString (config.proxyPass != null && config.recommendedProxySettings) "include ${recommendedProxyConfig};"}
       ${mkBasicAuth "sublocation" config}
     }
   '') (sortProperties (mapAttrsToList (k: v: v // { location = k; }) locations)));
@@ -423,7 +423,7 @@ in
         default = false;
         type = types.bool;
         description = "
-          Enable recommended proxy settings.
+          Whether to enable recommended proxy settings if a vhost does not specify the option manually.
         ";
       };
 
diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix
index 6fd00b3869745..49dd8893015af 100644
--- a/nixos/modules/services/web-servers/nginx/location-options.nix
+++ b/nixos/modules/services/web-servers/nginx/location-options.nix
@@ -3,7 +3,7 @@
 # has additional options that affect the web server as a whole, like
 # the user/group to run under.)
 
-{ lib }:
+{ lib, config }:
 
 with lib;
 
@@ -128,5 +128,14 @@ with lib;
         a greater priority.
       '';
     };
+
+    recommendedProxySettings = mkOption {
+      type = types.bool;
+      default = config.services.nginx.recommendedProxySettings;
+      defaultText = literalExpression "config.services.nginx.recommendedProxySettings";
+      description = ''
+        Enable recommended proxy settings.
+      '';
+    };
   };
 }
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index 2c77d6ee81622..61eef9f7ac96e 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -60,7 +60,7 @@ with lib;
         Note: This option overrides <literal>enableIPv6</literal>
       '';
       default = [];
-      example = [ "127.0.0.1" "::1" ];
+      example = [ "127.0.0.1" "[::1]" ];
     };
 
     enableACME = mkOption {
@@ -281,7 +281,7 @@ with lib;
 
     locations = mkOption {
       type = types.attrsOf (types.submodule (import ./location-options.nix {
-        inherit lib;
+        inherit lib config;
       }));
       default = {};
       example = literalExpression ''
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 2c2f2cae4b749..ffdf7e9a86eb5 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -72,7 +72,9 @@ in
         apply = map (d: d // {
           manage = "desktop";
           start = d.start
-          + optionalString (needBGCond d) ''\n\n
+          # literal newline to ensure d.start's last line is not appended to
+          + optionalString (needBGCond d) ''
+
             if [ -e $HOME/.background-image ]; then
               ${pkgs.feh}/bin/feh --bg-${cfg.wallpaper.mode} ${optionalString cfg.wallpaper.combineScreens "--no-xinerama"} $HOME/.background-image
             fi
diff --git a/nixos/modules/services/x11/desktop-managers/lxqt.nix b/nixos/modules/services/x11/desktop-managers/lxqt.nix
index 46f35f11b4a59..2aa9e5ab331de 100644
--- a/nixos/modules/services/x11/desktop-managers/lxqt.nix
+++ b/nixos/modules/services/x11/desktop-managers/lxqt.nix
@@ -69,8 +69,7 @@ in
 
     services.xserver.libinput.enable = mkDefault true;
 
-    xdg.portal.enable = true;
-    xdg.portal.extraPortals = [ pkgs.lxqt.xdg-desktop-portal-lxqt ];
+    xdg.portal.lxqt.enable = true;
   };
 
 }
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix
index 083b12193dab0..d04e565f7d310 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.nix
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix
@@ -50,10 +50,6 @@ in
 
           Note that this should be a last resort; patching the package is preferred (see GPaste).
         '';
-        apply = list: list ++
-        [
-          pkgs.pantheon.pantheon-agent-geoclue2
-        ];
       };
 
       extraWingpanelIndicators = mkOption {
@@ -96,6 +92,9 @@ in
 
   config = mkMerge [
     (mkIf cfg.enable {
+      services.xserver.desktopManager.pantheon.sessionPath = utils.removePackagesByName [
+        pkgs.pantheon.pantheon-agent-geoclue2
+      ] config.environment.pantheon.excludePackages;
 
       services.xserver.displayManager.sessionPackages = [ pkgs.pantheon.elementary-session-settings ];
 
@@ -177,19 +176,28 @@ in
       networking.networkmanager.enable = mkDefault true;
 
       # Global environment
-      environment.systemPackages = with pkgs; [
+      environment.systemPackages = (with pkgs.pantheon; [
+        elementary-session-settings
+        elementary-settings-daemon
+        gala
+        gnome-settings-daemon
+        (switchboard-with-plugs.override {
+          plugs = cfg.extraSwitchboardPlugs;
+        })
+        (wingpanel-with-indicators.override {
+          indicators = cfg.extraWingpanelIndicators;
+        })
+      ]) ++ utils.removePackagesByName ((with pkgs; [
         desktop-file-utils
-        glib
+        glib # for gsettings program
         gnome-menus
         gnome.adwaita-icon-theme
-        gtk3.out
-        hicolor-icon-theme
+        gtk3.out # for gtk-launch program
         onboard
         qgnomeplatform
-        shared-mime-info
         sound-theme-freedesktop
-        xdg-user-dirs
-      ] ++ (with pkgs.pantheon; [
+        xdg-user-dirs # Update user dirs as described in http://freedesktop.org/wiki/Software/xdg-user-dirs/
+      ]) ++ (with pkgs.pantheon; [
         # Artwork
         elementary-gtk-theme
         elementary-icon-theme
@@ -199,31 +207,21 @@ in
         # Desktop
         elementary-default-settings
         elementary-dock
-        elementary-session-settings
         elementary-shortcut-overlay
-        gala
-        (switchboard-with-plugs.override {
-          plugs = cfg.extraSwitchboardPlugs;
-        })
-        (wingpanel-with-indicators.override {
-          indicators = cfg.extraWingpanelIndicators;
-        })
 
         # Services
         elementary-capnet-assist
         elementary-notifications
-        elementary-settings-daemon
-        gnome-settings-daemon
         pantheon-agent-geoclue2
         pantheon-agent-polkit
-      ]);
-
-      programs.evince.enable = mkDefault true;
-      programs.file-roller.enable = mkDefault true;
+      ])) config.environment.pantheon.excludePackages;
 
       # Settings from elementary-default-settings
       environment.etc."gtk-3.0/settings.ini".source = "${pkgs.pantheon.elementary-default-settings}/etc/gtk-3.0/settings.ini";
 
+      xdg.mime.enable = true;
+      xdg.icons.enable = true;
+
       xdg.portal.enable = true;
       xdg.portal.extraPortals = with pkgs.pantheon; [
         elementary-files
@@ -271,6 +269,9 @@ in
     })
 
     (mkIf serviceCfg.apps.enable {
+      programs.evince.enable = mkDefault true;
+      programs.file-roller.enable = mkDefault true;
+
       environment.systemPackages = utils.removePackagesByName ([
         pkgs.gnome.gnome-font-viewer
       ] ++ (with pkgs.pantheon; [
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 3ca044ad5bc85..0d17d136798c8 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, utils, ... }:
 
 let
   xcfg = config.services.xserver;
@@ -30,7 +30,7 @@ let
   inherit (libsForQt5) kdeGear kdeFrameworks plasma5;
   inherit (pkgs) writeText;
   inherit (lib)
-    getBin optionalString
+    getBin optionalString literalExpression
     mkRemovedOptionModule mkRenamedOptionModule
     mkDefault mkIf mkMerge mkOption types;
 
@@ -192,6 +192,13 @@ in
       default = false;
     };
 
+    excludePackages = mkOption {
+      description = "List of default packages to exclude from the configuration";
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression "[ pkgs.plasma5Packages.oxygen ]";
+    };
+
     # Internally allows configuring kdeglobals globally
     kdeglobals = mkOption {
       internal = true;
@@ -234,11 +241,11 @@ in
     (mkIf (cfg.enable || cfg.mobile.enable) {
 
       security.wrappers = {
-        kcheckpass = {
+        kscreenlocker_greet = {
           setuid = true;
           owner = "root";
           group = "root";
-          source = "${getBin libsForQt5.kscreenlocker}/libexec/kcheckpass";
+          source = "${getBin libsForQt5.kscreenlocker}/libexec/kscreenlocker_greet";
         };
         start_kdeinit = {
           setuid = true;
@@ -263,89 +270,95 @@ in
       environment.systemPackages =
         with libsForQt5;
         with plasma5; with kdeGear; with kdeFrameworks;
-        [
-          frameworkintegration
-          kactivities
-          kauth
-          kcmutils
-          kconfig
-          kconfigwidgets
-          kcoreaddons
-          kdoctools
-          kdbusaddons
-          kdeclarative
-          kded
-          kdesu
-          kdnssd
-          kemoticons
-          kfilemetadata
-          kglobalaccel
-          kguiaddons
-          kiconthemes
-          kidletime
-          kimageformats
-          kinit
-          kirigami2 # In system profile for SDDM theme. TODO: wrapper.
-          kio
-          kjobwidgets
-          knewstuff
-          knotifications
-          knotifyconfig
-          kpackage
-          kparts
-          kpeople
-          krunner
-          kservice
-          ktextwidgets
-          kwallet
-          kwallet-pam
-          kwalletmanager
-          kwayland
-          kwayland-integration
-          kwidgetsaddons
-          kxmlgui
-          kxmlrpcclient
-          plasma-framework
-          solid
-          sonnet
-          threadweaver
-
-          breeze-qt5
-          kactivitymanagerd
-          kde-cli-tools
-          kdecoration
-          kdeplasma-addons
-          kgamma5
-          khotkeys
-          kscreen
-          kscreenlocker
-          kwayland
-          kwin
-          kwrited
-          libkscreen
-          libksysguard
-          milou
-          plasma-browser-integration
-          plasma-integration
-          polkit-kde-agent
-
-          plasma-desktop
-          plasma-workspace
-          plasma-workspace-wallpapers
-
-          konsole
-          oxygen
-
-          breeze-icons
-          pkgs.hicolor-icon-theme
-
-          kde-gtk-config
-          breeze-gtk
-
-          qtvirtualkeyboard
-
-          pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
-        ]
+        let
+          requiredPackages = [
+            frameworkintegration
+            kactivities
+            kauth
+            kcmutils
+            kconfig
+            kconfigwidgets
+            kcoreaddons
+            kdoctools
+            kdbusaddons
+            kdeclarative
+            kded
+            kdesu
+            kdnssd
+            kemoticons
+            kfilemetadata
+            kglobalaccel
+            kguiaddons
+            kiconthemes
+            kidletime
+            kimageformats
+            kinit
+            kirigami2 # In system profile for SDDM theme. TODO: wrapper.
+            kio
+            kjobwidgets
+            knewstuff
+            knotifications
+            knotifyconfig
+            kpackage
+            kparts
+            kpeople
+            krunner
+            kservice
+            ktextwidgets
+            kwallet
+            kwallet-pam
+            kwalletmanager
+            kwayland
+            kwayland-integration
+            kwidgetsaddons
+            kxmlgui
+            kxmlrpcclient
+            plasma-framework
+            solid
+            sonnet
+            threadweaver
+
+            breeze-qt5
+            kactivitymanagerd
+            kde-cli-tools
+            kdecoration
+            kdeplasma-addons
+            kgamma5
+            khotkeys
+            kscreen
+            kscreenlocker
+            kwayland
+            kwin
+            kwrited
+            libkscreen
+            libksysguard
+            milou
+            plasma-integration
+            polkit-kde-agent
+
+            plasma-desktop
+            plasma-workspace
+            plasma-workspace-wallpapers
+
+            breeze-icons
+            pkgs.hicolor-icon-theme
+
+            kde-gtk-config
+            breeze-gtk
+
+            qtvirtualkeyboard
+
+            pkgs.xdg-user-dirs # Update user dirs as described in https://freedesktop.org/wiki/Software/xdg-user-dirs/
+          ];
+          optionalPackages = [
+            plasma-browser-integration
+            konsole
+            oxygen
+            (lib.getBin qttools) # Expose qdbus in PATH
+          ];
+        in
+        requiredPackages
+        ++ utils.removePackagesByName optionalPackages cfg.excludePackages
 
         # Phonon audio backend
         ++ lib.optional (cfg.phononBackend == "gstreamer") libsForQt5.phonon-backend-gstreamer
@@ -387,9 +400,10 @@ in
       services.accounts-daemon.enable = true;
       # when changing an account picture the accounts-daemon reads a temporary file containing the image which systemsettings5 may place under /tmp
       systemd.services.accounts-daemon.serviceConfig.PrivateTmp = false;
+      services.power-profiles-daemon.enable = mkDefault true;
+      services.system-config-printer.enable = mkIf config.services.printing.enable (mkDefault true);
       services.udisks2.enable = true;
       services.upower.enable = config.powerManagement.enable;
-      services.system-config-printer.enable = mkIf config.services.printing.enable (mkDefault true);
       services.xserver.libinput.enable = mkDefault true;
 
       # Extra UDEV rules used by Solid
@@ -457,27 +471,29 @@ in
       environment.systemPackages =
         with libsForQt5;
         with plasma5; with kdeGear; with kdeFrameworks;
-        [
-          ksystemstats
-          kinfocenter
-          kmenuedit
-          plasma-systemmonitor
-          spectacle
-          systemsettings
-
-          dolphin
-          dolphin-plugins
-          ffmpegthumbs
-          kdegraphics-thumbnailers
-          khelpcenter
-          kio-extras
-          print-manager
-
-          elisa
-          gwenview
-          okular
-        ]
-      ;
+        let
+          requiredPackages = [
+            ksystemstats
+            kinfocenter
+            kmenuedit
+            plasma-systemmonitor
+            spectacle
+            systemsettings
+
+            dolphin
+            dolphin-plugins
+            ffmpegthumbs
+            kdegraphics-thumbnailers
+            kio-extras
+          ];
+          optionalPackages = [
+            elisa
+            gwenview
+            okular
+            khelpcenter
+            print-manager
+          ];
+      in requiredPackages ++ utils.removePackagesByName optionalPackages cfg.excludePackages;
 
       systemd.user.services = {
         plasma-run-with-systemd = {
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 88b21e59aaa6b..3a468b550627f 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -4,10 +4,9 @@ with lib;
 
 let
   cfg = config.services.xserver.desktopManager.xfce;
-in
 
+in
 {
-
   meta = {
     maintainers = teams.xfce.members;
   };
@@ -36,6 +35,12 @@ in
       [ "services" "xserver" "desktopManager" "xfce" "extraSessionCommands" ]
       [ "services" "xserver" "displayManager" "sessionCommands" ])
     (mkRemovedOptionModule [ "services" "xserver" "desktopManager" "xfce" "screenLock" ] "")
+
+    # added 2022-06-26
+    # thunar has its own module
+    (mkRenamedOptionModule
+      [ "services" "xserver" "desktopManager" "xfce" "thunarPlugins" ]
+      [ "programs" "thunar" "plugins" ])
   ];
 
   options = {
@@ -46,15 +51,6 @@ in
         description = "Enable the Xfce desktop environment.";
       };
 
-      thunarPlugins = mkOption {
-        default = [];
-        type = types.listOf types.package;
-        example = literalExpression "[ pkgs.xfce.thunar-archive-plugin ]";
-        description = ''
-          A list of plugin that should be installed with Thunar.
-        '';
-      };
-
       noDesktop = mkOption {
         type = types.bool;
         default = false;
@@ -98,7 +94,6 @@ in
       exo
       garcon
       libxfce4ui
-      xfconf
 
       mousepad
       parole
@@ -110,8 +105,6 @@ in
       xfce4-settings
       xfce4-taskmanager
       xfce4-terminal
-
-      (thunar.override { thunarPlugins = cfg.thunarPlugins; })
     ] # TODO: NetworkManager doesn't belong here
       ++ optional config.networking.networkmanager.enable networkmanagerapplet
       ++ optional config.powerManagement.enable xfce4-power-manager
@@ -130,6 +123,9 @@ in
         xfdesktop
       ] ++ optional cfg.enableScreensaver xfce4-screensaver;
 
+    programs.xfconf.enable = true;
+    programs.thunar.enable = true;
+
     environment.pathsToLink = [
       "/share/xfce4"
       "/lib/xfce4"
@@ -170,7 +166,6 @@ in
 
     # Systemd services
     systemd.packages = with pkgs.xfce; [
-      (thunar.override { thunarPlugins = cfg.thunarPlugins; })
       xfce4-notifyd
     ];
 
diff --git a/nixos/modules/services/x11/display-managers/xpra.nix b/nixos/modules/services/x11/display-managers/xpra.nix
index c23e479140f09..1566e38da0839 100644
--- a/nixos/modules/services/x11/display-managers/xpra.nix
+++ b/nixos/modules/services/x11/display-managers/xpra.nix
@@ -26,6 +26,13 @@ in
         description = "Bind xpra to TCP";
       };
 
+      desktop = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        example = "gnome-shell";
+        description = "Start a desktop environment instead of seamless mode";
+      };
+
       auth = mkOption {
         type = types.str;
         default = "pam";
@@ -222,7 +229,7 @@ in
     services.xserver.displayManager.job.execCmd = ''
       ${optionalString (cfg.pulseaudio)
         "export PULSE_COOKIE=/run/pulse/.config/pulse/cookie"}
-      exec ${pkgs.xpra}/bin/xpra start \
+      exec ${pkgs.xpra}/bin/xpra ${if cfg.desktop == null then "start" else "start-desktop --start=${cfg.desktop}"} \
         --daemon=off \
         --log-dir=/var/log \
         --log-file=xpra.log \
diff --git a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
index 1437ab3877009..5ace5dd06fd43 100644
--- a/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
+++ b/nixos/modules/system/boot/loader/generations-dir/generations-dir.nix
@@ -22,11 +22,11 @@ in
       enable = mkOption {
         default = false;
         type = types.bool;
-        description = ''
+        description = lib.mdDoc ''
           Whether to create symlinks to the system generations under
-          <literal>/boot</literal>.  When enabled,
-          <literal>/boot/default/kernel</literal>,
-          <literal>/boot/default/initrd</literal>, etc., are updated to
+          `/boot`.  When enabled,
+          `/boot/default/kernel`,
+          `/boot/default/initrd`, etc., are updated to
           point to the current generation's kernel image, initial RAM
           disk, and other bootstrap files.
 
@@ -41,7 +41,7 @@ in
       copyKernels = mkOption {
         default = false;
         type = types.bool;
-        description = ''
+        description = lib.mdDoc ''
           Whether copy the necessary boot files into /boot, so
           /nix/store is not needed by the boot loader.
         '';
diff --git a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
index 1023361f0b1f6..367feb5e88ec8 100644
--- a/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
+++ b/nixos/modules/system/boot/loader/raspberrypi/raspberrypi.nix
@@ -86,7 +86,7 @@ in
         type = types.nullOr types.lines;
         description = ''
           Extra options that will be appended to <literal>/boot/config.txt</literal> file.
-          For possible values, see: https://www.raspberrypi.org/documentation/configuration/config-txt/
+          For possible values, see: https://www.raspberrypi.com/documentation/computers/config_txt.html
         '';
       };
     };
diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index aca6a1ca2cc20..77280a9680e38 100644
--- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -264,8 +264,6 @@ def main() -> None:
         if installed_version < available_version:
             print("updating systemd-boot from %s to %s" % (installed_version, available_version))
             subprocess.check_call(["@systemd@/bin/bootctl", "--path=@efiSysMountPoint@", "update"])
-        else:
-            print("leaving systemd-boot %s in place (%s is not newer)" % (installed_version, available_version))
 
     mkdir_p("@efiSysMountPoint@/efi/nixos")
     mkdir_p("@efiSysMountPoint@/loader/entries")
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 0336930b3ab7d..357c2bab993b5 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -452,6 +452,7 @@ let
           "AllMulticast"
           "Unmanaged"
           "RequiredForOnline"
+          "RequiredFamilyForOnline"
           "ActivationPolicy"
         ])
         (assertMacAddress "MACAddress")
@@ -471,6 +472,12 @@ let
           "enslaved"
           "routable"
         ]))
+        (assertValueOneOf "RequiredFamilyForOnline" [
+          "ipv4"
+          "ipv6"
+          "both"
+          "any"
+        ])
         (assertValueOneOf "ActivationPolicy" ([
           "up"
           "always-up"
diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix
index 21d3fab2f35df..0ab2a875975d6 100644
--- a/nixos/modules/system/boot/resolved.nix
+++ b/nixos/modules/system/boot/resolved.nix
@@ -1,4 +1,4 @@
-{ config, lib, ... }:
+{ config, lib, pkgs, ... }:
 
 with lib;
 let
@@ -15,7 +15,7 @@ in
     services.resolved.enable = mkOption {
       default = false;
       type = types.bool;
-      description = ''
+      description = lib.mdDoc ''
         Whether to enable the systemd DNS resolver daemon.
       '';
     };
@@ -24,7 +24,7 @@ in
       default = [ ];
       example = [ "8.8.8.8" "2001:4860:4860::8844" ];
       type = types.listOf types.str;
-      description = ''
+      description = lib.mdDoc ''
         A list of IPv4 and IPv6 addresses to use as the fallback DNS servers.
         If this option is empty, a compiled-in list of DNS servers is used instead.
       '';
@@ -35,7 +35,7 @@ in
       defaultText = literalExpression "config.networking.search";
       example = [ "example.com" ];
       type = types.listOf types.str;
-      description = ''
+      description = lib.mdDoc ''
         A list of domains. These domains are used as search suffixes
         when resolving single-label host names (domain names which
         contain no dot), in order to qualify them into fully-qualified
@@ -43,7 +43,7 @@ in
 
         For compatibility reasons, if this setting is not specified,
         the search domains listed in
-        <filename>/etc/resolv.conf</filename> are used instead, if
+        {file}`/etc/resolv.conf` are used instead, if
         that file exists and any domains are configured in it.
       '';
     };
@@ -52,32 +52,14 @@ in
       default = "true";
       example = "false";
       type = types.enum [ "true" "resolve" "false" ];
-      description = ''
+      description = lib.mdDoc ''
         Controls Link-Local Multicast Name Resolution support
         (RFC 4795) on the local host.
 
         If set to
-
-        <variablelist>
-        <varlistentry>
-          <term><literal>"true"</literal></term>
-          <listitem><para>
-            Enables full LLMNR responder and resolver support.
-          </para></listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><literal>"false"</literal></term>
-          <listitem><para>
-            Disables both.
-          </para></listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><literal>"resolve"</literal></term>
-          <listitem><para>
-            Only resolution support is enabled, but responding is disabled.
-          </para></listitem>
-        </varlistentry>
-        </variablelist>
+        - `"true"`: Enables full LLMNR responder and resolver support.
+        - `"false"`: Disables both.
+        - `"resolve"`: Only resolution support is enabled, but responding is disabled.
       '';
     };
 
@@ -85,21 +67,14 @@ in
       default = "allow-downgrade";
       example = "true";
       type = types.enum [ "true" "allow-downgrade" "false" ];
-      description = ''
+      description = lib.mdDoc ''
         If set to
-        <variablelist>
-        <varlistentry>
-          <term><literal>"true"</literal></term>
-          <listitem><para>
+        - `"true"`:
             all DNS lookups are DNSSEC-validated locally (excluding
             LLMNR and Multicast DNS). Note that this mode requires a
             DNS server that supports DNSSEC. If the DNS server does
             not properly support DNSSEC all validations will fail.
-          </para></listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><literal>"allow-downgrade"</literal></term>
-          <listitem><para>
+        - `"allow-downgrade"`:
             DNSSEC validation is attempted, but if the server does not
             support DNSSEC properly, DNSSEC mode is automatically
             disabled. Note that this mode makes DNSSEC validation
@@ -107,22 +82,14 @@ in
             be able to trigger a downgrade to non-DNSSEC mode by
             synthesizing a DNS response that suggests DNSSEC was not
             supported.
-          </para></listitem>
-        </varlistentry>
-        <varlistentry>
-          <term><literal>"false"</literal></term>
-          <listitem><para>
-            DNS lookups are not DNSSEC validated.
-          </para></listitem>
-        </varlistentry>
-        </variablelist>
+        - `"false"`: DNS lookups are not DNSSEC validated.
       '';
     };
 
     services.resolved.extraConfig = mkOption {
       default = "";
       type = types.lines;
-      description = ''
+      description = lib.mdDoc ''
         Extra config to append to resolved.conf.
       '';
     };
@@ -178,6 +145,8 @@ in
     # If networkmanager is enabled, ask it to interface with resolved.
     networking.networkmanager.dns = "systemd-resolved";
 
+    networking.resolvconf.package = pkgs.systemd;
+
   };
 
 }
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 645fbc2b713a9..c7b62871eb4ef 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -177,11 +177,11 @@ in
       default = pkgs.systemd;
       defaultText = literalExpression "pkgs.systemd";
       type = types.package;
-      description = "The systemd package.";
+      description = lib.mdDoc "The systemd package.";
     };
 
     systemd.units = mkOption {
-      description = "Definition of systemd units.";
+      description = lib.mdDoc "Definition of systemd units.";
       default = {};
       type = systemdUtils.types.units;
     };
@@ -190,43 +190,43 @@ in
       default = [];
       type = types.listOf types.package;
       example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
-      description = "Packages providing systemd units and hooks.";
+      description = lib.mdDoc "Packages providing systemd units and hooks.";
     };
 
     systemd.targets = mkOption {
       default = {};
       type = systemdUtils.types.targets;
-      description = "Definition of systemd target units.";
+      description = lib.mdDoc "Definition of systemd target units.";
     };
 
     systemd.services = mkOption {
       default = {};
       type = systemdUtils.types.services;
-      description = "Definition of systemd service units.";
+      description = lib.mdDoc "Definition of systemd service units.";
     };
 
     systemd.sockets = mkOption {
       default = {};
       type = systemdUtils.types.sockets;
-      description = "Definition of systemd socket units.";
+      description = lib.mdDoc "Definition of systemd socket units.";
     };
 
     systemd.timers = mkOption {
       default = {};
       type = systemdUtils.types.timers;
-      description = "Definition of systemd timer units.";
+      description = lib.mdDoc "Definition of systemd timer units.";
     };
 
     systemd.paths = mkOption {
       default = {};
       type = systemdUtils.types.paths;
-      description = "Definition of systemd path units.";
+      description = lib.mdDoc "Definition of systemd path units.";
     };
 
     systemd.mounts = mkOption {
       default = [];
       type = systemdUtils.types.mounts;
-      description = ''
+      description = lib.mdDoc ''
         Definition of systemd mount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
         the 'where' attribute.
@@ -236,7 +236,7 @@ in
     systemd.automounts = mkOption {
       default = [];
       type = systemdUtils.types.automounts;
-      description = ''
+      description = lib.mdDoc ''
         Definition of systemd automount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
         the 'where' attribute.
@@ -246,41 +246,41 @@ in
     systemd.slices = mkOption {
       default = {};
       type = systemdUtils.types.slices;
-      description = "Definition of slice configurations.";
+      description = lib.mdDoc "Definition of slice configurations.";
     };
 
     systemd.generators = mkOption {
       type = types.attrsOf types.path;
       default = {};
       example = { systemd-gpt-auto-generator = "/dev/null"; };
-      description = ''
+      description = lib.mdDoc ''
         Definition of systemd generators.
-        For each <literal>NAME = VALUE</literal> pair of the attrSet, a link is generated from
-        <literal>/etc/systemd/system-generators/NAME</literal> to <literal>VALUE</literal>.
+        For each `NAME = VALUE` pair of the attrSet, a link is generated from
+        `/etc/systemd/system-generators/NAME` to `VALUE`.
       '';
     };
 
     systemd.shutdown = mkOption {
       type = types.attrsOf types.path;
       default = {};
-      description = ''
+      description = lib.mdDoc ''
         Definition of systemd shutdown executables.
-        For each <literal>NAME = VALUE</literal> pair of the attrSet, a link is generated from
-        <literal>/etc/systemd/system-shutdown/NAME</literal> to <literal>VALUE</literal>.
+        For each `NAME = VALUE` pair of the attrSet, a link is generated from
+        `/etc/systemd/system-shutdown/NAME` to `VALUE`.
       '';
     };
 
     systemd.defaultUnit = mkOption {
       default = "multi-user.target";
       type = types.str;
-      description = "Default unit started when the system boots.";
+      description = lib.mdDoc "Default unit started when the system boots.";
     };
 
     systemd.ctrlAltDelUnit = mkOption {
       default = "reboot.target";
       type = types.str;
       example = "poweroff.target";
-      description = ''
+      description = lib.mdDoc ''
         Target that should be started when Ctrl-Alt-Delete is pressed.
       '';
     };
@@ -289,8 +289,8 @@ in
       type = with types; attrsOf (nullOr (oneOf [ str path package ]));
       default = {};
       example = { TZ = "CET"; };
-      description = ''
-        Environment variables passed to <emphasis>all</emphasis> systemd units.
+      description = lib.mdDoc ''
+        Environment variables passed to *all* systemd units.
       '';
     };
 
@@ -298,16 +298,16 @@ in
       type = with types; attrsOf (nullOr (oneOf [ str path package ]));
       default = {};
       example = { SYSTEMD_LOG_LEVEL = "debug"; };
-      description = ''
+      description = lib.mdDoc ''
         Environment variables of PID 1. These variables are
-        <emphasis>not</emphasis> passed to started units.
+        *not* passed to started units.
       '';
     };
 
     systemd.enableCgroupAccounting = mkOption {
       default = true;
       type = types.bool;
-      description = ''
+      description = lib.mdDoc ''
         Whether to enable cgroup accounting.
       '';
     };
@@ -315,7 +315,7 @@ in
     systemd.enableUnifiedCgroupHierarchy = mkOption {
       default = true;
       type = types.bool;
-      description = ''
+      description = lib.mdDoc ''
         Whether to enable the unified cgroup hierarchy (cgroupsv2).
       '';
     };
@@ -324,7 +324,7 @@ in
       default = "";
       type = types.lines;
       example = "DefaultLimitCORE=infinity";
-      description = ''
+      description = lib.mdDoc ''
         Extra config options for systemd. See man systemd-system.conf for
         available options.
       '';
@@ -334,7 +334,7 @@ in
       default = "";
       type = types.lines;
       example = "HibernateDelaySec=1h";
-      description = ''
+      description = lib.mdDoc ''
         Extra config options for systemd sleep state logic.
         See sleep.conf.d(5) man page for available options.
       '';
@@ -344,7 +344,7 @@ in
       default = [ ];
       type = types.listOf types.str;
       example = [ "debug-shell.service" "systemd-quotacheck.service" ];
-      description = ''
+      description = lib.mdDoc ''
         Additional units shipped with systemd that shall be enabled.
       '';
     };
@@ -353,10 +353,10 @@ in
       default = [ ];
       type = types.listOf types.str;
       example = [ "systemd-backlight@.service" ];
-      description = ''
+      description = lib.mdDoc ''
         A list of units to skip when generating system systemd configuration directory. This has
-        priority over upstream units, <option>systemd.units</option>, and
-        <option>systemd.additionalUpstreamSystemUnits</option>. The main purpose of this is to
+        priority over upstream units, {option}`systemd.units`, and
+        {option}`systemd.additionalUpstreamSystemUnits`. The main purpose of this is to
         prevent a upstream systemd unit from being added to the initrd with any modifications made to it
         by other NixOS modules.
       '';
@@ -366,7 +366,7 @@ in
       type = types.nullOr types.path;
       default = null;
       example = "/dev/watchdog";
-      description = ''
+      description = lib.mdDoc ''
         The path to a hardware watchdog device which will be managed by systemd.
         If not specified, systemd will default to /dev/watchdog.
       '';
@@ -376,7 +376,7 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "30s";
-      description = ''
+      description = lib.mdDoc ''
         The amount of time which can elapse before a watchdog hardware device
         will automatically reboot the system. Valid time units include "ms",
         "s", "min", "h", "d", and "w".
@@ -387,7 +387,7 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "10m";
-      description = ''
+      description = lib.mdDoc ''
         The amount of time which can elapse after a reboot has been triggered
         before a watchdog hardware device will automatically reboot the system.
         Valid time units include "ms", "s", "min", "h", "d", and "w".
@@ -398,7 +398,7 @@ in
       type = types.nullOr types.str;
       default = null;
       example = "10m";
-      description = ''
+      description = lib.mdDoc ''
         The amount of time which can elapse when kexec is being executed before
         a watchdog hardware device will automatically reboot the system. This
         option should only be enabled if reloadTime is also enabled. Valid
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index cdec7f532917d..88e2bf9ac7043 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -150,7 +150,7 @@ in {
     };
 
     contents = mkOption {
-      description = "Set of files that have to be linked into the initrd";
+      description = lib.mdDoc "Set of files that have to be linked into the initrd";
       example = literalExpression ''
         {
           "/etc/hostname".text = "mymachine";
@@ -162,7 +162,7 @@ in {
     };
 
     storePaths = mkOption {
-      description = ''
+      description = lib.mdDoc ''
         Store paths to copy into the initrd as well.
       '';
       type = with types; listOf (oneOf [ singleLineStr package ]);
@@ -170,7 +170,7 @@ in {
     };
 
     extraBin = mkOption {
-      description = ''
+      description = lib.mdDoc ''
         Tools to add to /bin
       '';
       example = literalExpression ''
@@ -183,7 +183,7 @@ in {
     };
 
     suppressedStorePaths = mkOption {
-      description = ''
+      description = lib.mdDoc ''
         Store paths specified in the storePaths option that
         should not be copied.
       '';
@@ -192,9 +192,9 @@ in {
     };
 
     emergencyAccess = mkOption {
-      type = with types; oneOf [ bool singleLineStr ];
+      type = with types; oneOf [ bool (nullOr (passwdEntry str)) ];
       visible = false;
-      description = ''
+      description = lib.mdDoc ''
         Set to true for unauthenticated emergency access, and false for
         no emergency access.
 
@@ -208,7 +208,7 @@ in {
       type = types.listOf types.package;
       default = [];
       visible = false;
-      description = ''
+      description = lib.mdDoc ''
         Packages to include in /bin for the stage 1 emergency shell.
       '';
     };
@@ -218,7 +218,7 @@ in {
       type = types.listOf types.str;
       visible = false;
       example = [ "debug-shell.service" "systemd-quotacheck.service" ];
-      description = ''
+      description = lib.mdDoc ''
         Additional units shipped with systemd that shall be enabled.
       '';
     };
@@ -228,17 +228,17 @@ in {
       type = types.listOf types.str;
       example = [ "systemd-backlight@.service" ];
       visible = false;
-      description = ''
+      description = lib.mdDoc ''
         A list of units to skip when generating system systemd configuration directory. This has
-        priority over upstream units, <option>boot.initrd.systemd.units</option>, and
-        <option>boot.initrd.systemd.additionalUpstreamUnits</option>. The main purpose of this is to
+        priority over upstream units, {option}`boot.initrd.systemd.units`, and
+        {option}`boot.initrd.systemd.additionalUpstreamUnits`. The main purpose of this is to
         prevent a upstream systemd unit from being added to the initrd with any modifications made to it
         by other NixOS modules.
       '';
     };
 
     units = mkOption {
-      description = "Definition of systemd units.";
+      description = lib.mdDoc "Definition of systemd units.";
       default = {};
       visible = false;
       type = systemdUtils.types.units;
@@ -249,49 +249,49 @@ in {
       visible = false;
       type = types.listOf types.package;
       example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
-      description = "Packages providing systemd units and hooks.";
+      description = lib.mdDoc "Packages providing systemd units and hooks.";
     };
 
     targets = mkOption {
       default = {};
       visible = false;
       type = systemdUtils.types.initrdTargets;
-      description = "Definition of systemd target units.";
+      description = lib.mdDoc "Definition of systemd target units.";
     };
 
     services = mkOption {
       default = {};
       type = systemdUtils.types.initrdServices;
       visible = false;
-      description = "Definition of systemd service units.";
+      description = lib.mdDoc "Definition of systemd service units.";
     };
 
     sockets = mkOption {
       default = {};
       type = systemdUtils.types.initrdSockets;
       visible = false;
-      description = "Definition of systemd socket units.";
+      description = lib.mdDoc "Definition of systemd socket units.";
     };
 
     timers = mkOption {
       default = {};
       type = systemdUtils.types.initrdTimers;
       visible = false;
-      description = "Definition of systemd timer units.";
+      description = lib.mdDoc "Definition of systemd timer units.";
     };
 
     paths = mkOption {
       default = {};
       type = systemdUtils.types.initrdPaths;
       visible = false;
-      description = "Definition of systemd path units.";
+      description = lib.mdDoc "Definition of systemd path units.";
     };
 
     mounts = mkOption {
       default = [];
       type = systemdUtils.types.initrdMounts;
       visible = false;
-      description = ''
+      description = lib.mdDoc ''
         Definition of systemd mount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
         the 'where' attribute.
@@ -302,7 +302,7 @@ in {
       default = [];
       type = systemdUtils.types.automounts;
       visible = false;
-      description = ''
+      description = lib.mdDoc ''
         Definition of systemd automount units.
         This is a list instead of an attrSet, because systemd mandates the names to be derived from
         the 'where' attribute.
@@ -313,7 +313,7 @@ in {
       default = {};
       type = systemdUtils.types.slices;
       visible = false;
-      description = "Definition of slice configurations.";
+      description = lib.mdDoc "Definition of slice configurations.";
     };
   };
 
diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix
index 4951aef95584b..edfff5abaa9e3 100644
--- a/nixos/modules/system/boot/systemd/user.nix
+++ b/nixos/modules/system/boot/systemd/user.nix
@@ -14,6 +14,7 @@ let
     generateUnits
     targetToUnit
     serviceToUnit
+    sliceToUnit
     socketToUnit
     timerToUnit
     pathToUnit;
@@ -144,6 +145,10 @@ in {
       { # Ensure that pam_systemd gets included. This is special-cased
         # in systemd to provide XDG_RUNTIME_DIR.
         startSession = true;
+        # Disable pam_mount in systemd-user to prevent it from being called
+        # multiple times during login, because it will prevent pam_mount from
+        # unmounting the previously mounted volumes.
+        pamMount = false;
       };
 
     # Some overrides to upstream units.
diff --git a/nixos/modules/system/boot/tmp.nix b/nixos/modules/system/boot/tmp.nix
index cf6d19eb5f0ec..1f9431710aec4 100644
--- a/nixos/modules/system/boot/tmp.nix
+++ b/nixos/modules/system/boot/tmp.nix
@@ -14,23 +14,23 @@ in
     boot.cleanTmpDir = mkOption {
       type = types.bool;
       default = false;
-      description = ''
-        Whether to delete all files in <filename>/tmp</filename> during boot.
+      description = lib.mdDoc ''
+        Whether to delete all files in {file}`/tmp` during boot.
       '';
     };
 
     boot.tmpOnTmpfs = mkOption {
       type = types.bool;
       default = false;
-      description = ''
-         Whether to mount a tmpfs on <filename>/tmp</filename> during boot.
+      description = lib.mdDoc ''
+         Whether to mount a tmpfs on {file}`/tmp` during boot.
       '';
     };
 
     boot.tmpOnTmpfsSize = mkOption {
       type = types.oneOf [ types.str types.types.ints.positive ];
       default = "50%";
-      description = ''
+      description = lib.mdDoc ''
         Size of tmpfs in percentage.
         Percentage is defined by systemd.
       '';
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index ed552fecec53a..cfb9c39458ea3 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -82,8 +82,8 @@ in
           "default/useradd".text = "GROUP=100 ...";
         }
       '';
-      description = ''
-        Set of files that have to be linked in <filename>/etc</filename>.
+      description = lib.mdDoc ''
+        Set of files that have to be linked in {file}`/etc`.
       '';
 
       type = with types; attrsOf (submodule (
@@ -93,7 +93,7 @@ in
             enable = mkOption {
               type = types.bool;
               default = true;
-              description = ''
+              description = lib.mdDoc ''
                 Whether this /etc file should be generated.  This
                 option allows specific /etc files to be disabled.
               '';
@@ -101,9 +101,9 @@ in
 
             target = mkOption {
               type = types.str;
-              description = ''
+              description = lib.mdDoc ''
                 Name of symlink (relative to
-                <filename>/etc</filename>).  Defaults to the attribute
+                {file}`/etc`).  Defaults to the attribute
                 name.
               '';
             };
@@ -111,20 +111,20 @@ in
             text = mkOption {
               default = null;
               type = types.nullOr types.lines;
-              description = "Text of the file.";
+              description = lib.mdDoc "Text of the file.";
             };
 
             source = mkOption {
               type = types.path;
-              description = "Path of the source file.";
+              description = lib.mdDoc "Path of the source file.";
             };
 
             mode = mkOption {
               type = types.str;
               default = "symlink";
               example = "0600";
-              description = ''
-                If set to something else than <literal>symlink</literal>,
+              description = lib.mdDoc ''
+                If set to something else than `symlink`,
                 the file is copied instead of symlinked, with the given
                 file mode.
               '';
@@ -133,7 +133,7 @@ in
             uid = mkOption {
               default = 0;
               type = types.int;
-              description = ''
+              description = lib.mdDoc ''
                 UID of created file. Only takes effect when the file is
                 copied (that is, the mode is not 'symlink').
                 '';
@@ -142,7 +142,7 @@ in
             gid = mkOption {
               default = 0;
               type = types.int;
-              description = ''
+              description = lib.mdDoc ''
                 GID of created file. Only takes effect when the file is
                 copied (that is, the mode is not 'symlink').
               '';
@@ -151,20 +151,20 @@ in
             user = mkOption {
               default = "+${toString config.uid}";
               type = types.str;
-              description = ''
+              description = lib.mdDoc ''
                 User name of created file.
                 Only takes effect when the file is copied (that is, the mode is not 'symlink').
-                Changing this option takes precedence over <literal>uid</literal>.
+                Changing this option takes precedence over `uid`.
               '';
             };
 
             group = mkOption {
               default = "+${toString config.gid}";
               type = types.str;
-              description = ''
+              description = lib.mdDoc ''
                 Group name of created file.
                 Only takes effect when the file is copied (that is, the mode is not 'symlink').
-                Changing this option takes precedence over <literal>gid</literal>.
+                Changing this option takes precedence over `gid`.
               '';
             };
 
diff --git a/nixos/modules/tasks/encrypted-devices.nix b/nixos/modules/tasks/encrypted-devices.nix
index 06117d19af468..7837a34b49844 100644
--- a/nixos/modules/tasks/encrypted-devices.nix
+++ b/nixos/modules/tasks/encrypted-devices.nix
@@ -16,33 +16,33 @@ let
       enable = mkOption {
         default = false;
         type = types.bool;
-        description = "The block device is backed by an encrypted one, adds this device as a initrd luks entry.";
+        description = lib.mdDoc "The block device is backed by an encrypted one, adds this device as a initrd luks entry.";
       };
 
       blkDev = mkOption {
         default = null;
         example = "/dev/sda1";
         type = types.nullOr types.str;
-        description = "Location of the backing encrypted device.";
+        description = lib.mdDoc "Location of the backing encrypted device.";
       };
 
       label = mkOption {
         default = null;
         example = "rootfs";
         type = types.nullOr types.str;
-        description = "Label of the unlocked encrypted device. Set <literal>fileSystems.&lt;name?&gt;.device</literal> to <literal>/dev/mapper/&lt;label&gt;</literal> to mount the unlocked device.";
+        description = lib.mdDoc "Label of the unlocked encrypted device. Set `fileSystems.<name?>.device` to `/dev/mapper/<label>` to mount the unlocked device.";
       };
 
       keyFile = mkOption {
         default = null;
         example = "/mnt-root/root/.swapkey";
         type = types.nullOr types.str;
-        description = ''
+        description = lib.mdDoc ''
           Path to a keyfile used to unlock the backing encrypted
           device. At the time this keyfile is accessed, the
-          <literal>neededForBoot</literal> filesystems (see
-          <literal>fileSystems.&lt;name?&gt;.neededForBoot</literal>)
-          will have been mounted under <literal>/mnt-root</literal>,
+          `neededForBoot` filesystems (see
+          `fileSystems.<name?>.neededForBoot`)
+          will have been mounted under `/mnt-root`,
           so the keyfile path should usually start with "/mnt-root/".
         '';
       };
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index b8afe231dd2e1..82adc499ad0e7 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -280,7 +280,8 @@ in
     environment.etc.fstab.text =
       let
         fsToSkipCheck = [ "none" "bindfs" "btrfs" "zfs" "tmpfs" "nfs" "vboxsf" "glusterfs" "apfs" ];
-        skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck;
+        isBindMount = fs: builtins.elem "bind" fs.options;
+        skipCheck = fs: fs.noCheck || fs.device == "none" || builtins.elem fs.fsType fsToSkipCheck || isBindMount fs;
         # https://wiki.archlinux.org/index.php/fstab#Filepath_spaces
         escape = string: builtins.replaceStrings [ " " "\t" ] [ "\\040" "\\011" ] string;
         swapOptions = sw: concatStringsSep "," (
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index c8bbfe9769b21..05174e03754b6 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -549,7 +549,7 @@ in
                 zfs load-key -a
               ''
               else concatMapStrings (fs: ''
-                zfs load-key ${fs}
+                zfs load-key -- ${escapeShellArg fs}
               '') cfgZfs.requestEncryptionCredentials}
         '') rootPools));
 
@@ -701,7 +701,7 @@ in
           # expand every pool. Otherwise we want to enumerate
           # just the specifically provided list of pools.
           poolListProvider = if cfgExpandOnBoot == "all"
-            then "$(zpool list -H | awk '{print $1}')"
+            then "$(zpool list -H -o name)"
             else lib.escapeShellArgs cfgExpandOnBoot;
         in
         {
diff --git a/nixos/modules/tasks/lvm.nix b/nixos/modules/tasks/lvm.nix
index 4108b482cce51..2f98e3e7da5b2 100644
--- a/nixos/modules/tasks/lvm.nix
+++ b/nixos/modules/tasks/lvm.nix
@@ -85,13 +85,15 @@ in {
           systemd.initrdBin = lib.mkIf config.boot.initrd.services.lvm.enable [ pkgs.vdo ];
 
           extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable)''
-            ls ${pkgs.vdo}/bin/ | grep -v adaptLVMVDO | while read BIN; do
+            ls ${pkgs.vdo}/bin/ | while read BIN; do
               copy_bin_and_libs ${pkgs.vdo}/bin/$BIN
             done
+            substituteInPlace $out/bin/vdorecover --replace "${pkgs.bash}/bin/bash" "/bin/sh"
+            substituteInPlace $out/bin/adaptLVMVDO.sh --replace "${pkgs.bash}/bin/bash" "/bin/sh"
           '';
 
           extraUtilsCommandsTest = mkIf (!config.boot.initrd.systemd.enable)''
-            ls ${pkgs.vdo}/bin/ | grep -v adaptLVMVDO | while read BIN; do
+            ls ${pkgs.vdo}/bin/ | grep -vE '(adaptLVMVDO|vdorecover)' | while read BIN; do
               $out/bin/$(basename $BIN) --help > /dev/null
             done
           '';
diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix
index 66fdc61d28357..f44dafc9706a0 100644
--- a/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -85,12 +85,14 @@ let
         hasDefaultGatewaySet = (cfg.defaultGateway != null && cfg.defaultGateway.address != "")
                             || (cfg.enableIPv6 && cfg.defaultGateway6 != null && cfg.defaultGateway6.address != "");
 
-        networkLocalCommands = {
+        needNetworkSetup = cfg.resolvconf.enable || cfg.defaultGateway != null || cfg.defaultGateway6 != null;
+
+        networkLocalCommands = lib.mkIf needNetworkSetup {
           after = [ "network-setup.service" ];
           bindsTo = [ "network-setup.service" ];
         };
 
-        networkSetup =
+        networkSetup = lib.mkIf needNetworkSetup
           { description = "Networking Setup";
 
             after = [ "network-pre.target" "systemd-udevd.service" "systemd-sysctl.service" ];
diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix
index 80808e0c08fa5..1657fabcd9b1c 100644
--- a/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -59,15 +59,14 @@ in
         genericNetwork = override:
           let gateway = optional (cfg.defaultGateway != null && (cfg.defaultGateway.address or "") != "") cfg.defaultGateway.address
             ++ optional (cfg.defaultGateway6 != null && (cfg.defaultGateway6.address or "") != "") cfg.defaultGateway6.address;
-          in optionalAttrs (gateway != [ ]) {
-            routes = override [
-              {
+              makeGateway = gateway: {
                 routeConfig = {
                   Gateway = gateway;
                   GatewayOnLink = false;
                 };
-              }
-            ];
+              };
+          in optionalAttrs (gateway != [ ]) {
+            routes = override (map makeGateway gateway);
           } // optionalAttrs (domains != [ ]) {
             domains = override domains;
           };
@@ -89,20 +88,22 @@ in
           # more likely to result in interfaces being configured to
           # use DHCP when they shouldn't.
 
-          # We set RequiredForOnline to false, because it's fairly
-          # common for such devices to have multiple interfaces and
-          # only one of them to be connected (e.g. a laptop with
-          # ethernet and WiFi interfaces). Maybe one day networkd will
-          # support "any"-style RequiredForOnline...
+          # When wait-online.anyInterface is enabled, RequiredForOnline really
+          # means "sufficient for online", so we can enable it.
+          # Otherwise, don't block the network coming online because of default networks.
           matchConfig.Name = ["en*" "eth*"];
           DHCP = "yes";
-          linkConfig.RequiredForOnline = lib.mkDefault false;
+          linkConfig.RequiredForOnline =
+            lib.mkDefault config.systemd.network.wait-online.anyInterface;
+          networkConfig.IPv6PrivacyExtensions = "kernel";
         };
         networks."99-wireless-client-dhcp" = lib.mkIf cfg.useDHCP {
           # Like above, but this is much more likely to be correct.
           matchConfig.WLANInterfaceType = "station";
           DHCP = "yes";
-          linkConfig.RequiredForOnline = lib.mkDefault false;
+          linkConfig.RequiredForOnline =
+            lib.mkDefault config.systemd.network.wait-online.anyInterface;
+          networkConfig.IPv6PrivacyExtensions = "kernel";
           # We also set the route metric to one more than the default
           # of 1024, so that Ethernet is preferred if both are
           # available.
diff --git a/nixos/modules/tasks/snapraid.nix b/nixos/modules/tasks/snapraid.nix
index c8dde5b48993a..634ffa0311134 100644
--- a/nixos/modules/tasks/snapraid.nix
+++ b/nixos/modules/tasks/snapraid.nix
@@ -14,7 +14,7 @@ in
         d2 = "/mnt/disk2/";
         d3 = "/mnt/disk3/";
       };
-      description = "SnapRAID data disks.";
+      description = lib.mdDoc "SnapRAID data disks.";
       type = attrsOf str;
     };
     parityFiles = mkOption {
@@ -27,7 +27,7 @@ in
         "/mnt/diskt/snapraid.5-parity"
         "/mnt/disku/snapraid.6-parity"
       ];
-      description = "SnapRAID parity files.";
+      description = lib.mdDoc "SnapRAID parity files.";
       type = listOf str;
     };
     contentFiles = mkOption {
@@ -37,46 +37,46 @@ in
         "/mnt/disk1/snapraid.content"
         "/mnt/disk2/snapraid.content"
       ];
-      description = "SnapRAID content list files.";
+      description = lib.mdDoc "SnapRAID content list files.";
       type = listOf str;
     };
     exclude = mkOption {
       default = [ ];
       example = [ "*.unrecoverable" "/tmp/" "/lost+found/" ];
-      description = "SnapRAID exclude directives.";
+      description = lib.mdDoc "SnapRAID exclude directives.";
       type = listOf str;
     };
     touchBeforeSync = mkOption {
       default = true;
       example = false;
-      description =
-        "Whether <command>snapraid touch</command> should be run before <command>snapraid sync</command>.";
+      description = lib.mdDoc
+        "Whether {command}`snapraid touch` should be run before {command}`snapraid sync`.";
       type = bool;
     };
     sync.interval = mkOption {
       default = "01:00";
       example = "daily";
-      description = "How often to run <command>snapraid sync</command>.";
+      description = lib.mdDoc "How often to run {command}`snapraid sync`.";
       type = str;
     };
     scrub = {
       interval = mkOption {
         default = "Mon *-*-* 02:00:00";
         example = "weekly";
-        description = "How often to run <command>snapraid scrub</command>.";
+        description = lib.mdDoc "How often to run {command}`snapraid scrub`.";
         type = str;
       };
       plan = mkOption {
         default = 8;
         example = 5;
-        description =
-          "Percent of the array that should be checked by <command>snapraid scrub</command>.";
+        description = lib.mdDoc
+          "Percent of the array that should be checked by {command}`snapraid scrub`.";
         type = int;
       };
       olderThan = mkOption {
         default = 10;
         example = 20;
-        description =
+        description = lib.mdDoc
           "Number of days since data was last scrubbed before it can be scrubbed again.";
         type = int;
       };
@@ -90,7 +90,7 @@ in
         autosave 500
         pool /pool
       '';
-      description = "Extra config options for SnapRAID.";
+      description = lib.mdDoc "Extra config options for SnapRAID.";
       type = lines;
     };
   };
diff --git a/nixos/modules/tasks/swraid.nix b/nixos/modules/tasks/swraid.nix
index 0b53a6d152d09..26d6bd914d5ea 100644
--- a/nixos/modules/tasks/swraid.nix
+++ b/nixos/modules/tasks/swraid.nix
@@ -10,7 +10,7 @@ in {
     };
 
     mdadmConf = lib.mkOption {
-      description = "Contents of <filename>/etc/mdadm.conf</filename> in initrd.";
+      description = lib.mdDoc "Contents of {file}`/etc/mdadm.conf` in initrd.";
       type = lib.types.lines;
       default = "";
     };
diff --git a/nixos/modules/virtualisation/anbox.nix b/nixos/modules/virtualisation/anbox.nix
index c70da573533fa..6ba71ede7df0c 100644
--- a/nixos/modules/virtualisation/anbox.nix
+++ b/nixos/modules/virtualisation/anbox.nix
@@ -10,7 +10,7 @@ let
     address = mkOption {
       default = addr;
       type = types.str;
-      description = ''
+      description = lib.mdDoc ''
         IPv${toString v} ${name} address.
       '';
     };
@@ -18,9 +18,9 @@ let
     prefixLength = mkOption {
       default = pref;
       type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
-      description = ''
+      description = lib.mdDoc ''
         Subnet mask of the ${name} address, specified as the number of
-        bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
+        bits in the prefix (`${if v == 4 then "24" else "64"}`).
       '';
     };
   };
@@ -37,7 +37,7 @@ in
       default = pkgs.anbox.image;
       defaultText = literalExpression "pkgs.anbox.image";
       type = types.package;
-      description = ''
+      description = lib.mdDoc ''
         Base android image for Anbox.
       '';
     };
@@ -45,7 +45,7 @@ in
     extraInit = mkOption {
       type = types.lines;
       default = "";
-      description = ''
+      description = lib.mdDoc ''
         Extra shell commands to be run inside the container image during init.
       '';
     };
@@ -57,7 +57,7 @@ in
       dns = mkOption {
         default = "1.1.1.1";
         type = types.str;
-        description = ''
+        description = lib.mdDoc ''
           Container DNS server.
         '';
       };
diff --git a/nixos/modules/virtualisation/build-vm.nix b/nixos/modules/virtualisation/build-vm.nix
index 4a4694950f988..e94254416316b 100644
--- a/nixos/modules/virtualisation/build-vm.nix
+++ b/nixos/modules/virtualisation/build-vm.nix
@@ -25,8 +25,8 @@ in
   options = {
 
     virtualisation.vmVariant = mkOption {
-      description = ''
-        Machine configuration to be added for the vm script produced by <literal>nixos-rebuild build-vm</literal>.
+      description = lib.mdDoc ''
+        Machine configuration to be added for the vm script produced by `nixos-rebuild build-vm`.
       '';
       inherit (vmVariant) type;
       default = {};
@@ -34,8 +34,8 @@ in
     };
 
     virtualisation.vmVariantWithBootLoader = mkOption {
-      description = ''
-        Machine configuration to be added for the vm script produced by <literal>nixos-rebuild build-vm-with-bootloader</literal>.
+      description = lib.mdDoc ''
+        Machine configuration to be added for the vm script produced by `nixos-rebuild build-vm-with-bootloader`.
       '';
       inherit (vmVariantWithBootLoader) type;
       default = {};
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index cea3d51d3aefe..a9a2f3c1488ae 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -31,7 +31,7 @@ in
       mkOption {
         type = types.bool;
         default = false;
-        description = ''
+        description = lib.mdDoc ''
           This option enables the common /etc/containers configuration module.
         '';
       };
@@ -39,13 +39,13 @@ in
     ociSeccompBpfHook.enable = mkOption {
       type = types.bool;
       default = false;
-      description = "Enable the OCI seccomp BPF hook";
+      description = lib.mdDoc "Enable the OCI seccomp BPF hook";
     };
 
     containersConf.settings = mkOption {
       type = toml.type;
       default = { };
-      description = "containers.conf configuration";
+      description = lib.mdDoc "containers.conf configuration";
     };
 
     containersConf.cniPlugins = mkOption {
@@ -60,7 +60,7 @@ in
           pkgs.cniPlugins.dnsname
         ]
       '';
-      description = ''
+      description = lib.mdDoc ''
         CNI plugins to install on the system.
       '';
     };
@@ -74,14 +74,14 @@ in
           runroot = "/run/containers/storage";
         };
       };
-      description = "storage.conf configuration";
+      description = lib.mdDoc "storage.conf configuration";
     };
 
     registries = {
       search = mkOption {
         type = types.listOf types.str;
         default = [ "docker.io" "quay.io" ];
-        description = ''
+        description = lib.mdDoc ''
           List of repositories to search.
         '';
       };
@@ -89,7 +89,7 @@ in
       insecure = mkOption {
         default = [];
         type = types.listOf types.str;
-        description = ''
+        description = lib.mdDoc ''
           List of insecure repositories.
         '';
       };
@@ -97,7 +97,7 @@ in
       block = mkOption {
         default = [];
         type = types.listOf types.str;
-        description = ''
+        description = lib.mdDoc ''
           List of blocked repositories.
         '';
       };
@@ -116,10 +116,10 @@ in
           };
         }
       '';
-      description = ''
+      description = lib.mdDoc ''
         Signature verification policy file.
         If this option is empty the default policy file from
-        <literal>skopeo</literal> will be used.
+        `skopeo` will be used.
       '';
     };
 
diff --git a/nixos/modules/virtualisation/cri-o.nix b/nixos/modules/virtualisation/cri-o.nix
index cf51100015033..38766113f3916 100644
--- a/nixos/modules/virtualisation/cri-o.nix
+++ b/nixos/modules/virtualisation/cri-o.nix
@@ -71,10 +71,6 @@ in
     package = mkOption {
       type = types.package;
       default = crioPackage;
-      defaultText = literalDocBook ''
-        <literal>pkgs.cri-o</literal> built with
-        <literal>config.${opt.extraPackages}</literal>.
-      '';
       internal = true;
       description = ''
         The final CRI-O package (including extra packages).
diff --git a/nixos/modules/virtualisation/digital-ocean-image.nix b/nixos/modules/virtualisation/digital-ocean-image.nix
index 0ff2ee591f24a..a57c89245f2e1 100644
--- a/nixos/modules/virtualisation/digital-ocean-image.nix
+++ b/nixos/modules/virtualisation/digital-ocean-image.nix
@@ -13,7 +13,7 @@ in
       type = with types; either (enum [ "auto" ]) int;
       default = "auto";
       example = 4096;
-      description = ''
+      description = lib.mdDoc ''
         Size of disk image. Unit is MB.
       '';
     };
@@ -21,12 +21,12 @@ in
     virtualisation.digitalOceanImage.configFile = mkOption {
       type = with types; nullOr path;
       default = null;
-      description = ''
+      description = lib.mdDoc ''
         A path to a configuration file which will be placed at
-        <literal>/etc/nixos/configuration.nix</literal> and be used when switching
-        to a new configuration. If set to <literal>null</literal>, a default
+        `/etc/nixos/configuration.nix` and be used when switching
+        to a new configuration. If set to `null`, a default
         configuration is used that imports
-        <literal>(modulesPath + "/virtualisation/digital-ocean-config.nix")</literal>.
+        `(modulesPath + "/virtualisation/digital-ocean-config.nix")`.
       '';
     };
 
@@ -34,7 +34,7 @@ in
       type = types.enum [ "gzip" "bzip2" ];
       default = "gzip";
       example = "bzip2";
-      description = ''
+      description = lib.mdDoc ''
         Disk image compression method. Choose bzip2 to generate smaller images that
         take longer to generate but will consume less metered storage space on your
         Digital Ocean account.
diff --git a/nixos/modules/virtualisation/digital-ocean-init.nix b/nixos/modules/virtualisation/digital-ocean-init.nix
index df30104b7d7fd..e29e34c4775fc 100644
--- a/nixos/modules/virtualisation/digital-ocean-init.nix
+++ b/nixos/modules/virtualisation/digital-ocean-init.nix
@@ -15,18 +15,18 @@ in {
     type = types.bool;
     default = true;
     example = true;
-    description = "Whether to reconfigure the system from Digital Ocean user data";
+    description = lib.mdDoc "Whether to reconfigure the system from Digital Ocean user data";
   };
   options.virtualisation.digitalOcean.defaultConfigFile = mkOption {
     type = types.path;
     default = defaultConfigFile;
     defaultText = literalDocBook ''
       The default configuration imports user-data if applicable and
-      <literal>(modulesPath + "/virtualisation/digital-ocean-config.nix")</literal>.
+      `(modulesPath + "/virtualisation/digital-ocean-config.nix")`.
     '';
-    description = ''
+    description = lib.mdDoc ''
       A path to a configuration file which will be placed at
-      <literal>/etc/nixos/configuration.nix</literal> and be used when switching to
+      `/etc/nixos/configuration.nix` and be used when switching to
       a new configuration.
     '';
   };
diff --git a/nixos/modules/virtualisation/docker-rootless.nix b/nixos/modules/virtualisation/docker-rootless.nix
index b814fa1c4358c..f4e4bdc0963a7 100644
--- a/nixos/modules/virtualisation/docker-rootless.nix
+++ b/nixos/modules/virtualisation/docker-rootless.nix
@@ -18,18 +18,18 @@ in
     enable = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = lib.mdDoc ''
         This option enables docker in a rootless mode, a daemon that manages
         linux containers. To interact with the daemon, one needs to set
-        <command>DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock</command>.
+        {command}`DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock`.
       '';
     };
 
     setSocketVariable = mkOption {
       type = types.bool;
       default = false;
-      description = ''
-        Point <command>DOCKER_HOST</command> to rootless Docker instance for
+      description = lib.mdDoc ''
+        Point {command}`DOCKER_HOST` to rootless Docker instance for
         normal users by default.
       '';
     };
@@ -41,7 +41,7 @@ in
         ipv6 = true;
         "fixed-cidr-v6" = "fd00::/80";
       };
-      description = ''
+      description = lib.mdDoc ''
         Configuration for docker daemon. The attributes are serialized to JSON used as daemon.conf.
         See https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
       '';
@@ -51,7 +51,7 @@ in
       default = pkgs.docker;
       defaultText = literalExpression "pkgs.docker";
       type = types.package;
-      description = ''
+      description = lib.mdDoc ''
         Docker package to be used in the module.
       '';
     };
diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix
index 9816cc2332fbd..d3a2e0ed151d3 100644
--- a/nixos/modules/virtualisation/lxc-container.nix
+++ b/nixos/modules/virtualisation/lxc-container.nix
@@ -63,18 +63,18 @@ in
         default = {};
         example = literalExpression ''
           {
-            # create /etc/hostname on container creation
+            # create /etc/hostname on container creation. also requires networking.hostName = "" to be set
             "hostname" = {
               enable = true;
               target = "/etc/hostname";
-              template = builtins.writeFile "hostname.tpl" "{{ container.name }}";
+              template = builtins.toFile "hostname.tpl" "{{ container.name }}";
               when = [ "create" ];
             };
             # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied
             "hostname-nix" = {
               enable = true;
               target = "/etc/nixos/hostname.nix";
-              template = builtins.writeFile "hostname-nix.tpl" "{ ... }: { networking.hostName = "{{ container.name }}"; }";
+              template = builtins.toFile "hostname-nix.tpl" "{ ... }: { networking.hostName = \"{{ container.name }}\"; }";
               # copy keeps the file updated when the container is changed
               when = [ "create" "copy" ];
             };
@@ -82,7 +82,7 @@ in
             "configuration-nix" = {
               enable = true;
               target = "/etc/nixos/configuration.nix";
-              template = builtins.writeFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}";
+              template = builtins.toFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}";
               when = [ "create" ];
             };
           };
diff --git a/nixos/modules/virtualisation/oci-containers.nix b/nixos/modules/virtualisation/oci-containers.nix
index fa5fe99730440..81cdf1dd72b46 100644
--- a/nixos/modules/virtualisation/oci-containers.nix
+++ b/nixos/modules/virtualisation/oci-containers.nix
@@ -14,18 +14,18 @@ let
 
         image = mkOption {
           type = with types; str;
-          description = "OCI image to run.";
+          description = lib.mdDoc "OCI image to run.";
           example = "library/hello-world";
         };
 
         imageFile = mkOption {
           type = with types; nullOr package;
           default = null;
-          description = ''
+          description = lib.mdDoc ''
             Path to an image file to load before running the image. This can
             be used to bypass pulling the image from the registry.
 
-            The <literal>image</literal> attribute must match the name and
+            The `image` attribute must match the name and
             tag of the image contained in this file, as they will be used to
             run the container with that image. If they do not match, the
             image will be pulled from the registry as usual.
@@ -38,20 +38,20 @@ let
           username = mkOption {
             type = with types; nullOr str;
             default = null;
-            description = "Username for login.";
+            description = lib.mdDoc "Username for login.";
           };
 
           passwordFile = mkOption {
             type = with types; nullOr str;
             default = null;
-            description = "Path to file containing password.";
+            description = lib.mdDoc "Path to file containing password.";
             example = "/etc/nixos/dockerhub-password.txt";
           };
 
           registry = mkOption {
             type = with types; nullOr str;
             default = null;
-            description = "Registry where to login to.";
+            description = lib.mdDoc "Registry where to login to.";
             example = "https://docker.pkg.github.com";
           };
 
@@ -60,7 +60,7 @@ let
         cmd = mkOption {
           type =  with types; listOf str;
           default = [];
-          description = "Commandline arguments to pass to the image's entrypoint.";
+          description = lib.mdDoc "Commandline arguments to pass to the image's entrypoint.";
           example = literalExpression ''
             ["--port=9000"]
           '';
@@ -68,7 +68,7 @@ let
 
         entrypoint = mkOption {
           type = with types; nullOr str;
-          description = "Override the default entrypoint of the image.";
+          description = lib.mdDoc "Override the default entrypoint of the image.";
           default = null;
           example = "/bin/my-app";
         };
@@ -76,7 +76,7 @@ let
         environment = mkOption {
           type = with types; attrsOf str;
           default = {};
-          description = "Environment variables to set for this container.";
+          description = lib.mdDoc "Environment variables to set for this container.";
           example = literalExpression ''
             {
               DATABASE_HOST = "db.example.com";
@@ -88,7 +88,7 @@ let
         environmentFiles = mkOption {
           type = with types; listOf path;
           default = [];
-          description = "Environment files for this container.";
+          description = lib.mdDoc "Environment files for this container.";
           example = literalExpression ''
             [
               /path/to/.env
@@ -100,15 +100,15 @@ let
         log-driver = mkOption {
           type = types.str;
           default = "journald";
-          description = ''
+          description = lib.mdDoc ''
             Logging driver for the container.  The default of
-            <literal>"journald"</literal> means that the container's logs will be
+            `"journald"` means that the container's logs will be
             handled as part of the systemd unit.
 
             For more details and a full list of logging drivers, refer to respective backends documentation.
 
             For Docker:
-            <link xlink:href="https://docs.docker.com/engine/reference/run/#logging-drivers---log-driver">Docker engine documentation</link>
+            [Docker engine documentation](https://docs.docker.com/engine/reference/run/#logging-drivers---log-driver)
 
             For Podman:
             Refer to the docker-run(1) man page.
@@ -118,49 +118,27 @@ let
         ports = mkOption {
           type = with types; listOf str;
           default = [];
-          description = ''
+          description = lib.mdDoc ''
             Network ports to publish from the container to the outer host.
 
             Valid formats:
+            - `<ip>:<hostPort>:<containerPort>`
+            - `<ip>::<containerPort>`
+            - `<hostPort>:<containerPort>`
+            - `<containerPort>`
 
-            <itemizedlist>
-              <listitem>
-                <para>
-                  <literal>&lt;ip&gt;:&lt;hostPort&gt;:&lt;containerPort&gt;</literal>
-                </para>
-              </listitem>
-              <listitem>
-                <para>
-                  <literal>&lt;ip&gt;::&lt;containerPort&gt;</literal>
-                </para>
-              </listitem>
-              <listitem>
-                <para>
-                  <literal>&lt;hostPort&gt;:&lt;containerPort&gt;</literal>
-                </para>
-              </listitem>
-              <listitem>
-                <para>
-                  <literal>&lt;containerPort&gt;</literal>
-                </para>
-              </listitem>
-            </itemizedlist>
-
-            Both <literal>hostPort</literal> and
-            <literal>containerPort</literal> can be specified as a range of
+            Both `hostPort` and `containerPort` can be specified as a range of
             ports.  When specifying ranges for both, the number of container
             ports in the range must match the number of host ports in the
-            range.  Example: <literal>1234-1236:1234-1236/tcp</literal>
+            range.  Example: `1234-1236:1234-1236/tcp`
 
-            When specifying a range for <literal>hostPort</literal> only, the
-            <literal>containerPort</literal> must <emphasis>not</emphasis> be a
-            range.  In this case, the container port is published somewhere
-            within the specified <literal>hostPort</literal> range.  Example:
-            <literal>1234-1236:1234/tcp</literal>
+            When specifying a range for `hostPort` only, the `containerPort`
+            must *not* be a range.  In this case, the container port is published
+            somewhere within the specified `hostPort` range.
+            Example: `1234-1236:1234/tcp`
 
             Refer to the
-            <link xlink:href="https://docs.docker.com/engine/reference/run/#expose-incoming-ports">
-            Docker engine documentation</link> for full details.
+            [Docker engine documentation](https://docs.docker.com/engine/reference/run/#expose-incoming-ports) for full details.
           '';
           example = literalExpression ''
             [
@@ -172,7 +150,7 @@ let
         user = mkOption {
           type = with types; nullOr str;
           default = null;
-          description = ''
+          description = lib.mdDoc ''
             Override the username or UID (and optionally groupname or GID) used
             in the container.
           '';
@@ -182,16 +160,15 @@ let
         volumes = mkOption {
           type = with types; listOf str;
           default = [];
-          description = ''
+          description = lib.mdDoc ''
             List of volumes to attach to this container.
 
-            Note that this is a list of <literal>"src:dst"</literal> strings to
-            allow for <literal>src</literal> to refer to
-            <literal>/nix/store</literal> paths, which would be difficult with an
-            attribute set.  There are also a variety of mount options available
-            as a third field; please refer to the
-            <link xlink:href="https://docs.docker.com/engine/reference/run/#volume-shared-filesystems">
-            docker engine documentation</link> for details.
+            Note that this is a list of `"src:dst"` strings to
+            allow for `src` to refer to `/nix/store` paths, which
+            would be difficult with an attribute set.  There are
+            also a variety of mount options available as a third
+            field; please refer to the
+            [docker engine documentation](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems) for details.
           '';
           example = literalExpression ''
             [
@@ -204,17 +181,17 @@ let
         workdir = mkOption {
           type = with types; nullOr str;
           default = null;
-          description = "Override the default working directory for the container.";
+          description = lib.mdDoc "Override the default working directory for the container.";
           example = "/var/lib/hello_world";
         };
 
         dependsOn = mkOption {
           type = with types; listOf str;
           default = [];
-          description = ''
+          description = lib.mdDoc ''
             Define which other containers this one depends on. They will be added to both After and Requires for the unit.
 
-            Use the same name as the attribute under <literal>virtualisation.oci-containers.containers</literal>.
+            Use the same name as the attribute under `virtualisation.oci-containers.containers`.
           '';
           example = literalExpression ''
             virtualisation.oci-containers.containers = {
@@ -229,7 +206,7 @@ let
         extraOptions = mkOption {
           type = with types; listOf str;
           default = [];
-          description = "Extra options for <command>${defaultBackend} run</command>.";
+          description = lib.mdDoc "Extra options for {command}`${defaultBackend} run`.";
           example = literalExpression ''
             ["--network=host"]
           '';
@@ -238,7 +215,7 @@ let
         autoStart = mkOption {
           type = types.bool;
           default = true;
-          description = ''
+          description = lib.mdDoc ''
             When enabled, the container is automatically started on boot.
             If this option is set to false, the container has to be started on-demand via its service.
           '';
@@ -339,13 +316,13 @@ in {
     backend = mkOption {
       type = types.enum [ "podman" "docker" ];
       default = if versionAtLeast config.system.stateVersion "22.05" then "podman" else "docker";
-      description = "The underlying Docker implementation to use.";
+      description = lib.mdDoc "The underlying Docker implementation to use.";
     };
 
     containers = mkOption {
       default = {};
       type = types.attrsOf (types.submodule containerOptions);
-      description = "OCI (Docker) containers to run as systemd services.";
+      description = lib.mdDoc "OCI (Docker) containers to run as systemd services.";
     };
 
   };
diff --git a/nixos/modules/virtualisation/openvswitch.nix b/nixos/modules/virtualisation/openvswitch.nix
index 436a375fb5ebc..32646f60f8e04 100644
--- a/nixos/modules/virtualisation/openvswitch.nix
+++ b/nixos/modules/virtualisation/openvswitch.nix
@@ -13,7 +13,7 @@ in {
     enable = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = lib.mdDoc ''
         Whether to enable Open vSwitch. A configuration daemon (ovs-server)
         will be started.
         '';
@@ -22,9 +22,9 @@ in {
     resetOnStart = mkOption {
       type = types.bool;
       default = false;
-      description = ''
+      description = lib.mdDoc ''
         Whether to reset the Open vSwitch configuration database to a default
-        configuration on every start of the systemd <literal>ovsdb.service</literal>.
+        configuration on every start of the systemd `ovsdb.service`.
         '';
     };
 
@@ -32,7 +32,7 @@ in {
       type = types.package;
       default = pkgs.openvswitch;
       defaultText = literalExpression "pkgs.openvswitch";
-      description = ''
+      description = lib.mdDoc ''
         Open vSwitch package to use.
       '';
     };
diff --git a/nixos/modules/virtualisation/parallels-guest.nix b/nixos/modules/virtualisation/parallels-guest.nix
index d950cecff6f0c..53ad2ac708b80 100644
--- a/nixos/modules/virtualisation/parallels-guest.nix
+++ b/nixos/modules/virtualisation/parallels-guest.nix
@@ -34,7 +34,8 @@ in
       package = mkOption {
         type = types.nullOr types.package;
         default = config.boot.kernelPackages.prl-tools;
-        defaultText = literalExpression "config.boot.kernelPackages.prl-tools";
+        defaultText = "config.boot.kernelPackages.prl-tools";
+        example = literalExpression "config.boot.kernelPackages.prl-tools";
         description = ''
           Defines which package to use for prl-tools. Override to change the version.
         '';
@@ -44,27 +45,6 @@ in
   };
 
   config = mkIf config.hardware.parallels.enable {
-    services.xserver = {
-      drivers = singleton
-        { name = "prlvideo"; modules = [ prl-tools ]; };
-
-      screenSection = ''
-        Option "NoMTRR"
-      '';
-
-      config = ''
-        Section "InputClass"
-          Identifier "prlmouse"
-          MatchIsPointer "on"
-          MatchTag "prlmouse"
-          Driver "prlmouse"
-        EndSection
-      '';
-    };
-
-    hardware.opengl.package = prl-tools;
-    hardware.opengl.package32 = pkgs.pkgsi686Linux.linuxPackages.prl-tools.override { libsOnly = true; kernel = null; };
-    hardware.opengl.setLdLibraryPath = true;
 
     services.udev.packages = [ prl-tools ];
 
@@ -72,37 +52,44 @@ in
 
     boot.extraModulePackages = [ prl-tools ];
 
-    boot.kernelModules = [ "prl_tg" "prl_eth" "prl_fs" "prl_fs_freeze" ];
+    boot.kernelModules = [ "prl_fs" "prl_fs_freeze" "prl_tg" ]
+      ++ optional (pkgs.stdenv.hostPlatform.system == "aarch64-linux") "prl_notifier";
 
     services.timesyncd.enable = false;
 
     systemd.services.prltoolsd = {
-      description = "Parallels Tools' service";
+      description = "Parallels Tools Service";
       wantedBy = [ "multi-user.target" ];
+      path = [ prl-tools ];
       serviceConfig = {
         ExecStart = "${prl-tools}/bin/prltoolsd -f";
         PIDFile = "/var/run/prltoolsd.pid";
+        WorkingDirectory = "${prl-tools}/bin";
       };
     };
 
     systemd.services.prlfsmountd = mkIf config.hardware.parallels.autoMountShares {
-      description = "Parallels Shared Folders Daemon";
+      description = "Parallels Guest File System Sharing Tool";
       wantedBy = [ "multi-user.target" ];
+      path = [ prl-tools ];
       serviceConfig = rec {
         ExecStart = "${prl-tools}/sbin/prlfsmountd ${PIDFile}";
         ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /media";
         ExecStopPost = "${prl-tools}/sbin/prlfsmountd -u";
         PIDFile = "/run/prlfsmountd.pid";
+        WorkingDirectory = "${prl-tools}/bin";
       };
     };
 
     systemd.services.prlshprint = {
-      description = "Parallels Shared Printer Tool";
+      description = "Parallels Printing Tool";
       wantedBy = [ "multi-user.target" ];
       bindsTo = [ "cups.service" ];
+      path = [ prl-tools ];
       serviceConfig = {
         Type = "forking";
         ExecStart = "${prl-tools}/bin/prlshprint";
+        WorkingDirectory = "${prl-tools}/bin";
       };
     };
 
@@ -110,43 +97,47 @@ in
       prlcc = {
         description = "Parallels Control Center";
         wantedBy = [ "graphical-session.target" ];
+        path = [ prl-tools ];
         serviceConfig = {
           ExecStart = "${prl-tools}/bin/prlcc";
+          WorkingDirectory = "${prl-tools}/bin";
         };
       };
       prldnd = {
-        description = "Parallels Control Center";
+        description = "Parallels Drag And Drop Tool";
         wantedBy = [ "graphical-session.target" ];
+        path = [ prl-tools ];
         serviceConfig = {
           ExecStart = "${prl-tools}/bin/prldnd";
-        };
-      };
-      prl_wmouse_d  = {
-        description = "Parallels Walking Mouse Daemon";
-        wantedBy = [ "graphical-session.target" ];
-        serviceConfig = {
-          ExecStart = "${prl-tools}/bin/prl_wmouse_d";
+          WorkingDirectory = "${prl-tools}/bin";
         };
       };
       prlcp = {
-        description = "Parallels CopyPaste Tool";
+        description = "Parallels Copy Paste Tool";
         wantedBy = [ "graphical-session.target" ];
+        path = [ prl-tools ];
         serviceConfig = {
           ExecStart = "${prl-tools}/bin/prlcp";
+          Restart = "always";
+          WorkingDirectory = "${prl-tools}/bin";
         };
       };
       prlsga = {
         description = "Parallels Shared Guest Applications Tool";
         wantedBy = [ "graphical-session.target" ];
+        path = [ prl-tools ];
         serviceConfig = {
           ExecStart = "${prl-tools}/bin/prlsga";
+          WorkingDirectory = "${prl-tools}/bin";
         };
       };
       prlshprof = {
         description = "Parallels Shared Profile Tool";
         wantedBy = [ "graphical-session.target" ];
+        path = [ prl-tools ];
         serviceConfig = {
           ExecStart = "${prl-tools}/bin/prlshprof";
+          WorkingDirectory = "${prl-tools}/bin";
         };
       };
     };
diff --git a/nixos/modules/virtualisation/proxmox-image.nix b/nixos/modules/virtualisation/proxmox-image.nix
index c537d5aed4471..e07d1d1eb3fc6 100644
--- a/nixos/modules/virtualisation/proxmox-image.nix
+++ b/nixos/modules/virtualisation/proxmox-image.nix
@@ -127,16 +127,26 @@ with lib;
       name = "proxmox-${cfg.filenameSuffix}";
       postVM = let
         # Build qemu with PVE's patch that adds support for the VMA format
-        vma = pkgs.qemu_kvm.overrideAttrs ( super: {
+        vma = pkgs.qemu_kvm.overrideAttrs ( super: rec {
+
+          # proxmox's VMA patch doesn't work with qemu 7.0 yet
+          version = "6.2.0";
+          src = pkgs.fetchurl {
+            url= "https://download.qemu.org/qemu-${version}.tar.xz";
+            hash = "sha256-aOFdjkWsVjJuC5pK+otJo9/oq6NIgiHQmMhGmLymW0U=";
+          };
+
           patches = let
-            rev = "cc707c362ea5c8d832aac270d1ffa7ac66a8908f";
-            path = "debian/patches/pve/0025-PVE-Backup-add-vma-backup-format-code.patch";
+            rev = "b37b17c286da3d32945fbee8ee4fd97a418a50db";
+            path = "debian/patches/pve/0026-PVE-Backup-add-vma-backup-format-code.patch";
             vma-patch = pkgs.fetchpatch {
-              url = "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;hb=${rev};f=${path}";
-              sha256 = "1z467xnmfmry3pjy7p34psd5xdil9x0apnbvfz8qbj0bf9fgc8zf";
+              url = "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;h=${rev};f=${path}";
+              hash = "sha256-siuDWDUnM9Zq0/L2Faww3ELAOUHhVIHu5RAQn6L4Atc=";
             };
-          in super.patches ++ [ vma-patch ];
+          in [ vma-patch ];
+
           buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
+
         });
       in
       ''
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index f622897aa6207..e87f540fd57cb 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -684,6 +684,21 @@ in
           '';
       };
 
+    virtualisation.useDefaultFilesystems =
+      mkOption {
+        type = types.bool;
+        default = true;
+        description =
+          ''
+            If enabled, the boot disk of the virtual machine will be
+            formatted and mounted with the default filesystems for
+            testing. Swap devices and LUKS will be disabled.
+
+            If disabled, a root filesystem has to be specified and
+            formatted (for example in the initial ramdisk).
+          '';
+      };
+
     virtualisation.efiVars =
       mkOption {
         type = types.str;
@@ -754,13 +769,13 @@ in
     );
     boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
 
-    boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable)
+    boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
       ''
         # We need mke2fs in the initrd.
         copy_bin_and_libs ${pkgs.e2fsprogs}/bin/mke2fs
       '';
 
-    boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable)
+    boot.initrd.postDeviceCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable)
       ''
         # If the disk image appears to be empty, run mke2fs to
         # initialise.
@@ -930,38 +945,41 @@ in
         };
     in
       mkVMOverride (cfg.fileSystems //
-      {
+      optionalAttrs cfg.useDefaultFilesystems {
         "/".device = cfg.bootDevice;
         "/".fsType = "ext4";
         "/".autoFormat = true;
-
-        "/tmp" = mkIf config.boot.tmpOnTmpfs
-          { device = "tmpfs";
-            fsType = "tmpfs";
-            neededForBoot = true;
-            # Sync with systemd's tmp.mount;
-            options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmpOnTmpfsSize}" ];
-          };
-
-        "/nix/${if cfg.writableStore then ".ro-store" else "store"}" =
-          mkIf cfg.useNixStoreImage
-            { device = "${lookupDriveDeviceName "nix-store" cfg.qemu.drives}";
-              neededForBoot = true;
-              options = [ "ro" ];
-            };
-
-        "/nix/.rw-store" = mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs)
-          { fsType = "tmpfs";
-            options = [ "mode=0755" ];
-            neededForBoot = true;
-          };
-
-        "/boot" = mkIf cfg.useBootLoader
-          # see note [Disk layout with `useBootLoader`]
-          { device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk`
-            fsType = "vfat";
-            noCheck = true; # fsck fails on a r/o filesystem
-          };
+      } //
+      optionalAttrs config.boot.tmpOnTmpfs {
+        "/tmp" = {
+          device = "tmpfs";
+          fsType = "tmpfs";
+          neededForBoot = true;
+          # Sync with systemd's tmp.mount;
+          options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmpOnTmpfsSize}" ];
+        };
+      } //
+      optionalAttrs cfg.useNixStoreImage {
+        "/nix/${if cfg.writableStore then ".ro-store" else "store"}" = {
+          device = "${lookupDriveDeviceName "nix-store" cfg.qemu.drives}";
+          neededForBoot = true;
+          options = [ "ro" ];
+        };
+      } //
+      optionalAttrs (cfg.writableStore && cfg.writableStoreUseTmpfs) {
+        "/nix/.rw-store" = {
+          fsType = "tmpfs";
+          options = [ "mode=0755" ];
+          neededForBoot = true;
+        };
+      } //
+      optionalAttrs cfg.useBootLoader {
+        # see note [Disk layout with `useBootLoader`]
+        "/boot" = {
+          device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk`
+          fsType = "vfat";
+          noCheck = true; # fsck fails on a r/o filesystem
+        };
       } // lib.mapAttrs' mkSharedDir cfg.sharedDirectories);
 
     boot.initrd.systemd = lib.mkIf (config.boot.initrd.systemd.enable && cfg.writableStore) {
@@ -986,8 +1004,8 @@ in
       };
     };
 
-    swapDevices = mkVMOverride [ ];
-    boot.initrd.luks.devices = mkVMOverride {};
+    swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
+    boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {};
 
     # Don't run ntpd in the guest.  It should get the correct time from KVM.
     services.timesyncd.enable = false;
diff --git a/nixos/modules/virtualisation/railcar.nix b/nixos/modules/virtualisation/railcar.nix
deleted file mode 100644
index e719e25650d37..0000000000000
--- a/nixos/modules/virtualisation/railcar.nix
+++ /dev/null
@@ -1,124 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
-  cfg = config.services.railcar;
-  generateUnit = name: containerConfig:
-    let
-      container = pkgs.ociTools.buildContainer {
-        args = [
-          (pkgs.writeShellScript "run.sh" containerConfig.cmd).outPath
-        ];
-      };
-    in
-      nameValuePair "railcar-${name}" {
-        enable = true;
-        wantedBy = [ "multi-user.target" ];
-        serviceConfig = {
-            ExecStart = ''
-              ${cfg.package}/bin/railcar -r ${cfg.stateDir} run ${name} -b ${container}
-            '';
-            Type = containerConfig.runType;
-          };
-      };
-  mount = with types; (submodule {
-    options = {
-      type = mkOption {
-        type = str;
-        default = "none";
-        description = ''
-          The type of the filesystem to be mounted.
-          Linux: filesystem types supported by the kernel as listed in
-          `/proc/filesystems` (e.g., "minix", "ext2", "ext3", "jfs", "xfs",
-          "reiserfs", "msdos", "proc", "nfs", "iso9660"). For bind mounts
-          (when options include either bind or rbind), the type is a dummy,
-          often "none" (not listed in /proc/filesystems).
-        '';
-      };
-      source = mkOption {
-        type = str;
-        description = "Source for the in-container mount";
-      };
-      options = mkOption {
-        type = listOf str;
-        default = [ "bind" ];
-        description = ''
-          Mount options of the filesystem to be used.
-
-          Support options are listed in the mount(8) man page. Note that
-          both filesystem-independent and filesystem-specific options
-          are listed.
-        '';
-      };
-    };
-  });
-in
-{
-  options.services.railcar = {
-    enable = mkEnableOption "railcar";
-
-    containers = mkOption {
-      default = {};
-      description = "Declarative container configuration";
-      type = with types; attrsOf (submodule ({ name, config, ... }: {
-        options = {
-          cmd = mkOption {
-            type = types.lines;
-            description = "Command or script to run inside the container";
-          };
-
-          mounts = mkOption {
-            type = with types; attrsOf mount;
-            default = {};
-            description = ''
-              A set of mounts inside the container.
-
-              The defaults have been chosen for simple bindmounts, meaning
-              that you only need to provide the "source" parameter.
-            '';
-            example = { "/data" = { source = "/var/lib/data"; }; };
-          };
-
-          runType = mkOption {
-            type = types.str;
-            default = "oneshot";
-            description = "The systemd service run type";
-          };
-
-          os = mkOption {
-            type = types.str;
-            default = "linux";
-            description = "OS type of the container";
-          };
-
-          arch = mkOption {
-            type = types.str;
-            default = "x86_64";
-            description = "Computer architecture type of the container";
-          };
-        };
-      }));
-    };
-
-    stateDir = mkOption {
-      type = types.path;
-      default = "/var/railcar";
-      description = "Railcar persistent state directory";
-    };
-
-    package = mkOption {
-      type = types.package;
-      default = pkgs.railcar;
-      defaultText = literalExpression "pkgs.railcar";
-      description = "Railcar package to use";
-    };
-  };
-
-  config = mkIf cfg.enable {
-    systemd.services = flip mapAttrs' cfg.containers (name: containerConfig:
-      generateUnit name containerConfig
-    );
-  };
-}
-
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index 1a0c4df42cb3d..0c095c01ad80f 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -14,42 +14,42 @@ in {
         type = with types; either (enum [ "auto" ]) int;
         default = "auto";
         example = 50 * 1024;
-        description = ''
+        description = lib.mdDoc ''
           The size of the VirtualBox base image in MiB.
         '';
       };
       baseImageFreeSpace = mkOption {
         type = with types; int;
         default = 30 * 1024;
-        description = ''
+        description = lib.mdDoc ''
           Free space in the VirtualBox base image in MiB.
         '';
       };
       memorySize = mkOption {
         type = types.int;
         default = 1536;
-        description = ''
+        description = lib.mdDoc ''
           The amount of RAM the VirtualBox appliance can use in MiB.
         '';
       };
       vmDerivationName = mkOption {
         type = types.str;
         default = "nixos-ova-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}";
-        description = ''
+        description = lib.mdDoc ''
           The name of the derivation for the VirtualBox appliance.
         '';
       };
       vmName = mkOption {
         type = types.str;
         default = "NixOS ${config.system.nixos.label} (${pkgs.stdenv.hostPlatform.system})";
-        description = ''
+        description = lib.mdDoc ''
           The name of the VirtualBox appliance.
         '';
       };
       vmFileName = mkOption {
         type = types.str;
         default = "nixos-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}.ova";
-        description = ''
+        description = lib.mdDoc ''
           The file name of the VirtualBox appliance.
         '';
       };
@@ -60,10 +60,10 @@ in {
           rtcuseutc = "on";
           usb = "off";
         };
-        description = ''
+        description = lib.mdDoc ''
           Parameters passed to the Virtualbox appliance.
 
-          Run <literal>VBoxManage modifyvm --help</literal> to see more options.
+          Run `VBoxManage modifyvm --help` to see more options.
         '';
       };
       exportParams = mkOption {
@@ -72,14 +72,14 @@ in {
           "--vsys" "0" "--vendor" "ACME Inc."
         ];
         default = [];
-        description = ''
+        description = lib.mdDoc ''
           Parameters passed to the Virtualbox export command.
 
-          Run <literal>VBoxManage export --help</literal> to see more options.
+          Run `VBoxManage export --help` to see more options.
         '';
       };
       extraDisk = mkOption {
-        description = ''
+        description = lib.mdDoc ''
           Optional extra disk/hdd configuration.
           The disk will be an 'ext4' partition on a separate VMDK file.
         '';
@@ -93,16 +93,16 @@ in {
           options = {
             size = mkOption {
               type = types.int;
-              description = "Size in MiB";
+              description = lib.mdDoc "Size in MiB";
             };
             label = mkOption {
               type = types.str;
               default = "vm-extra-storage";
-              description = "Label for the disk partition";
+              description = lib.mdDoc "Label for the disk partition";
             };
             mountPoint = mkOption {
               type = types.str;
-              description = "Path where to mount this disk.";
+              description = lib.mdDoc "Path where to mount this disk.";
             };
           };
         });
diff --git a/nixos/modules/virtualisation/waydroid.nix b/nixos/modules/virtualisation/waydroid.nix
index 2c0b658948dd5..84abf6065810e 100644
--- a/nixos/modules/virtualisation/waydroid.nix
+++ b/nixos/modules/virtualisation/waydroid.nix
@@ -34,7 +34,7 @@ in
     system.requiredKernelConfig = with config.lib.kernelConfig; [
       (isEnabled "ANDROID_BINDER_IPC")
       (isEnabled "ANDROID_BINDERFS")
-      (isEnabled "ASHMEM")
+      (isEnabled "ASHMEM") # FIXME Needs memfd support instead on Linux 5.18 and waydroid 1.2.1
     ];
 
     /* NOTE: we always enable this flag even if CONFIG_PSI_DEFAULT_DISABLED is not on
diff --git a/nixos/modules/virtualisation/xen-dom0.nix b/nixos/modules/virtualisation/xen-dom0.nix
index 975eed10cd267..25d06e3c721da 100644
--- a/nixos/modules/virtualisation/xen-dom0.nix
+++ b/nixos/modules/virtualisation/xen-dom0.nix
@@ -23,12 +23,12 @@ in
         default = false;
         type = types.bool;
         description =
-          ''
+          mdDoc ''
             Setting this option enables the Xen hypervisor, a
             virtualisation technology that allows multiple virtual
-            machines, known as <emphasis>domains</emphasis>, to run
+            machines, known as *domains*, to run
             concurrently on the physical machine.  NixOS runs as the
-            privileged <emphasis>Domain 0</emphasis>.  This option
+            privileged *Domain 0*.  This option
             requires a reboot to take effect.
           '';
       };
@@ -37,7 +37,7 @@ in
       type = types.package;
       defaultText = literalExpression "pkgs.xen";
       example = literalExpression "pkgs.xen-light";
-      description = ''
+      description = lib.mdDoc ''
         The package used for Xen binary.
       '';
       relatedPackages = [ "xen" "xen-light" ];
@@ -47,7 +47,7 @@ in
       type = types.package;
       defaultText = literalExpression "pkgs.xen";
       example = literalExpression "pkgs.qemu_xen-light";
-      description = ''
+      description = lib.mdDoc ''
         The package with qemu binaries for dom0 qemu and xendomains.
       '';
       relatedPackages = [ "xen"
@@ -59,7 +59,7 @@ in
       mkOption {
         default = [];
         type = types.listOf types.str;
-        description =
+        description = lib.mdDoc
           ''
             Parameters passed to the Xen hypervisor at boot time.
           '';
@@ -70,7 +70,7 @@ in
         default = 0;
         example = 512;
         type = types.addCheck types.int (n: n >= 0);
-        description =
+        description = lib.mdDoc
           ''
             Amount of memory (in MiB) allocated to Domain 0 on boot.
             If set to 0, all memory is assigned to Domain 0.
@@ -81,7 +81,7 @@ in
         name = mkOption {
           default = "xenbr0";
           type = types.str;
-          description = ''
+          description = lib.mdDoc ''
               Name of bridge the Xen domUs connect to.
             '';
         };
@@ -89,7 +89,7 @@ in
         address = mkOption {
           type = types.str;
           default = "172.16.0.1";
-          description = ''
+          description = lib.mdDoc ''
             IPv4 address of the bridge.
           '';
         };
@@ -97,9 +97,9 @@ in
         prefixLength = mkOption {
           type = types.addCheck types.int (n: n >= 0 && n <= 32);
           default = 16;
-          description = ''
+          description = lib.mdDoc ''
             Subnet mask of the bridge interface, specified as the number of
-            bits in the prefix (<literal>24</literal>).
+            bits in the prefix (`24`).
             A DHCP server will provide IP addresses for the whole, remaining
             subnet.
           '';
@@ -108,8 +108,8 @@ in
         forwardDns = mkOption {
           type = types.bool;
           default = false;
-          description = ''
-            If set to <literal>true</literal>, the DNS queries from the
+          description = lib.mdDoc ''
+            If set to `true`, the DNS queries from the
             hosts connected to the bridge will be forwarded to the DNS
             servers specified in /etc/resolv.conf .
             '';
@@ -120,7 +120,7 @@ in
     virtualisation.xen.stored =
       mkOption {
         type = types.path;
-        description =
+        description = lib.mdDoc
           ''
             Xen Store daemon to use. Defaults to oxenstored of the xen package.
           '';
@@ -130,7 +130,7 @@ in
         extraConfig = mkOption {
           type = types.lines;
           default = "";
-          description =
+          description = lib.mdDoc
             ''
               Options defined here will override the defaults for xendomains.
               The default options can be seen in the file included from
diff --git a/nixos/release.nix b/nixos/release.nix
index beafdf9ff5bce..f70b02c4292b4 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -15,8 +15,8 @@ let
     (if stableBranch then "." else "pre") + "${toString nixpkgs.revCount}.${nixpkgs.shortRev}";
 
   # Run the tests for each platform.  You can run a test by doing
-  # e.g. ‘nix-build -A tests.login.x86_64-linux’, or equivalently,
-  # ‘nix-build tests/login.nix -A result’.
+  # e.g. ‘nix-build release.nix -A tests.login.x86_64-linux’,
+  # or equivalently, ‘nix-build tests/login.nix’.
   # See also nixosTests in pkgs/top-level/all-packages.nix
   allTestsForSystem = system:
     import ./tests/all-tests.nix {
@@ -221,6 +221,29 @@ in rec {
 
   );
 
+  # KVM image for proxmox in VMA format
+  proxmoxImage = forMatchingSystems [ "x86_64-linux" ] (system:
+    with import ./.. { inherit system; };
+
+    hydraJob ((import lib/eval-config.nix {
+      inherit system;
+      modules = [
+        ./modules/virtualisation/proxmox-image.nix
+      ];
+    }).config.system.build.VMA)
+  );
+
+  # LXC tarball for proxmox
+  proxmoxLXC = forMatchingSystems [ "x86_64-linux" ] (system:
+    with import ./.. { inherit system; };
+
+    hydraJob ((import lib/eval-config.nix {
+      inherit system;
+      modules = [
+        ./modules/virtualisation/proxmox-lxc.nix
+      ];
+    }).config.system.build.tarball)
+  );
 
   # A disk image that can be imported to Amazon EC2 and registered as an AMI
   amazonImage = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system:
@@ -318,39 +341,12 @@ in rec {
     "mkdir $out; ln -s $toplevel $out/dummy");
 
 
-  # Provide a tarball that can be unpacked into an SD card, and easily
-  # boot that system from uboot (like for the sheevaplug).
-  # The pc variant helps preparing the expression for the system tarball
-  # in a machine faster than the sheevpalug
-  /*
-  system_tarball_pc = forAllSystems (system: makeSystemTarball {
-    module = ./modules/installer/cd-dvd/system-tarball-pc.nix;
-    inherit system;
-  });
-  */
-
   # Provide container tarball for lxc, libvirt-lxc, docker-lxc, ...
   containerTarball = forAllSystems (system: makeSystemTarball {
     module = ./modules/virtualisation/lxc-container.nix;
     inherit system;
   });
 
-  /*
-  system_tarball_fuloong2f =
-    assert builtins.currentSystem == "mips64-linux";
-    makeSystemTarball {
-      module = ./modules/installer/cd-dvd/system-tarball-fuloong2f.nix;
-      system = "mips64-linux";
-    };
-
-  system_tarball_sheevaplug =
-    assert builtins.currentSystem == "armv5tel-linux";
-    makeSystemTarball {
-      module = ./modules/installer/cd-dvd/system-tarball-sheevaplug.nix;
-      system = "armv5tel-linux";
-    };
-  */
-
   tests = allTests;
 
   /* Build a bunch of typical closures so that Hydra can keep track of
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix
index 2dd06a50f40ba..c07f99c5db3a2 100644
--- a/nixos/tests/acme.nix
+++ b/nixos/tests/acme.nix
@@ -578,7 +578,7 @@ in {
               webserver.wait_for_unit(f"acme-finished-{test_domain}.target")
               wait_for_server()
               check_connection(client, test_domain)
-              rc, _ = client.execute(
+              rc, _s = client.execute(
                   f"openssl s_client -CAfile /tmp/ca.crt -connect {test_alias}:443"
                   " </dev/null 2>/dev/null | openssl x509 -noout -text"
                   f" | grep DNS: | grep {test_alias}"
diff --git a/nixos/tests/airsonic.nix b/nixos/tests/airsonic.nix
index 2f60c56f643be..69f979726bce4 100644
--- a/nixos/tests/airsonic.nix
+++ b/nixos/tests/airsonic.nix
@@ -15,7 +15,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
   testScript = ''
     def airsonic_is_up(_) -> bool:
-        return machine.succeed("curl --fail http://localhost:4040/login")
+        status, _ = machine.execute("curl --fail http://localhost:4040/login")
+        return status == 0
 
 
     machine.start()
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 38c320886d1a4..b0dd7ca0766fe 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -110,7 +110,6 @@ in {
   cri-o = handleTestOn ["x86_64-linux"] ./cri-o.nix {};
   custom-ca = handleTest ./custom-ca.nix {};
   croc = handleTest ./croc.nix {};
-  cryptpad = handleTest ./cryptpad.nix {};
   deluge = handleTest ./deluge.nix {};
   dendrite = handleTest ./matrix/dendrite.nix {};
   dex-oidc = handleTest ./dex-oidc.nix {};
@@ -159,6 +158,7 @@ in {
   firefox = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox; };
   firefox-esr    = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr; }; # used in `tested` job
   firefox-esr-91 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-91; };
+  firefox-esr-102 = handleTest ./firefox.nix { firefoxPackage = pkgs.firefox-esr-102; };
   firejail = handleTest ./firejail.nix {};
   firewall = handleTest ./firewall.nix {};
   fish = handleTest ./fish.nix {};
@@ -189,6 +189,7 @@ in {
   google-oslogin = handleTest ./google-oslogin {};
   gotify-server = handleTest ./gotify-server.nix {};
   grafana = handleTest ./grafana.nix {};
+  grafana-agent = handleTest ./grafana-agent.nix {};
   graphite = handleTest ./graphite.nix {};
   graylog = handleTest ./graylog.nix {};
   grocy = handleTest ./grocy.nix {};
@@ -201,6 +202,7 @@ in {
   haste-server = handleTest ./haste-server.nix {};
   haproxy = handleTest ./haproxy.nix {};
   hardened = handleTest ./hardened.nix {};
+  healthchecks = handleTest ./web-apps/healthchecks.nix {};
   hbase1 = handleTest ./hbase.nix { package=pkgs.hbase1; };
   hbase2 = handleTest ./hbase.nix { package=pkgs.hbase2; };
   hbase3 = handleTest ./hbase.nix { package=pkgs.hbase3; };
@@ -253,7 +255,6 @@ in {
   jirafeau = handleTest ./jirafeau.nix {};
   jitsi-meet = handleTest ./jitsi-meet.nix {};
   k3s-single-node = handleTest ./k3s-single-node.nix {};
-  k3s-single-node-docker = handleTest ./k3s-single-node-docker.nix {};
   kafka = handleTest ./kafka.nix {};
   kanidm = handleTest ./kanidm.nix {};
   kbd-setfont-decompress = handleTest ./kbd-setfont-decompress.nix {};
@@ -277,8 +278,11 @@ in {
   libreddit = handleTest ./libreddit.nix {};
   libresprite = handleTest ./libresprite.nix {};
   libreswan = handleTest ./libreswan.nix {};
+  librewolf = handleTest ./firefox.nix { firefoxPackage = pkgs.librewolf; };
+  libuiohook = handleTest ./libuiohook.nix {};
   lidarr = handleTest ./lidarr.nix {};
   lightdm = handleTest ./lightdm.nix {};
+  lighttpd = handleTest ./lighttpd.nix {};
   limesurvey = handleTest ./limesurvey.nix {};
   litestream = handleTest ./litestream.nix {};
   locate = handleTest ./locate.nix {};
@@ -382,6 +386,7 @@ in {
   nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; };
   node-red = handleTest ./node-red.nix {};
   nomad = handleTest ./nomad.nix {};
+  non-default-filesystems = handleTest ./non-default-filesystems.nix {};
   noto-fonts = handleTest ./noto-fonts.nix {};
   novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {};
   nsd = handleTest ./nsd.nix {};
@@ -410,6 +415,7 @@ in {
   pam-oath-login = handleTest ./pam/pam-oath-login.nix {};
   pam-u2f = handleTest ./pam/pam-u2f.nix {};
   pam-ussh = handleTest ./pam/pam-ussh.nix {};
+  pass-secret-service = handleTest ./pass-secret-service.nix {};
   pantalaimon = handleTest ./matrix/pantalaimon.nix {};
   pantheon = handleTest ./pantheon.nix {};
   paperless = handleTest ./paperless.nix {};
@@ -424,6 +430,7 @@ in {
   php = handleTest ./php {};
   php80 = handleTest ./php { php = pkgs.php80; };
   php81 = handleTest ./php { php = pkgs.php81; };
+  phylactery = handleTest ./web-apps/phylactery.nix {};
   pict-rs = handleTest ./pict-rs.nix {};
   pinnwand = handleTest ./pinnwand.nix {};
   plasma5 = handleTest ./plasma5.nix {};
@@ -436,6 +443,7 @@ in {
   podman = handleTestOn ["x86_64-linux"] ./podman/default.nix {};
   podman-dnsname = handleTestOn ["x86_64-linux"] ./podman/dnsname.nix {};
   podman-tls-ghostunnel = handleTestOn ["x86_64-linux"] ./podman/tls-ghostunnel.nix {};
+  polaris = handleTest ./polaris.nix {};
   pomerium = handleTestOn ["x86_64-linux"] ./pomerium.nix {};
   postfix = handleTest ./postfix.nix {};
   postfix-raise-smtpd-tls-security-level = handleTest ./postfix-raise-smtpd-tls-security-level.nix {};
@@ -458,6 +466,7 @@ in {
   proxy = handleTest ./proxy.nix {};
   prowlarr = handleTest ./prowlarr.nix {};
   pt2-clone = handleTest ./pt2-clone.nix {};
+  pykms = handleTest ./pykms.nix {};
   public-inbox = handleTest ./public-inbox.nix {};
   pulseaudio = discoverTests (import ./pulseaudio.nix);
   qboot = handleTestOn ["x86_64-linux" "i686-linux"] ./qboot.nix {};
@@ -472,7 +481,6 @@ in {
   restartByActivationScript = handleTest ./restart-by-activation-script.nix {};
   restic = handleTest ./restic.nix {};
   retroarch = handleTest ./retroarch.nix {};
-  riak = handleTest ./riak.nix {};
   robustirc-bridge = handleTest ./robustirc-bridge.nix {};
   roundcube = handleTest ./roundcube.nix {};
   rspamd = handleTest ./rspamd.nix {};
@@ -485,6 +493,7 @@ in {
   samba = handleTest ./samba.nix {};
   samba-wsdd = handleTest ./samba-wsdd.nix {};
   sanoid = handleTest ./sanoid.nix {};
+  schleuder = handleTest ./schleuder.nix {};
   sddm = handleTest ./sddm.nix {};
   seafile = handleTest ./seafile.nix {};
   searx = handleTest ./searx.nix {};
@@ -514,7 +523,9 @@ in {
   starship = handleTest ./starship.nix {};
   step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {};
   strongswan-swanctl = handleTest ./strongswan-swanctl.nix {};
+  stunnel = handleTest ./stunnel.nix {};
   sudo = handleTest ./sudo.nix {};
+  swap-partition = handleTest ./swap-partition.nix {};
   sway = handleTest ./sway.nix {};
   switchTest = handleTest ./switch-test.nix {};
   sympa = handleTest ./sympa.nix {};
@@ -584,6 +595,7 @@ in {
   uwsgi = handleTest ./uwsgi.nix {};
   v2ray = handleTest ./v2ray.nix {};
   vault = handleTest ./vault.nix {};
+  vault-dev = handleTest ./vault-dev.nix {};
   vault-postgresql = handleTest ./vault-postgresql.nix {};
   vaultwarden = handleTest ./vaultwarden.nix {};
   vector = handleTest ./vector.nix {};
@@ -613,6 +625,7 @@ in {
   yabar = handleTest ./yabar.nix {};
   yggdrasil = handleTest ./yggdrasil.nix {};
   zammad = handleTest ./zammad.nix {};
+  zeronet-conservancy = handleTest ./zeronet-conservancy.nix {};
   zfs = handleTest ./zfs.nix {};
   zigbee2mqtt = handleTest ./zigbee2mqtt.nix {};
   zoneminder = handleTest ./zoneminder.nix {};
diff --git a/nixos/tests/containers-custom-pkgs.nix b/nixos/tests/containers-custom-pkgs.nix
index 9894e6643762e..e8740ac631345 100644
--- a/nixos/tests/containers-custom-pkgs.nix
+++ b/nixos/tests/containers-custom-pkgs.nix
@@ -9,7 +9,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: let
 in {
   name = "containers-custom-pkgs";
   meta = {
-    maintainers = with lib.maintainers; [ adisbladis earvstedt ];
+    maintainers = with lib.maintainers; [ adisbladis erikarvstedt ];
   };
 
   nodes.machine = { config, ... }: {
diff --git a/nixos/tests/convos.nix b/nixos/tests/convos.nix
index 72275ab390d99..cc0c2e75893c1 100644
--- a/nixos/tests/convos.nix
+++ b/nixos/tests/convos.nix
@@ -23,7 +23,7 @@ in
 
   testScript = ''
     machine.wait_for_unit("convos")
-    machine.wait_for_open_port(port)
+    machine.wait_for_open_port(${toString port})
     machine.succeed("journalctl -u convos | grep -q 'Listening at.*${toString port}'")
     machine.succeed("curl -f http://localhost:${toString port}/")
   '';
diff --git a/nixos/tests/cryptpad.nix b/nixos/tests/cryptpad.nix
deleted file mode 100644
index db271f937ef42..0000000000000
--- a/nixos/tests/cryptpad.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-import ./make-test-python.nix ({ lib, ... }:
-
-with lib;
-
-{
-  name = "cryptpad";
-  meta.maintainers = with maintainers; [ davhau ];
-
-  nodes.machine =
-    { pkgs, ... }:
-    { services.cryptpad.enable = true; };
-
-  testScript = ''
-    machine.wait_for_unit("cryptpad.service")
-    machine.wait_for_open_port(3000)
-    machine.succeed("curl -L --fail http://localhost:3000/sheet")
-  '';
-})
diff --git a/nixos/tests/docker-tools-cross.nix b/nixos/tests/docker-tools-cross.nix
index 8791ec2581279..14cb14ceeaea9 100644
--- a/nixos/tests/docker-tools-cross.nix
+++ b/nixos/tests/docker-tools-cross.nix
@@ -24,7 +24,11 @@ let
   hello1 = remoteCrossPkgs.dockerTools.buildImage {
     name = "hello1";
     tag = "latest";
-    contents = remoteCrossPkgs.hello;
+    copyToRoot = remoteCrossPkgs.buildEnv {
+      name = "image-root";
+      pathsToLink = [ "/bin" ];
+      paths = [ remoteCrossPkgs.hello ];
+    };
   };
 
   hello2 = remoteCrossPkgs.dockerTools.buildLayeredImage {
diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix
index 80859ac7a96ec..99a968f17af2f 100644
--- a/nixos/tests/docker-tools.nix
+++ b/nixos/tests/docker-tools.nix
@@ -419,5 +419,10 @@ import ./make-test-python.nix ({ pkgs, ... }: {
             "docker rmi layered-image-with-path",
         )
 
+    with subtest("etc"):
+        docker.succeed("${examples.etc} | docker load")
+        docker.succeed("docker run --rm etc | grep localhost")
+        docker.succeed("docker image rm etc:latest")
+
   '';
 })
diff --git a/nixos/tests/fcitx/default.nix b/nixos/tests/fcitx/default.nix
index 78b322d351d3b..c132249fcb249 100644
--- a/nixos/tests/fcitx/default.nix
+++ b/nixos/tests/fcitx/default.nix
@@ -5,6 +5,7 @@ import ../make-test-python.nix (
     # copy_from_host works only for store paths
     rec {
         name = "fcitx";
+        meta.broken = true; # takes hours to time out since October 2021
         nodes.machine =
         {
           pkgs,
diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix
index 63ccc6efb5bb4..9bb78a97f36d7 100644
--- a/nixos/tests/firefox.nix
+++ b/nixos/tests/firefox.nix
@@ -1,5 +1,14 @@
-import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: {
-  name = "firefox";
+import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }:
+let firefoxPackage' = firefoxPackage.override (args: {
+      extraPrefsFiles = (args.extraPrefsFiles or []) ++ [
+        # make sure that autoplay is enabled by default for the audio test
+        (builtins.toString (builtins.toFile "autoplay-pref.js" ''defaultPref("media.autoplay.default",0);''))
+      ];
+  });
+
+in
+{
+  name = firefoxPackage'.unwrapped.binaryName;
   meta = with pkgs.lib.maintainers; {
     maintainers = [ eelco shlevy ];
   };
@@ -9,7 +18,7 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: {
 
     { imports = [ ./common/x11.nix ];
       environment.systemPackages = [
-        firefoxPackage
+        firefoxPackage'
         pkgs.xdotool
       ];
 
@@ -88,7 +97,7 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: {
 
       with subtest("Wait until Firefox has finished loading the Valgrind docs page"):
           machine.execute(
-              "xterm -e 'firefox file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' >&2 &"
+              "xterm -e '${firefoxPackage'.unwrapped.binaryName} file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' >&2 &"
           )
           machine.wait_for_window("Valgrind")
           machine.sleep(40)
@@ -96,7 +105,7 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: {
       with subtest("Check whether Firefox can play sound"):
           with record_audio(machine):
               machine.succeed(
-                  "firefox file://${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/phone-incoming-call.oga >&2 &"
+                  "${firefoxPackage'.unwrapped.binaryName} file://${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/phone-incoming-call.oga >&2 &"
               )
               wait_for_sound(machine)
           machine.copy_from_vm("/tmp/record.wav")
diff --git a/nixos/tests/grafana-agent.nix b/nixos/tests/grafana-agent.nix
new file mode 100644
index 0000000000000..a9f34d8cea31a
--- /dev/null
+++ b/nixos/tests/grafana-agent.nix
@@ -0,0 +1,32 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+
+  let
+    nodes = {
+      machine = {
+        services.grafana-agent = {
+          enable = true;
+        };
+      };
+    };
+  in
+  {
+    name = "grafana-agent";
+
+    meta = with lib.maintainers; {
+      maintainers = [ zimbatm ];
+    };
+
+    inherit nodes;
+
+    testScript = ''
+      start_all()
+
+      with subtest("Grafana-agent is running"):
+          machine.wait_for_unit("grafana-agent.service")
+          machine.wait_for_open_port(12345)
+          machine.succeed(
+              "curl -sSfN http://127.0.0.1:12345/-/healthy"
+          )
+          machine.shutdown()
+    '';
+  })
diff --git a/nixos/tests/graphite.nix b/nixos/tests/graphite.nix
index 496f16846ea6a..c534d45428e19 100644
--- a/nixos/tests/graphite.nix
+++ b/nixos/tests/graphite.nix
@@ -12,14 +12,8 @@ import ./make-test-python.nix ({ pkgs, ... } :
               SECRET_KEY = "abcd";
             '';
           };
-          api = {
-            enable = true;
-            port = 8082;
-            finders = [ ];
-          };
           carbon.enableCache = true;
           seyren.enable = false;  # Implicitely requires openssl-1.0.2u which is marked insecure
-          beacon.enable = true;
         };
       };
   };
@@ -28,21 +22,15 @@ import ./make-test-python.nix ({ pkgs, ... } :
     start_all()
     one.wait_for_unit("default.target")
     one.wait_for_unit("graphiteWeb.service")
-    one.wait_for_unit("graphiteApi.service")
-    one.wait_for_unit("graphite-beacon.service")
     one.wait_for_unit("carbonCache.service")
     # The services above are of type "simple". systemd considers them active immediately
     # even if they're still in preStart (which takes quite long for graphiteWeb).
     # Wait for ports to open so we're sure the services are up and listening.
     one.wait_for_open_port(8080)
-    one.wait_for_open_port(8082)
     one.wait_for_open_port(2003)
     one.succeed('echo "foo 1 `date +%s`" | nc -N localhost 2003')
     one.wait_until_succeeds(
         "curl 'http://localhost:8080/metrics/find/?query=foo&format=treejson' --silent | grep foo >&2"
     )
-    one.wait_until_succeeds(
-        "curl 'http://localhost:8082/metrics/find/?query=foo&format=treejson' --silent | grep foo >&2"
-    )
   '';
 })
diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix
index 3afa8ebf2b5f7..ccb8581685478 100644
--- a/nixos/tests/hardened.nix
+++ b/nixos/tests/hardened.nix
@@ -12,6 +12,11 @@ import ./make-test-python.nix ({ pkgs, ... } : {
       imports = [ ../modules/profiles/hardened.nix ];
       environment.memoryAllocator.provider = "graphene-hardened";
       nix.settings.sandbox = false;
+      nixpkgs.overlays = [
+        (final: super: {
+          dhcpcd = super.dhcpcd.override { enablePrivSep = false; };
+        })
+      ];
       virtualisation.emptyDiskImages = [ 4096 ];
       boot.initrd.postDeviceCommands = ''
         ${pkgs.dosfstools}/bin/mkfs.vfat -n EFISYS /dev/vdb
@@ -85,8 +90,8 @@ import ./make-test-python.nix ({ pkgs, ... } : {
 
       # Test Nix dæmon usage
       with subtest("nix-daemon cannot be used by all users"):
-          machine.fail("su -l nobody -s /bin/sh -c 'nix ping-store'")
-          machine.succeed("su -l alice -c 'nix ping-store'")
+          machine.fail("su -l nobody -s /bin/sh -c 'nix --extra-experimental-features nix-command ping-store'")
+          machine.succeed("su -l alice -c 'nix --extra-experimental-features nix-command ping-store'")
 
 
       # Test kernel image protection
diff --git a/nixos/tests/hedgedoc.nix b/nixos/tests/hedgedoc.nix
index 657d49c555e99..410350d83627c 100644
--- a/nixos/tests/hedgedoc.nix
+++ b/nixos/tests/hedgedoc.nix
@@ -11,7 +11,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
       services = {
         hedgedoc = {
           enable = true;
-          configuration.dbURL = "sqlite:///var/lib/hedgedoc/hedgedoc.db";
+          settings.dbURL = "sqlite:///var/lib/hedgedoc/hedgedoc.db";
         };
       };
     };
@@ -21,7 +21,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
       services = {
         hedgedoc = {
           enable = true;
-          configuration.dbURL = "postgres://hedgedoc:\${DB_PASSWORD}@localhost:5432/hedgedocdb";
+          settings.dbURL = "postgres://hedgedoc:\${DB_PASSWORD}@localhost:5432/hedgedocdb";
 
           /*
            * Do not use pkgs.writeText for secrets as
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index f7b9d283e4572..0bbeffd18cf0d 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -7,8 +7,6 @@ in {
   meta.maintainers = lib.teams.home-assistant.members;
 
   nodes.hass = { pkgs, ... }: {
-    environment.systemPackages = with pkgs; [ mosquitto ];
-
     services.postgresql = {
       enable = true;
       ensureDatabases = [ "hass" ];
@@ -98,10 +96,26 @@ in {
       };
       lovelaceConfigWritable = true;
     };
+
+    # Cause a configuration change inside `configuration.yml` and verify that the process is being reloaded.
+    specialisation.differentName = {
+      inheritParentConfig = true;
+      configuration.services.home-assistant.config.homeassistant.name = lib.mkForce "Test Home";
+    };
+
+    # Cause a configuration change that requires a service restart as we added a new runtime dependency
+    specialisation.newFeature = {
+      inheritParentConfig = true;
+      configuration.services.home-assistant.config.esphome = {};
+    };
   };
 
-  testScript = ''
+  testScript = { nodes, ... }: let
+    system = nodes.hass.config.system.build.toplevel;
+  in
+  ''
     import re
+    import json
 
     start_all()
 
@@ -114,7 +128,19 @@ in {
     assert match
     package = match.group('path')
 
+
+    def get_journal_cursor(host) -> str:
+        exit, out = host.execute("journalctl -u home-assistant.service -n1 -o json-pretty --output-fields=__CURSOR")
+        assert exit == 0
+        return json.loads(out)["__CURSOR"]
+
+
+    def wait_for_homeassistant(host, cursor):
+        host.wait_until_succeeds(f"journalctl --after-cursor='{cursor}' -u home-assistant.service | grep -q 'Home Assistant initialized in'")
+
+
     hass.wait_for_unit("home-assistant.service")
+    cursor = get_journal_cursor(hass)
 
     with subtest("Check that YAML configuration file is in place"):
         hass.succeed("test -L ${configDir}/configuration.yaml")
@@ -131,7 +157,7 @@ in {
         hass.succeed(f"grep -q 'wake_on_lan' {package}/extra_components")
 
     with subtest("Check that Home Assistant's web interface and API can be reached"):
-        hass.wait_until_succeeds("journalctl -u home-assistant.service | grep -q 'Home Assistant initialized in'")
+        wait_for_homeassistant(hass, cursor)
         hass.wait_for_open_port(8123)
         hass.succeed("curl --fail http://localhost:8123/lovelace")
 
@@ -142,12 +168,25 @@ in {
     with subtest("Check extra components are considered in systemd unit hardening"):
         hass.succeed("systemctl show -p DeviceAllow home-assistant.service | grep -q char-ttyUSB")
 
-    with subtest("Print log to ease debugging"):
-        output_log = hass.succeed("cat ${configDir}/home-assistant.log")
-        print("\n### home-assistant.log ###\n")
-        print(output_log + "\n")
+    with subtest("Check service reloads when configuration changes"):
+      # store the old pid of the process
+      pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
+      cursor = get_journal_cursor(hass)
+      hass.succeed("${system}/specialisation/differentName/bin/switch-to-configuration test")
+      new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
+      assert pid == new_pid, "The PID of the process should not change between process reloads"
+      wait_for_homeassistant(hass, cursor)
+
+    with subtest("check service restarts when package changes"):
+      pid = new_pid
+      cursor = get_journal_cursor(hass)
+      hass.succeed("${system}/specialisation/newFeature/bin/switch-to-configuration test")
+      new_pid = hass.succeed("systemctl show --property=MainPID home-assistant.service")
+      assert pid != new_pid, "The PID of the process shoudl change when the HA binary changes"
+      wait_for_homeassistant(hass, cursor)
 
     with subtest("Check that no errors were logged"):
+        output_log = hass.succeed("cat ${configDir}/home-assistant.log")
         assert "ERROR" not in output_log
 
     with subtest("Check systemd unit hardening"):
diff --git a/nixos/tests/installed-tests/default.nix b/nixos/tests/installed-tests/default.nix
index c6fb37cfe5842..b81384aa8c0b5 100644
--- a/nixos/tests/installed-tests/default.nix
+++ b/nixos/tests/installed-tests/default.nix
@@ -92,6 +92,7 @@ in
   fwupd = callInstalledTest ./fwupd.nix {};
   gcab = callInstalledTest ./gcab.nix {};
   gdk-pixbuf = callInstalledTest ./gdk-pixbuf.nix {};
+  geocode-glib = callInstalledTest ./geocode-glib.nix {};
   gjs = callInstalledTest ./gjs.nix {};
   glib-networking = callInstalledTest ./glib-networking.nix {};
   gnome-photos = callInstalledTest ./gnome-photos.nix {};
diff --git a/nixos/tests/installed-tests/flatpak-builder.nix b/nixos/tests/installed-tests/flatpak-builder.nix
index 31b9f2b258fdf..41f4060fb69e5 100644
--- a/nixos/tests/installed-tests/flatpak-builder.nix
+++ b/nixos/tests/installed-tests/flatpak-builder.nix
@@ -6,6 +6,7 @@ makeInstalledTest {
   testConfig = {
     services.flatpak.enable = true;
     xdg.portal.enable = true;
+    xdg.portal.extraPortals = with pkgs; [ xdg-desktop-portal-gtk ];
     environment.systemPackages = with pkgs; [ flatpak-builder ] ++ flatpak-builder.installedTestsDependencies;
     virtualisation.diskSize = 2048;
   };
diff --git a/nixos/tests/installed-tests/geocode-glib.nix b/nixos/tests/installed-tests/geocode-glib.nix
new file mode 100644
index 0000000000000..fcb38c96ab0f6
--- /dev/null
+++ b/nixos/tests/installed-tests/geocode-glib.nix
@@ -0,0 +1,13 @@
+{ pkgs, makeInstalledTest, ... }:
+
+makeInstalledTest {
+  testConfig = {
+    i18n.supportedLocales = [
+      "en_US.UTF-8/UTF-8"
+      # The tests require this locale available.
+      "en_GB.UTF-8/UTF-8"
+    ];
+  };
+
+  tested = pkgs.geocode-glib;
+}
diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix
index 5e7c967028e43..024822745ada5 100644
--- a/nixos/tests/ipfs.nix
+++ b/nixos/tests/ipfs.nix
@@ -14,6 +14,14 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     };
   };
 
+  nodes.fuse = { ... }: {
+    services.ipfs = {
+      enable = true;
+      apiAddress = "/ip4/127.0.0.1/tcp/2324";
+      autoMount = true;
+    };
+  };
+
   testScript = ''
     start_all()
 
@@ -40,5 +48,14 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     # Test if setting dataDir works properly with the hardened systemd unit
     machine.succeed("test -e /mnt/ipfs/config")
     machine.succeed("test ! -e /var/lib/ipfs/")
+
+    # Test FUSE mountpoint
+    ipfs_hash = fuse.succeed(
+        "echo fnord3 | ipfs --api /ip4/127.0.0.1/tcp/2324 add --quieter"
+    )
+
+    # The FUSE mount functionality is broken as of v0.13.0.
+    # See https://github.com/ipfs/kubo/issues/9044.
+    # fuse.succeed(f"cat /ipfs/{ipfs_hash.strip()} | grep fnord3")
   '';
 })
diff --git a/nixos/tests/jellyfin.nix b/nixos/tests/jellyfin.nix
index 4ac3786996378..7d3097b586299 100644
--- a/nixos/tests/jellyfin.nix
+++ b/nixos/tests/jellyfin.nix
@@ -52,18 +52,18 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
             machine.succeed(api_post("/Startup/Complete"))
 
         with machine.nested("Can login"):
-            auth_result = machine.succeed(
+            auth_result_str = machine.succeed(
                 api_post(
                     "/Users/AuthenticateByName",
                     "${payloads.auth}",
                 )
             )
-            auth_result = json.loads(auth_result)
+            auth_result = json.loads(auth_result_str)
             auth_token = auth_result["AccessToken"]
             auth_header += f", Token={auth_token}"
 
-            sessions_result = machine.succeed(api_get("/Sessions"))
-            sessions_result = json.loads(sessions_result)
+            sessions_result_str = machine.succeed(api_get("/Sessions"))
+            sessions_result = json.loads(sessions_result_str)
 
             this_session = [
                 session for session in sessions_result if session["DeviceId"] == "1337"
@@ -71,8 +71,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
             if len(this_session) != 1:
                 raise Exception("Session not created")
 
-            me = machine.succeed(api_get("/Users/Me"))
-            me = json.loads(me)["Id"]
+            me_str = machine.succeed(api_get("/Users/Me"))
+            me = json.loads(me_str)["Id"]
 
         with machine.nested("Can add library"):
             tempdir = machine.succeed("mktemp -d -p /var/lib/jellyfin").strip()
@@ -100,8 +100,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
 
 
         def is_refreshed(_):
-            folders = machine.succeed(api_get("/Library/VirtualFolders"))
-            folders = json.loads(folders)
+            folders_str = machine.succeed(api_get("/Library/VirtualFolders"))
+            folders = json.loads(folders_str)
             print(folders)
             return all(folder["RefreshStatus"] == "Idle" for folder in folders)
 
@@ -116,10 +116,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
             def has_movie(_):
                 global items
 
-                items = machine.succeed(
+                items_str = machine.succeed(
                     api_get(f"/Users/{me}/Items?IncludeItemTypes=Movie&Recursive=true")
                 )
-                items = json.loads(items)["Items"]
+                items = json.loads(items_str)["Items"]
 
                 return len(items) == 1
 
@@ -127,8 +127,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
 
             video = items[0]["Id"]
 
-            item_info = machine.succeed(api_get(f"/Users/{me}/Items/{video}"))
-            item_info = json.loads(item_info)
+            item_info_str = machine.succeed(api_get(f"/Users/{me}/Items/{video}"))
+            item_info = json.loads(item_info_str)
 
             if item_info["Name"] != "Big Buck Bunny":
                 raise Exception("Jellyfin failed to properly identify file")
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
index 265d1a330cd94..3f111426db388 100644
--- a/nixos/tests/jenkins.nix
+++ b/nixos/tests/jenkins.nix
@@ -18,6 +18,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
           enable = true;
           jobBuilder = {
             enable = true;
+            accessUser = "admin";
+            accessTokenFile = "/var/lib/jenkins/secrets/initialAdminPassword";
             nixJobs = [
               { job = {
                   name = "job-1";
@@ -79,7 +81,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     in ''
     start_all()
 
-    master.wait_for_unit("jenkins")
+    master.wait_for_unit("default.target")
 
     assert "Authentication required" in master.succeed("curl http://localhost:8080")
 
@@ -94,18 +96,12 @@ import ./make-test-python.nix ({ pkgs, ...} : {
 
     with subtest("jobs are declarative"):
         # Check that jobs are created on disk.
-        master.wait_for_unit("jenkins-job-builder")
-        master.wait_until_fails("systemctl is-active jenkins-job-builder")
         master.wait_until_succeeds("test -f /var/lib/jenkins/jobs/job-1/config.xml")
         master.wait_until_succeeds("test -f /var/lib/jenkins/jobs/folder-1/config.xml")
         master.wait_until_succeeds("test -f /var/lib/jenkins/jobs/folder-1/jobs/job-2/config.xml")
 
-        # Wait until jenkins is ready, reload configuration and verify it also
-        # sees the jobs.
-        master.succeed("curl --fail ${jenkinsUrl}/cli")
-        master.succeed("curl ${jenkinsUrl}/jnlpJars/jenkins-cli.jar -O")
-        master.succeed("${pkgs.jre}/bin/java -jar jenkins-cli.jar -s ${jenkinsUrl} -auth admin:$(cat /var/lib/jenkins/secrets/initialAdminPassword) reload-configuration")
-        out = master.succeed("${pkgs.jre}/bin/java -jar jenkins-cli.jar -s ${jenkinsUrl} -auth admin:$(cat /var/lib/jenkins/secrets/initialAdminPassword) list-jobs")
+        # Verify that jenkins also sees the jobs.
+        out = master.succeed("${pkgs.jenkins}/bin/jenkins-cli -s ${jenkinsUrl} -auth admin:$(cat /var/lib/jenkins/secrets/initialAdminPassword) list-jobs")
         jobs = [x.strip() for x in out.splitlines()]
         # Seeing jobs inside folders requires the Folders plugin
         # (https://plugins.jenkins.io/cloudbees-folder/), which we don't have
@@ -117,15 +113,12 @@ import ./make-test-python.nix ({ pkgs, ...} : {
         )
 
         # Check that jobs are removed from disk.
-        master.wait_for_unit("jenkins-job-builder")
-        master.wait_until_fails("systemctl is-active jenkins-job-builder")
         master.wait_until_fails("test -f /var/lib/jenkins/jobs/job-1/config.xml")
         master.wait_until_fails("test -f /var/lib/jenkins/jobs/folder-1/config.xml")
         master.wait_until_fails("test -f /var/lib/jenkins/jobs/folder-1/jobs/job-2/config.xml")
 
-        # Reload jenkins' configuration and verify it also sees the jobs as removed.
-        master.succeed("${pkgs.jre}/bin/java -jar jenkins-cli.jar -s ${jenkinsUrl} -auth admin:$(cat /var/lib/jenkins/secrets/initialAdminPassword) reload-configuration")
-        out = master.succeed("${pkgs.jre}/bin/java -jar jenkins-cli.jar -s ${jenkinsUrl} -auth admin:$(cat /var/lib/jenkins/secrets/initialAdminPassword) list-jobs")
+        # Verify that jenkins also sees the jobs as removed.
+        out = master.succeed("${pkgs.jenkins}/bin/jenkins-cli -s ${jenkinsUrl} -auth admin:$(cat /var/lib/jenkins/secrets/initialAdminPassword) list-jobs")
         jobs = [x.strip() for x in out.splitlines()]
         assert jobs == [], f"jobs != []: {jobs}"
   '';
diff --git a/nixos/tests/jitsi-meet.nix b/nixos/tests/jitsi-meet.nix
index 41d53bc73800e..c39cd19e1f0a7 100644
--- a/nixos/tests/jitsi-meet.nix
+++ b/nixos/tests/jitsi-meet.nix
@@ -34,9 +34,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     server.wait_for_unit("prosody.service")
 
     server.wait_until_succeeds(
-        "journalctl -b -u jitsi-videobridge2 -o cat | grep -q 'Performed a successful health check'"
-    )
-    server.wait_until_succeeds(
         "journalctl -b -u prosody -o cat | grep -q 'Authenticated as focus@auth.server'"
     )
     server.wait_until_succeeds(
diff --git a/nixos/tests/k3s-single-node-docker.nix b/nixos/tests/k3s-single-node-docker.nix
deleted file mode 100644
index 735aa5ac2975d..0000000000000
--- a/nixos/tests/k3s-single-node-docker.nix
+++ /dev/null
@@ -1,84 +0,0 @@
-import ./make-test-python.nix ({ pkgs, ... }:
-
-  let
-    imageEnv = pkgs.buildEnv {
-      name = "k3s-pause-image-env";
-      paths = with pkgs; [ tini (hiPrio coreutils) busybox ];
-    };
-    pauseImage = pkgs.dockerTools.streamLayeredImage {
-      name = "test.local/pause";
-      tag = "local";
-      contents = imageEnv;
-      config.Entrypoint = [ "/bin/tini" "--" "/bin/sleep" "inf" ];
-    };
-    # Don't use the default service account because there's a race where it may
-    # not be created yet; make our own instead.
-    testPodYaml = pkgs.writeText "test.yml" ''
-      apiVersion: v1
-      kind: ServiceAccount
-      metadata:
-        name: test
-      ---
-      apiVersion: v1
-      kind: Pod
-      metadata:
-        name: test
-      spec:
-        serviceAccountName: test
-        containers:
-        - name: test
-          image: test.local/pause:local
-          imagePullPolicy: Never
-          command: ["sh", "-c", "sleep inf"]
-    '';
-  in
-  {
-    name = "k3s";
-    meta = with pkgs.lib.maintainers; {
-      maintainers = [ euank ];
-    };
-
-    nodes.machine = { pkgs, ... }: {
-      environment.systemPackages = with pkgs; [ k3s gzip ];
-
-      # k3s uses enough resources the default vm fails.
-      virtualisation.memorySize = 1536;
-      virtualisation.diskSize = 4096;
-
-      services.k3s = {
-        enable = true;
-        role = "server";
-        docker = true;
-        # Slightly reduce resource usage
-        extraFlags = "--no-deploy coredns,servicelb,traefik,local-storage,metrics-server --pause-image test.local/pause:local";
-      };
-
-      users.users = {
-        noprivs = {
-          isNormalUser = true;
-          description = "Can't access k3s by default";
-          password = "*";
-        };
-      };
-    };
-
-    testScript = ''
-      start_all()
-
-      machine.wait_for_unit("k3s")
-      machine.succeed("k3s kubectl cluster-info")
-      machine.fail("sudo -u noprivs k3s kubectl cluster-info")
-      # FIXME: this fails with the current nixos kernel config; once it passes, we should uncomment it
-      # machine.succeed("k3s check-config")
-
-      machine.succeed(
-          "${pauseImage} | docker load"
-      )
-
-      machine.succeed("k3s kubectl apply -f ${testPodYaml}")
-      machine.succeed("k3s kubectl wait --for 'condition=Ready' pod/test")
-      machine.succeed("k3s kubectl delete -f ${testPodYaml}")
-
-      machine.shutdown()
-    '';
-  })
diff --git a/nixos/tests/kea.nix b/nixos/tests/kea.nix
index 6b345893108fe..b1d5894cc7cd2 100644
--- a/nixos/tests/kea.nix
+++ b/nixos/tests/kea.nix
@@ -1,6 +1,8 @@
 import ./make-test-python.nix ({ pkgs, lib, ...}: {
   meta.maintainers = with lib.maintainers; [ hexa ];
 
+  name = "kea";
+
   nodes = {
     router = { config, pkgs, ... }: {
       virtualisation.vlans = [ 1 ];
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 1e60198abdd04..04d52cf82e42b 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -30,7 +30,7 @@ let
       linux_5_4_hardened
       linux_5_10_hardened
       linux_5_15_hardened
-      linux_5_17_hardened
+      linux_5_18_hardened
 
       linux_testing;
   };
diff --git a/nixos/tests/kubernetes/dns.nix b/nixos/tests/kubernetes/dns.nix
index 3fd1dd31f746b..6299b7ff988af 100644
--- a/nixos/tests/kubernetes/dns.nix
+++ b/nixos/tests/kubernetes/dns.nix
@@ -33,7 +33,11 @@ let
   redisImage = pkgs.dockerTools.buildImage {
     name = "redis";
     tag = "latest";
-    contents = [ pkgs.redis pkgs.bind.host ];
+    copyToRoot = pkgs.buildEnv {
+      name = "image-root";
+      pathsToLink = [ "/bin" ];
+      paths = [ pkgs.redis pkgs.bind.host ];
+    };
     config.Entrypoint = ["/bin/redis-server"];
   };
 
@@ -54,7 +58,11 @@ let
   probeImage = pkgs.dockerTools.buildImage {
     name = "probe";
     tag = "latest";
-    contents = [ pkgs.bind.host pkgs.busybox ];
+    copyToRoot = pkgs.buildEnv {
+      name = "image-root";
+      pathsToLink = [ "/bin" ];
+      paths = [ pkgs.bind.host pkgs.busybox ];
+    };
     config.Entrypoint = ["/bin/tail"];
   };
 
diff --git a/nixos/tests/kubernetes/rbac.nix b/nixos/tests/kubernetes/rbac.nix
index 9e73fbbd32a8f..779eafbb1d24c 100644
--- a/nixos/tests/kubernetes/rbac.nix
+++ b/nixos/tests/kubernetes/rbac.nix
@@ -84,7 +84,11 @@ let
   kubectlImage = pkgs.dockerTools.buildImage {
     name = "kubectl";
     tag = "latest";
-    contents = [ copyKubectl pkgs.busybox kubectlPod2 ];
+    copyToRoot = pkgs.buildEnv {
+      name = "image-root";
+      pathsToLink = [ "/bin" ];
+      paths = [ copyKubectl pkgs.busybox kubectlPod2 ];
+    };
     config.Entrypoint = ["/bin/sh"];
   };
 
diff --git a/nixos/tests/libuiohook.nix b/nixos/tests/libuiohook.nix
new file mode 100644
index 0000000000000..66c5033d96886
--- /dev/null
+++ b/nixos/tests/libuiohook.nix
@@ -0,0 +1,21 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "libuiohook";
+  meta = with lib.maintainers; { maintainers = [ anoa ]; };
+
+  nodes.client = { nodes, ... }:
+      let user = nodes.client.config.users.users.alice;
+      in {
+        imports = [ ./common/user-account.nix ./common/x11.nix ];
+
+        environment.systemPackages = [ pkgs.libuiohook.test ];
+
+        test-support.displayManager.auto.user = user.name;
+      };
+
+  testScript = { nodes, ... }:
+    let user = nodes.client.config.users.users.alice;
+    in ''
+      client.wait_for_x()
+      client.succeed("su - alice -c ${pkgs.libuiohook.test}/share/uiohook_tests >&2 &")
+    '';
+})
diff --git a/nixos/tests/lighttpd.nix b/nixos/tests/lighttpd.nix
new file mode 100644
index 0000000000000..36e2745c55c15
--- /dev/null
+++ b/nixos/tests/lighttpd.nix
@@ -0,0 +1,21 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "lighttpd";
+  meta.maintainers = with lib.maintainers; [ bjornfor ];
+
+  nodes = {
+    server = {
+      services.lighttpd.enable = true;
+      services.lighttpd.document-root = pkgs.runCommand "document-root" {} ''
+        mkdir -p "$out"
+        echo "hello nixos test" > "$out/file.txt"
+      '';
+    };
+  };
+
+  testScript = ''
+    start_all()
+    server.wait_for_unit("lighttpd.service")
+    res = server.succeed("curl --fail http://localhost/file.txt")
+    assert "hello nixos test" in res, f"bad server response: '{res}'"
+  '';
+})
diff --git a/nixos/tests/maddy.nix b/nixos/tests/maddy.nix
index 581748c1fa59b..b9d0416482da1 100644
--- a/nixos/tests/maddy.nix
+++ b/nixos/tests/maddy.nix
@@ -49,7 +49,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     server.wait_for_open_port(143)
     server.wait_for_open_port(587)
 
-    server.succeed("echo test | maddyctl creds create postmaster@server")
+    server.succeed("maddyctl creds create --password test postmaster@server")
     server.succeed("maddyctl imap-acct create postmaster@server")
 
     client.succeed("send-testmail")
diff --git a/nixos/tests/matrix/appservice-irc.nix b/nixos/tests/matrix/appservice-irc.nix
index 7dd44da8305e0..78c53024ca6c4 100644
--- a/nixos/tests/matrix/appservice-irc.nix
+++ b/nixos/tests/matrix/appservice-irc.nix
@@ -193,6 +193,7 @@ import ../make-test-python.nix ({ pkgs, ... }:
 
     testScript = ''
       import pathlib
+      import os
 
       start_all()
 
@@ -206,7 +207,7 @@ import ../make-test-python.nix ({ pkgs, ... }:
       with subtest("copy the registration file"):
           appservice.copy_from_vm("/var/lib/matrix-appservice-irc/registration.yml")
           homeserver.copy_from_host(
-              pathlib.Path(os.environ.get("out", os.getcwd())) / "registration.yml", "/"
+              str(pathlib.Path(os.environ.get("out", os.getcwd())) / "registration.yml"), "/"
           )
           homeserver.succeed("chmod 444 /registration.yml")
 
diff --git a/nixos/tests/matrix/mjolnir.nix b/nixos/tests/matrix/mjolnir.nix
index 3864f0ff2bb6d..cb843e2e9e3e8 100644
--- a/nixos/tests/matrix/mjolnir.nix
+++ b/nixos/tests/matrix/mjolnir.nix
@@ -45,7 +45,6 @@ import ../make-test-python.nix (
             enable_registration = true;
             enable_registration_without_verification = true;
             registration_shared_secret = "supersecret-registration";
-            enable_registration_without_verification = true;
 
             listeners = [ {
               # The default but tls=false
diff --git a/nixos/tests/minecraft-server.nix b/nixos/tests/minecraft-server.nix
index dbe2cd6d56fe1..a51b36ff53084 100644
--- a/nixos/tests/minecraft-server.nix
+++ b/nixos/tests/minecraft-server.nix
@@ -33,5 +33,6 @@ in import ./make-test-python.nix ({ pkgs, ... }: {
     assert "${seed}" in server.succeed(
         "mcrcon -H localhost -P ${toString rcon-port} -p '${rcon-pass}' -c 'seed'"
     )
+    server.succeed("systemctl stop minecraft-server")
   '';
 })
diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix
index 1fe1229f24a4a..71b82b871270e 100644
--- a/nixos/tests/networking.nix
+++ b/nixos/tests/networking.nix
@@ -98,6 +98,7 @@ let
           useNetworkd = networkd;
           useDHCP = false;
           defaultGateway = "192.168.1.1";
+          defaultGateway6 = "fd00:1234:5678:1::1";
           interfaces.eth1.ipv4.addresses = mkOverride 0 [
             { address = "192.168.1.2"; prefixLength = 24; }
             { address = "192.168.1.3"; prefixLength = 32; }
@@ -139,6 +140,8 @@ let
           with subtest("Test default gateway"):
               router.wait_until_succeeds("ping -c 1 192.168.3.1")
               client.wait_until_succeeds("ping -c 1 192.168.3.1")
+              router.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1")
+              client.wait_until_succeeds("ping -c 1 fd00:1234:5678:3::1")
         '';
     };
     routeType = {
@@ -679,6 +682,46 @@ let
               client2.succeed("ip addr show dev vlan >&2")
         '';
     };
+    vlan-ping = let
+        baseIP = number: "10.10.10.${number}";
+        vlanIP = number: "10.1.1.${number}";
+        baseInterface = "eth1";
+        vlanInterface = "vlan42";
+        node = number: {pkgs, ... }: with pkgs.lib; {
+          virtualisation.vlans = [ 1 ];
+          networking = {
+            #useNetworkd = networkd;
+            useDHCP = false;
+            vlans.${vlanInterface} = { id = 42; interface = baseInterface; };
+            interfaces.${baseInterface}.ipv4.addresses = mkOverride 0 [{ address = baseIP number; prefixLength = 24; }];
+            interfaces.${vlanInterface}.ipv4.addresses = mkOverride 0 [{ address = vlanIP number; prefixLength = 24; }];
+          };
+        };
+
+        serverNodeNum = "1";
+        clientNodeNum = "2";
+
+    in {
+      name = "vlan-ping";
+      nodes.server = node serverNodeNum;
+      nodes.client = node clientNodeNum;
+      testScript = { ... }:
+        ''
+          start_all()
+
+          with subtest("Wait for networking to be configured"):
+              server.wait_for_unit("network.target")
+              client.wait_for_unit("network.target")
+
+          with subtest("Test ping on base interface in setup"):
+              client.succeed("ping -I ${baseInterface} -c 1 ${baseIP serverNodeNum}")
+              server.succeed("ping -I ${baseInterface} -c 1 ${baseIP clientNodeNum}")
+
+          with subtest("Test ping on vlan subinterface in setup"):
+              client.succeed("ping -I ${vlanInterface} -c 1 ${vlanIP serverNodeNum}")
+              server.succeed("ping -I ${vlanInterface} -c 1 ${vlanIP clientNodeNum}")
+        '';
+    };
     virtual = {
       name = "Virtual";
       nodes.machine = {
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
index 45165b04bf899..9e378fe6a52d3 100644
--- a/nixos/tests/nextcloud/default.nix
+++ b/nixos/tests/nextcloud/default.nix
@@ -16,6 +16,10 @@ foldl
       inherit system pkgs;
       nextcloudVersion = ver;
     };
+    "with-declarative-redis-and-secrets${toString ver}" = import ./with-declarative-redis-and-secrets.nix {
+      inherit system pkgs;
+      nextcloudVersion = ver;
+    };
   })
 { }
   [ 23 24 ]
diff --git a/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
new file mode 100644
index 0000000000000..fda05bacb4fe4
--- /dev/null
+++ b/nixos/tests/nextcloud/with-declarative-redis-and-secrets.nix
@@ -0,0 +1,118 @@
+import ../make-test-python.nix ({ pkgs, ...}: let
+  adminpass = "hunter2";
+  adminuser = "custom-admin-username";
+in {
+  name = "nextcloud-with-declarative-redis";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ eqyiel ];
+  };
+
+  nodes = {
+    # The only thing the client needs to do is download a file.
+    client = { ... }: {};
+
+    nextcloud = { config, pkgs, ... }: {
+      networking.firewall.allowedTCPPorts = [ 80 ];
+
+      services.nextcloud = {
+        enable = true;
+        hostName = "nextcloud";
+        caching = {
+          apcu = false;
+          redis = true;
+          memcached = false;
+        };
+        config = {
+          dbtype = "pgsql";
+          dbname = "nextcloud";
+          dbuser = "nextcloud";
+          dbhost = "/run/postgresql";
+          inherit adminuser;
+          adminpassFile = toString (pkgs.writeText "admin-pass-file" ''
+            ${adminpass}
+          '');
+        };
+        secretFile = "/etc/nextcloud-secrets.json";
+
+        extraOptions.redis = {
+          host = "/run/redis/redis.sock";
+          port = 0;
+          dbindex = 0;
+          timeout = 1.5;
+          # password handled via secretfile below
+        };
+        extraOptions.memcache = {
+          local = "\OC\Memcache\Redis";
+          locking = "\OC\Memcache\Redis";
+        };
+      };
+
+      services.redis = {
+        enable = true;
+      };
+
+      systemd.services.nextcloud-setup= {
+        requires = ["postgresql.service"];
+        after = [
+          "postgresql.service"
+        ];
+      };
+
+      services.postgresql = {
+        enable = true;
+        ensureDatabases = [ "nextcloud" ];
+        ensureUsers = [
+          { name = "nextcloud";
+            ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES";
+          }
+        ];
+      };
+
+      # This file is meant to contain secret options which should
+      # not go into the nix store. Here it is just used to set the
+      # databyse type to postgres.
+      environment.etc."nextcloud-secrets.json".text = ''
+        {
+          "redis": {
+            "password": "secret"
+          }
+        }
+      '';
+    };
+  };
+
+  testScript = let
+    withRcloneEnv = pkgs.writeScript "with-rclone-env" ''
+      #!${pkgs.runtimeShell}
+      export RCLONE_CONFIG_NEXTCLOUD_TYPE=webdav
+      export RCLONE_CONFIG_NEXTCLOUD_URL="http://nextcloud/remote.php/webdav/"
+      export RCLONE_CONFIG_NEXTCLOUD_VENDOR="nextcloud"
+      export RCLONE_CONFIG_NEXTCLOUD_USER="${adminuser}"
+      export RCLONE_CONFIG_NEXTCLOUD_PASS="$(${pkgs.rclone}/bin/rclone obscure ${adminpass})"
+      "''${@}"
+    '';
+    copySharedFile = pkgs.writeScript "copy-shared-file" ''
+      #!${pkgs.runtimeShell}
+      echo 'hi' | ${pkgs.rclone}/bin/rclone rcat nextcloud:test-shared-file
+    '';
+
+    diffSharedFile = pkgs.writeScript "diff-shared-file" ''
+      #!${pkgs.runtimeShell}
+      diff <(echo 'hi') <(${pkgs.rclone}/bin/rclone cat nextcloud:test-shared-file)
+    '';
+  in ''
+    start_all()
+    nextcloud.wait_for_unit("multi-user.target")
+    nextcloud.succeed("curl -sSf http://nextcloud/login")
+    nextcloud.succeed(
+        "${withRcloneEnv} ${copySharedFile}"
+    )
+    client.wait_for_unit("multi-user.target")
+    client.succeed(
+        "${withRcloneEnv} ${diffSharedFile}"
+    )
+
+    # redis cache should not be empty
+    nextcloud.fail("redis-cli KEYS * | grep -q 'empty array'")
+  '';
+})
diff --git a/nixos/tests/nginx-http3.nix b/nixos/tests/nginx-http3.nix
index edd0759464c8a..319f6aac184ab 100644
--- a/nixos/tests/nginx-http3.nix
+++ b/nixos/tests/nginx-http3.nix
@@ -70,6 +70,9 @@ in
   testScript = ''
     start_all()
 
+    server.wait_for_unit("nginx")
+    server.wait_for_open_port(443)
+
     # Check http connections
     client.succeed("curl --verbose --http3 https://acme.test | grep 'Hello World!'")
 
diff --git a/nixos/tests/non-default-filesystems.nix b/nixos/tests/non-default-filesystems.nix
new file mode 100644
index 0000000000000..7fa75aaad724d
--- /dev/null
+++ b/nixos/tests/non-default-filesystems.nix
@@ -0,0 +1,54 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+{
+  name = "non-default-filesystems";
+
+  nodes.machine =
+    { config, pkgs, lib, ... }:
+    let
+      disk = config.virtualisation.bootDevice;
+    in
+    {
+      virtualisation.useDefaultFilesystems = false;
+
+      boot.initrd.availableKernelModules = [ "btrfs" ];
+      boot.supportedFilesystems = [ "btrfs" ];
+
+      boot.initrd.postDeviceCommands = ''
+        FSTYPE=$(blkid -o value -s TYPE ${disk} || true)
+        if test -z "$FSTYPE"; then
+          modprobe btrfs
+          ${pkgs.btrfs-progs}/bin/mkfs.btrfs ${disk}
+
+          mkdir /nixos
+          mount -t btrfs ${disk} /nixos
+
+          ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root
+          ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home
+
+          umount /nixos
+        fi
+      '';
+
+      virtualisation.fileSystems = {
+        "/" = {
+          device = disk;
+          fsType = "btrfs";
+          options = [ "subvol=/root" ];
+        };
+
+        "/home" = {
+          device = disk;
+          fsType = "btrfs";
+          options = [ "subvol=/home" ];
+        };
+      };
+    };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+
+    with subtest("BTRFS filesystems are mounted correctly"):
+      machine.succeed("grep -E '/dev/vda / btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/root 0 0' /proc/mounts")
+      machine.succeed("grep -E '/dev/vda /home btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/home 0 0' /proc/mounts")
+  '';
+})
diff --git a/nixos/tests/pam/pam-u2f.nix b/nixos/tests/pam/pam-u2f.nix
index d7c540982cfa0..07408dea797e4 100644
--- a/nixos/tests/pam/pam-u2f.nix
+++ b/nixos/tests/pam/pam-u2f.nix
@@ -12,6 +12,7 @@ import ../make-test-python.nix ({ ... }:
         debug = true;
         enable = true;
         interactive = true;
+        origin = "nixos-test";
       };
     };
 
@@ -19,7 +20,7 @@ import ../make-test-python.nix ({ ... }:
     ''
       machine.wait_for_unit("multi-user.target")
       machine.succeed(
-          'egrep "auth required .*/lib/security/pam_u2f.so.*debug.*interactive.*cue" /etc/pam.d/ -R'
+          'egrep "auth required .*/lib/security/pam_u2f.so.*debug.*interactive.*cue.*origin=nixos-test" /etc/pam.d/ -R'
       )
     '';
 })
diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix
index 51fe7c2078514..12883cd62c60c 100644
--- a/nixos/tests/paperless.nix
+++ b/nixos/tests/paperless.nix
@@ -1,6 +1,6 @@
 import ./make-test-python.nix ({ lib, ... }: {
   name = "paperless";
-  meta.maintainers = with lib.maintainers; [ earvstedt Flakebi ];
+  meta.maintainers = with lib.maintainers; [ erikarvstedt Flakebi ];
 
   nodes.machine = { pkgs, ... }: {
     environment.systemPackages = with pkgs; [ imagemagick jq ];
diff --git a/nixos/tests/pass-secret-service.nix b/nixos/tests/pass-secret-service.nix
new file mode 100644
index 0000000000000..a85a508bfe16b
--- /dev/null
+++ b/nixos/tests/pass-secret-service.nix
@@ -0,0 +1,69 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "pass-secret-service";
+  meta.maintainers = with lib; [ aidalgol ];
+
+  nodes.machine = { nodes, pkgs, ... }:
+    {
+      imports = [ ./common/user-account.nix ];
+
+      services.passSecretService.enable = true;
+
+      environment.systemPackages = [
+        # Create a script that tries to make a request to the D-Bus secrets API.
+        (pkgs.writers.writePython3Bin "secrets-dbus-init"
+          {
+            libraries = [ pkgs.python3Packages.secretstorage ];
+          } ''
+          import secretstorage
+          print("Initializing dbus connection...")
+          connection = secretstorage.dbus_init()
+          print("Requesting default collection...")
+          collection = secretstorage.get_default_collection(connection)
+          print("Done!  dbus-org.freedesktop.secrets should now be active.")
+        '')
+        pkgs.pass
+      ];
+
+      programs.gnupg = {
+        agent.enable = true;
+        agent.pinentryFlavor = "tty";
+        dirmngr.enable = true;
+      };
+    };
+
+  # Some of the commands are run via a virtual console because they need to be
+  # run under a real login session, with D-Bus running in the environment.
+  testScript = { nodes, ... }:
+    let
+      user = nodes.machine.config.users.users.alice;
+      gpg-uid = "alice@example.net";
+      gpg-pw = "foobar9000";
+      ready-file = "/tmp/secrets-dbus-init.done";
+    in
+    ''
+      # Initialise the pass(1) storage.
+      machine.succeed("""
+        sudo -u alice gpg --pinentry-mode loopback --batch --passphrase ${gpg-pw} \
+        --quick-gen-key ${gpg-uid} \
+      """)
+      machine.succeed("sudo -u alice pass init ${gpg-uid}")
+
+      with subtest("Service is not running on login"):
+          machine.wait_until_tty_matches("1", "login: ")
+          machine.send_chars("alice\n")
+          machine.wait_until_tty_matches("1", "login: alice")
+          machine.wait_until_succeeds("pgrep login")
+          machine.wait_until_tty_matches("1", "Password: ")
+          machine.send_chars("${user.password}\n")
+          machine.wait_until_succeeds("pgrep -u alice bash")
+
+          _, output = machine.systemctl("status dbus-org.freedesktop.secrets --no-pager", "alice")
+          assert "Active: inactive (dead)" in output
+
+      with subtest("Service starts after a client tries to talk to the D-Bus API"):
+          machine.send_chars("secrets-dbus-init; touch ${ready-file}\n")
+          machine.wait_for_file("${ready-file}")
+          _, output = machine.systemctl("status dbus-org.freedesktop.secrets --no-pager", "alice")
+          assert "Active: active (running)" in output
+    '';
+})
diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix
index b30299d307eb9..9f5ac3d8d9229 100644
--- a/nixos/tests/pgadmin4.nix
+++ b/nixos/tests/pgadmin4.nix
@@ -107,11 +107,14 @@ import ./make-test-python.nix ({ pkgs, lib, buildDeps ? [ ], pythonEnv ? [ ], ..
       )
 
       # don't bother to test LDAP authentification
+      # exclude resql test due to recent postgres 14.4 update
+      # see bugreport here https://redmine.postgresql.org/issues/7527
       with subtest("run browser test"):
           machine.succeed(
                'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \
-               && python regression/runtests.py --pkg browser --exclude \
-               browser.tests.test_ldap_login.LDAPLoginTestCase,browser.tests.test_ldap_login'
+               && python regression/runtests.py \
+               --pkg browser \
+               --exclude browser.tests.test_ldap_login.LDAPLoginTestCase,browser.tests.test_ldap_login,resql'
           )
 
       # fontconfig is necessary for chromium to run
@@ -123,10 +126,11 @@ import ./make-test-python.nix ({ pkgs, lib, buildDeps ? [ ], pythonEnv ? [ ], ..
                && python regression/runtests.py --pkg feature_tests'
           )
 
-      with subtest("run resql test"):
-          machine.succeed(
-               'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \
-               && python regression/runtests.py --pkg resql'
-          )
+      # reactivate this test again, when the postgres 14.4 test has been fixed
+      # with subtest("run resql test"):
+      #    machine.succeed(
+      #         'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \
+      #         && python regression/runtests.py --pkg resql'
+      #    )
     '';
   })
diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix
index 3358a72570e87..b3836cf641d4b 100644
--- a/nixos/tests/plasma5.nix
+++ b/nixos/tests/plasma5.nix
@@ -13,7 +13,10 @@ import ./make-test-python.nix ({ pkgs, ...} :
     services.xserver.enable = true;
     services.xserver.displayManager.sddm.enable = true;
     services.xserver.displayManager.defaultSession = "plasma";
-    services.xserver.desktopManager.plasma5.enable = true;
+    services.xserver.desktopManager.plasma5 = {
+      enable = true;
+      excludePackages = [ pkgs.plasma5Packages.elisa ];
+    };
     services.xserver.displayManager.autoLogin = {
       enable = true;
       user = "alice";
@@ -40,6 +43,9 @@ import ./make-test-python.nix ({ pkgs, ...} :
     with subtest("Check that logging in has given the user ownership of devices"):
         machine.succeed("getfacl -p /dev/snd/timer | grep -q ${user.name}")
 
+    with subtest("Ensure Elisa is not installed"):
+        machine.fail("which elisa")
+
     with subtest("Run Dolphin"):
         machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 dolphin >&2 &'")
         machine.wait_for_window(" Dolphin")
diff --git a/nixos/tests/podgrab.nix b/nixos/tests/podgrab.nix
index e5a340dc2ac83..dc9dfebaf49bd 100644
--- a/nixos/tests/podgrab.nix
+++ b/nixos/tests/podgrab.nix
@@ -22,11 +22,11 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     start_all()
 
     default.wait_for_unit("podgrab")
-    default.wait_for_open_port(defaultPort)
+    default.wait_for_open_port(${toString defaultPort})
     default.succeed("curl --fail http://localhost:${toString defaultPort}")
 
     customized.wait_for_unit("podgrab")
-    customized.wait_for_open_port(customPort)
+    customized.wait_for_open_port(${toString customPort})
     customized.succeed("curl --fail http://localhost:${toString customPort}")
   '';
 
diff --git a/nixos/tests/polaris.nix b/nixos/tests/polaris.nix
new file mode 100644
index 0000000000000..fb2e67f075aac
--- /dev/null
+++ b/nixos/tests/polaris.nix
@@ -0,0 +1,31 @@
+import ./make-test-python.nix ({ lib, ... }:
+
+with lib;
+
+{
+  name = "polaris";
+  meta.maintainers = with maintainers; [ pbsds ];
+
+  nodes.machine =
+    { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+      services.polaris = {
+        enable = true;
+        port = 5050;
+        settings.users = [
+          {
+            name = "test_user";
+            password = "very_secret_password";
+            admin = true;
+          }
+        ];
+      };
+    };
+
+  testScript = ''
+    machine.wait_for_unit("polaris.service")
+    machine.wait_for_open_port(5050)
+    machine.succeed("curl http://localhost:5050/api/version")
+    machine.succeed("curl -X GET http://localhost:5050/api/initial_setup -H  'accept: application/json' | jq -e '.has_any_users == true'")
+  '';
+})
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 849f5ee159f0c..0a1ec824986ab 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -557,10 +557,12 @@ let
         systemd.services.prometheus-mail-exporter = {
           after = [ "postfix.service" ];
           requires = [ "postfix.service" ];
-          preStart = ''
-            mkdir -p -m 0700 mail-exporter/new
-          '';
           serviceConfig = {
+            ExecStartPre = [
+              "${pkgs.writeShellScript "create-maildir" ''
+                mkdir -p -m 0700 mail-exporter/new
+              ''}"
+            ];
             ProtectHome = true;
             ReadOnlyPaths = "/";
             ReadWritePaths = "/var/spool/mail";
@@ -1335,7 +1337,7 @@ mapAttrs
       '';
 
       meta = with maintainers; {
-        maintainers = [ willibutz elseym ];
+        maintainers = [ willibutz ];
       };
     }
   )))
diff --git a/nixos/tests/pykms.nix b/nixos/tests/pykms.nix
new file mode 100644
index 0000000000000..14d776a2f113e
--- /dev/null
+++ b/nixos/tests/pykms.nix
@@ -0,0 +1,14 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+  {
+    name = "pykms-test";
+    meta.maintainers = with pkgs.lib.maintainers; [ zopieux ];
+
+    nodes.machine = { config, lib, pkgs, ... }: {
+      services.pykms.enable = true;
+    };
+
+    testScript = ''
+      machine.wait_for_unit("pykms.service")
+      machine.succeed("${pkgs.pykms}/bin/client")
+    '';
+  })
diff --git a/nixos/tests/restic.nix b/nixos/tests/restic.nix
index 7523d5e5ed5da..75fffe9d9a84d 100644
--- a/nixos/tests/restic.nix
+++ b/nixos/tests/restic.nix
@@ -63,6 +63,12 @@ import ./make-test-python.nix (
               inherit repository passwordFile;
               pruneOpts = [ "--keep-last 1" ];
             };
+            custompackage = {
+              inherit repository passwordFile paths;
+              package = pkgs.writeShellScriptBin "restic" ''
+                echo "$@" >> /tmp/fake-restic.log;
+              '';
+            };
           };
 
           environment.sessionVariables.RCLONE_CONFIG_LOCAL_TYPE = "local";
@@ -76,6 +82,7 @@ import ./make-test-python.nix (
           "${pkgs.restic}/bin/restic -r ${repository} -p ${passwordFile} snapshots",
           '${pkgs.restic}/bin/restic --repository-file ${repositoryFile} -p ${passwordFile} snapshots"',
           "${pkgs.restic}/bin/restic -r ${rcloneRepository} -p ${passwordFile} snapshots",
+          "grep 'backup .* /opt' /tmp/fake-restic.log",
       )
       server.succeed(
           "mkdir -p /opt",
@@ -89,6 +96,8 @@ import ./make-test-python.nix (
           '${pkgs.restic}/bin/restic -r ${repository} -p ${passwordFile} snapshots -c | grep -e "^1 snapshot"',
           '${pkgs.restic}/bin/restic --repository-file ${repositoryFile} -p ${passwordFile} snapshots -c | grep -e "^1 snapshot"',
           '${pkgs.restic}/bin/restic -r ${rcloneRepository} -p ${passwordFile} snapshots -c | grep -e "^1 snapshot"',
+          "systemctl start restic-backups-custompackage.service",
+          "grep 'backup .* /opt' /tmp/fake-restic.log",
           "timedatectl set-time '2017-12-13 13:45'",
           "systemctl start restic-backups-remotebackup.service",
           "rm /opt/backupCleanupCommand",
diff --git a/nixos/tests/riak.nix b/nixos/tests/riak.nix
deleted file mode 100644
index e75d40fa25695..0000000000000
--- a/nixos/tests/riak.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-import ./make-test-python.nix ({ lib, pkgs, ... }: {
-  name = "riak";
-  meta = with lib.maintainers; {
-    maintainers = [ Br1ght0ne ];
-  };
-
-  nodes.machine = {
-    services.riak.enable = true;
-    services.riak.package = pkgs.riak;
-  };
-
-  testScript = ''
-    machine.start()
-
-    machine.wait_for_unit("riak")
-    machine.wait_until_succeeds("riak ping 2>&1")
-  '';
-})
diff --git a/nixos/tests/schleuder.nix b/nixos/tests/schleuder.nix
new file mode 100644
index 0000000000000..a9e4cc325bc76
--- /dev/null
+++ b/nixos/tests/schleuder.nix
@@ -0,0 +1,128 @@
+let
+  certs = import ./common/acme/server/snakeoil-certs.nix;
+  domain = certs.domain;
+in
+import ./make-test-python.nix {
+  name = "schleuder";
+  nodes.machine = { pkgs, ... }: {
+    imports = [ ./common/user-account.nix ];
+    services.postfix = {
+      enable = true;
+      enableSubmission = true;
+      tlsTrustedAuthorities = "${certs.ca.cert}";
+      sslCert = "${certs.${domain}.cert}";
+      sslKey = "${certs.${domain}.key}";
+      inherit domain;
+      destination = [ domain ];
+      localRecipients = [ "root" "alice" "bob" ];
+    };
+    services.schleuder = {
+      enable = true;
+      # Don't do it like this in production! The point of this setting
+      # is to allow loading secrets from _outside_ the world-readable
+      # Nix store.
+      extraSettingsFile = pkgs.writeText "schleuder-api-keys.yml" ''
+        api:
+          valid_api_keys:
+            - fnord
+      '';
+      lists = [ "security@${domain}" ];
+      settings.api = {
+        tls_cert_file = "${certs.${domain}.cert}";
+        tls_key_file = "${certs.${domain}.key}";
+      };
+    };
+
+    environment.systemPackages = [
+      pkgs.gnupg
+      pkgs.msmtp
+      (pkgs.writeScriptBin "do-test" ''
+        #!${pkgs.runtimeShell}
+        set -exuo pipefail
+
+        # Generate a GPG key with no passphrase and export it
+        sudo -u alice gpg --passphrase-fd 0 --batch --yes --quick-generate-key 'alice@${domain}' rsa4096 sign,encr < <(echo)
+        sudo -u alice gpg --armor --export alice@${domain} > alice.asc
+        # Create a new mailing list with alice as the owner, and alice's key
+        schleuder-cli list new security@${domain} alice@${domain} alice.asc
+
+        # Send an email from a non-member of the list. Use --auto-from so we don't have to specify who it's from twice.
+        msmtp --auto-from security@${domain} --host=${domain} --port=25 --tls --tls-starttls <<EOF
+          Subject: really big security issue!!
+          From: root@${domain}
+
+          I found a big security problem!
+        EOF
+
+        # Wait for delivery
+        (set +o pipefail; journalctl -f -n 1000 -u postfix | grep -m 1 'delivered to maildir')
+
+        # There should be exactly one email
+        mail=(/var/spool/mail/alice/new/*)
+        [[ "''${#mail[@]}" = 1 ]]
+
+        # Find the fingerprint of the mailing list key
+        read list_key_fp address < <(schleuder-cli keys list security@${domain} | grep security@)
+        schleuder-cli keys export security@${domain} $list_key_fp > list.asc
+
+        # Import the key into alice's keyring, so we can verify it as well as decrypting
+        sudo -u alice gpg --import <list.asc
+        # And perform the decryption.
+        sudo -u alice gpg -d $mail >decrypted
+        # And check that the text matches.
+        grep "big security problem" decrypted
+      '')
+
+      # For debugging:
+      # pkgs.vim pkgs.openssl pkgs.sqliteinteractive
+    ];
+
+    security.pki.certificateFiles = [ certs.ca.cert ];
+
+    # Since we don't have internet here, use dnsmasq to provide MX records from /etc/hosts
+    services.dnsmasq = {
+      enable = true;
+      extraConfig = ''
+        selfmx
+      '';
+    };
+
+    networking.extraHosts = ''
+      127.0.0.1 ${domain}
+    '';
+
+    # schleuder-cli's config is not quite optimal in several ways:
+    # - A fingerprint _must_ be pinned, it doesn't even have an option
+    #   to trust the PKI
+    # - It compares certificate fingerprints rather than key
+    #   fingerprints, so renewals break the pin (though that's not
+    #   relevant for this test)
+    # - It compares them as strings, which means we need to match the
+    #   expected format exactly. This means removing the :s and
+    #   lowercasing it.
+    # Refs:
+    # https://0xacab.org/schleuder/schleuder-cli/-/issues/16
+    # https://0xacab.org/schleuder/schleuder-cli/-/blob/f8895b9f47083d8c7b99a2797c93f170f3c6a3c0/lib/schleuder-cli/helper.rb#L230-238
+    systemd.tmpfiles.rules = let cliconfig = pkgs.runCommand "schleuder-cli.yml"
+      {
+        nativeBuildInputs = [ pkgs.jq pkgs.openssl ];
+      } ''
+      fp=$(openssl x509 -in ${certs.${domain}.cert} -noout -fingerprint -sha256 | cut -d = -f 2 | tr -d : | tr 'A-Z' 'a-z')
+      cat > $out <<EOF
+      host: localhost
+      port: 4443
+      tls_fingerprint: "$fp"
+      api_key: fnord
+      EOF
+    ''; in
+      [
+        "L+ /root/.schleuder-cli/schleuder-cli.yml - - - - ${cliconfig}"
+      ];
+  };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+    machine.wait_until_succeeds("nc -z localhost 4443")
+    machine.succeed("do-test")
+  '';
+}
diff --git a/nixos/tests/signal-desktop.nix b/nixos/tests/signal-desktop.nix
index fbe9cdf84d05a..5e2b648c7cf59 100644
--- a/nixos/tests/signal-desktop.nix
+++ b/nixos/tests/signal-desktop.nix
@@ -60,7 +60,7 @@ in {
     )
     # Only SQLCipher should be able to read the encrypted DB:
     machine.fail(
-        "su - alice -c 'sqlite3 ~/.config/Signal/sql/db.sqlite .databases'"
+        "su - alice -c 'sqlite3 ~/.config/Signal/sql/db.sqlite .tables'"
     )
     print(machine.succeed(
         "su - alice -c 'sqlcipher ~/.config/Signal/sql/db.sqlite'"
diff --git a/nixos/tests/stunnel.nix b/nixos/tests/stunnel.nix
new file mode 100644
index 0000000000000..22c087290fc7b
--- /dev/null
+++ b/nixos/tests/stunnel.nix
@@ -0,0 +1,174 @@
+{ system ? builtins.currentSystem, config ? { }
+, pkgs ? import ../.. { inherit system config; } }:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+with pkgs.lib;
+
+let
+  stunnelCommon = {
+    services.stunnel = {
+      enable = true;
+      user = "stunnel";
+    };
+    users.groups.stunnel = { };
+    users.users.stunnel = {
+      isSystemUser = true;
+      group = "stunnel";
+    };
+  };
+  makeCert = { config, pkgs, ... }: {
+    system.activationScripts.create-test-cert = stringAfter [ "users" ] ''
+      ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName}
+      ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem )
+      chown stunnel /test-key.pem /test-key-and-cert.pem
+    '';
+  };
+  serverCommon = { pkgs, ... }: {
+    networking.firewall.allowedTCPPorts = [ 443 ];
+    services.stunnel.servers.https = {
+      accept = "443";
+      connect = 80;
+      cert = "/test-key-and-cert.pem";
+    };
+    systemd.services.simple-webserver = {
+      wantedBy = [ "multi-user.target" ];
+      script = ''
+        cd /etc/webroot
+        ${pkgs.python3}/bin/python -m http.server 80
+      '';
+    };
+  };
+  copyCert = src: dest: filename: ''
+    from shlex import quote
+    ${src}.wait_for_file("/test-key-and-cert.pem")
+    server_cert = ${src}.succeed("cat /test-cert.pem")
+    ${dest}.succeed("echo %s > ${filename}" % quote(server_cert))
+  '';
+
+in {
+  basicServer = makeTest {
+    name = "basicServer";
+
+    nodes = {
+      client = { };
+      server = {
+        imports = [ makeCert serverCommon stunnelCommon ];
+        environment.etc."webroot/index.html".text = "well met";
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      ${copyCert "server" "client" "/authorized-server-cert.crt"}
+
+      server.wait_for_unit("simple-webserver")
+      server.wait_for_unit("stunnel")
+
+      client.succeed("curl --fail --cacert /authorized-server-cert.crt https://server/ > out")
+      client.succeed('[[ "$(< out)" == "well met" ]]')
+    '';
+  };
+
+  serverAndClient = makeTest {
+    name = "serverAndClient";
+
+    nodes = {
+      client = {
+        imports = [ stunnelCommon ];
+        services.stunnel.clients = {
+          httpsClient = {
+            accept = "80";
+            connect = "server:443";
+            CAFile = "/authorized-server-cert.crt";
+          };
+          httpsClientWithHostVerify = {
+            accept = "81";
+            connect = "server:443";
+            CAFile = "/authorized-server-cert.crt";
+            verifyHostname = "server";
+          };
+          httpsClientWithHostVerifyFail = {
+            accept = "82";
+            connect = "server:443";
+            CAFile = "/authorized-server-cert.crt";
+            verifyHostname = "wronghostname";
+          };
+        };
+      };
+      server = {
+        imports = [ makeCert serverCommon stunnelCommon ];
+        environment.etc."webroot/index.html".text = "hello there";
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      ${copyCert "server" "client" "/authorized-server-cert.crt"}
+
+      server.wait_for_unit("simple-webserver")
+      server.wait_for_unit("stunnel")
+
+      # In case stunnel came up before we got the server's cert copied over
+      client.succeed("systemctl reload-or-restart stunnel")
+
+      client.succeed("curl --fail http://localhost/ > out")
+      client.succeed('[[ "$(< out)" == "hello there" ]]')
+
+      client.succeed("curl --fail http://localhost:81/ > out")
+      client.succeed('[[ "$(< out)" == "hello there" ]]')
+
+      client.fail("curl --fail http://localhost:82/ > out")
+      client.succeed('[[ "$(< out)" == "" ]]')
+    '';
+  };
+
+  mutualAuth = makeTest {
+    name = "mutualAuth";
+
+    nodes = rec {
+      client = {
+        imports = [ makeCert stunnelCommon ];
+        services.stunnel.clients.authenticated-https = {
+          accept = "80";
+          connect = "server:443";
+          verifyPeer = true;
+          CAFile = "/authorized-server-cert.crt";
+          cert = "/test-cert.pem";
+          key = "/test-key.pem";
+        };
+      };
+      wrongclient = client;
+      server = {
+        imports = [ makeCert serverCommon stunnelCommon ];
+        services.stunnel.servers.https = {
+          CAFile = "/authorized-client-certs.crt";
+          verifyPeer = true;
+        };
+        environment.etc."webroot/index.html".text = "secret handshake";
+      };
+    };
+
+    testScript = ''
+      start_all()
+
+      ${copyCert "server" "client" "/authorized-server-cert.crt"}
+      ${copyCert "client" "server" "/authorized-client-certs.crt"}
+      ${copyCert "server" "wrongclient" "/authorized-server-cert.crt"}
+
+      # In case stunnel came up before we got the cross-certs in place
+      client.succeed("systemctl reload-or-restart stunnel")
+      server.succeed("systemctl reload-or-restart stunnel")
+      wrongclient.succeed("systemctl reload-or-restart stunnel")
+
+      server.wait_for_unit("simple-webserver")
+      client.fail("curl --fail --insecure https://server/ > out")
+      client.succeed('[[ "$(< out)" == "" ]]')
+      client.succeed("curl --fail http://localhost/ > out")
+      client.succeed('[[ "$(< out)" == "secret handshake" ]]')
+      wrongclient.fail("curl --fail http://localhost/ > out")
+      wrongclient.succeed('[[ "$(< out)" == "" ]]')
+    '';
+  };
+}
diff --git a/nixos/tests/swap-partition.nix b/nixos/tests/swap-partition.nix
new file mode 100644
index 0000000000000..2279630b57b8f
--- /dev/null
+++ b/nixos/tests/swap-partition.nix
@@ -0,0 +1,48 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }:
+{
+  name = "swap-partition";
+
+  nodes.machine =
+    { config, pkgs, lib, ... }:
+    {
+      virtualisation.useDefaultFilesystems = false;
+
+      virtualisation.bootDevice = "/dev/vda1";
+
+      boot.initrd.postDeviceCommands = ''
+        if ! test -b /dev/vda1; then
+          ${pkgs.parted}/bin/parted --script /dev/vda -- mklabel msdos
+          ${pkgs.parted}/bin/parted --script /dev/vda -- mkpart primary 1MiB -250MiB
+          ${pkgs.parted}/bin/parted --script /dev/vda -- mkpart primary -250MiB 100%
+          sync
+        fi
+
+        FSTYPE=$(blkid -o value -s TYPE /dev/vda1 || true)
+        if test -z "$FSTYPE"; then
+          ${pkgs.e2fsprogs}/bin/mke2fs -t ext4 -L root /dev/vda1
+          ${pkgs.util-linux}/bin/mkswap --label swap /dev/vda2
+        fi
+      '';
+
+      virtualisation.fileSystems = {
+        "/" = {
+          device = "/dev/disk/by-label/root";
+          fsType = "ext4";
+        };
+      };
+
+      swapDevices = [
+        {
+          device = "/dev/disk/by-label/swap";
+        }
+      ];
+    };
+
+  testScript = ''
+    machine.wait_for_unit("multi-user.target")
+
+    with subtest("Swap is active"):
+      # Doesn't matter if the numbers reported by `free` are slightly off due to unit conversions.
+      machine.succeed("free -h | grep -E 'Swap:\s+2[45][0-9]Mi'")
+  '';
+})
diff --git a/nixos/tests/systemd-networkd-vrf.nix b/nixos/tests/systemd-networkd-vrf.nix
index 8a1580fc2ada7..3c18f788e927d 100644
--- a/nixos/tests/systemd-networkd-vrf.nix
+++ b/nixos/tests/systemd-networkd-vrf.nix
@@ -138,18 +138,18 @@ in {
   };
 
   testScript = ''
-    def compare_tables(expected, actual):
-        assert (
-            expected == actual
-        ), """
-        Routing tables don't match!
-        Expected:
-          {}
-        Actual:
-          {}
-        """.format(
-            expected, actual
-        )
+    import json
+
+    def compare(raw_json, to_compare):
+        data = json.loads(raw_json)
+        assert len(raw_json) >= len(to_compare)
+        for i, row in enumerate(to_compare):
+            actual = data[i]
+            assert len(row.keys()) > 0
+            for key, value in row.items():
+                assert value == actual[key], f"""
+                  In entry {i}, value {key}: got: {actual[key]}, expected {value}
+                """
 
 
     start_all()
@@ -159,23 +159,18 @@ in {
     node2.wait_for_unit("network.target")
     node3.wait_for_unit("network.target")
 
-    # NOTE: please keep in mind that the trailing whitespaces in the following strings
-    # are intentional as the output is compared against the raw `iproute2`-output.
-    # editorconfig-checker-disable
     client_ipv4_table = """
-    192.168.1.2 dev vrf1 proto static metric 100 
+    192.168.1.2 dev vrf1 proto static metric 100\x20
     192.168.2.3 dev vrf2 proto static metric 100
     """.strip()
     vrf1_table = """
-    broadcast 192.168.1.0 dev eth1 proto kernel scope link src 192.168.1.1 
-    192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1 
-    local 192.168.1.1 dev eth1 proto kernel scope host src 192.168.1.1 
+    192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.1\x20
+    local 192.168.1.1 dev eth1 proto kernel scope host src 192.168.1.1\x20
     broadcast 192.168.1.255 dev eth1 proto kernel scope link src 192.168.1.1
     """.strip()
     vrf2_table = """
-    broadcast 192.168.2.0 dev eth2 proto kernel scope link src 192.168.2.1 
-    192.168.2.0/24 dev eth2 proto kernel scope link src 192.168.2.1 
-    local 192.168.2.1 dev eth2 proto kernel scope host src 192.168.2.1 
+    192.168.2.0/24 dev eth2 proto kernel scope link src 192.168.2.1\x20
+    local 192.168.2.1 dev eth2 proto kernel scope host src 192.168.2.1\x20
     broadcast 192.168.2.255 dev eth2 proto kernel scope link src 192.168.2.1
     """.strip()
     # editorconfig-checker-enable
@@ -183,14 +178,28 @@ in {
     # Check that networkd properly configures the main routing table
     # and the routing tables for the VRF.
     with subtest("check vrf routing tables"):
-        compare_tables(
-            client_ipv4_table, client.succeed("ip -4 route list | head -n2").strip()
+        compare(
+            client.succeed("ip --json -4 route list"),
+            [
+                {"dst": "192.168.1.2", "dev": "vrf1", "metric": 100},
+                {"dst": "192.168.2.3", "dev": "vrf2", "metric": 100}
+            ]
         )
-        compare_tables(
-            vrf1_table, client.succeed("ip -4 route list table 23 | head -n4").strip()
+        compare(
+            client.succeed("ip --json -4 route list table 23"),
+            [
+                {"dst": "192.168.1.0/24", "dev": "eth1", "prefsrc": "192.168.1.1"},
+                {"type": "local", "dst": "192.168.1.1", "dev": "eth1", "prefsrc": "192.168.1.1"},
+                {"type": "broadcast", "dev": "eth1", "prefsrc": "192.168.1.1", "dst": "192.168.1.255"}
+            ]
         )
-        compare_tables(
-            vrf2_table, client.succeed("ip -4 route list table 42 | head -n4").strip()
+        compare(
+            client.succeed("ip --json -4 route list table 42"),
+            [
+                {"dst": "192.168.2.0/24", "dev": "eth2", "prefsrc": "192.168.2.1"},
+                {"type": "local", "dst": "192.168.2.1", "dev": "eth2", "prefsrc": "192.168.2.1"},
+                {"type": "broadcast", "dev": "eth2", "prefsrc": "192.168.2.1", "dst": "192.168.2.255"}
+            ]
         )
 
     # Ensure that other nodes are reachable via ICMP through the VRF.
diff --git a/nixos/tests/terminal-emulators.nix b/nixos/tests/terminal-emulators.nix
index 6ea0f1c18725e..c724608b91554 100644
--- a/nixos/tests/terminal-emulators.nix
+++ b/nixos/tests/terminal-emulators.nix
@@ -197,6 +197,7 @@ in mapAttrs (name: { pkg, executable ? name, cmd ? "SHELL=$command ${executable}
 
     with subtest("have the terminal display a colour"):
         # We run this command in the background
+        assert machine.shell is not None
         machine.shell.send(b"(run-in-this-term display-colour |& systemd-cat -t terminal) &\n")
 
         with machine.nested("Waiting for the screen to have pink on it:"):
diff --git a/nixos/tests/traefik.nix b/nixos/tests/traefik.nix
index 1d6c0a479ef62..989ec390c0603 100644
--- a/nixos/tests/traefik.nix
+++ b/nixos/tests/traefik.nix
@@ -11,14 +11,20 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       environment.systemPackages = [ pkgs.curl ];
     };
     traefik = { config, pkgs, ... }: {
-      virtualisation.oci-containers.containers.nginx = {
-        extraOptions = [
-          "-l" "traefik.enable=true"
-          "-l" "traefik.http.routers.nginx.entrypoints=web"
-          "-l" "traefik.http.routers.nginx.rule=Host(`nginx.traefik.test`)"
-        ];
-        image = "nginx-container";
-        imageFile = pkgs.dockerTools.examples.nginx;
+      virtualisation.oci-containers = {
+        backend = "docker";
+        containers.nginx = {
+          extraOptions = [
+            "-l"
+            "traefik.enable=true"
+            "-l"
+            "traefik.http.routers.nginx.entrypoints=web"
+            "-l"
+            "traefik.http.routers.nginx.rule=Host(`nginx.traefik.test`)"
+          ];
+          image = "nginx-container";
+          imageFile = pkgs.dockerTools.examples.nginx;
+        };
       };
 
       networking.firewall.allowedTCPPorts = [ 80 ];
diff --git a/nixos/tests/vault-dev.nix b/nixos/tests/vault-dev.nix
new file mode 100644
index 0000000000000..ba9a1015cc13c
--- /dev/null
+++ b/nixos/tests/vault-dev.nix
@@ -0,0 +1,35 @@
+import ./make-test-python.nix ({ pkgs, ... }:
+{
+  name = "vault-dev";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ lnl7 mic92 ];
+  };
+  nodes.machine = { pkgs, config, ... }: {
+    environment.systemPackages = [ pkgs.vault ];
+    environment.variables.VAULT_ADDR = "http://127.0.0.1:8200";
+    environment.variables.VAULT_TOKEN = "phony-secret";
+
+    services.vault = {
+      enable = true;
+      dev = true;
+      devRootTokenID = config.environment.variables.VAULT_TOKEN;
+    };
+  };
+
+  testScript = ''
+    import json
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+    machine.wait_for_unit("vault.service")
+    machine.wait_for_open_port(8200)
+    out = machine.succeed("vault status -format=json")
+    print(out)
+    status = json.loads(out)
+    assert status.get("initialized") == True
+    machine.succeed("vault kv put secret/foo bar=baz")
+    out = machine.succeed("vault kv get -format=json secret/foo")
+    print(out)
+    status = json.loads(out)
+    assert status.get("data", {}).get("data", {}).get("bar") == "baz"
+  '';
+})
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index 814d8d7c0ab3e..408019666da3a 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -74,7 +74,10 @@ let
             services.vaultwarden = {
               enable = true;
               dbBackend = backend;
-              config.rocketPort = 80;
+              config = {
+                rocketAddress = "0.0.0.0";
+                rocketPort = 80;
+              };
             };
 
             networking.firewall.allowedTCPPorts = [ 80 ];
@@ -85,6 +88,8 @@ let
                   {
                     libraries = [ pkgs.python3Packages.selenium ];
                   } ''
+
+                  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
@@ -101,40 +106,40 @@ let
 
                   wait.until(EC.title_contains("Create Account"))
 
-                  driver.find_element_by_css_selector('input#email').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#email').send_keys(
                     '${userEmail}'
                   )
-                  driver.find_element_by_css_selector('input#name').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
                     'A Cat'
                   )
-                  driver.find_element_by_css_selector('input#masterPassword').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#masterPassword').send_keys(
                     '${userPassword}'
                   )
-                  driver.find_element_by_css_selector('input#masterPasswordRetype').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#masterPasswordRetype').send_keys(
                     '${userPassword}'
                   )
 
-                  driver.find_element_by_xpath("//button[contains(., 'Submit')]").click()
+                  driver.find_element(By.XPATH, "//button[contains(., 'Submit')]").click()
 
                   wait.until_not(EC.title_contains("Create Account"))
 
-                  driver.find_element_by_css_selector('input#masterPassword').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#masterPassword').send_keys(
                     '${userPassword}'
                   )
-                  driver.find_element_by_xpath("//button[contains(., 'Log In')]").click()
+                  driver.find_element(By.XPATH, "//button[contains(., 'Log In')]").click()
 
-                  wait.until(EC.title_contains("My Vault"))
+                  wait.until(EC.title_contains("Bitwarden Web Vault"))
 
-                  driver.find_element_by_xpath("//button[contains(., 'Add Item')]").click()
+                  driver.find_element(By.XPATH, "//button[contains(., 'Add Item')]").click()
 
-                  driver.find_element_by_css_selector('input#name').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
                     'secrets'
                   )
-                  driver.find_element_by_css_selector('input#loginPassword').send_keys(
+                  driver.find_element(By.CSS_SELECTOR, 'input#loginPassword').send_keys(
                     '${storedPassword}'
                   )
 
-                  driver.find_element_by_xpath("//button[contains(., 'Save')]").click()
+                  driver.find_element(By.XPATH, "//button[contains(., 'Save')]").click()
                 '';
               in
               [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
diff --git a/nixos/tests/vengi-tools.nix b/nixos/tests/vengi-tools.nix
index 8b80a13384e5a..5bc8d72c77237 100644
--- a/nixos/tests/vengi-tools.nix
+++ b/nixos/tests/vengi-tools.nix
@@ -23,7 +23,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
       # OCR on voxedit's window is very expensive, so we avoid wasting a try
       # by letting the window load fully first
       machine.sleep(15)
-      machine.wait_for_text("Palette")
+      machine.wait_for_text("Solid")
       machine.screenshot("screen")
     '';
 })
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index 4eb402a7d36ef..1c1b0dac7f37c 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -3,18 +3,9 @@
   pkgs ? import ../.. { inherit system config; },
   debug ? false,
   enableUnfree ? false,
-  # Nested KVM virtualization (https://www.linux-kvm.org/page/Nested_Guests)
-  # requires a modprobe flag on the build machine: (kvm-amd for AMD CPUs)
-  #   boot.extraModprobeConfig = "options kvm-intel nested=Y";
-  # Without this VirtualBox will use SW virtualization and will only be able
-  # to run 32-bit guests.
-  useKvmNestedVirt ? false,
-  # Whether to run 64-bit guests instead of 32-bit. Requires nested KVM.
-  use64bitGuest ? false
+  use64bitGuest ? true
 }:
 
-assert use64bitGuest -> useKvmNestedVirt;
-
 with import ../lib/testing-python.nix { inherit system pkgs; };
 with pkgs.lib;
 
@@ -26,7 +17,8 @@ let
       #!${pkgs.runtimeShell} -xe
       export PATH="${lib.makeBinPath [ pkgs.coreutils pkgs.util-linux ]}"
 
-      mkdir -p /run/dbus
+      mkdir -p /run/dbus /var
+      ln -s /run /var
       cat > /etc/passwd <<EOF
       root:x:0:0::/root:/bin/false
       messagebus:x:1:1::/run/dbus:/bin/false
@@ -200,6 +192,7 @@ let
       systemd.services."vboxtestlog-${name}@" = {
         description = "VirtualBox Test Machine Log For ${name}";
         serviceConfig.StandardInput = "socket";
+        serviceConfig.StandardOutput = "journal";
         serviceConfig.SyslogIdentifier = "GUEST-${name}";
         serviceConfig.ExecStart = "${pkgs.coreutils}/bin/cat";
       };
@@ -226,6 +219,7 @@ let
 
 
       def create_vm_${name}():
+          cleanup_${name}()
           vbm("createvm --name ${name} ${createFlags}")
           vbm("modifyvm ${name} ${vmFlags}")
           vbm("setextradata ${name} VBoxInternal/PDM/HaltOnReset 1")
@@ -233,7 +227,6 @@ let
           vbm("storageattach ${name} ${diskFlags}")
           vbm("sharedfolder add ${name} ${sharedFlags}")
           vbm("sharedfolder add ${name} ${nixstoreFlags}")
-          cleanup_${name}()
 
           ${mkLog "$HOME/VirtualBox VMs/${name}/Logs/VBox.log" "HOST-${name}"}
 
@@ -317,10 +310,7 @@ let
   ];
 
   dhcpScript = pkgs: ''
-    ${pkgs.dhcp}/bin/dhclient \
-      -lf /run/dhcp.leases \
-      -pf /run/dhclient.pid \
-      -v eth0 eth1
+    ${pkgs.dhcpcd}/bin/dhcpcd eth0 eth1
 
     otherIP="$(${pkgs.netcat}/bin/nc -l 1234 || :)"
     ${pkgs.iputils}/bin/ping -I eth1 -c1 "$otherIP"
@@ -359,8 +349,7 @@ let
         vmConfigs = mapAttrsToList mkVMConf vms;
       in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs;
       virtualisation.memorySize = 2048;
-      virtualisation.qemu.options =
-        if useKvmNestedVirt then ["-cpu" "kvm64,vmx=on"] else [];
+      virtualisation.qemu.options = ["-cpu" "kvm64,svm=on,vmx=on"];
       virtualisation.virtualbox.host.enable = true;
       test-support.displayManager.auto.user = "alice";
       users.users.alice.extraGroups = let
@@ -468,7 +457,7 @@ in mapAttrs (mkVBoxTest false vboxVMs) {
 
   headless = ''
     create_vm_headless()
-    machine.succeed(ru("VBoxHeadless --startvm headless & disown %1"))
+    machine.succeed(ru("VBoxHeadless --startvm headless >&2 & disown %1"))
     wait_for_startup_headless()
     wait_for_vm_boot_headless()
     shutdown_vm_headless()
@@ -476,6 +465,8 @@ in mapAttrs (mkVBoxTest false vboxVMs) {
   '';
 
   host-usb-permissions = ''
+    import sys
+
     user_usb = remove_uuids(vbm("list usbhost"))
     print(user_usb, file=sys.stderr)
     root_usb = remove_uuids(machine.succeed("VBoxManage list usbhost"))
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index 688ddfe07e3e1..3bdb99947a40b 100644
--- a/nixos/tests/vscodium.nix
+++ b/nixos/tests/vscodium.nix
@@ -32,6 +32,14 @@ let
         maintainers = [ synthetica turion ];
       };
       enableOCR = true;
+
+      # testScriptWithTypes:55: error: Item "function" of
+      # "Union[Callable[[Callable[..., Any]], ContextManager[Any]], ContextManager[Any]]"
+      # has no attribute "__enter__"
+      #     with codium_running:
+      #          ^
+      skipTypeCheck = true;
+
       testScript = ''
         @polling_condition
         def codium_running():
diff --git a/nixos/tests/web-apps/healthchecks.nix b/nixos/tests/web-apps/healthchecks.nix
new file mode 100644
index 0000000000000..41374f5e314bb
--- /dev/null
+++ b/nixos/tests/web-apps/healthchecks.nix
@@ -0,0 +1,42 @@
+import ../make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "healthchecks";
+
+  meta = with lib.maintainers; {
+    maintainers = [ phaer ];
+  };
+
+  nodes.machine = { ... }: {
+    services.healthchecks = {
+      enable = true;
+      settings = {
+        SITE_NAME = "MyUniqueInstance";
+        COMPRESS_ENABLED = "True";
+        SECRET_KEY_FILE = pkgs.writeText "secret"
+          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+      };
+    };
+  };
+
+  testScript = ''
+    machine.start()
+    machine.wait_for_unit("healthchecks.target")
+    machine.wait_until_succeeds("journalctl --since -1m --unit healthchecks --grep Listening")
+
+    with subtest("Home screen loads"):
+        machine.succeed(
+            "curl -sSfL http://localhost:8000 | grep '<title>Sign In'"
+        )
+
+    with subtest("Setting SITE_NAME via freeform option works"):
+        machine.succeed(
+            "curl -sSfL http://localhost:8000 | grep 'MyUniqueInstance</title>'"
+        )
+
+    with subtest("Manage script works"):
+        # Should fail if not called by healthchecks user
+        machine.fail("echo 'print(\"foo\")' | healthchecks-manage help")
+
+        # "shell" sucommand should succeed, needs python in PATH.
+        assert "foo\n" == machine.succeed("echo 'print(\"foo\")' | sudo -u healthchecks healthchecks-manage shell")
+  '';
+})
diff --git a/nixos/tests/web-apps/phylactery.nix b/nixos/tests/web-apps/phylactery.nix
new file mode 100644
index 0000000000000..cf2689d2300d3
--- /dev/null
+++ b/nixos/tests/web-apps/phylactery.nix
@@ -0,0 +1,20 @@
+import ../make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "phylactery";
+
+  nodes.machine = { ... }: {
+    services.phylactery = rec {
+      enable = true;
+      port = 8080;
+      library = "/tmp";
+    };
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit('phylactery')
+    machine.wait_for_open_port(8080)
+    machine.wait_until_succeeds('curl localhost:8080')
+  '';
+
+  meta.maintainers = with lib.maintainers; [ McSinyx ];
+})
diff --git a/nixos/tests/web-servers/unit-php.nix b/nixos/tests/web-servers/unit-php.nix
index 5bef7fab3efff..f0df371945e5b 100644
--- a/nixos/tests/web-servers/unit-php.nix
+++ b/nixos/tests/web-servers/unit-php.nix
@@ -10,15 +10,15 @@ in {
     services.unit = {
       enable = true;
       config = pkgs.lib.strings.toJSON {
-        listeners."*:9080".application = "php_80";
-        applications.php_80 = {
-          type = "php 8.0";
+        listeners."*:9081".application = "php_81";
+        applications.php_81 = {
+          type = "php 8.1";
           processes = 1;
           user = "testuser";
           group = "testgroup";
           root = "${testdir}/www";
           index = "info.php";
-          options.file = "${pkgs.unit.usedPhp80}/lib/php.ini";
+          options.file = "${pkgs.unit.usedPhp81}/lib/php.ini";
         };
       };
     };
@@ -34,14 +34,19 @@ in {
     };
   };
   testScript = ''
+    machine.start()
+
     machine.wait_for_unit("unit.service")
+    machine.wait_for_open_port(9081)
 
     # Check so we get an evaluated PHP back
-    response = machine.succeed("curl -f -vvv -s http://127.0.0.1:9080/")
-    assert "PHP Version ${pkgs.unit.usedPhp80.version}" in response, "PHP version not detected"
+    response = machine.succeed("curl -f -vvv -s http://127.0.0.1:9081/")
+    assert "PHP Version ${pkgs.unit.usedPhp81.version}" in response, "PHP version not detected"
 
     # Check so we have database and some other extensions loaded
     for ext in ["json", "opcache", "pdo_mysql", "pdo_pgsql", "pdo_sqlite"]:
         assert ext in response, f"Missing {ext} extension"
+
+    machine.shutdown()
   '';
 })
diff --git a/nixos/tests/wireguard/wg-quick.nix b/nixos/tests/wireguard/wg-quick.nix
index 961c2e15c30f9..bc2cba9118889 100644
--- a/nixos/tests/wireguard/wg-quick.nix
+++ b/nixos/tests/wireguard/wg-quick.nix
@@ -29,6 +29,8 @@ import ../make-test-python.nix ({ pkgs, lib, ... }:
 
               inherit (wg-snakeoil-keys.peer1) publicKey;
             };
+
+            dns = [ "10.23.42.2" "fc00::2" "wg0" ];
           };
         };
       };
@@ -38,6 +40,7 @@ import ../make-test-python.nix ({ pkgs, lib, ... }:
         ip6 = "fd00::2";
         extraConfig = {
           boot = lib.mkIf (kernelPackages != null) { inherit kernelPackages; };
+          networking.useNetworkd = true;
           networking.wg-quick.interfaces.wg0 = {
             address = [ "10.23.42.2/32" "fc00::2/128" ];
             inherit (wg-snakeoil-keys.peer1) privateKey;
@@ -49,6 +52,8 @@ import ../make-test-python.nix ({ pkgs, lib, ... }:
 
               inherit (wg-snakeoil-keys.peer0) publicKey;
             };
+
+            dns = [ "10.23.42.1" "fc00::1" "wg0" ];
           };
         };
       };
diff --git a/nixos/tests/xrdp.nix b/nixos/tests/xrdp.nix
index 0e1d521c5aced..f277d4b795256 100644
--- a/nixos/tests/xrdp.nix
+++ b/nixos/tests/xrdp.nix
@@ -1,7 +1,7 @@
 import ./make-test-python.nix ({ pkgs, ...} : {
   name = "xrdp";
   meta = with pkgs.lib.maintainers; {
-    maintainers = [ volth ];
+    maintainers = [ ];
   };
 
   nodes = {
diff --git a/nixos/tests/zeronet-conservancy.nix b/nixos/tests/zeronet-conservancy.nix
new file mode 100644
index 0000000000000..8cb649cbdaabc
--- /dev/null
+++ b/nixos/tests/zeronet-conservancy.nix
@@ -0,0 +1,25 @@
+let
+  port = 43110;
+in
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "zeronet-conservancy";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ fgaz ];
+  };
+
+  nodes.machine = { config, pkgs, ... }: {
+    services.zeronet = {
+      enable = true;
+      package = pkgs.zeronet-conservancy;
+      inherit port;
+    };
+  };
+
+  testScript = ''
+    machine.wait_for_unit("zeronet.service")
+
+    machine.wait_for_open_port(${toString port})
+
+    machine.succeed("curl --fail -H 'Accept: text/html, application/xml, */*' localhost:${toString port}/Stats")
+  '';
+})