about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/gpu-accel.chapter.md34
-rw-r--r--nixos/doc/manual/configuration/profiles/hardened.section.md2
-rw-r--r--nixos/doc/manual/configuration/wireless.section.md2
-rw-r--r--nixos/doc/manual/contributing-to-this-manual.chapter.md24
-rw-r--r--nixos/doc/manual/default.nix9
-rw-r--r--nixos/doc/manual/development/activation-script.section.md4
-rw-r--r--nixos/doc/manual/development/bootspec.chapter.md36
-rw-r--r--nixos/doc/manual/development/development.xml1
-rw-r--r--nixos/doc/manual/development/option-types.section.md2
-rw-r--r--nixos/doc/manual/development/writing-nixos-tests.section.md4
-rw-r--r--nixos/doc/manual/from_md/configuration/gpu-accel.chapter.xml42
-rw-r--r--nixos/doc/manual/from_md/configuration/profiles/hardened.section.xml2
-rw-r--r--nixos/doc/manual/from_md/configuration/wireless.section.xml2
-rw-r--r--nixos/doc/manual/from_md/contributing-to-this-manual.chapter.xml32
-rw-r--r--nixos/doc/manual/from_md/development/activation-script.section.xml4
-rw-r--r--nixos/doc/manual/from_md/development/bootspec.chapter.xml73
-rw-r--r--nixos/doc/manual/from_md/development/option-types.section.xml2
-rw-r--r--nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml4
-rw-r--r--nixos/doc/manual/from_md/installation/building-nixos.chapter.xml4
-rw-r--r--nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml2
-rw-r--r--nixos/doc/manual/from_md/installation/installing-virtualbox-guest.section.xml2
-rw-r--r--nixos/doc/manual/from_md/installation/installing.chapter.xml2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-1603.section.xml2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-1709.section.xml2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-1903.section.xml2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-1909.section.xml4
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2105.section.xml2
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2205.section.xml4
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2211.section.xml6
-rw-r--r--nixos/doc/manual/from_md/release-notes/rl-2305.section.xml117
-rw-r--r--nixos/doc/manual/installation/building-nixos.chapter.md4
-rw-r--r--nixos/doc/manual/installation/installing-from-other-distro.section.md2
-rw-r--r--nixos/doc/manual/installation/installing-virtualbox-guest.section.md2
-rw-r--r--nixos/doc/manual/installation/installing.chapter.md2
-rw-r--r--nixos/doc/manual/man-nixos-rebuild.xml18
-rw-r--r--nixos/doc/manual/release-notes/rl-1603.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-1709.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-1903.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-1909.section.md4
-rw-r--r--nixos/doc/manual/release-notes/rl-2105.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2205.section.md4
-rw-r--r--nixos/doc/manual/release-notes/rl-2211.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2305.section.md30
-rw-r--r--nixos/lib/make-options-doc/default.nix2
-rw-r--r--nixos/lib/systemd-lib.nix6
-rw-r--r--nixos/lib/utils.nix6
-rw-r--r--nixos/maintainers/scripts/lxd/lxd-image-inner.nix5
-rw-r--r--nixos/maintainers/scripts/lxd/lxd-image.nix5
-rw-r--r--nixos/modules/config/no-x-libs.nix3
-rw-r--r--nixos/modules/config/swap.nix2
-rw-r--r--nixos/modules/hardware/gpgsmartcards.nix2
-rw-r--r--nixos/modules/hardware/openrazer.nix2
-rw-r--r--nixos/modules/hardware/printers.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix2
-rw-r--r--nixos/modules/installer/netboot/netboot-minimal.nix2
-rw-r--r--nixos/modules/misc/label.nix2
-rw-r--r--nixos/modules/misc/man-db.nix10
-rw-r--r--nixos/modules/module-list.nix353
-rw-r--r--nixos/modules/profiles/base.nix1
-rw-r--r--nixos/modules/profiles/keys/ssh_host_ed25519_key7
-rw-r--r--nixos/modules/profiles/keys/ssh_host_ed25519_key.pub1
-rw-r--r--nixos/modules/profiles/macos-builder.nix134
-rw-r--r--nixos/modules/programs/firefox.nix121
-rw-r--r--nixos/modules/programs/i3lock.nix58
-rw-r--r--nixos/modules/programs/nix-ld.nix62
-rw-r--r--nixos/modules/programs/streamdeck-ui.nix14
-rw-r--r--nixos/modules/programs/vim.nix2
-rw-r--r--nixos/modules/programs/xfs_quota.nix2
-rw-r--r--nixos/modules/programs/xonsh.nix4
-rw-r--r--nixos/modules/security/acme/default.nix4
-rw-r--r--nixos/modules/security/apparmor.nix2
-rw-r--r--nixos/modules/security/pam.nix24
-rw-r--r--nixos/modules/security/pam_mount.nix2
-rw-r--r--nixos/modules/security/wrappers/default.nix2
-rw-r--r--nixos/modules/services/audio/botamusique.nix3
-rw-r--r--nixos/modules/services/audio/icecast.nix2
-rw-r--r--nixos/modules/services/audio/roon-server.nix1
-rw-r--r--nixos/modules/services/backup/borgbackup.nix47
-rw-r--r--nixos/modules/services/backup/zfs-replication.nix4
-rw-r--r--nixos/modules/services/backup/znapzend.nix6
-rw-r--r--nixos/modules/services/blockchain/ethereum/lighthouse.nix4
-rw-r--r--nixos/modules/services/cluster/kubernetes/addon-manager.nix2
-rw-r--r--nixos/modules/services/cluster/kubernetes/pki.nix2
-rw-r--r--nixos/modules/services/continuous-integration/buildbot/master.nix2
-rw-r--r--nixos/modules/services/continuous-integration/github-runner/options.nix2
-rw-r--r--nixos/modules/services/continuous-integration/gitlab-runner.nix2
-rw-r--r--nixos/modules/services/continuous-integration/hail.nix4
-rw-r--r--nixos/modules/services/databases/firebird.nix2
-rw-r--r--nixos/modules/services/databases/postgresql.nix178
-rw-r--r--nixos/modules/services/databases/surrealdb.nix13
-rw-r--r--nixos/modules/services/desktops/pipewire/wireplumber.nix4
-rw-r--r--nixos/modules/services/display-managers/greetd.nix2
-rw-r--r--nixos/modules/services/games/asf.nix6
-rw-r--r--nixos/modules/services/games/minetest-server.nix2
-rw-r--r--nixos/modules/services/hardware/fwupd.nix68
-rw-r--r--nixos/modules/services/hardware/lirc.nix2
-rw-r--r--nixos/modules/services/hardware/sane.nix2
-rw-r--r--nixos/modules/services/hardware/usbmuxd.nix12
-rw-r--r--nixos/modules/services/logging/logrotate.nix2
-rw-r--r--nixos/modules/services/mail/listmonk.nix2
-rw-r--r--nixos/modules/services/mail/mailman.nix2
-rw-r--r--nixos/modules/services/mail/postfix.nix6
-rw-r--r--nixos/modules/services/mail/roundcube.nix4
-rw-r--r--nixos/modules/services/matrix/mautrix-telegram.nix4
-rw-r--r--nixos/modules/services/matrix/synapse.nix2
-rw-r--r--nixos/modules/services/misc/atuin.nix85
-rw-r--r--nixos/modules/services/misc/dysnomia.nix2
-rw-r--r--nixos/modules/services/misc/gammu-smsd.nix2
-rw-r--r--nixos/modules/services/misc/gitea.nix4
-rw-r--r--nixos/modules/services/misc/gitlab.xml2
-rw-r--r--nixos/modules/services/misc/gpsd.nix9
-rw-r--r--nixos/modules/services/misc/jellyfin.nix2
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix4
-rw-r--r--nixos/modules/services/misc/podgrab.nix2
-rw-r--r--nixos/modules/services/misc/portunus.nix2
-rw-r--r--nixos/modules/services/misc/serviio.nix2
-rw-r--r--nixos/modules/services/misc/sourcehut/service.nix2
-rw-r--r--nixos/modules/services/misc/taskserver/default.nix4
-rw-r--r--nixos/modules/services/monitoring/grafana.nix7
-rw-r--r--nixos/modules/services/monitoring/graphite.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/alertmanager.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix18
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters.xml4
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/statsd.nix19
-rw-r--r--nixos/modules/services/monitoring/thanos.nix2
-rw-r--r--nixos/modules/services/monitoring/ups.nix4
-rw-r--r--nixos/modules/services/network-filesystems/kubo.nix2
-rw-r--r--nixos/modules/services/network-filesystems/moosefs.nix4
-rw-r--r--nixos/modules/services/network-filesystems/orangefs/client.nix2
-rw-r--r--nixos/modules/services/network-filesystems/orangefs/server.nix2
-rw-r--r--nixos/modules/services/networking/3proxy.nix2
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix5
-rw-r--r--nixos/modules/services/networking/biboumi.nix4
-rw-r--r--nixos/modules/services/networking/bitcoind.nix2
-rw-r--r--nixos/modules/services/networking/bitlbee.nix2
-rw-r--r--nixos/modules/services/networking/cloudflared.nix332
-rw-r--r--nixos/modules/services/networking/consul.nix2
-rw-r--r--nixos/modules/services/networking/epmd.nix2
-rw-r--r--nixos/modules/services/networking/firefox-syncserver.nix7
-rw-r--r--nixos/modules/services/networking/hans.nix4
-rw-r--r--nixos/modules/services/networking/headscale.nix4
-rw-r--r--nixos/modules/services/networking/hylafax/systemd.nix2
-rw-r--r--nixos/modules/services/networking/i2pd.nix2
-rw-r--r--nixos/modules/services/networking/iperf3.nix2
-rw-r--r--nixos/modules/services/networking/kea.nix8
-rw-r--r--nixos/modules/services/networking/knot.nix2
-rw-r--r--nixos/modules/services/networking/libreswan.nix2
-rw-r--r--nixos/modules/services/networking/lxd-image-server.nix2
-rw-r--r--nixos/modules/services/networking/mmsd.nix38
-rw-r--r--nixos/modules/services/networking/mosquitto.nix2
-rw-r--r--nixos/modules/services/networking/ncdns.nix2
-rw-r--r--nixos/modules/services/networking/ndppd.nix4
-rw-r--r--nixos/modules/services/networking/nftables.nix2
-rw-r--r--nixos/modules/services/networking/nsd.nix2
-rw-r--r--nixos/modules/services/networking/ostinato.nix2
-rw-r--r--nixos/modules/services/networking/pleroma.nix2
-rw-r--r--nixos/modules/services/networking/prosody.nix8
-rw-r--r--nixos/modules/services/networking/radicale.nix2
-rw-r--r--nixos/modules/services/networking/rpcbind.nix10
-rw-r--r--nixos/modules/services/networking/searx.nix4
-rw-r--r--nixos/modules/services/networking/stunnel.nix2
-rw-r--r--nixos/modules/services/networking/supplicant.nix6
-rw-r--r--nixos/modules/services/networking/tailscale.nix29
-rw-r--r--nixos/modules/services/networking/unbound.nix2
-rw-r--r--nixos/modules/services/networking/unifi.nix6
-rw-r--r--nixos/modules/services/networking/vsftpd.nix2
-rw-r--r--nixos/modules/services/networking/wireguard.nix4
-rw-r--r--nixos/modules/services/networking/yggdrasil.xml4
-rw-r--r--nixos/modules/services/printing/ipp-usb.nix2
-rw-r--r--nixos/modules/services/security/fail2ban.nix4
-rw-r--r--nixos/modules/services/security/shibboleth-sp.nix4
-rw-r--r--nixos/modules/services/security/tor.nix20
-rw-r--r--nixos/modules/services/security/usbguard.nix4
-rw-r--r--nixos/modules/services/security/vaultwarden/default.nix4
-rw-r--r--nixos/modules/services/system/cloud-init.nix2
-rw-r--r--nixos/modules/services/system/kerberos/default.nix2
-rw-r--r--nixos/modules/services/torrent/deluge.nix4
-rw-r--r--nixos/modules/services/torrent/magnetico.nix2
-rw-r--r--nixos/modules/services/torrent/rtorrent.nix2
-rw-r--r--nixos/modules/services/torrent/transmission.nix12
-rw-r--r--nixos/modules/services/video/unifi-video.nix2
-rw-r--r--nixos/modules/services/web-apps/bookstack.nix2
-rw-r--r--nixos/modules/services/web-apps/healthchecks.nix2
-rw-r--r--nixos/modules/services/web-apps/ihatemoney/default.nix2
-rw-r--r--nixos/modules/services/web-apps/invidious.nix2
-rw-r--r--nixos/modules/services/web-apps/invoiceplane.nix2
-rw-r--r--nixos/modules/services/web-apps/jitsi-meet.nix2
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix58
-rw-r--r--nixos/modules/services/web-apps/matomo.nix2
-rw-r--r--nixos/modules/services/web-apps/mattermost.nix2
-rw-r--r--nixos/modules/services/web-apps/mediawiki.nix2
-rw-r--r--nixos/modules/services/web-apps/miniflux.nix11
-rw-r--r--nixos/modules/services/web-apps/netbox.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix14
-rw-r--r--nixos/modules/services/web-apps/nextcloud.xml2
-rw-r--r--nixos/modules/services/web-apps/onlyoffice.nix2
-rw-r--r--nixos/modules/services/web-apps/outline.nix2
-rw-r--r--nixos/modules/services/web-apps/peering-manager.nix2
-rw-r--r--nixos/modules/services/web-apps/pgpkeyserver-lite.nix2
-rw-r--r--nixos/modules/services/web-apps/snipe-it.nix2
-rw-r--r--nixos/modules/services/web-apps/sogo.nix2
-rw-r--r--nixos/modules/services/web-apps/wiki-js.nix2
-rw-r--r--nixos/modules/services/web-servers/agate.nix2
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/vhost-options.nix2
-rw-r--r--nixos/modules/services/web-servers/keter/default.nix2
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix51
-rw-r--r--nixos/modules/services/web-servers/nginx/vhost-options.nix4
-rw-r--r--nixos/modules/services/web-servers/ttyd.nix2
-rw-r--r--nixos/modules/services/web-servers/zope2.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/plasma5.nix10
-rw-r--r--nixos/modules/services/x11/display-managers/sddm.nix2
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix2
-rw-r--r--nixos/modules/services/x11/imwheel.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/katriawm.nix27
-rw-r--r--nixos/modules/services/x11/xautolock.nix2
-rw-r--r--nixos/modules/system/activation/bootspec.cue17
-rw-r--r--nixos/modules/system/activation/bootspec.nix124
-rw-r--r--nixos/modules/system/activation/top-level.nix5
-rw-r--r--nixos/modules/system/boot/loader/external/external.md26
-rw-r--r--nixos/modules/system/boot/loader/external/external.nix38
-rw-r--r--nixos/modules/system/boot/loader/external/external.xml41
-rw-r--r--nixos/modules/system/boot/loader/grub/grub.nix2
-rw-r--r--nixos/modules/system/boot/stage-1.nix5
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix4
-rw-r--r--nixos/modules/tasks/network-interfaces.nix6
-rw-r--r--nixos/modules/virtualisation/appvm.nix2
-rw-r--r--nixos/modules/virtualisation/hyperv-guest.nix2
-rw-r--r--nixos/modules/virtualisation/lxc-container.nix8
-rw-r--r--nixos/modules/virtualisation/proxmox-image.nix2
-rw-r--r--nixos/modules/virtualisation/vmware-host.nix2
-rw-r--r--nixos/tests/acme.nix2
-rw-r--r--nixos/tests/all-tests.nix4
-rw-r--r--nixos/tests/atuin.nix65
-rw-r--r--nixos/tests/bootspec.nix144
-rw-r--r--nixos/tests/borgbackup.nix20
-rw-r--r--nixos/tests/cockroachdb.nix2
-rw-r--r--nixos/tests/common/ec2.nix2
-rw-r--r--nixos/tests/grafana/basic.nix27
-rw-r--r--nixos/tests/graphite.nix2
-rw-r--r--nixos/tests/home-assistant.nix2
-rw-r--r--nixos/tests/installer.nix2
-rw-r--r--nixos/tests/networking-proxy.nix2
-rw-r--r--nixos/tests/nginx-globalredirect.nix24
-rw-r--r--nixos/tests/nginx-modsecurity.nix2
-rw-r--r--nixos/tests/nginx.nix2
-rw-r--r--nixos/tests/nix-ld.nix5
-rw-r--r--nixos/tests/ntfy-sh.nix1
-rw-r--r--nixos/tests/os-prober.nix2
-rw-r--r--nixos/tests/pgadmin4-standalone.nix2
-rw-r--r--nixos/tests/pgadmin4.nix2
-rw-r--r--nixos/tests/postgresql.nix93
-rw-r--r--nixos/tests/prometheus-exporters.nix29
-rw-r--r--nixos/tests/sourcehut.nix2
-rw-r--r--nixos/tests/vaultwarden.nix31
-rw-r--r--nixos/tests/web-apps/mastodon.nix130
-rw-r--r--nixos/tests/web-apps/mastodon/default.nix9
-rw-r--r--nixos/tests/web-apps/mastodon/remote-postgresql.nix160
-rw-r--r--nixos/tests/web-apps/mastodon/script.nix54
-rw-r--r--nixos/tests/web-apps/mastodon/standard.nix92
261 files changed, 3214 insertions, 735 deletions
diff --git a/nixos/doc/manual/configuration/gpu-accel.chapter.md b/nixos/doc/manual/configuration/gpu-accel.chapter.md
index 835cbad55485..aa41e25e56f3 100644
--- a/nixos/doc/manual/configuration/gpu-accel.chapter.md
+++ b/nixos/doc/manual/configuration/gpu-accel.chapter.md
@@ -159,6 +159,40 @@ environment.variables.VK_ICD_FILENAMES =
   "/run/opengl-driver/share/vulkan/icd.d/radeon_icd.x86_64.json";
 ```
 
+## VA-API {#sec-gpu-accel-va-api}
+
+[VA-API (Video Acceleration API)](https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html)
+is an open-source library and API specification, which provides access to
+graphics hardware acceleration capabilities for video processing.
+
+VA-API drivers are loaded by `libva`. The version in nixpkgs is built to search
+the opengl driver path, so drivers can be installed in
+[](#opt-hardware.opengl.extraPackages).
+
+VA-API can be tested using:
+
+```ShellSession
+$ nix-shell -p libva-utils --run vainfo
+```
+
+### Intel {#sec-gpu-accel-va-api-intel}
+
+Modern Intel GPUs use the iHD driver, which can be installed with:
+
+```nix
+hardware.opengl.extraPackages = [
+  intel-media-driver
+];
+```
+
+Older Intel GPUs use the i965 driver, which can be installed with:
+
+```nix
+hardware.opengl.extraPackages = [
+  vaapiIntel
+];
+```
+
 ## Common issues {#sec-gpu-accel-common-issues}
 
 ### User permissions {#sec-gpu-accel-common-issues-permissions}
diff --git a/nixos/doc/manual/configuration/profiles/hardened.section.md b/nixos/doc/manual/configuration/profiles/hardened.section.md
index 9fb5e18c384a..2e9bb196c054 100644
--- a/nixos/doc/manual/configuration/profiles/hardened.section.md
+++ b/nixos/doc/manual/configuration/profiles/hardened.section.md
@@ -7,7 +7,7 @@ This includes a hardened kernel, and limiting the system information
 available to processes through the `/sys` and
 `/proc` filesystems. It also disables the User Namespaces
 feature of the kernel, which stops Nix from being able to build anything
-(this particular setting can be overriden via
+(this particular setting can be overridden via
 [](#opt-security.allowUserNamespaces)). See the
 [profile source](https://github.com/nixos/nixpkgs/tree/master/nixos/modules/profiles/hardened.nix)
 for further detail on which settings are altered.
diff --git a/nixos/doc/manual/configuration/wireless.section.md b/nixos/doc/manual/configuration/wireless.section.md
index 6b223d843ac5..3299d2d7ecb8 100644
--- a/nixos/doc/manual/configuration/wireless.section.md
+++ b/nixos/doc/manual/configuration/wireless.section.md
@@ -50,7 +50,7 @@ networking.wireless.networks = {
   echelon = {
     pskRaw = "dca6d6ed41f4ab5a984c9f55f6f66d4efdc720ebf66959810f4329bb391c5435";
   };
-}
+};
 ```
 
 or you can use it to directly generate the `wpa_supplicant.conf`:
diff --git a/nixos/doc/manual/contributing-to-this-manual.chapter.md b/nixos/doc/manual/contributing-to-this-manual.chapter.md
index 26813d1042d6..557599809222 100644
--- a/nixos/doc/manual/contributing-to-this-manual.chapter.md
+++ b/nixos/doc/manual/contributing-to-this-manual.chapter.md
@@ -1,6 +1,6 @@
 # Contributing to this manual {#chap-contributing}
 
-The DocBook and CommonMark sources of NixOS' manual are in the [nixos/doc/manual](https://github.com/NixOS/nixpkgs/tree/master/nixos/doc/manual) subdirectory of the [Nixpkgs](https://github.com/NixOS/nixpkgs) repository.
+The [DocBook] and CommonMark sources of the NixOS manual are in the [nixos/doc/manual](https://github.com/NixOS/nixpkgs/tree/master/nixos/doc/manual) subdirectory of the [Nixpkgs](https://github.com/NixOS/nixpkgs) repository.
 
 You can quickly check your edits with the following:
 
@@ -11,3 +11,25 @@ $ nix-build nixos/release.nix -A manual.x86_64-linux
 ```
 
 If the build succeeds, the manual will be in `./result/share/doc/nixos/index.html`.
+
+**Contributing to the man pages**
+
+The man pages are written in [DocBook] which is XML.
+
+To see what your edits look like:
+
+```ShellSession
+$ cd /path/to/nixpkgs
+$ nix-build nixos/release.nix -A manpages.x86_64-linux
+```
+
+You can then read the man page you edited by running
+
+```ShellSession
+$ man --manpath=result/share/man nixos-rebuild # Replace nixos-rebuild with the command whose manual you edited
+```
+
+If you're on a different architecture that's supported by NixOS (check nixos/release.nix) then replace `x86_64-linux` with the architecture.
+`nix-build` will complain otherwise, but should also tell you which architecture you have + the supported ones.
+
+[DocBook]: https://en.wikipedia.org/wiki/DocBook
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 6db20cdd6418..9b72e840f4b1 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -102,11 +102,14 @@ let
     '';
 
   manualXsltprocOptions = toString [
-    "--param section.autolabel 1"
-    "--param section.label.includes.component.label 1"
+    "--param chapter.autolabel 0"
+    "--param part.autolabel 0"
+    "--param preface.autolabel 0"
+    "--param reference.autolabel 0"
+    "--param section.autolabel 0"
     "--stringparam html.stylesheet 'style.css overrides.css highlightjs/mono-blue.css'"
     "--stringparam html.script './highlightjs/highlight.pack.js ./highlightjs/loader.js'"
-    "--param xref.with.number.and.title 1"
+    "--param xref.with.number.and.title 0"
     "--param toc.section.depth 0"
     "--param generate.consistent.ids 1"
     "--stringparam admon.style ''"
diff --git a/nixos/doc/manual/development/activation-script.section.md b/nixos/doc/manual/development/activation-script.section.md
index 1aee252fddea..c339258c6dc4 100644
--- a/nixos/doc/manual/development/activation-script.section.md
+++ b/nixos/doc/manual/development/activation-script.section.md
@@ -34,7 +34,7 @@ read which is set to `dry-activate` when a dry activation is done.
 
 An activation script can write to special files instructing
 `switch-to-configuration` to restart/reload units. The script will take these
-requests into account and will incorperate the unit configuration as described
+requests into account and will incorporate the unit configuration as described
 above. This means that the activation script will "fake" a modified unit file
 and `switch-to-configuration` will act accordingly. By doing so, configuration
 like [systemd.services.\<name\>.restartIfChanged](#opt-systemd.services) is
@@ -49,7 +49,7 @@ dry activation being `/run/nixos/dry-activation-restart-list` and
 `/run/nixos/dry-activation-reload-list`. Those files can contain
 newline-separated lists of unit names where duplicates are being ignored. These
 files are not create automatically and activation scripts must take the
-possiblility into account that they have to create them first.
+possibility into account that they have to create them first.
 
 ## NixOS snippets {#sec-activation-script-nixos-snippets}
 
diff --git a/nixos/doc/manual/development/bootspec.chapter.md b/nixos/doc/manual/development/bootspec.chapter.md
new file mode 100644
index 000000000000..96c12f24e7f1
--- /dev/null
+++ b/nixos/doc/manual/development/bootspec.chapter.md
@@ -0,0 +1,36 @@
+# Experimental feature: Bootspec {#sec-experimental-bootspec}
+
+Bootspec is a experimental feature, introduced in the [RFC-0125 proposal](https://github.com/NixOS/rfcs/pull/125), the reference implementation can be found [there](https://github.com/NixOS/nixpkgs/pull/172237) in order to standardize bootloader support
+and advanced boot workflows such as SecureBoot and potentially more.
+
+You can enable the creation of bootspec documents through [`boot.bootspec.enable = true`](options.html#opt-boot.bootspec.enable), which will prompt a warning until [RFC-0125](https://github.com/NixOS/rfcs/pull/125) is officially merged.
+
+## Schema {#sec-experimental-bootspec-schema}
+
+The bootspec schema is versioned and validated against [a CUE schema file](https://cuelang.org/) which should considered as the source of truth for your applications.
+
+You will find the current version [here](../../../modules/system/activation/bootspec.cue).
+
+## Extensions mechanism {#sec-experimental-bootspec-extensions}
+
+Bootspec cannot account for all usecases.
+
+For this purpose, Bootspec offers a generic extension facility [`boot.bootspec.extensions`](options.html#opt-boot.bootspec.extensions) which can be used to inject any data needed for your usecases.
+
+An example for SecureBoot is to get the Nix store path to `/etc/os-release` in order to bake it into a unified kernel image:
+
+```nix
+{ config, lib, ... }: {
+  boot.bootspec.extensions = {
+    "org.secureboot.osRelease" = config.environment.etc."os-release".source;
+  };
+}
+```
+
+To reduce incompatibility and prevent names from clashing between applications, it is **highly recommended** to use a unique namespace for your extensions.
+
+## External bootloaders {#sec-experimental-bootspec-external-bootloaders}
+
+It is possible to enable your own bootloader through [`boot.loader.external.installHook`](options.html#opt-boot.loader.external.installHook) which can wrap an existing bootloader.
+
+Currently, there is no good story to compose existing bootloaders to enrich their features, e.g. SecureBoot, etc. It will be necessary to reimplement or reuse existing parts.
diff --git a/nixos/doc/manual/development/development.xml b/nixos/doc/manual/development/development.xml
index 624ee3931659..949468c9021d 100644
--- a/nixos/doc/manual/development/development.xml
+++ b/nixos/doc/manual/development/development.xml
@@ -12,6 +12,7 @@
  <xi:include href="../from_md/development/sources.chapter.xml" />
  <xi:include href="../from_md/development/writing-modules.chapter.xml" />
  <xi:include href="../from_md/development/building-parts.chapter.xml" />
+ <xi:include href="../from_md/development/bootspec.chapter.xml" />
  <xi:include href="../from_md/development/what-happens-during-a-system-switch.chapter.xml" />
  <xi:include href="../from_md/development/writing-documentation.chapter.xml" />
  <xi:include href="../from_md/development/nixos-tests.chapter.xml" />
diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md
index 40b4d78b250e..e398d6c30cce 100644
--- a/nixos/doc/manual/development/option-types.section.md
+++ b/nixos/doc/manual/development/option-types.section.md
@@ -345,7 +345,7 @@ that are handled like a separate module.
 It takes a parameter *`o`*, that should be a set, or a function returning
 a set with an `options` key defining the sub-options. Submodule option
 definitions are type-checked accordingly to the `options` declarations.
-Of course, you can nest submodule option definitons for even higher
+Of course, you can nest submodule option definitions for even higher
 modularity.
 
 The option set can be defined directly
diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md
index 2efe52b9883c..f3edea3e7047 100644
--- a/nixos/doc/manual/development/writing-nixos-tests.section.md
+++ b/nixos/doc/manual/development/writing-nixos-tests.section.md
@@ -298,7 +298,7 @@ The following methods are available on machine objects:
 
 :   Wait until the supplied regular expressions match a line of the
     serial console output. This method is useful when OCR is not
-    possibile or accurate enough.
+    possible or accurate enough.
 
 `wait_for_window`
 
@@ -351,7 +351,7 @@ This applies to `systemctl`, `get_unit_info`, `wait_for_unit`,
 `start_job` and `stop_job`.
 
 For faster dev cycles it\'s also possible to disable the code-linters
-(this shouldn\'t be commited though):
+(this shouldn\'t be committed though):
 
 ```nix
 {
diff --git a/nixos/doc/manual/from_md/configuration/gpu-accel.chapter.xml b/nixos/doc/manual/from_md/configuration/gpu-accel.chapter.xml
index cc559a1933d9..90d2c17e12ef 100644
--- a/nixos/doc/manual/from_md/configuration/gpu-accel.chapter.xml
+++ b/nixos/doc/manual/from_md/configuration/gpu-accel.chapter.xml
@@ -180,6 +180,48 @@ environment.variables.VK_ICD_FILENAMES =
 </programlisting>
     </section>
   </section>
+  <section xml:id="sec-gpu-accel-va-api">
+    <title>VA-API</title>
+    <para>
+      <link xlink:href="https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html">VA-API
+      (Video Acceleration API)</link> is an open-source library and API
+      specification, which provides access to graphics hardware
+      acceleration capabilities for video processing.
+    </para>
+    <para>
+      VA-API drivers are loaded by <literal>libva</literal>. The version
+      in nixpkgs is built to search the opengl driver path, so drivers
+      can be installed in
+      <xref linkend="opt-hardware.opengl.extraPackages" />.
+    </para>
+    <para>
+      VA-API can be tested using:
+    </para>
+    <programlisting>
+$ nix-shell -p libva-utils --run vainfo
+</programlisting>
+    <section xml:id="sec-gpu-accel-va-api-intel">
+      <title>Intel</title>
+      <para>
+        Modern Intel GPUs use the iHD driver, which can be installed
+        with:
+      </para>
+      <programlisting language="bash">
+hardware.opengl.extraPackages = [
+  intel-media-driver
+];
+</programlisting>
+      <para>
+        Older Intel GPUs use the i965 driver, which can be installed
+        with:
+      </para>
+      <programlisting language="bash">
+hardware.opengl.extraPackages = [
+  vaapiIntel
+];
+</programlisting>
+    </section>
+  </section>
   <section xml:id="sec-gpu-accel-common-issues">
     <title>Common issues</title>
     <section xml:id="sec-gpu-accel-common-issues-permissions">
diff --git a/nixos/doc/manual/from_md/configuration/profiles/hardened.section.xml b/nixos/doc/manual/from_md/configuration/profiles/hardened.section.xml
index 44c11786d940..1fd5a9179887 100644
--- a/nixos/doc/manual/from_md/configuration/profiles/hardened.section.xml
+++ b/nixos/doc/manual/from_md/configuration/profiles/hardened.section.xml
@@ -9,7 +9,7 @@
     available to processes through the <literal>/sys</literal> and
     <literal>/proc</literal> filesystems. It also disables the User
     Namespaces feature of the kernel, which stops Nix from being able to
-    build anything (this particular setting can be overriden via
+    build anything (this particular setting can be overridden via
     <xref linkend="opt-security.allowUserNamespaces" />). See the
     <link xlink:href="https://github.com/nixos/nixpkgs/tree/master/nixos/modules/profiles/hardened.nix">profile
     source</link> for further detail on which settings are altered.
diff --git a/nixos/doc/manual/from_md/configuration/wireless.section.xml b/nixos/doc/manual/from_md/configuration/wireless.section.xml
index 82bc20135157..d39ec4fac493 100644
--- a/nixos/doc/manual/from_md/configuration/wireless.section.xml
+++ b/nixos/doc/manual/from_md/configuration/wireless.section.xml
@@ -54,7 +54,7 @@ networking.wireless.networks = {
   echelon = {
     pskRaw = &quot;dca6d6ed41f4ab5a984c9f55f6f66d4efdc720ebf66959810f4329bb391c5435&quot;;
   };
-}
+};
 </programlisting>
   <para>
     or you can use it to directly generate the
diff --git a/nixos/doc/manual/from_md/contributing-to-this-manual.chapter.xml b/nixos/doc/manual/from_md/contributing-to-this-manual.chapter.xml
index a9b0c6a5eefa..99dc5ce30b4b 100644
--- a/nixos/doc/manual/from_md/contributing-to-this-manual.chapter.xml
+++ b/nixos/doc/manual/from_md/contributing-to-this-manual.chapter.xml
@@ -1,7 +1,9 @@
 <chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="chap-contributing">
   <title>Contributing to this manual</title>
   <para>
-    The DocBook and CommonMark sources of NixOS’ manual are in the
+    The
+    <link xlink:href="https://en.wikipedia.org/wiki/DocBook">DocBook</link>
+    and CommonMark sources of the NixOS manual are in the
     <link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/doc/manual">nixos/doc/manual</link>
     subdirectory of the
     <link xlink:href="https://github.com/NixOS/nixpkgs">Nixpkgs</link>
@@ -19,4 +21,32 @@ $ nix-build nixos/release.nix -A manual.x86_64-linux
     If the build succeeds, the manual will be in
     <literal>./result/share/doc/nixos/index.html</literal>.
   </para>
+  <para>
+    <emphasis role="strong">Contributing to the man pages</emphasis>
+  </para>
+  <para>
+    The man pages are written in
+    <link xlink:href="https://en.wikipedia.org/wiki/DocBook">DocBook</link>
+    which is XML.
+  </para>
+  <para>
+    To see what your edits look like:
+  </para>
+  <programlisting>
+$ cd /path/to/nixpkgs
+$ nix-build nixos/release.nix -A manpages.x86_64-linux
+</programlisting>
+  <para>
+    You can then read the man page you edited by running
+  </para>
+  <programlisting>
+$ man --manpath=result/share/man nixos-rebuild # Replace nixos-rebuild with the command whose manual you edited
+</programlisting>
+  <para>
+    If you’re on a different architecture that’s supported by NixOS
+    (check nixos/release.nix) then replace
+    <literal>x86_64-linux</literal> with the architecture.
+    <literal>nix-build</literal> will complain otherwise, but should
+    also tell you which architecture you have + the supported ones.
+  </para>
 </chapter>
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 981ebf37e60f..8672ab8afe54 100644
--- a/nixos/doc/manual/from_md/development/activation-script.section.xml
+++ b/nixos/doc/manual/from_md/development/activation-script.section.xml
@@ -45,7 +45,7 @@ system.activationScripts.my-activation-script = {
     An activation script can write to special files instructing
     <literal>switch-to-configuration</literal> to restart/reload units.
     The script will take these requests into account and will
-    incorperate the unit configuration as described above. This means
+    incorporate the unit configuration as described above. This means
     that the activation script will <quote>fake</quote> a modified unit
     file and <literal>switch-to-configuration</literal> will act
     accordingly. By doing so, configuration like
@@ -66,7 +66,7 @@ system.activationScripts.my-activation-script = {
     <literal>/run/nixos/dry-activation-reload-list</literal>. Those
     files can contain newline-separated lists of unit names where
     duplicates are being ignored. These files are not create
-    automatically and activation scripts must take the possiblility into
+    automatically and activation scripts must take the possibility into
     account that they have to create them first.
   </para>
   <section xml:id="sec-activation-script-nixos-snippets">
diff --git a/nixos/doc/manual/from_md/development/bootspec.chapter.xml b/nixos/doc/manual/from_md/development/bootspec.chapter.xml
new file mode 100644
index 000000000000..acf8ca76bf5c
--- /dev/null
+++ b/nixos/doc/manual/from_md/development/bootspec.chapter.xml
@@ -0,0 +1,73 @@
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-experimental-bootspec">
+  <title>Experimental feature: Bootspec</title>
+  <para>
+    Bootspec is a experimental feature, introduced in the
+    <link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125
+    proposal</link>, the reference implementation can be found
+    <link xlink:href="https://github.com/NixOS/nixpkgs/pull/172237">there</link>
+    in order to standardize bootloader support and advanced boot
+    workflows such as SecureBoot and potentially more.
+  </para>
+  <para>
+    You can enable the creation of bootspec documents through
+    <link xlink:href="options.html#opt-boot.bootspec.enable"><literal>boot.bootspec.enable = true</literal></link>,
+    which will prompt a warning until
+    <link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125</link>
+    is officially merged.
+  </para>
+  <section xml:id="sec-experimental-bootspec-schema">
+    <title>Schema</title>
+    <para>
+      The bootspec schema is versioned and validated against
+      <link xlink:href="https://cuelang.org/">a CUE schema file</link>
+      which should considered as the source of truth for your
+      applications.
+    </para>
+    <para>
+      You will find the current version
+      <link xlink:href="../../../modules/system/activation/bootspec.cue">here</link>.
+    </para>
+  </section>
+  <section xml:id="sec-experimental-bootspec-extensions">
+    <title>Extensions mechanism</title>
+    <para>
+      Bootspec cannot account for all usecases.
+    </para>
+    <para>
+      For this purpose, Bootspec offers a generic extension facility
+      <link xlink:href="options.html#opt-boot.bootspec.extensions"><literal>boot.bootspec.extensions</literal></link>
+      which can be used to inject any data needed for your usecases.
+    </para>
+    <para>
+      An example for SecureBoot is to get the Nix store path to
+      <literal>/etc/os-release</literal> in order to bake it into a
+      unified kernel image:
+    </para>
+    <programlisting language="bash">
+{ config, lib, ... }: {
+  boot.bootspec.extensions = {
+    &quot;org.secureboot.osRelease&quot; = config.environment.etc.&quot;os-release&quot;.source;
+  };
+}
+</programlisting>
+    <para>
+      To reduce incompatibility and prevent names from clashing between
+      applications, it is <emphasis role="strong">highly
+      recommended</emphasis> to use a unique namespace for your
+      extensions.
+    </para>
+  </section>
+  <section xml:id="sec-experimental-bootspec-external-bootloaders">
+    <title>External bootloaders</title>
+    <para>
+      It is possible to enable your own bootloader through
+      <link xlink:href="options.html#opt-boot.loader.external.installHook"><literal>boot.loader.external.installHook</literal></link>
+      which can wrap an existing bootloader.
+    </para>
+    <para>
+      Currently, there is no good story to compose existing bootloaders
+      to enrich their features, e.g. SecureBoot, etc. It will be
+      necessary to reimplement or reuse existing parts.
+    </para>
+  </section>
+</chapter>
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 4036bc0ba743..c0f40cb34232 100644
--- a/nixos/doc/manual/from_md/development/option-types.section.xml
+++ b/nixos/doc/manual/from_md/development/option-types.section.xml
@@ -712,7 +712,7 @@
       <literal>options</literal> key defining the sub-options. Submodule
       option definitions are type-checked accordingly to the
       <literal>options</literal> declarations. Of course, you can nest
-      submodule option definitons for even higher modularity.
+      submodule option definitions for even higher modularity.
     </para>
     <para>
       The option set can be defined directly
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 4db196273dad..99bd37808c20 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
@@ -536,7 +536,7 @@ start_all()
           <para>
             Wait until the supplied regular expressions match a line of
             the serial console output. This method is useful when OCR is
-            not possibile or accurate enough.
+            not possible or accurate enough.
           </para>
         </listitem>
       </varlistentry>
@@ -631,7 +631,7 @@ machine.wait_for_unit(&quot;xautolock.service&quot;, &quot;x-session-user&quot;)
     </para>
     <para>
       For faster dev cycles it's also possible to disable the
-      code-linters (this shouldn't be commited though):
+      code-linters (this shouldn't be committed though):
     </para>
     <programlisting language="bash">
 {
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 ea2d01bebcc2..080f1535e410 100644
--- a/nixos/doc/manual/from_md/installation/building-nixos.chapter.xml
+++ b/nixos/doc/manual/from_md/installation/building-nixos.chapter.xml
@@ -24,7 +24,7 @@
   </itemizedlist>
   <para>
     System images, such as the live installer ones, know how to enforce
-    configuration settings on wich they immediately depend in order to
+    configuration settings on which they immediately depend in order to
     work correctly.
   </para>
   <para>
@@ -102,7 +102,7 @@ $ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd
       it needs at a minimum for correct functioning, while the installer
       base image overrides the entire file system layout because there
       can’t be any other guarantees on a live medium than those given by
-      the live medium itself. The latter is especially true befor
+      the live medium itself. The latter is especially true before
       formatting the target block device(s). On the other hand, the
       netboot iso only overrides its minimum dependencies since netboot
       images are always made-to-target.
diff --git a/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml b/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml
index 35309a7aa326..f29200952ac5 100644
--- a/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml
+++ b/nixos/doc/manual/from_md/installation/installing-from-other-distro.section.xml
@@ -223,7 +223,7 @@ $ sudo `which nixos-generate-config`
       <para>
         You'll likely want to set a root password for your first boot
         using the configuration files because you won't have a chance to
-        enter a password until after you reboot. You can initalize the
+        enter a password until after you reboot. You can initialize the
         root password to an empty one with this line: (and of course
         don't forget to set one once you've rebooted or to lock the
         account with <literal>sudo passwd -l root</literal> if you use
diff --git a/nixos/doc/manual/from_md/installation/installing-virtualbox-guest.section.xml b/nixos/doc/manual/from_md/installation/installing-virtualbox-guest.section.xml
index c8bb286c8f33..8b82a617e7f5 100644
--- a/nixos/doc/manual/from_md/installation/installing-virtualbox-guest.section.xml
+++ b/nixos/doc/manual/from_md/installation/installing-virtualbox-guest.section.xml
@@ -1,4 +1,4 @@
-<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-instaling-virtualbox-guest">
+<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-installing-virtualbox-guest">
   <title>Installing in a VirtualBox guest</title>
   <para>
     Installing NixOS into a VirtualBox guest is convenient for users who
diff --git a/nixos/doc/manual/from_md/installation/installing.chapter.xml b/nixos/doc/manual/from_md/installation/installing.chapter.xml
index f2ed58c0c1fe..c8d1e26b5e77 100644
--- a/nixos/doc/manual/from_md/installation/installing.chapter.xml
+++ b/nixos/doc/manual/from_md/installation/installing.chapter.xml
@@ -256,7 +256,7 @@ $ sudo -i
       </para>
       <para>
         On the minimal installer, NetworkManager is not available, so
-        configuration must be perfomed manually. To configure the wifi,
+        configuration must be performed manually. To configure the wifi,
         first start wpa_supplicant with
         <literal>sudo systemctl start wpa_supplicant</literal>, then run
         <literal>wpa_cli</literal>. For most home networks, you need to
diff --git a/nixos/doc/manual/from_md/release-notes/rl-1603.section.xml b/nixos/doc/manual/from_md/release-notes/rl-1603.section.xml
index 172b800b5992..afbd2fd2c797 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-1603.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-1603.section.xml
@@ -545,7 +545,7 @@ nginx.override {
         <literal>services.udev.extraRules</literal> option now writes
         rules to <literal>99-local.rules</literal> instead of
         <literal>10-local.rules</literal>. This makes all the user rules
-        apply after others, so their results wouldn't be overriden by
+        apply after others, so their results wouldn't be overridden by
         anything else.
       </para>
     </listitem>
diff --git a/nixos/doc/manual/from_md/release-notes/rl-1709.section.xml b/nixos/doc/manual/from_md/release-notes/rl-1709.section.xml
index 8f0efe816e51..fc5d11f07c8d 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-1709.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-1709.section.xml
@@ -666,7 +666,7 @@ rmdir /var/lib/ipfs/.ipfs
       <listitem>
         <para>
           <literal>services.firefox.syncserver</literal> now runs by
-          default as a non-root user. To accomodate this change, the
+          default as a non-root user. To accommodate this change, the
           default sqlite database location has also been changed.
           Migration should work automatically. Refer to the description
           of the options for more details.
diff --git a/nixos/doc/manual/from_md/release-notes/rl-1903.section.xml b/nixos/doc/manual/from_md/release-notes/rl-1903.section.xml
index f26e68e13200..31c5c1fc7f49 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-1903.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-1903.section.xml
@@ -271,7 +271,7 @@
       <listitem>
         <para>
           The versioned <literal>postgresql</literal> have been renamed
-          to use underscore number seperators. For example,
+          to use underscore number separators. For example,
           <literal>postgresql96</literal> has been renamed to
           <literal>postgresql_9_6</literal>.
         </para>
diff --git a/nixos/doc/manual/from_md/release-notes/rl-1909.section.xml b/nixos/doc/manual/from_md/release-notes/rl-1909.section.xml
index 83cd649f4ea0..f9b99961d277 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-1909.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-1909.section.xml
@@ -498,7 +498,7 @@
       <listitem>
         <para>
           The <literal>prometheus-nginx-exporter</literal> package now
-          uses the offical exporter provided by NGINX Inc. Its metrics
+          uses the official exporter provided by NGINX Inc. Its metrics
           are differently structured and are incompatible to the old
           ones. For information about the metrics, have a look at the
           <link xlink:href="https://github.com/nginxinc/nginx-prometheus-exporter">official
@@ -524,7 +524,7 @@
         <para>
           By default, prometheus exporters are now run with
           <literal>DynamicUser</literal> enabled. Exporters that need a
-          real user, now run under a seperate user and group which
+          real user, now run under a separate user and group which
           follow the pattern
           <literal>&lt;exporter-name&gt;-exporter</literal>, instead of
           the previous default <literal>nobody</literal> and
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2105.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2105.section.xml
index fb11b19229e2..3477f29f4281 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2105.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2105.section.xml
@@ -1304,7 +1304,7 @@ self: super:
       <listitem>
         <para>
           In the ACME module, the data used to build the hash for the
-          account directory has changed to accomodate new features to
+          account directory has changed to accommodate new features to
           reduce account rate limit issues. This will trigger new
           account creation on the first rebuild following this update.
           No issues are expected to arise from this, thanks to the new
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 97e993e83ff0..c43757a9a057 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
@@ -328,7 +328,7 @@
       <listitem>
         <para>
           <link xlink:href="https://maddy.email/">Maddy</link>, a free
-          an open source mail server. Availabe as
+          an open source mail server. Available as
           <link linkend="opt-services.maddy.enable">services.maddy</link>.
         </para>
       </listitem>
@@ -1422,7 +1422,7 @@
               derivation if <literal>name</literal> is
               <literal>&quot;vim&quot;</literal> (the default). This
               makes the <literal>wrapManual</literal> argument obsolete,
-              but this behavior can be overriden by setting the
+              but this behavior can be overridden by setting the
               <literal>standalone</literal> argument.
             </para>
           </listitem>
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 b47808dc2087..f7168d5ea17e 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
@@ -1280,9 +1280,9 @@ services.github-runner.serviceOverrides.SupplementaryGroups = [
       </listitem>
       <listitem>
         <para>
-          Option descriptions, examples, and defaults writting in
-          DocBook are now deprecated. Using CommonMark is preferred and
-          will become the default in a future release.
+          Option descriptions, examples, and defaults writing in DocBook
+          are now deprecated. Using CommonMark is preferred and will
+          become the default in a future release.
         </para>
       </listitem>
       <listitem>
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
index e7bfd27e249b..657b5c6f26d8 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2305.section.xml
@@ -39,6 +39,21 @@
       </listitem>
       <listitem>
         <para>
+          <link xlink:href="https://github.com/ellie/atuin">atuin</link>,
+          a sync server for shell history. Available as
+          <link linkend="opt-services.atuin.enable">services.atuin</link>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <link xlink:href="https://gitlab.com/kop316/mmsd">mmsd</link>,
+          a lower level daemon that transmits and recieves MMSes.
+          Available as
+          <link linkend="opt-services.mmsd.enable">services.mmsd</link>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <link xlink:href="https://v2raya.org">v2rayA</link>, a Linux
           web GUI client of Project V which supports V2Ray, Xray, SS,
           SSR, Trojan and Pingtunnel. Available as
@@ -63,6 +78,14 @@
       </listitem>
       <listitem>
         <para>
+          <literal>borgbackup</literal> module now has an option for
+          inhibiting system sleep while backups are running, defaulting
+          to off (not inhibiting sleep), available as
+          <link linkend="opt-services.borgbackup.jobs._name_.inhibitsSleep"><literal>services.borgbackup.jobs.&lt;name&gt;.inhibitsSleep</literal></link>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The EC2 image module no longer fetches instance metadata in
           stage-1. This results in a significantly smaller initramfs,
           since network drivers no longer need to be included, and
@@ -91,38 +114,38 @@
           <link linkend="opt-services.snapserver.openFirewall">services.snapserver.openFirewall</link>
           module option default value has been changed from
           <literal>true</literal> to <literal>false</literal>. You will
-          need to explicitely set this option to
-          <literal>true</literal>, or configure your firewall.
+          need to explicitly set this option to <literal>true</literal>,
+          or configure your firewall.
         </para>
       </listitem>
       <listitem>
         <para>
           The
-          <link linkend="opt-services.avahi.openFirewall">services.avahi.openFirewall</link>
+          <link linkend="opt-services.tmate-ssh-server.openFirewall">services.tmate-ssh-server.openFirewall</link>
           module option default value has been changed from
           <literal>true</literal> to <literal>false</literal>. You will
-          need to explicitely set this option to
-          <literal>true</literal>, or configure your firewall.
+          need to explicitly set this option to <literal>true</literal>,
+          or configure your firewall.
         </para>
       </listitem>
       <listitem>
         <para>
           The
-          <link linkend="opt-services.tmate-ssh-server.openFirewall">services.tmate-ssh-server.openFirewall</link>
+          <link linkend="opt-services.unifi-video.openFirewall">services.unifi-video.openFirewall</link>
           module option default value has been changed from
           <literal>true</literal> to <literal>false</literal>. You will
-          need to explicitely set this option to
-          <literal>true</literal>, or configure your firewall.
+          need to explicitly set this option to <literal>true</literal>,
+          or configure your firewall.
         </para>
       </listitem>
       <listitem>
         <para>
-          The
-          <link linkend="opt-services.unifi-video.openFirewall">services.unifi-video.openFirewall</link>
-          module option default value has been changed from
-          <literal>true</literal> to <literal>false</literal>. You will
-          need to explicitely set this option to
-          <literal>true</literal>, or configure your firewall.
+          The Nginx module now validates the syntax of config files at
+          build time. For more complex configurations (using
+          <literal>include</literal> with out-of-store files notably)
+          you may need to disable this check by setting
+          <link linkend="opt-services.nginx.validateConfig">services.nginx.validateConfig</link>
+          to <literal>false</literal>.
         </para>
       </listitem>
       <listitem>
@@ -156,6 +179,17 @@
       </listitem>
       <listitem>
         <para>
+          In <literal>mastodon</literal> it is now necessary to specify
+          location of file with <literal>PostgreSQL</literal> database
+          password. In
+          <literal>services.mastodon.database.passwordFile</literal>
+          parameter default value
+          <literal>/var/lib/mastodon/secrets/db-password</literal> has
+          been changed to <literal>null</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>nix.readOnlyStore</literal> option has been
           renamed to <literal>boot.readOnlyNixStore</literal> to clarify
           that it configures the NixOS boot process, not the Nix daemon.
@@ -187,6 +221,16 @@
       </listitem>
       <listitem>
         <para>
+          The module <literal>usbmuxd</literal> now has the ability to
+          change the package used by the daemon. In case you’re
+          experiencing issues with <literal>usbmuxd</literal> you can
+          try an alternative program like <literal>usbmuxd2</literal>.
+          Available as
+          <link linkend="opt-services.usbmuxd.package">services.usbmuxd.package</link>
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <literal>services.mastodon</literal> gained a tootctl wrapped
           named <literal>mastodon-tootctl</literal> similar to
           <literal>nextcloud-occ</literal> which can be executed from
@@ -214,12 +258,18 @@
       </listitem>
       <listitem>
         <para>
-          The minimal ISO image now use
+          The minimal ISO image now uses the
           <literal>nixos/modules/profiles/minimal.nix</literal> profile.
         </para>
       </listitem>
       <listitem>
         <para>
+          <literal>mastodon</literal> now supports connection to a
+          remote <literal>PostgreSQL</literal> database.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           A new <literal>virtualisation.rosetta</literal> module was
           added to allow running <literal>x86_64</literal> binaries
           through
@@ -233,6 +283,21 @@
       </listitem>
       <listitem>
         <para>
+          The new option <literal>users.motdFile</literal> allows
+          configuring a Message Of The Day that can be updated
+          dynamically.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Enabling global redirect in
+          <literal>services.nginx.virtualHosts</literal> now allows one
+          to add exceptions with the <literal>locations</literal>
+          option.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           Resilio sync secret keys can now be provided using a secrets
           file at runtime, preventing these secrets from ending up in
           the Nix store.
@@ -240,11 +305,33 @@
       </listitem>
       <listitem>
         <para>
+          The <literal>services.fwupd</literal> module now allows
+          arbitrary daemon settings to be configured in a structured
+          manner
+          (<link linkend="opt-services.fwupd.daemonSettings"><literal>services.fwupd.daemonSettings</literal></link>).
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>unifi-poller</literal> package and corresponding
           NixOS module have been renamed to <literal>unpoller</literal>
           to match upstream.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The new option
+          <literal>services.tailscale.useRoutingFeatures</literal>
+          controls various settings for using Tailscale features like
+          exit nodes and subnet routers. If you wish to use your machine
+          as an exit node, you can set this setting to
+          <literal>server</literal>, otherwise if you wish to use an
+          exit node you can set this setting to
+          <literal>client</literal>. The strict RPF warning has been
+          removed as the RPF will be loosened automatically based on the
+          value of this setting.
+        </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 17da261fbdaa..7b0b5ea1c447 100644
--- a/nixos/doc/manual/installation/building-nixos.chapter.md
+++ b/nixos/doc/manual/installation/building-nixos.chapter.md
@@ -9,7 +9,7 @@ You have two options:
 - Combine them with (any of) your host config(s)
 
 System images, such as the live installer ones, know how to enforce configuration settings
-on wich they immediately depend in order to work correctly.
+on which they immediately depend in order to work correctly.
 
 However, if you are confident, you can opt to override those
 enforced values with `mkForce`.
@@ -75,6 +75,6 @@ configuration values upon which the correct functioning of the image depends.
 For example, the iso base image overrides those file systems which it needs at a minimum
 for correct functioning, while the installer base image overrides the entire file system
 layout because there can't be any other guarantees on a live medium than those given
-by the live medium itself. The latter is especially true befor formatting the target
+by the live medium itself. The latter is especially true before formatting the target
 block device(s). On the other hand, the netboot iso only overrides its minimum dependencies
 since netboot images are always made-to-target.
diff --git a/nixos/doc/manual/installation/installing-from-other-distro.section.md b/nixos/doc/manual/installation/installing-from-other-distro.section.md
index b9ccf1415179..36ef29d44639 100644
--- a/nixos/doc/manual/installation/installing-from-other-distro.section.md
+++ b/nixos/doc/manual/installation/installing-from-other-distro.section.md
@@ -158,7 +158,7 @@ The first steps to all these are the same:
 
     You\'ll likely want to set a root password for your first boot using
     the configuration files because you won\'t have a chance to enter a
-    password until after you reboot. You can initalize the root password
+    password until after you reboot. You can initialize the root password
     to an empty one with this line: (and of course don\'t forget to set
     one once you\'ve rebooted or to lock the account with
     `sudo passwd -l root` if you use `sudo`)
diff --git a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
index e9c2a621c1bb..c3bbfe12152e 100644
--- a/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
+++ b/nixos/doc/manual/installation/installing-virtualbox-guest.section.md
@@ -1,4 +1,4 @@
-# Installing in a VirtualBox guest {#sec-instaling-virtualbox-guest}
+# Installing in a VirtualBox guest {#sec-installing-virtualbox-guest}
 
 Installing NixOS into a VirtualBox guest is convenient for users who
 want to try NixOS without installing it on bare metal. If you want to
diff --git a/nixos/doc/manual/installation/installing.chapter.md b/nixos/doc/manual/installation/installing.chapter.md
index 2c86cb923a5c..04bc7b1f2072 100644
--- a/nixos/doc/manual/installation/installing.chapter.md
+++ b/nixos/doc/manual/installation/installing.chapter.md
@@ -162,7 +162,7 @@ network manually, disable NetworkManager with
 `systemctl stop NetworkManager`.
 
 On the minimal installer, NetworkManager is not available, so
-configuration must be perfomed manually. To configure the wifi, first
+configuration must be performed manually. To configure the wifi, first
 start wpa_supplicant with `sudo systemctl start wpa_supplicant`, then
 run `wpa_cli`. For most home networks, you need to type in the following
 commands:
diff --git a/nixos/doc/manual/man-nixos-rebuild.xml b/nixos/doc/manual/man-nixos-rebuild.xml
index ea96f49fa977..cab871661a75 100644
--- a/nixos/doc/manual/man-nixos-rebuild.xml
+++ b/nixos/doc/manual/man-nixos-rebuild.xml
@@ -134,7 +134,7 @@
    </arg>
    <arg>
     <option>-I</option>
-    <replaceable>path</replaceable>
+    <replaceable>NIX_PATH</replaceable>
    </arg>
    <arg>
     <group choice='req'>
@@ -624,7 +624,7 @@
 
   <para>
    In addition, <command>nixos-rebuild</command> accepts various Nix-related
-   flags, including <option>--max-jobs</option> / <option>-j</option>,
+   flags, including <option>--max-jobs</option> / <option>-j</option>, <option>-I</option>,
    <option>--show-trace</option>, <option>--keep-failed</option>,
    <option>--keep-going</option>, <option>--impure</option>, and <option>--verbose</option> /
    <option>-v</option>. See the Nix manual for details.
@@ -649,6 +649,20 @@
 
    <varlistentry>
     <term>
+     <envar>NIX_PATH</envar>
+    </term>
+    <listitem>
+     <para>
+      A colon-separated list of directories used to look up Nix expressions enclosed in angle brackets (e.g &lt;nixpkgs&gt;). Example
+      <screen>
+          nixpkgs=./my-nixpkgs
+      </screen>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
      <envar>NIX_SSHOPTS</envar>
     </term>
     <listitem>
diff --git a/nixos/doc/manual/release-notes/rl-1603.section.md b/nixos/doc/manual/release-notes/rl-1603.section.md
index dce879ec16d0..e4da7fd3094d 100644
--- a/nixos/doc/manual/release-notes/rl-1603.section.md
+++ b/nixos/doc/manual/release-notes/rl-1603.section.md
@@ -202,7 +202,7 @@ When upgrading from a previous release, please be aware of the following incompa
   }
   ```
 
-- `services.udev.extraRules` option now writes rules to `99-local.rules` instead of `10-local.rules`. This makes all the user rules apply after others, so their results wouldn\'t be overriden by anything else.
+- `services.udev.extraRules` option now writes rules to `99-local.rules` instead of `10-local.rules`. This makes all the user rules apply after others, so their results wouldn\'t be overridden by anything else.
 
 - Large parts of the `services.gitlab` module has been been rewritten. There are new configuration options available. The `stateDir` option was renamned to `statePath` and the `satellitesDir` option was removed. Please review the currently available options.
 
diff --git a/nixos/doc/manual/release-notes/rl-1709.section.md b/nixos/doc/manual/release-notes/rl-1709.section.md
index e5af22721b0c..970a0c2b7dd1 100644
--- a/nixos/doc/manual/release-notes/rl-1709.section.md
+++ b/nixos/doc/manual/release-notes/rl-1709.section.md
@@ -238,7 +238,7 @@ When upgrading from a previous release, please be aware of the following incompa
 
 - `cc-wrapper`\'s setup-hook now exports a number of environment variables corresponding to binutils binaries, (e.g. `LD`, `STRIP`, `RANLIB`, etc). This is done to prevent packages\' build systems guessing, which is harder to predict, especially when cross-compiling. However, some packages have broken due to this---their build systems either not supporting, or claiming to support without adequate testing, taking such environment variables as parameters.
 
-- `services.firefox.syncserver` now runs by default as a non-root user. To accomodate this change, the default sqlite database location has also been changed. Migration should work automatically. Refer to the description of the options for more details.
+- `services.firefox.syncserver` now runs by default as a non-root user. To accommodate this change, the default sqlite database location has also been changed. Migration should work automatically. Refer to the description of the options for more details.
 
 - The `compiz` window manager and package was removed. The system support had been broken for several years.
 
diff --git a/nixos/doc/manual/release-notes/rl-1903.section.md b/nixos/doc/manual/release-notes/rl-1903.section.md
index 7637a70c1bf8..e560b9f30448 100644
--- a/nixos/doc/manual/release-notes/rl-1903.section.md
+++ b/nixos/doc/manual/release-notes/rl-1903.section.md
@@ -73,7 +73,7 @@ When upgrading from a previous release, please be aware of the following incompa
 
 - OpenSMTPD has been upgraded to version 6.4.0p1. This release makes backwards-incompatible changes to the configuration file format. See `man smtpd.conf` for more information on the new file format.
 
-- The versioned `postgresql` have been renamed to use underscore number seperators. For example, `postgresql96` has been renamed to `postgresql_9_6`.
+- The versioned `postgresql` have been renamed to use underscore number separators. For example, `postgresql96` has been renamed to `postgresql_9_6`.
 
 - Package `consul-ui` and passthrough `consul.ui` have been removed. The package `consul` now uses upstream releases that vendor the UI into the binary. See [\#48714](https://github.com/NixOS/nixpkgs/pull/48714#issuecomment-433454834) for details.
 
diff --git a/nixos/doc/manual/release-notes/rl-1909.section.md b/nixos/doc/manual/release-notes/rl-1909.section.md
index 572f1bf5a255..0f09f9b92734 100644
--- a/nixos/doc/manual/release-notes/rl-1909.section.md
+++ b/nixos/doc/manual/release-notes/rl-1909.section.md
@@ -154,13 +154,13 @@ When upgrading from a previous release, please be aware of the following incompa
 
 - The setopt declarations will be evaluated at the end of `/etc/zshrc`, so any code in [programs.zsh.interactiveShellInit](options.html#opt-programs.zsh.interactiveShellInit), [programs.zsh.loginShellInit](options.html#opt-programs.zsh.loginShellInit) and [programs.zsh.promptInit](options.html#opt-programs.zsh.promptInit) may break if it relies on those options being set.
 
-- The `prometheus-nginx-exporter` package now uses the offical exporter provided by NGINX Inc. Its metrics are differently structured and are incompatible to the old ones. For information about the metrics, have a look at the [official repo](https://github.com/nginxinc/nginx-prometheus-exporter).
+- The `prometheus-nginx-exporter` package now uses the official exporter provided by NGINX Inc. Its metrics are differently structured and are incompatible to the old ones. For information about the metrics, have a look at the [official repo](https://github.com/nginxinc/nginx-prometheus-exporter).
 
 - The `shibboleth-sp` package has been updated to version 3. It is largely backward compatible, for further information refer to the [release notes](https://wiki.shibboleth.net/confluence/display/SP3/ReleaseNotes) and [upgrade guide](https://wiki.shibboleth.net/confluence/display/SP3/UpgradingFromV2).
 
   Nodejs 8 is scheduled EOL under the lifetime of 19.09 and has been dropped.
 
-- By default, prometheus exporters are now run with `DynamicUser` enabled. Exporters that need a real user, now run under a seperate user and group which follow the pattern `<exporter-name>-exporter`, instead of the previous default `nobody` and `nogroup`. Only some exporters are affected by the latter, namely the exporters `dovecot`, `node`, `postfix` and `varnish`.
+- By default, prometheus exporters are now run with `DynamicUser` enabled. Exporters that need a real user, now run under a separate user and group which follow the pattern `<exporter-name>-exporter`, instead of the previous default `nobody` and `nogroup`. Only some exporters are affected by the latter, namely the exporters `dovecot`, `node`, `postfix` and `varnish`.
 
 - The `ibus-qt` package is not installed by default anymore when [i18n.inputMethod.enabled](options.html#opt-i18n.inputMethod.enabled) is set to `ibus`. If IBus support in Qt 4.x applications is required, add the `ibus-qt` package to your [environment.systemPackages](options.html#opt-environment.systemPackages) manually.
 
diff --git a/nixos/doc/manual/release-notes/rl-2105.section.md b/nixos/doc/manual/release-notes/rl-2105.section.md
index 359f2e5b2e58..77c4a9cd7a0a 100644
--- a/nixos/doc/manual/release-notes/rl-2105.section.md
+++ b/nixos/doc/manual/release-notes/rl-2105.section.md
@@ -369,7 +369,7 @@ When upgrading from a previous release, please be aware of the following incompa
 
 - The zookeeper package does not provide `zooInspector.sh` anymore, as that \"contrib\" has been dropped from upstream releases.
 
-- In the ACME module, the data used to build the hash for the account directory has changed to accomodate new features to reduce account rate limit issues. This will trigger new account creation on the first rebuild following this update. No issues are expected to arise from this, thanks to the new account creation handling.
+- In the ACME module, the data used to build the hash for the account directory has changed to accommodate new features to reduce account rate limit issues. This will trigger new account creation on the first rebuild following this update. No issues are expected to arise from this, thanks to the new account creation handling.
 
 - [users.users._name_.createHome](options.html#opt-users.users._name_.createHome) now always ensures home directory permissions to be `0700`. Permissions had previously been ignored for already existing home directories, possibly leaving them readable by others. The option\'s description was incorrect regarding ownership management and has been simplified greatly.
 
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md
index 217aa6056cad..7cc0c308ee65 100644
--- a/nixos/doc/manual/release-notes/rl-2205.section.md
+++ b/nixos/doc/manual/release-notes/rl-2205.section.md
@@ -107,7 +107,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [kanidm](https://kanidm.github.io/kanidm/stable/), an identity management server written in Rust. Available as [services.kanidm](#opt-services.kanidm.enableServer)
 
-- [Maddy](https://maddy.email/), a free an open source mail server. Availabe as [services.maddy](#opt-services.maddy.enable).
+- [Maddy](https://maddy.email/), a free an open source mail server. Available as [services.maddy](#opt-services.maddy.enable).
 
 - [matrix-conduit](https://conduit.rs/), a simple, fast and reliable chat server powered by matrix. Available as [services.matrix-conduit](option.html#opt-services.matrix-conduit.enable).
 
@@ -562,7 +562,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 - `pkgs._7zz` is now correctly licensed as LGPL3+ and BSD3 with optional unfree unRAR licensed code
 
 - The `vim.customize` function produced by `vimUtils.makeCustomizable` now has a slightly different interface:
-  * The wrapper now includes everything in the given Vim derivation if `name` is `"vim"` (the default). This makes the `wrapManual` argument obsolete, but this behavior can be overriden by setting the `standalone` argument.
+  * The wrapper now includes everything in the given Vim derivation if `name` is `"vim"` (the default). This makes the `wrapManual` argument obsolete, but this behavior can be overridden by setting the `standalone` argument.
   * All the executables present in the given derivation (or, in `standalone` mode, only the `*vim` ones) are wrapped. This makes the `wrapGui` argument obsolete.
   * The `vimExecutableName` and `gvimExecutableName` arguments were replaced by a single `executableName` argument in which the shell variable `$exe` can be used to refer to the wrapped executable's name.
 
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index cbede784f069..e92c776b33e3 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -385,7 +385,7 @@ In addition to numerous new and upgraded packages, this release includes the fol
 
 - 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.
 
-- Option descriptions, examples, and defaults writting in DocBook are now deprecated. Using CommonMark is preferred and will become the default in a future release.
+- Option descriptions, examples, and defaults writing in DocBook are now deprecated. Using CommonMark is preferred and will become the default in a future release.
 
 - The `documentation.nixos.options.allowDocBook` option was added to ease the transition to CommonMark option documentation. Setting this option to `false` causes an error for every option included in the manual that uses DocBook documentation; it defaults to `true` to preserve the previous behavior and will be removed once the transition to CommonMark is complete.
 
diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md
index cfebfd9a86c4..27bd64e514f1 100644
--- a/nixos/doc/manual/release-notes/rl-2305.section.md
+++ b/nixos/doc/manual/release-notes/rl-2305.section.md
@@ -18,6 +18,10 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [fzf](https://github.com/junegunn/fzf), a command line fuzzyfinder. Available as [programs.fzf](#opt-programs.fzf.fuzzyCompletion).
 
+- [atuin](https://github.com/ellie/atuin), a sync server for shell history. Available as [services.atuin](#opt-services.atuin.enable).
+
+- [mmsd](https://gitlab.com/kop316/mmsd), a lower level daemon that transmits and recieves MMSes. Available as [services.mmsd](#opt-services.mmsd.enable).
+
 - [v2rayA](https://v2raya.org), a Linux web GUI client of Project V which supports V2Ray, Xray, SS, SSR, Trojan and Pingtunnel. Available as [services.v2raya](options.html#opt-services.v2raya.enable).
 
 ## Backward Incompatibilities {#sec-release-23.05-incompatibilities}
@@ -26,18 +30,20 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `carnix` and `cratesIO` has been removed due to being unmaintained, use alternatives such as [naersk](https://github.com/nix-community/naersk) and [crate2nix](https://github.com/kolloch/crate2nix) instead.
 
+- `borgbackup` module now has an option for inhibiting system sleep while backups are running, defaulting to off (not inhibiting sleep), available as [`services.borgbackup.jobs.<name>.inhibitsSleep`](#opt-services.borgbackup.jobs._name_.inhibitsSleep). 
+
 - The EC2 image module no longer fetches instance metadata in stage-1. This results in a significantly smaller initramfs, since network drivers no longer need to be included, and faster boots, since metadata fetching can happen in parallel with startup of other services.
   This breaks services which rely on metadata being present by the time stage-2 is entered. Anything which reads EC2 metadata from `/etc/ec2-metadata` should now have an `after` dependency on `fetch-ec2-metadata.service`
 
 - `services.sourcehut.dispatch` and the corresponding package (`sourcehut.dispatchsrht`) have been removed due to [upstream deprecation](https://sourcehut.org/blog/2022-08-01-dispatch-deprecation-plans/).
 
-- The [services.snapserver.openFirewall](#opt-services.snapserver.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitely set this option to `true`, or configure your firewall.
+- The [services.snapserver.openFirewall](#opt-services.snapserver.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitly set this option to `true`, or configure your firewall.
 
-- The [services.avahi.openFirewall](#opt-services.avahi.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitely set this option to `true`, or configure your firewall.
+- The [services.tmate-ssh-server.openFirewall](#opt-services.tmate-ssh-server.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitly set this option to `true`, or configure your firewall.
 
-- The [services.tmate-ssh-server.openFirewall](#opt-services.tmate-ssh-server.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitely set this option to `true`, or configure your firewall.
+- The [services.unifi-video.openFirewall](#opt-services.unifi-video.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitly set this option to `true`, or configure your firewall.
 
-- The [services.unifi-video.openFirewall](#opt-services.unifi-video.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitely set this option to `true`, or configure your firewall.
+- The Nginx module now validates the syntax of config files at build time. For more complex configurations (using `include` with out-of-store files notably) you may need to disable this check by setting [services.nginx.validateConfig](#opt-services.nginx.validateConfig) to `false`.
 
 - The EC2 image module previously detected and automatically mounted ext3-formatted instance store devices and partitions in stage-1 (initramfs), storing `/tmp` on the first discovered device. This behaviour, which only catered to very specific use cases and could not be disabled, has been removed. Users relying on this should provide their own implementation, and probably use ext4 and perform the mount in stage-2.
 
@@ -45,6 +51,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - Qt 5.12 and 5.14 have been removed, as the corresponding branches have been EOL upstream for a long time. This affected under 10 packages in nixpkgs, largely unmaintained upstream as well, however, out-of-tree package expressions may need to be updated manually.
 
+- In `mastodon` it is now necessary to specify location of file with `PostgreSQL` database password. In `services.mastodon.database.passwordFile` parameter default value `/var/lib/mastodon/secrets/db-password` has been changed to `null`.
+
 - The `nix.readOnlyStore` option has been renamed to `boot.readOnlyNixStore` to clarify that it configures the NixOS boot process, not the Nix daemon.
 
 ## Other Notable Changes {#sec-release-23.05-notable-changes}
@@ -55,6 +63,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - The module for the application firewall `opensnitch` got the ability to configure rules. Available as [services.opensnitch.rules](#opt-services.opensnitch.rules)
 
+- The module `usbmuxd` now has the ability to change the package used by the daemon. In case you're experiencing issues with `usbmuxd` you can try an alternative program like `usbmuxd2`. Available as [services.usbmuxd.package](#opt-services.usbmuxd.package)
+
 - `services.mastodon` gained a tootctl wrapped named `mastodon-tootctl` similar to `nextcloud-occ` which can be executed from any user and switches to the configured mastodon user with sudo and sources the environment variables.
 
 - The `dnsmasq` service now takes configuration via the
@@ -64,10 +74,20 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - To reduce closure size in `nixos/modules/profiles/minimal.nix` profile disabled installation documentations and manuals. Also disabled `logrotate` and `udisks2` services.
 
-- The minimal ISO image now use `nixos/modules/profiles/minimal.nix` profile.
+- The minimal ISO image now uses the `nixos/modules/profiles/minimal.nix` profile.
+
+- `mastodon` now supports connection to a remote `PostgreSQL` database.
 
 - A new `virtualisation.rosetta` module was added to allow running `x86_64` binaries through [Rosetta](https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment) inside virtualised NixOS guests on Apple silicon. This feature works by default with the [UTM](https://docs.getutm.app/) virtualisation [package](https://search.nixos.org/packages?channel=unstable&show=utm&from=0&size=1&sort=relevance&type=packages&query=utm).
 
+- The new option `users.motdFile` allows configuring a Message Of The Day that can be updated dynamically.
+
+- Enabling global redirect in `services.nginx.virtualHosts` now allows one to add exceptions with the `locations` option.
+
 - Resilio sync secret keys can now be provided using a secrets file at runtime, preventing these secrets from ending up in the Nix store.
 
+- The `services.fwupd` module now allows arbitrary daemon settings to be configured in a structured manner ([`services.fwupd.daemonSettings`](#opt-services.fwupd.daemonSettings)).
+
 - The `unifi-poller` package and corresponding NixOS module have been renamed to `unpoller` to match upstream.
+
+- The new option `services.tailscale.useRoutingFeatures` controls various settings for using Tailscale features like exit nodes and subnet routers. If you wish to use your machine as an exit node, you can set this setting to `server`, otherwise if you wish to use an exit node you can set this setting to `client`. The strict RPF warning has been removed as the RPF will be loosened automatically based on the value of this setting.
diff --git a/nixos/lib/make-options-doc/default.nix b/nixos/lib/make-options-doc/default.nix
index dea3eec5bd6d..a3436caad8f9 100644
--- a/nixos/lib/make-options-doc/default.nix
+++ b/nixos/lib/make-options-doc/default.nix
@@ -19,7 +19,7 @@
 { pkgs
 , lib
 , options
-, transformOptions ? lib.id  # function for additional tranformations of the options
+, transformOptions ? lib.id  # function for additional transformations of the options
 , documentType ? "appendix" # TODO deprecate "appendix" in favor of "none"
                             #      and/or rename function to moduleOptionDoc for clean slate
 
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index 4c52643446ed..c6c8753d5325 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -8,9 +8,9 @@ let
   systemd = cfg.package;
 in rec {
 
-  shellEscape = s: (replaceChars [ "\\" ] [ "\\\\" ] s);
+  shellEscape = s: (replaceStrings [ "\\" ] [ "\\\\" ] s);
 
-  mkPathSafeName = lib.replaceChars ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
+  mkPathSafeName = lib.replaceStrings ["@" ":" "\\" "[" "]"] ["-" "-" "-" "" ""];
 
   # a type for options that take a unit name
   unitNameType = types.strMatching "[a-zA-Z0-9@%:_.\\-]+[.](service|socket|device|mount|automount|swap|target|path|timer|scope|slice)";
@@ -258,7 +258,7 @@ in rec {
 
   makeJobScript = name: text:
     let
-      scriptName = replaceChars [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
+      scriptName = replaceStrings [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
       out = (pkgs.writeShellScriptBin scriptName ''
         set -e
         ${text}
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index 9eefa80d1c8b..def3aa13f320 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -48,7 +48,7 @@ rec {
     trim = s: removeSuffix "/" (removePrefix "/" s);
     normalizedPath = strings.normalizePath s;
   in
-    replaceChars ["/"] ["-"]
+    replaceStrings ["/"] ["-"]
     (replacePrefix "." (strings.escapeC ["."] ".")
     (strings.escapeC (stringToCharacters " !\"#$%&'()*+,;<=>=@[\\]^`{|}~-")
     (if normalizedPath == "/" then normalizedPath else trim normalizedPath)));
@@ -67,7 +67,7 @@ rec {
         else if builtins.isInt arg || builtins.isFloat arg then toString arg
         else throw "escapeSystemdExecArg only allows strings, paths and numbers";
     in
-      replaceChars [ "%" "$" ] [ "%%" "$$" ] (builtins.toJSON s);
+      replaceStrings [ "%" "$" ] [ "%%" "$$" ] (builtins.toJSON s);
 
   # Quotes a list of arguments into a single string for use in a Exec*
   # line.
@@ -112,7 +112,7 @@ rec {
         else if isAttrs item then
           map (name:
             let
-              escapedName = ''"${replaceChars [''"'' "\\"] [''\"'' "\\\\"] name}"'';
+              escapedName = ''"${replaceStrings [''"'' "\\"] [''\"'' "\\\\"] name}"'';
             in
               recurse (prefix + "." + escapedName) item.${name}) (attrNames item)
         else if isList item then
diff --git a/nixos/maintainers/scripts/lxd/lxd-image-inner.nix b/nixos/maintainers/scripts/lxd/lxd-image-inner.nix
index ead3d4e99401..c8cf2a04fb10 100644
--- a/nixos/maintainers/scripts/lxd/lxd-image-inner.nix
+++ b/nixos/maintainers/scripts/lxd/lxd-image-inner.nix
@@ -94,9 +94,4 @@ with lib;
   # Before changing this value read the documentation for this option
   # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
   system.stateVersion = "21.05"; # Did you read the comment?
-
-  # As this is intended as a stadalone image, undo some of the minimal profile stuff
-  documentation.enable = true;
-  documentation.nixos.enable = true;
-  environment.noXlibs = false;
 }
diff --git a/nixos/maintainers/scripts/lxd/lxd-image.nix b/nixos/maintainers/scripts/lxd/lxd-image.nix
index 6aa3f2f55847..cf30836dffe5 100644
--- a/nixos/maintainers/scripts/lxd/lxd-image.nix
+++ b/nixos/maintainers/scripts/lxd/lxd-image.nix
@@ -26,9 +26,4 @@ with lib;
   # Network
   networking.useDHCP = false;
   networking.interfaces.eth0.useDHCP = true;
-
-  # As this is intended as a standalone image, undo some of the minimal profile stuff
-  documentation.enable = true;
-  documentation.nixos.enable = true;
-  environment.noXlibs = false;
 }
diff --git a/nixos/modules/config/no-x-libs.nix b/nixos/modules/config/no-x-libs.nix
index e5699161ec4b..5a2a1a0e8ac8 100644
--- a/nixos/modules/config/no-x-libs.nix
+++ b/nixos/modules/config/no-x-libs.nix
@@ -35,7 +35,9 @@ with lib;
       gobject-introspection = super.gobject-introspection.override { x11Support = false; };
       imagemagick = super.imagemagick.override { libX11Support = false; libXtSupport = false; };
       imagemagickBig = super.imagemagickBig.override { libX11Support = false; libXtSupport = false; };
+      libextractor = super.libextractor.override { gstreamerSupport = false; gtkSupport = false; };
       libva = super.libva-minimal;
+      msmtp = super.msmtp.override { withKeyring = false; };
       networkmanager-fortisslvpn = super.networkmanager-fortisslvpn.override { withGnome = false; };
       networkmanager-iodine = super.networkmanager-iodine.override { withGnome = false; };
       networkmanager-l2tp = super.networkmanager-l2tp.override { withGnome = false; };
@@ -45,6 +47,7 @@ with lib;
       networkmanager-vpnc = super.networkmanager-vpnc.override { withGnome = false; };
       pinentry = super.pinentry.override { enabledFlavors = [ "curses" "tty" "emacs" ]; withLibsecret = false; };
       qemu = super.qemu.override { gtkSupport = false; spiceSupport = false; sdlSupport = false; };
+      qrencode = super.qrencode.overrideAttrs (_: { doCheck = false; });
       zbar = super.zbar.override { enableVideo = false; withXorg = false; };
     }));
   };
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index 10d52ade288b..76a054b100eb 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -160,7 +160,7 @@ let
     config = rec {
       device = mkIf options.label.isDefined
         "/dev/disk/by-label/${config.label}";
-      deviceName = lib.replaceChars ["\\"] [""] (escapeSystemdPath config.device);
+      deviceName = lib.replaceStrings ["\\"] [""] (escapeSystemdPath config.device);
       realDevice = if config.randomEncryption.enable then "/dev/mapper/${deviceName}" else config.device;
     };
 
diff --git a/nixos/modules/hardware/gpgsmartcards.nix b/nixos/modules/hardware/gpgsmartcards.nix
index 43ade4d12e12..68e1e5f74e2e 100644
--- a/nixos/modules/hardware/gpgsmartcards.nix
+++ b/nixos/modules/hardware/gpgsmartcards.nix
@@ -8,7 +8,7 @@ let
   # https://salsa.debian.org/debian/gnupg2/-/blob/debian/main/debian/scdaemon.udev
 
   # the latest rev of the entire debian gnupg2 repo as of 2021-04-28
-  # the scdaemon.udev file was last commited on 2021-01-05 (7817a03):
+  # the scdaemon.udev file was last committed on 2021-01-05 (7817a03):
   scdaemonUdevRev = "01898735a015541e3ffb43c7245ac1e612f40836";
 
   scdaemonRules = pkgs.fetchurl {
diff --git a/nixos/modules/hardware/openrazer.nix b/nixos/modules/hardware/openrazer.nix
index 247913297c9e..aaa4000e758f 100644
--- a/nixos/modules/hardware/openrazer.nix
+++ b/nixos/modules/hardware/openrazer.nix
@@ -110,7 +110,7 @@ in
     boot.extraModulePackages = [ kernelPackages.openrazer ];
     boot.kernelModules = drivers;
 
-    # Makes the man pages available so you can succesfully run
+    # Makes the man pages available so you can successfully run
     # > systemctl --user help openrazer-daemon
     environment.systemPackages = [ pkgs.python3Packages.openrazer-daemon.man ];
 
diff --git a/nixos/modules/hardware/printers.nix b/nixos/modules/hardware/printers.nix
index 64c29bb0a5b3..85e3215127fd 100644
--- a/nixos/modules/hardware/printers.nix
+++ b/nixos/modules/hardware/printers.nix
@@ -100,7 +100,7 @@ in {
               default = {};
               description = lib.mdDoc ''
                 Sets PPD options for the printer.
-                {command}`lpoptions [-p printername] -l` shows suported PPD options for the given printer.
+                {command}`lpoptions [-p printername] -l` shows supported PPD options for the given printer.
               '';
             };
           };
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index e37142f05f41..5bd343c85fa2 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -81,7 +81,7 @@ let
 
   # The configuration file for syslinux.
 
-  # Notes on syslinux configuration and UNetbootin compatiblity:
+  # Notes on syslinux configuration and UNetbootin compatibility:
   #   * Do not use '/syslinux/syslinux.cfg' as the path for this
   #     configuration. UNetbootin will not parse the file and use it as-is.
   #     This results in a broken configuration if the partition label does
diff --git a/nixos/modules/installer/netboot/netboot-minimal.nix b/nixos/modules/installer/netboot/netboot-minimal.nix
index 268357c0e41a..91065d52faf4 100644
--- a/nixos/modules/installer/netboot/netboot-minimal.nix
+++ b/nixos/modules/installer/netboot/netboot-minimal.nix
@@ -1,6 +1,6 @@
 # This module defines a small netboot environment.
 
-{ ... }:
+{ lib, ... }:
 
 {
   imports = [
diff --git a/nixos/modules/misc/label.nix b/nixos/modules/misc/label.nix
index 0c29d13aab1d..44ee812249ce 100644
--- a/nixos/modules/misc/label.nix
+++ b/nixos/modules/misc/label.nix
@@ -27,7 +27,7 @@ in
         variable (defaults to the value of
         {option}`system.nixos.version`).
 
-        Can be overriden by setting {env}`NIXOS_LABEL`.
+        Can be overridden by setting {env}`NIXOS_LABEL`.
 
         Useful for not loosing track of configurations built from different
         nixos branches/revisions, e.g.:
diff --git a/nixos/modules/misc/man-db.nix b/nixos/modules/misc/man-db.nix
index df903259fa42..299b11d1fcef 100644
--- a/nixos/modules/misc/man-db.nix
+++ b/nixos/modules/misc/man-db.nix
@@ -52,13 +52,11 @@ in
     environment.systemPackages = [ cfg.package ];
     environment.etc."man_db.conf".text =
       let
-        mandbForBuild = if pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform then
-                          cfg.package
-                        else
-                          pkgs.buildPackages.man-db;
-        manualCache = pkgs.runCommand "man-cache" { } ''
+        manualCache = pkgs.runCommand "man-cache" {
+          nativeBuildInputs = [ cfg.package ];
+        } ''
           echo "MANDB_MAP ${cfg.manualPages}/share/man $out" > man.conf
-          ${mandbForBuild}/bin/mandb -C man.conf -psc >/dev/null 2>&1
+          mandb -C man.conf -psc >/dev/null 2>&1
         '';
       in
       ''
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 842797d24302..d2f40fafc2b6 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1,21 +1,13 @@
 [
+  ./config/appstream.nix
+  ./config/console.nix
   ./config/debug-info.nix
   ./config/fonts/fontconfig.nix
   ./config/fonts/fontdir.nix
   ./config/fonts/fonts.nix
   ./config/fonts/ghostscript.nix
-  ./config/xdg/autostart.nix
-  ./config/xdg/icons.nix
-  ./config/xdg/menus.nix
-  ./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
-  ./config/gtk/gtk-icon-cache.nix
   ./config/gnu.nix
+  ./config/gtk/gtk-icon-cache.nix
   ./config/i18n.nix
   ./config/iproute2.nix
   ./config/krb5/default.nix
@@ -39,26 +31,32 @@
   ./config/unix-odbc-drivers.nix
   ./config/users-groups.nix
   ./config/vte.nix
+  ./config/xdg/autostart.nix
+  ./config/xdg/icons.nix
+  ./config/xdg/menus.nix
+  ./config/xdg/mime.nix
+  ./config/xdg/portal.nix
+  ./config/xdg/portals/lxqt.nix
+  ./config/xdg/portals/wlr.nix
+  ./config/xdg/sounds.nix
   ./config/zram.nix
   ./hardware/acpilight.nix
   ./hardware/all-firmware.nix
   ./hardware/bladeRF.nix
   ./hardware/brillo.nix
   ./hardware/ckb-next.nix
+  ./hardware/corectrl.nix
   ./hardware/cpu/amd-microcode.nix
   ./hardware/cpu/amd-sev.nix
   ./hardware/cpu/intel-microcode.nix
   ./hardware/cpu/intel-sgx.nix
-  ./hardware/corectrl.nix
-  ./hardware/digitalbitbox.nix
   ./hardware/device-tree.nix
-  ./hardware/gkraken.nix
+  ./hardware/digitalbitbox.nix
   ./hardware/flirc.nix
+  ./hardware/gkraken.nix
   ./hardware/gpgsmartcards.nix
-  ./hardware/i2c.nix
   ./hardware/hackrf.nix
-  ./hardware/sensor/hddtemp.nix
-  ./hardware/sensor/iio.nix
+  ./hardware/i2c.nix
   ./hardware/keyboard/teck.nix
   ./hardware/keyboard/uhk.nix
   ./hardware/keyboard/zsa.nix
@@ -71,33 +69,35 @@
   ./hardware/network/intel-2200bg.nix
   ./hardware/new-lg4ff.nix
   ./hardware/nitrokey.nix
+  ./hardware/onlykey/default.nix
   ./hardware/opengl.nix
   ./hardware/openrazer.nix
+  ./hardware/opentabletdriver.nix
   ./hardware/pcmcia.nix
   ./hardware/printers.nix
   ./hardware/raid/hpsa.nix
   ./hardware/rtl-sdr.nix
   ./hardware/saleae-logic.nix
+  ./hardware/sata.nix
+  ./hardware/sensor/hddtemp.nix
+  ./hardware/sensor/iio.nix
   ./hardware/steam-hardware.nix
   ./hardware/system-76.nix
   ./hardware/tuxedo-keyboard.nix
   ./hardware/ubertooth.nix
-  ./hardware/usb-wwan.nix
-  ./hardware/usb-storage.nix
-  ./hardware/onlykey/default.nix
-  ./hardware/opentabletdriver.nix
-  ./hardware/sata.nix
-  ./hardware/wooting.nix
   ./hardware/uinput.nix
+  ./hardware/usb-storage.nix
+  ./hardware/usb-wwan.nix
   ./hardware/video/amdgpu-pro.nix
-  ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/bumblebee.nix
+  ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/displaylink.nix
   ./hardware/video/hidpi.nix
   ./hardware/video/nvidia.nix
   ./hardware/video/switcheroo-control.nix
   ./hardware/video/uvcvideo/default.nix
   ./hardware/video/webcam/facetimehd.nix
+  ./hardware/wooting.nix
   ./hardware/xone.nix
   ./hardware/xpadneo.nix
   ./i18n/input-method/default.nix
@@ -105,40 +105,40 @@
   ./i18n/input-method/fcitx5.nix
   ./i18n/input-method/hime.nix
   ./i18n/input-method/ibus.nix
+  ./i18n/input-method/kime.nix
   ./i18n/input-method/nabi.nix
   ./i18n/input-method/uim.nix
-  ./i18n/input-method/kime.nix
   ./installer/tools/tools.nix
   ./misc/assertions.nix
   ./misc/crashdump.nix
   ./misc/documentation.nix
   ./misc/extra-arguments.nix
   ./misc/ids.nix
-  ./misc/lib.nix
   ./misc/label.nix
+  ./misc/lib.nix
   ./misc/locate.nix
   ./misc/man-db.nix
   ./misc/mandoc.nix
   ./misc/meta.nix
+  ./misc/nixops-autoluks.nix
   ./misc/nixpkgs.nix
   ./misc/passthru.nix
   ./misc/version.nix
   ./misc/wordlist.nix
-  ./misc/nixops-autoluks.nix
-  ./programs/_1password.nix
   ./programs/_1password-gui.nix
+  ./programs/_1password.nix
   ./programs/adb.nix
   ./programs/appgate-sdp.nix
   ./programs/atop.nix
   ./programs/ausweisapp.nix
   ./programs/autojump.nix
   ./programs/bandwhich.nix
-  ./programs/bash/bash.nix
+  ./programs/bash-my-aws.nix
   ./programs/bash/bash-completion.nix
+  ./programs/bash/bash.nix
   ./programs/bash/blesh.nix
   ./programs/bash/ls-colors.nix
   ./programs/bash/undistract-me.nix
-  ./programs/bash-my-aws.nix
   ./programs/bcc.nix
   ./programs/browserpass.nix
   ./programs/calls.nix
@@ -173,38 +173,39 @@
   ./programs/gnome-disks.nix
   ./programs/gnome-documents.nix
   ./programs/gnome-terminal.nix
-  ./programs/gpaste.nix
   ./programs/gnupg.nix
+  ./programs/gpaste.nix
   ./programs/gphoto2.nix
   ./programs/haguichi.nix
   ./programs/hamster.nix
   ./programs/htop.nix
   ./programs/iftop.nix
+  ./programs/i3lock.nix
   ./programs/iotop.nix
   ./programs/java.nix
+  ./programs/k3b.nix
   ./programs/k40-whisperer.nix
+  ./programs/kbdlight.nix
   ./programs/kclock.nix
-  ./programs/k3b.nix
   ./programs/kdeconnect.nix
-  ./programs/kbdlight.nix
   ./programs/less.nix
   ./programs/liboping.nix
   ./programs/light.nix
   ./programs/mdevctl.nix
   ./programs/mepo.nix
-  ./programs/mosh.nix
   ./programs/mininet.nix
+  ./programs/mosh.nix
   ./programs/msmtp.nix
   ./programs/mtr.nix
   ./programs/nano.nix
   ./programs/nbd.nix
-  ./programs/nix-ld.nix
   ./programs/neovim.nix
   ./programs/nethoscope.nix
+  ./programs/nix-ld.nix
   ./programs/nm-applet.nix
   ./programs/nncp.nix
-  ./programs/npm.nix
   ./programs/noisetorch.nix
+  ./programs/npm.nix
   ./programs/oblogout.nix
   ./programs/openvpn3.nix
   ./programs/pantheon-tweaks.nix
@@ -215,21 +216,21 @@
   ./programs/rog-control-center.nix
   ./programs/rust-motd.nix
   ./programs/screen.nix
-  ./programs/sedutil.nix
   ./programs/seahorse.nix
+  ./programs/sedutil.nix
+  ./programs/shadow.nix
+  ./programs/singularity.nix
   ./programs/skim.nix
   ./programs/slock.nix
-  ./programs/shadow.nix
   ./programs/spacefm.nix
-  ./programs/singularity.nix
   ./programs/ssh.nix
-  ./programs/sysdig.nix
-  ./programs/systemtap.nix
   ./programs/starship.nix
   ./programs/steam.nix
   ./programs/streamdeck-ui.nix
   ./programs/sway.nix
+  ./programs/sysdig.nix
   ./programs/system-config-printer.nix
+  ./programs/systemtap.nix
   ./programs/thefuck.nix
   ./programs/thunar.nix
   ./programs/tmux.nix
@@ -252,10 +253,10 @@
   ./programs/yabar.nix
   ./programs/zmap.nix
   ./programs/zsh/oh-my-zsh.nix
-  ./programs/zsh/zsh.nix
   ./programs/zsh/zsh-autoenv.nix
   ./programs/zsh/zsh-autosuggestions.nix
   ./programs/zsh/zsh-syntax-highlighting.nix
+  ./programs/zsh/zsh.nix
   ./rename.nix
   ./security/acme
   ./security/apparmor.nix
@@ -264,23 +265,23 @@
   ./security/ca.nix
   ./security/chromium-suid-sandbox.nix
   ./security/dhparams.nix
+  ./security/doas.nix
   ./security/duosec.nix
   ./security/google_oslogin.nix
   ./security/lock-kernel-modules.nix
   ./security/misc.nix
   ./security/oath.nix
   ./security/pam.nix
-  ./security/pam_usb.nix
   ./security/pam_mount.nix
+  ./security/pam_usb.nix
   ./security/please.nix
   ./security/polkit.nix
   ./security/rngd.nix
   ./security/rtkit.nix
-  ./security/wrappers/default.nix
   ./security/sudo.nix
-  ./security/doas.nix
   ./security/systemd-confinement.nix
   ./security/tpm2.nix
+  ./security/wrappers/default.nix
   ./services/admin/meshcentral.nix
   ./services/admin/oxidized.nix
   ./services/admin/pgadmin.nix
@@ -295,17 +296,17 @@
   ./services/audio/jack.nix
   ./services/audio/jmusicbot.nix
   ./services/audio/liquidsoap.nix
+  ./services/audio/mopidy.nix
   ./services/audio/mpd.nix
   ./services/audio/mpdscribble.nix
-  ./services/audio/mopidy.nix
+  ./services/audio/navidrome.nix
   ./services/audio/networkaudiod.nix
   ./services/audio/roon-bridge.nix
-  ./services/audio/navidrome.nix
   ./services/audio/roon-server.nix
   ./services/audio/slimserver.nix
   ./services/audio/snapserver.nix
-  ./services/audio/squeezelite.nix
   ./services/audio/spotifyd.nix
+  ./services/audio/squeezelite.nix
   ./services/audio/ympd.nix
   ./services/backup/automysqlbackup.nix
   ./services/backup/bacula.nix
@@ -317,8 +318,8 @@
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
   ./services/backup/postgresql-wal-receiver.nix
-  ./services/backup/restic.nix
   ./services/backup/restic-rest-server.nix
+  ./services/backup/restic.nix
   ./services/backup/rsnapshot.nix
   ./services/backup/sanoid.nix
   ./services/backup/syncoid.nix
@@ -326,15 +327,15 @@
   ./services/backup/tsm.nix
   ./services/backup/zfs-replication.nix
   ./services/backup/znapzend.nix
-  ./services/blockchain/ethereum/geth.nix
+  ./services/backup/zrepl.nix
   ./services/blockchain/ethereum/erigon.nix
+  ./services/blockchain/ethereum/geth.nix
   ./services/blockchain/ethereum/lighthouse.nix
-  ./services/backup/zrepl.nix
   ./services/cluster/corosync/default.nix
   ./services/cluster/hadoop/default.nix
   ./services/cluster/k3s/default.nix
-  ./services/cluster/kubernetes/addons/dns.nix
   ./services/cluster/kubernetes/addon-manager.nix
+  ./services/cluster/kubernetes/addons/dns.nix
   ./services/cluster/kubernetes/apiserver.nix
   ./services/cluster/kubernetes/controller-manager.nix
   ./services/cluster/kubernetes/default.nix
@@ -354,14 +355,14 @@
   ./services/continuous-integration/buildbot/master.nix
   ./services/continuous-integration/buildbot/worker.nix
   ./services/continuous-integration/buildkite-agents.nix
-  ./services/continuous-integration/hail.nix
-  ./services/continuous-integration/hercules-ci-agent/default.nix
-  ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/github-runner.nix
   ./services/continuous-integration/github-runners.nix
   ./services/continuous-integration/gitlab-runner.nix
   ./services/continuous-integration/gocd-agent/default.nix
   ./services/continuous-integration/gocd-server/default.nix
+  ./services/continuous-integration/hail.nix
+  ./services/continuous-integration/hercules-ci-agent/default.nix
+  ./services/continuous-integration/hydra/default.nix
   ./services/continuous-integration/jenkins/default.nix
   ./services/continuous-integration/jenkins/job-builder.nix
   ./services/continuous-integration/jenkins/slave.nix
@@ -370,8 +371,8 @@
   ./services/databases/clickhouse.nix
   ./services/databases/cockroachdb.nix
   ./services/databases/couchdb.nix
-  ./services/databases/dragonflydb.nix
   ./services/databases/dgraph.nix
+  ./services/databases/dragonflydb.nix
   ./services/databases/firebird.nix
   ./services/databases/foundationdb.nix
   ./services/databases/hbase-standalone.nix
@@ -398,12 +399,6 @@
   ./services/desktops/espanso.nix
   ./services/desktops/flatpak.nix
   ./services/desktops/geoclue2.nix
-  ./services/desktops/gsignond.nix
-  ./services/desktops/gvfs.nix
-  ./services/desktops/malcontent.nix
-  ./services/desktops/pipewire/pipewire.nix
-  ./services/desktops/pipewire/pipewire-media-session.nix
-  ./services/desktops/pipewire/wireplumber.nix
   ./services/desktops/gnome/at-spi2-core.nix
   ./services/desktops/gnome/evolution-data-server.nix
   ./services/desktops/gnome/glib-networking.nix
@@ -417,27 +412,33 @@
   ./services/desktops/gnome/gnome-user-share.nix
   ./services/desktops/gnome/rygel.nix
   ./services/desktops/gnome/sushi.nix
-  ./services/desktops/gnome/tracker.nix
   ./services/desktops/gnome/tracker-miners.nix
+  ./services/desktops/gnome/tracker.nix
+  ./services/desktops/gsignond.nix
+  ./services/desktops/gvfs.nix
+  ./services/desktops/malcontent.nix
   ./services/desktops/neard.nix
+  ./services/desktops/pipewire/pipewire-media-session.nix
+  ./services/desktops/pipewire/pipewire.nix
+  ./services/desktops/pipewire/wireplumber.nix
   ./services/desktops/profile-sync-daemon.nix
   ./services/desktops/system-config-printer.nix
   ./services/desktops/telepathy.nix
   ./services/desktops/tumbler.nix
   ./services/desktops/zeitgeist.nix
-  ./services/development/bloop.nix
   ./services/development/blackfire.nix
+  ./services/development/bloop.nix
   ./services/development/distccd.nix
   ./services/development/hoogle.nix
   ./services/development/jupyter/default.nix
   ./services/development/jupyterhub/default.nix
-  ./services/development/rstudio-server/default.nix
   ./services/development/lorri.nix
+  ./services/development/rstudio-server/default.nix
   ./services/development/zammad.nix
   ./services/display-managers/greetd.nix
   ./services/editors/emacs.nix
-  ./services/editors/infinoted.nix
   ./services/editors/haste.nix
+  ./services/editors/infinoted.nix
   ./services/finance/odoo.nix
   ./services/games/asf.nix
   ./services/games/crossfire-server.nix
@@ -483,22 +484,22 @@
   ./services/hardware/spacenavd.nix
   ./services/hardware/supergfxd.nix
   ./services/hardware/tcsd.nix
-  ./services/hardware/tlp.nix
+  ./services/hardware/thermald.nix
   ./services/hardware/thinkfan.nix
   ./services/hardware/throttled.nix
+  ./services/hardware/tlp.nix
   ./services/hardware/trezord.nix
   ./services/hardware/triggerhappy.nix
   ./services/hardware/udev.nix
   ./services/hardware/udisks2.nix
+  ./services/hardware/undervolt.nix
   ./services/hardware/upower.nix
   ./services/hardware/usbmuxd.nix
   ./services/hardware/usbrelayd.nix
-  ./services/hardware/thermald.nix
-  ./services/hardware/undervolt.nix
   ./services/hardware/vdr.nix
+  ./services/home-automation/evcc.nix
   ./services/home-automation/home-assistant.nix
   ./services/home-automation/zigbee2mqtt.nix
-  ./services/home-automation/evcc.nix
   ./services/logging/SystemdJournal2Gelf.nix
   ./services/logging/awstats.nix
   ./services/logging/filebeat.nix
@@ -530,22 +531,22 @@
   ./services/mail/mailhog.nix
   ./services/mail/mailman.nix
   ./services/mail/mlmmj.nix
+  ./services/mail/nullmailer.nix
   ./services/mail/offlineimap.nix
   ./services/mail/opendkim.nix
   ./services/mail/opensmtpd.nix
   ./services/mail/pfix-srsd.nix
   ./services/mail/postfix.nix
   ./services/mail/postfixadmin.nix
-  ./services/mail/postsrsd.nix
   ./services/mail/postgrey.nix
+  ./services/mail/postsrsd.nix
   ./services/mail/public-inbox.nix
-  ./services/mail/spamassassin.nix
+  ./services/mail/roundcube.nix
   ./services/mail/rspamd.nix
   ./services/mail/rss2email.nix
-  ./services/mail/roundcube.nix
   ./services/mail/schleuder.nix
+  ./services/mail/spamassassin.nix
   ./services/mail/sympa.nix
-  ./services/mail/nullmailer.nix
   ./services/matrix/appservice-discord.nix
   ./services/matrix/appservice-irc.nix
   ./services/matrix/conduit.nix
@@ -555,32 +556,33 @@
   ./services/matrix/mjolnir.nix
   ./services/matrix/pantalaimon.nix
   ./services/matrix/synapse.nix
-  ./services/misc/ananicy.nix
   ./services/misc/airsonic.nix
+  ./services/misc/ananicy.nix
   ./services/misc/ankisyncd.nix
   ./services/misc/apache-kafka.nix
+  ./services/misc/atuin.nix
   ./services/misc/autofs.nix
   ./services/misc/autorandr.nix
   ./services/misc/bazarr.nix
   ./services/misc/beanstalkd.nix
   ./services/misc/bees.nix
   ./services/misc/bepasty.nix
-  ./services/misc/canto-daemon.nix
   ./services/misc/calibre-server.nix
+  ./services/misc/canto-daemon.nix
   ./services/misc/cfdyndns.nix
-  ./services/misc/clipmenu.nix
-  ./services/misc/clipcat.nix
-  ./services/misc/cpuminer-cryptonight.nix
   ./services/misc/cgminer.nix
+  ./services/misc/clipcat.nix
+  ./services/misc/clipmenu.nix
   ./services/misc/confd.nix
+  ./services/misc/cpuminer-cryptonight.nix
   ./services/misc/devmon.nix
   ./services/misc/dictd.nix
-  ./services/misc/duckling.nix
-  ./services/misc/dwm-status.nix
-  ./services/misc/dysnomia.nix
   ./services/misc/disnix.nix
   ./services/misc/docker-registry.nix
   ./services/misc/domoticz.nix
+  ./services/misc/duckling.nix
+  ./services/misc/dwm-status.nix
+  ./services/misc/dysnomia.nix
   ./services/misc/errbot.nix
   ./services/misc/etcd.nix
   ./services/misc/etebase-server.nix
@@ -592,16 +594,16 @@
   ./services/misc/gammu-smsd.nix
   ./services/misc/geoipupdate.nix
   ./services/misc/gitea.nix
-  #./services/misc/gitit.nix
+  # ./services/misc/gitit.nix
   ./services/misc/gitlab.nix
   ./services/misc/gitolite.nix
   ./services/misc/gitweb.nix
   ./services/misc/gogs.nix
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
+  ./services/misc/greenclip.nix
   ./services/misc/headphones.nix
   ./services/misc/heisenbridge.nix
-  ./services/misc/greenclip.nix
   ./services/misc/ihaskell.nix
   ./services/misc/input-remapper.nix
   ./services/misc/irkerd.nix
@@ -609,11 +611,11 @@
   ./services/misc/jellyfin.nix
   ./services/misc/klipper.nix
   ./services/misc/languagetool.nix
-  ./services/misc/logkeys.nix
   ./services/misc/leaps.nix
-  ./services/misc/lidarr.nix
   ./services/misc/libreddit.nix
+  ./services/misc/lidarr.nix
   ./services/misc/lifecycled.nix
+  ./services/misc/logkeys.nix
   ./services/misc/mame.nix
   ./services/misc/mbpfan.nix
   ./services/misc/mediatomb.nix
@@ -638,23 +640,22 @@
   ./services/misc/paperless.nix
   ./services/misc/parsoid.nix
   ./services/misc/persistent-evdev.nix
+  ./services/misc/pinnwand.nix
   ./services/misc/plex.nix
   ./services/misc/plikd.nix
   ./services/misc/podgrab.nix
   ./services/misc/polaris.nix
   ./services/misc/portunus.nix
   ./services/misc/prowlarr.nix
-  ./services/misc/tautulli.nix
-  ./services/misc/pinnwand.nix
   ./services/misc/pykms.nix
   ./services/misc/radarr.nix
   ./services/misc/redmine.nix
-  ./services/misc/rippled.nix
   ./services/misc/ripple-data-api.nix
+  ./services/misc/rippled.nix
   ./services/misc/rmfakecloud.nix
-  ./services/misc/serviio.nix
   ./services/misc/safeeyes.nix
   ./services/misc/sdrplay.nix
+  ./services/misc/serviio.nix
   ./services/misc/sickbeard.nix
   ./services/misc/signald.nix
   ./services/misc/siproxd.nix
@@ -672,6 +673,7 @@
   ./services/misc/sysprof.nix
   ./services/misc/tandoor-recipes.nix
   ./services/misc/taskserver
+  ./services/misc/tautulli.nix
   ./services/misc/tiddlywiki.nix
   ./services/misc/tp-auto-kbbl.nix
   ./services/misc/tzupdate.nix
@@ -691,10 +693,10 @@
   ./services/monitoring/datadog-agent.nix
   ./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/grafana.nix
   ./services/monitoring/graphite.nix
   ./services/monitoring/hdaps.nix
   ./services/monitoring/heapster.nix
@@ -712,15 +714,15 @@
   ./services/monitoring/nagios.nix
   ./services/monitoring/netdata.nix
   ./services/monitoring/parsedmarc.nix
-  ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/alertmanager.nix
+  ./services/monitoring/prometheus/default.nix
   ./services/monitoring/prometheus/exporters.nix
   ./services/monitoring/prometheus/pushgateway.nix
   ./services/monitoring/prometheus/sachet.nix
   ./services/monitoring/prometheus/xmpp-alerts.nix
-  ./services/monitoring/riemann.nix
   ./services/monitoring/riemann-dash.nix
   ./services/monitoring/riemann-tools.nix
+  ./services/monitoring/riemann.nix
   ./services/monitoring/scollector.nix
   ./services/monitoring/smartd.nix
   ./services/monitoring/sysstat.nix
@@ -731,38 +733,38 @@
   ./services/monitoring/tuptime.nix
   ./services/monitoring/unpoller.nix
   ./services/monitoring/ups.nix
+  ./services/monitoring/uptime-kuma.nix
   ./services/monitoring/uptime.nix
   ./services/monitoring/vmagent.nix
-  ./services/monitoring/uptime-kuma.nix
   ./services/monitoring/vnstat.nix
   ./services/monitoring/zabbix-agent.nix
   ./services/monitoring/zabbix-proxy.nix
   ./services/monitoring/zabbix-server.nix
   ./services/network-filesystems/cachefilesd.nix
+  ./services/network-filesystems/ceph.nix
   ./services/network-filesystems/davfs2.nix
+  ./services/network-filesystems/diod.nix
   ./services/network-filesystems/drbd.nix
   ./services/network-filesystems/glusterfs.nix
   ./services/network-filesystems/kbfs.nix
   ./services/network-filesystems/kubo.nix
   ./services/network-filesystems/litestream/default.nix
+  ./services/network-filesystems/moosefs.nix
   ./services/network-filesystems/netatalk.nix
   ./services/network-filesystems/nfsd.nix
-  ./services/network-filesystems/moosefs.nix
   ./services/network-filesystems/openafs/client.nix
   ./services/network-filesystems/openafs/server.nix
-  ./services/network-filesystems/orangefs/server.nix
   ./services/network-filesystems/orangefs/client.nix
+  ./services/network-filesystems/orangefs/server.nix
   ./services/network-filesystems/rsyncd.nix
-  ./services/network-filesystems/samba.nix
   ./services/network-filesystems/samba-wsdd.nix
+  ./services/network-filesystems/samba.nix
   ./services/network-filesystems/tahoe.nix
-  ./services/network-filesystems/diod.nix
   ./services/network-filesystems/u9fs.nix
-  ./services/network-filesystems/webdav.nix
   ./services/network-filesystems/webdav-server-rs.nix
-  ./services/network-filesystems/yandex-disk.nix
+  ./services/network-filesystems/webdav.nix
   ./services/network-filesystems/xtreemfs.nix
-  ./services/network-filesystems/ceph.nix
+  ./services/network-filesystems/yandex-disk.nix
   ./services/networking/3proxy.nix
   ./services/networking/adguardhome.nix
   ./services/networking/amuled.nix
@@ -770,16 +772,16 @@
   ./services/networking/aria2.nix
   ./services/networking/asterisk.nix
   ./services/networking/atftpd.nix
+  ./services/networking/autossh.nix
   ./services/networking/avahi-daemon.nix
   ./services/networking/babeld.nix
-  ./services/networking/bee.nix
   ./services/networking/bee-clef.nix
+  ./services/networking/bee.nix
   ./services/networking/biboumi.nix
   ./services/networking/bind.nix
-  ./services/networking/bitcoind.nix
-  ./services/networking/autossh.nix
-  ./services/networking/bird.nix
   ./services/networking/bird-lg.nix
+  ./services/networking/bird.nix
+  ./services/networking/bitcoind.nix
   ./services/networking/bitlbee.nix
   ./services/networking/blockbook-frontend.nix
   ./services/networking/blocky.nix
@@ -787,6 +789,7 @@
   ./services/networking/chisel-server.nix
   ./services/networking/cjdns.nix
   ./services/networking/cloudflare-dyndns.nix
+  ./services/networking/cloudflared.nix
   ./services/networking/cntlm.nix
   ./services/networking/connman.nix
   ./services/networking/consul.nix
@@ -805,8 +808,6 @@
   ./services/networking/dnsdist.nix
   ./services/networking/dnsmasq.nix
   ./services/networking/doh-proxy-rust.nix
-  ./services/networking/ncdns.nix
-  ./services/networking/nomad.nix
   ./services/networking/ejabberd.nix
   ./services/networking/envoy.nix
   ./services/networking/epmd.nix
@@ -841,10 +842,10 @@
   ./services/networking/htpdate.nix
   ./services/networking/https-dns-proxy.nix
   ./services/networking/hylafax/default.nix
-  ./services/networking/i2pd.nix
   ./services/networking/i2p.nix
-  ./services/networking/icecream/scheduler.nix
+  ./services/networking/i2pd.nix
   ./services/networking/icecream/daemon.nix
+  ./services/networking/icecream/scheduler.nix
   ./services/networking/inspircd.nix
   ./services/networking/iodine.nix
   ./services/networking/iperf3.nix
@@ -869,14 +870,16 @@
   ./services/networking/lxd-image-server.nix
   ./services/networking/magic-wormhole-mailbox-server.nix
   ./services/networking/matterbridge.nix
-  ./services/networking/mjpg-streamer.nix
   ./services/networking/minidlna.nix
   ./services/networking/miniupnpd.nix
+  ./services/networking/miredo.nix
+  ./services/networking/mjpg-streamer.nix
+  ./services/networking/mmsd.nix
   ./services/networking/mosquitto.nix
   ./services/networking/monero.nix
   ./services/networking/morty.nix
+  ./services/networking/mosquitto.nix
   ./services/networking/mozillavpn.nix
-  ./services/networking/miredo.nix
   ./services/networking/mstpd.nix
   ./services/networking/mtprotoproxy.nix
   ./services/networking/mtr-exporter.nix
@@ -889,18 +892,20 @@
   ./services/networking/nat.nix
   ./services/networking/nats.nix
   ./services/networking/nbd.nix
+  ./services/networking/ncdns.nix
   ./services/networking/ndppd.nix
   ./services/networking/nebula.nix
   ./services/networking/netbird.nix
   ./services/networking/networkmanager.nix
   ./services/networking/nextdns.nix
   ./services/networking/nftables.nix
-  ./services/networking/ngircd.nix
   ./services/networking/nghttpx/default.nix
+  ./services/networking/ngircd.nix
   ./services/networking/nix-serve.nix
   ./services/networking/nix-store-gcs-proxy.nix
   ./services/networking/nixops-dns.nix
   ./services/networking/nntp-proxy.nix
+  ./services/networking/nomad.nix
   ./services/networking/nsd.nix
   ./services/networking/ntopng.nix
   ./services/networking/ntp/chrony.nix
@@ -916,20 +921,20 @@
   ./services/networking/openvpn.nix
   ./services/networking/ostinato.nix
   ./services/networking/owamp.nix
+  ./services/networking/pdns-recursor.nix
   ./services/networking/pdnsd.nix
   ./services/networking/pixiecore.nix
   ./services/networking/pleroma.nix
   ./services/networking/polipo.nix
   ./services/networking/powerdns.nix
-  ./services/networking/pdns-recursor.nix
   ./services/networking/pppd.nix
   ./services/networking/pptpd.nix
   ./services/networking/prayer.nix
   ./services/networking/privoxy.nix
   ./services/networking/prosody.nix
   ./services/networking/quassel.nix
-  ./services/networking/quorum.nix
   ./services/networking/quicktun.nix
+  ./services/networking/quorum.nix
   ./services/networking/r53-ddns.nix
   ./services/networking/radicale.nix
   ./services/networking/radvd.nix
@@ -943,58 +948,56 @@
   ./services/networking/sabnzbd.nix
   ./services/networking/seafile.nix
   ./services/networking/searx.nix
-  ./services/networking/skydns.nix
   ./services/networking/shadowsocks.nix
   ./services/networking/shairport-sync.nix
   ./services/networking/shellhub-agent.nix
   ./services/networking/shorewall.nix
   ./services/networking/shorewall6.nix
   ./services/networking/shout.nix
-  ./services/networking/sniproxy.nix
-  ./services/networking/snowflake-proxy.nix
+  ./services/networking/skydns.nix
   ./services/networking/smartdns.nix
   ./services/networking/smokeping.nix
+  ./services/networking/sniproxy.nix
+  ./services/networking/snowflake-proxy.nix
   ./services/networking/softether.nix
-  ./services/networking/solanum.nix
   ./services/networking/soju.nix
+  ./services/networking/solanum.nix
   ./services/networking/spacecookie.nix
   ./services/networking/spiped.nix
   ./services/networking/squid.nix
-  ./services/networking/sslh.nix
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
-  ./services/networking/strongswan.nix
+  ./services/networking/sslh.nix
   ./services/networking/strongswan-swanctl/module.nix
-  ./services/networking/stunnel.nix
+  ./services/networking/strongswan.nix
   ./services/networking/stubby.nix
+  ./services/networking/stunnel.nix
   ./services/networking/supplicant.nix
   ./services/networking/supybot.nix
-  ./services/networking/syncthing.nix
-  ./services/networking/syncthing-relay.nix
   ./services/networking/syncplay.nix
+  ./services/networking/syncthing-relay.nix
+  ./services/networking/syncthing.nix
   ./services/networking/tailscale.nix
   ./services/networking/tayga.nix
   ./services/networking/tcpcrypt.nix
   ./services/networking/teamspeak3.nix
   ./services/networking/tedicross.nix
-  ./services/networking/tetrd.nix
   ./services/networking/teleport.nix
+  ./services/networking/tetrd.nix
+  ./services/networking/tftpd.nix
   ./services/networking/thelounge.nix
   ./services/networking/tinc.nix
   ./services/networking/tinydns.nix
-  ./services/networking/tftpd.nix
   ./services/networking/tmate-ssh-server.nix
-  ./services/networking/trickster.nix
   ./services/networking/tox-bootstrapd.nix
   ./services/networking/tox-node.nix
   ./services/networking/toxvpn.nix
+  ./services/networking/trickster.nix
   ./services/networking/tvheadend.nix
   ./services/networking/twingate.nix
   ./services/networking/ucarp.nix
   ./services/networking/unbound.nix
   ./services/networking/unifi.nix
-  ./services/video/unifi-video.nix
-  ./services/video/rtsp-simple-server.nix
   ./services/networking/uptermd.nix
   ./services/networking/v2ray.nix
   ./services/networking/v2raya.nix
@@ -1006,10 +1009,10 @@
   ./services/networking/wg-quick.nix
   ./services/networking/wireguard.nix
   ./services/networking/wpa_supplicant.nix
+  ./services/networking/x2goserver.nix
   ./services/networking/xandikos.nix
   ./services/networking/xinetd.nix
   ./services/networking/xl2tpd.nix
-  ./services/networking/x2goserver.nix
   ./services/networking/xray.nix
   ./services/networking/xrdp.nix
   ./services/networking/yggdrasil.nix
@@ -1022,8 +1025,8 @@
   ./services/scheduling/atd.nix
   ./services/scheduling/cron.nix
   ./services/scheduling/fcron.nix
-  ./services/search/elasticsearch.nix
   ./services/search/elasticsearch-curator.nix
+  ./services/search/elasticsearch.nix
   ./services/search/hound.nix
   ./services/search/kibana.nix
   ./services/search/meilisearch.nix
@@ -1032,25 +1035,25 @@
   ./services/security/certmgr.nix
   ./services/security/cfssl.nix
   ./services/security/clamav.nix
-  ./services/security/endlessh.nix
   ./services/security/endlessh-go.nix
+  ./services/security/endlessh.nix
   ./services/security/fail2ban.nix
   ./services/security/fprintd.nix
   ./services/security/haka.nix
   ./services/security/haveged.nix
   ./services/security/hockeypuck.nix
-  ./services/security/hologram-server.nix
   ./services/security/hologram-agent.nix
-  ./services/security/kanidm.nix
+  ./services/security/hologram-server.nix
   ./services/security/infnoise.nix
+  ./services/security/kanidm.nix
   ./services/security/munge.nix
   ./services/security/nginx-sso.nix
   ./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/privacyidea.nix
   ./services/security/shibboleth-sp.nix
   ./services/security/sks.nix
   ./services/security/sshguard.nix
@@ -1069,8 +1072,8 @@
   ./services/system/cloud-init.nix
   ./services/system/dbus.nix
   ./services/system/earlyoom.nix
-  ./services/system/localtimed.nix
   ./services/system/kerberos/default.nix
+  ./services/system/localtimed.nix
   ./services/system/nscd.nix
   ./services/system/saslauthd.nix
   ./services/system/self-deploy.nix
@@ -1087,19 +1090,21 @@
   ./services/ttys/getty.nix
   ./services/ttys/gpm.nix
   ./services/ttys/kmscon.nix
-  ./services/wayland/cage.nix
   ./services/video/epgstation/default.nix
   ./services/video/mirakurun.nix
   ./services/video/replay-sorcery.nix
+  ./services/video/rtsp-simple-server.nix
+  ./services/video/unifi-video.nix
+  ./services/wayland/cage.nix
   ./services/web-apps/alps.nix
   ./services/web-apps/atlassian/confluence.nix
   ./services/web-apps/atlassian/crowd.nix
   ./services/web-apps/atlassian/jira.nix
+  ./services/web-apps/baget.nix
   ./services/web-apps/bookstack.nix
   ./services/web-apps/calibre-web.nix
-  ./services/web-apps/code-server.nix
-  ./services/web-apps/baget.nix
   ./services/web-apps/changedetection-io.nix
+  ./services/web-apps/code-server.nix
   ./services/web-apps/convos.nix
   ./services/web-apps/dex.nix
   ./services/web-apps/discourse.nix
@@ -1120,16 +1125,17 @@
   ./services/web-apps/icingaweb2/icingaweb2.nix
   ./services/web-apps/icingaweb2/module-monitoring.nix
   ./services/web-apps/ihatemoney
+  ./services/web-apps/invidious.nix
+  ./services/web-apps/invoiceplane.nix
   ./services/web-apps/isso.nix
   ./services/web-apps/jirafeau.nix
   ./services/web-apps/jitsi-meet.nix
   ./services/web-apps/keycloak.nix
   ./services/web-apps/komga.nix
   ./services/web-apps/lemmy.nix
-  ./services/web-apps/invidious.nix
-  ./services/web-apps/invoiceplane.nix
   ./services/web-apps/limesurvey.nix
   ./services/web-apps/mastodon.nix
+  ./services/web-apps/matomo.nix
   ./services/web-apps/mattermost.nix
   ./services/web-apps/mediawiki.nix
   ./services/web-apps/miniflux.nix
@@ -1139,30 +1145,29 @@
   ./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/openwebrx.nix
+  ./services/web-apps/outline.nix
   ./services/web-apps/peering-manager.nix
+  ./services/web-apps/peertube.nix
+  ./services/web-apps/pgpkeyserver-lite.nix
+  ./services/web-apps/phylactery.nix
+  ./services/web-apps/pict-rs.nix
   ./services/web-apps/plantuml-server.nix
   ./services/web-apps/plausible.nix
-  ./services/web-apps/pgpkeyserver-lite.nix
   ./services/web-apps/powerdns-admin.nix
   ./services/web-apps/prosody-filer.nix
-  ./services/web-apps/matomo.nix
-  ./services/web-apps/openwebrx.nix
-  ./services/web-apps/outline.nix
   ./services/web-apps/restya-board.nix
-  ./services/web-apps/sogo.nix
   ./services/web-apps/rss-bridge.nix
-  ./services/web-apps/tt-rss.nix
-  ./services/web-apps/trilium.nix
   ./services/web-apps/selfoss.nix
   ./services/web-apps/shiori.nix
   ./services/web-apps/snipe-it.nix
+  ./services/web-apps/sogo.nix
+  ./services/web-apps/trilium.nix
+  ./services/web-apps/tt-rss.nix
   ./services/web-apps/vikunja.nix
-  ./services/web-apps/wiki-js.nix
   ./services/web-apps/whitebophir.nix
+  ./services/web-apps/wiki-js.nix
   ./services/web-apps/wordpress.nix
   ./services/web-apps/writefreely.nix
   ./services/web-apps/youtrack.nix
@@ -1176,6 +1181,7 @@
   ./services/web-servers/hitch/default.nix
   ./services/web-servers/hydron.nix
   ./services/web-servers/jboss/default.nix
+  ./services/web-servers/keter
   ./services/web-servers/lighttpd/cgit.nix
   ./services/web-servers/lighttpd/collectd.nix
   ./services/web-servers/lighttpd/default.nix
@@ -1188,21 +1194,16 @@
   ./services/web-servers/nginx/gitweb.nix
   ./services/web-servers/phpfpm/default.nix
   ./services/web-servers/pomerium.nix
-  ./services/web-servers/unit/default.nix
   ./services/web-servers/tomcat.nix
-  ./services/web-servers/keter
   ./services/web-servers/traefik.nix
   ./services/web-servers/trafficserver/default.nix
   ./services/web-servers/ttyd.nix
+  ./services/web-servers/unit/default.nix
   ./services/web-servers/uwsgi.nix
   ./services/web-servers/varnish/default.nix
   ./services/web-servers/zope2.nix
-  ./services/x11/extra-layouts.nix
   ./services/x11/clight.nix
   ./services/x11/colord.nix
-  ./services/x11/picom.nix
-  ./services/x11/unclutter.nix
-  ./services/x11/unclutter-xfixes.nix
   ./services/x11/desktop-managers/default.nix
   ./services/x11/display-managers/default.nix
   ./services/x11/display-managers/gdm.nix
@@ -1212,24 +1213,30 @@
   ./services/x11/display-managers/startx.nix
   ./services/x11/display-managers/sx.nix
   ./services/x11/display-managers/xpra.nix
+  ./services/x11/extra-layouts.nix
   ./services/x11/fractalart.nix
+  ./services/x11/gdk-pixbuf.nix
+  ./services/x11/hardware/cmt.nix
+  ./services/x11/hardware/digimend.nix
   ./services/x11/hardware/libinput.nix
   ./services/x11/hardware/synaptics.nix
   ./services/x11/hardware/wacom.nix
-  ./services/x11/hardware/digimend.nix
-  ./services/x11/hardware/cmt.nix
-  ./services/x11/gdk-pixbuf.nix
   ./services/x11/imwheel.nix
+  ./services/x11/picom.nix
   ./services/x11/redshift.nix
   ./services/x11/touchegg.nix
+  ./services/x11/unclutter-xfixes.nix
+  ./services/x11/unclutter.nix
   ./services/x11/urserver.nix
   ./services/x11/urxvtd.nix
   ./services/x11/window-managers/awesome.nix
-  ./services/x11/window-managers/default.nix
+  ./services/x11/window-managers/bspwm.nix
   ./services/x11/window-managers/clfswm.nix
+  ./services/x11/window-managers/default.nix
   ./services/x11/window-managers/fluxbox.nix
   ./services/x11/window-managers/icewm.nix
   ./services/x11/window-managers/bspwm.nix
+  ./services/x11/window-managers/katriawm.nix
   ./services/x11/window-managers/metacity.nix
   ./services/x11/window-managers/none.nix
   ./services/x11/window-managers/twm.nix
@@ -1242,13 +1249,14 @@
   ./services/x11/xserver.nix
   ./system/activation/activation-script.nix
   ./system/activation/specialisation.nix
+  ./system/activation/bootspec.nix
   ./system/activation/top-level.nix
   ./system/boot/binfmt.nix
   ./system/boot/emergency-mode.nix
   ./system/boot/grow-partition.nix
   ./system/boot/initrd-network.nix
-  ./system/boot/initrd-ssh.nix
   ./system/boot/initrd-openvpn.nix
+  ./system/boot/initrd-ssh.nix
   ./system/boot/kernel.nix
   ./system/boot/kexec.nix
   ./system/boot/loader/efi.nix
@@ -1257,6 +1265,7 @@
   ./system/boot/loader/grub/grub.nix
   ./system/boot/loader/grub/ipxe.nix
   ./system/boot/loader/grub/memtest.nix
+  ./system/boot/loader/external/external.nix
   ./system/boot/loader/init-script/init-script.nix
   ./system/boot/loader/loader.nix
   ./system/boot/loader/raspberrypi/raspberrypi.nix
@@ -1307,35 +1316,35 @@
   ./tasks/filesystems/xfs.nix
   ./tasks/filesystems/zfs.nix
   ./tasks/lvm.nix
-  ./tasks/network-interfaces.nix
-  ./tasks/network-interfaces-systemd.nix
   ./tasks/network-interfaces-scripted.nix
+  ./tasks/network-interfaces-systemd.nix
+  ./tasks/network-interfaces.nix
+  ./tasks/powertop.nix
   ./tasks/scsi-link-power-management.nix
   ./tasks/snapraid.nix
   ./tasks/stratis.nix
   ./tasks/swraid.nix
   ./tasks/trackpoint.nix
-  ./tasks/powertop.nix
   ./testing/service-runner.nix
+  ./virtualisation/amazon-options.nix
   ./virtualisation/anbox.nix
   ./virtualisation/appvm.nix
   ./virtualisation/build-vm.nix
   ./virtualisation/container-config.nix
   ./virtualisation/containerd.nix
   ./virtualisation/containers.nix
-  ./virtualisation/nixos-containers.nix
-  ./virtualisation/oci-containers.nix
   ./virtualisation/cri-o.nix
-  ./virtualisation/docker.nix
   ./virtualisation/docker-rootless.nix
+  ./virtualisation/docker.nix
   ./virtualisation/ecs-agent.nix
+  ./virtualisation/hyperv-guest.nix
+  ./virtualisation/kvmgt.nix
   ./virtualisation/libvirtd.nix
   ./virtualisation/lxc.nix
   ./virtualisation/lxcfs.nix
   ./virtualisation/lxd.nix
-  ./virtualisation/amazon-options.nix
-  ./virtualisation/hyperv-guest.nix
-  ./virtualisation/kvmgt.nix
+  ./virtualisation/nixos-containers.nix
+  ./virtualisation/oci-containers.nix
   ./virtualisation/openstack-options.nix
   ./virtualisation/openvswitch.nix
   ./virtualisation/parallels-guest.nix
@@ -1348,7 +1357,7 @@
   ./virtualisation/vmware-guest.nix
   ./virtualisation/vmware-host.nix
   ./virtualisation/waydroid.nix
-  ./virtualisation/xen-dom0.nix
   ./virtualisation/xe-guest-utilities.nix
+  ./virtualisation/xen-dom0.nix
   { documentation.nixos.extraModules = [ ./virtualisation/qemu-vm.nix ]; }
 ]
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
index eebc0df1040c..616b2470dcb4 100644
--- a/nixos/modules/profiles/base.nix
+++ b/nixos/modules/profiles/base.nix
@@ -42,6 +42,7 @@
     pkgs.smartmontools # for diagnosing hard disks
     pkgs.pciutils
     pkgs.usbutils
+    pkgs.nvme-cli
 
     # Tools to create / manipulate filesystems.
     pkgs.ntfsprogs # for resizing NTFS partitions
diff --git a/nixos/modules/profiles/keys/ssh_host_ed25519_key b/nixos/modules/profiles/keys/ssh_host_ed25519_key
new file mode 100644
index 000000000000..b18489795369
--- /dev/null
+++ b/nixos/modules/profiles/keys/ssh_host_ed25519_key
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmwAAAJASuMMnErjD
+JwAAAAtzc2gtZWQyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmw
+AAAEDIN2VWFyggtoSPXcAFy8dtG1uAig8sCuyE21eMDt2GgJBWcxb/Blaqt1auOtE+F8QU
+WrUotiC5qBJ+UuEWdVCbAAAACnJvb3RAbml4b3MBAgM=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/nixos/modules/profiles/keys/ssh_host_ed25519_key.pub b/nixos/modules/profiles/keys/ssh_host_ed25519_key.pub
new file mode 100644
index 000000000000..2c45826715fc
--- /dev/null
+++ b/nixos/modules/profiles/keys/ssh_host_ed25519_key.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJBWcxb/Blaqt1auOtE+F8QUWrUotiC5qBJ+UuEWdVCb root@nixos
diff --git a/nixos/modules/profiles/macos-builder.nix b/nixos/modules/profiles/macos-builder.nix
new file mode 100644
index 000000000000..895dd04cb485
--- /dev/null
+++ b/nixos/modules/profiles/macos-builder.nix
@@ -0,0 +1,134 @@
+{ config, pkgs, ... }:
+
+let
+  keysDirectory = "/var/keys";
+
+  user = "builder";
+
+  keyType = "ed25519";
+
+in
+
+{ imports = [
+    ../virtualisation/qemu-vm.nix
+  ];
+
+  # The builder is not intended to be used interactively
+  documentation.enable = false;
+
+  environment.etc = {
+    "ssh/ssh_host_ed25519_key" = {
+      mode = "0600";
+
+      source = ./keys/ssh_host_ed25519_key;
+    };
+
+    "ssh/ssh_host_ed25519_key.pub" = {
+      mode = "0644";
+
+      source = ./keys/ssh_host_ed25519_key.pub;
+    };
+  };
+
+  # DNS fails for QEMU user networking (SLiRP) on macOS.  See:
+  #
+  # https://github.com/utmapp/UTM/issues/2353
+  #
+  # This works around that by using a public DNS server other than the DNS
+  # server that QEMU provides (normally 10.0.2.3)
+  networking.nameservers = [ "8.8.8.8" ];
+
+  nix.settings = {
+    auto-optimise-store = true;
+
+    min-free = 1024 * 1024 * 1024;
+
+    max-free = 3 * 1024 * 1024 * 1024;
+
+    trusted-users = [ "root" user ];
+  };
+
+  services.openssh = {
+    enable = true;
+
+    authorizedKeysFiles = [ "${keysDirectory}/%u_${keyType}.pub" ];
+  };
+
+  system.build.macos-builder-installer =
+    let
+      privateKey = "/etc/nix/${user}_${keyType}";
+
+      publicKey = "${privateKey}.pub";
+
+      # This installCredentials script is written so that it's as easy as
+      # possible for a user to audit before confirming the `sudo`
+      installCredentials = pkgs.writeShellScript "install-credentials" ''
+        KEYS="''${1}"
+        INSTALL=${hostPkgs.coreutils}/bin/install
+        "''${INSTALL}" -g nixbld -m 600 "''${KEYS}/${user}_${keyType}" ${privateKey}
+        "''${INSTALL}" -g nixbld -m 644 "''${KEYS}/${user}_${keyType}.pub" ${publicKey}
+      '';
+
+      hostPkgs = config.virtualisation.host.pkgs;
+
+    in
+      hostPkgs.writeShellScriptBin "create-builder" ''
+        KEYS="''${KEYS:-./keys}"
+        ${hostPkgs.coreutils}/bin/mkdir --parent "''${KEYS}"
+        PRIVATE_KEY="''${KEYS}/${user}_${keyType}"
+        PUBLIC_KEY="''${PRIVATE_KEY}.pub"
+        if [ ! -e "''${PRIVATE_KEY}" ] || [ ! -e "''${PUBLIC_KEY}" ]; then
+            ${hostPkgs.coreutils}/bin/rm --force -- "''${PRIVATE_KEY}" "''${PUBLIC_KEY}"
+            ${hostPkgs.openssh}/bin/ssh-keygen -q -f "''${PRIVATE_KEY}" -t ${keyType} -N "" -C 'builder@localhost'
+        fi
+        if ! ${hostPkgs.diffutils}/bin/cmp "''${PUBLIC_KEY}" ${publicKey}; then
+          (set -x; sudo --reset-timestamp ${installCredentials} "''${KEYS}")
+        fi
+        KEYS="$(nix-store --add "$KEYS")" ${config.system.build.vm}/bin/run-nixos-vm
+      '';
+
+  system.stateVersion = "22.05";
+
+  users.users."${user}"= {
+    isNormalUser = true;
+  };
+
+  virtualisation = {
+    diskSize = 20 * 1024;
+
+    memorySize = 3 * 1024;
+
+    forwardPorts = [
+      { from = "host"; guest.port = 22; host.port = 22; }
+    ];
+
+    # Disable graphics for the builder since users will likely want to run it
+    # non-interactively in the background.
+    graphics = false;
+
+    sharedDirectories.keys = {
+      source = "\"$KEYS\"";
+      target = keysDirectory;
+    };
+
+    # If we don't enable this option then the host will fail to delegate builds
+    # to the guest, because:
+    #
+    # - The host will lock the path to build
+    # - The host will delegate the build to the guest
+    # - The guest will attempt to lock the same path and fail because
+    #   the lockfile on the host is visible on the guest
+    #
+    # Snapshotting the host's /nix/store as an image isolates the guest VM's
+    # /nix/store from the host's /nix/store, preventing this problem.
+    useNixStoreImage = true;
+
+    # Obviously the /nix/store needs to be writable on the guest in order for it
+    # to perform builds.
+    writableStore = true;
+
+    # This ensures that anything built on the guest isn't lost when the guest is
+    # restarted.
+    writableStoreUseTmpfs = false;
+  };
+}
diff --git a/nixos/modules/programs/firefox.nix b/nixos/modules/programs/firefox.nix
index dfd912cdf5c1..3a5105c57d76 100644
--- a/nixos/modules/programs/firefox.nix
+++ b/nixos/modules/programs/firefox.nix
@@ -42,7 +42,7 @@ in
       description = mdDoc ''
         Group policies to install.
 
-        See [Mozilla's documentation](https://github.com/mozilla/policy-templates/blob/master/README.md")
+        See [Mozilla's documentation](https://github.com/mozilla/policy-templates/blob/master/README.md)
         for a list of available options.
 
         This can be used to install extensions declaratively! Check out the
@@ -79,6 +79,114 @@ in
       '';
     };
 
+    languagePacks = mkOption {
+      # Available languages can be found in https://releases.mozilla.org/pub/firefox/releases/${cfg.package.version}/linux-x86_64/xpi/
+      type = types.listOf (types.enum ([
+        "ach"
+        "af"
+        "an"
+        "ar"
+        "ast"
+        "az"
+        "be"
+        "bg"
+        "bn"
+        "br"
+        "bs"
+        "ca-valencia"
+        "ca"
+        "cak"
+        "cs"
+        "cy"
+        "da"
+        "de"
+        "dsb"
+        "el"
+        "en-CA"
+        "en-GB"
+        "en-US"
+        "eo"
+        "es-AR"
+        "es-CL"
+        "es-ES"
+        "es-MX"
+        "et"
+        "eu"
+        "fa"
+        "ff"
+        "fi"
+        "fr"
+        "fy-NL"
+        "ga-IE"
+        "gd"
+        "gl"
+        "gn"
+        "gu-IN"
+        "he"
+        "hi-IN"
+        "hr"
+        "hsb"
+        "hu"
+        "hy-AM"
+        "ia"
+        "id"
+        "is"
+        "it"
+        "ja"
+        "ka"
+        "kab"
+        "kk"
+        "km"
+        "kn"
+        "ko"
+        "lij"
+        "lt"
+        "lv"
+        "mk"
+        "mr"
+        "ms"
+        "my"
+        "nb-NO"
+        "ne-NP"
+        "nl"
+        "nn-NO"
+        "oc"
+        "pa-IN"
+        "pl"
+        "pt-BR"
+        "pt-PT"
+        "rm"
+        "ro"
+        "ru"
+        "sco"
+        "si"
+        "sk"
+        "sl"
+        "son"
+        "sq"
+        "sr"
+        "sv-SE"
+        "szl"
+        "ta"
+        "te"
+        "th"
+        "tl"
+        "tr"
+        "trs"
+        "uk"
+        "ur"
+        "uz"
+        "vi"
+        "xh"
+        "zh-CN"
+        "zh-TW"
+      ]));
+      default = [ ];
+      description = mdDoc ''
+        The language packs to install.
+      '';
+    };
+
     autoConfig = mkOption {
       type = types.lines;
       default = "";
@@ -136,10 +244,19 @@ in
       };
 
     # Preferences are converted into a policy
-    programs.firefox.policies = mkIf (cfg.preferences != { }) {
+    programs.firefox.policies = {
       Preferences = (mapAttrs
         (_: value: { Value = value; Status = cfg.preferencesStatus; })
         cfg.preferences);
+      ExtensionSettings = listToAttrs (map
+        (lang: nameValuePair
+          "langpack-${lang}@firefox.mozilla.org"
+          {
+            installation_mode = "normal_installed";
+            install_url = "https://releases.mozilla.org/pub/firefox/releases/${cfg.package.version}/linux-x86_64/xpi/${lang}.xpi";
+          }
+        )
+        cfg.languagePacks);
     };
   };
 
diff --git a/nixos/modules/programs/i3lock.nix b/nixos/modules/programs/i3lock.nix
new file mode 100644
index 000000000000..466ae59c9277
--- /dev/null
+++ b/nixos/modules/programs/i3lock.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.programs.i3lock;
+
+in {
+
+  ###### interface
+
+  options = {
+    programs.i3lock = {
+      enable = mkEnableOption (mdDoc "i3lock");
+      package = mkOption {
+        type        = types.package;
+        default     = pkgs.i3lock;
+        defaultText = literalExpression "pkgs.i3lock";
+        example     = literalExpression ''
+          pkgs.i3lock-color
+        '';
+        description = mdDoc ''
+          Specify which package to use for the i3lock program,
+          The i3lock package must include a i3lock file or link in its out directory in order for the u2fSupport option to work correctly.
+        '';
+      };
+      u2fSupport = mkOption {
+        type        = types.bool;
+        default     = false;
+        example     = true;
+        description = mdDoc ''
+          Whether to enable U2F support in the i3lock program.
+          U2F enables authentication using a hardware device, such as a security key.
+          When U2F support is enabled, the i3lock program will set the setuid bit on the i3lock binary and enable the pam u2fAuth service,
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    security.wrappers.i3lock = mkIf cfg.u2fSupport {
+      setuid = true;
+      owner = "root";
+      group = "root";
+      source = "${cfg.package.out}/bin/i3lock";
+    };
+
+    security.pam.services.i3lock.u2fAuth = cfg.u2fSupport;
+
+  };
+
+}
diff --git a/nixos/modules/programs/nix-ld.nix b/nixos/modules/programs/nix-ld.nix
index 602278d1ba9b..f753cf5f97e5 100644
--- a/nixos/modules/programs/nix-ld.nix
+++ b/nixos/modules/programs/nix-ld.nix
@@ -1,10 +1,68 @@
 { pkgs, lib, config, ... }:
+let
+  cfg = config.programs.nix-ld;
+
+  # TODO make glibc here configureable?
+  nix-ld-so = pkgs.runCommand "ld.so" {} ''
+    ln -s "$(cat '${pkgs.stdenv.cc}/nix-support/dynamic-linker')" $out
+  '';
+
+  nix-ld-libraries = pkgs.buildEnv {
+    name = "lb-library-path";
+    pathsToLink = [ "/lib" ];
+    paths = map lib.getLib cfg.libraries;
+    extraPrefix = "/share/nix-ld";
+    ignoreCollisions = true;
+  };
+
+  # We currently take all libraries from systemd and nix as the default.
+  # Is there a better list?
+  baseLibraries = with pkgs; [
+    zlib
+    zstd
+    stdenv.cc.cc
+    curl
+    openssl
+    attr
+    libssh
+    bzip2
+    libxml2
+    acl
+    libsodium
+    util-linux
+    xz
+    systemd
+  ];
+in
 {
   meta.maintainers = [ lib.maintainers.mic92 ];
   options = {
-    programs.nix-ld.enable = lib.mkEnableOption (lib.mdDoc ''nix-ld, Documentation: <https://github.com/Mic92/nix-ld>'');
+    programs.nix-ld = {
+      enable = lib.mkEnableOption (lib.mdDoc ''nix-ld, Documentation: <https://github.com/Mic92/nix-ld>'');
+      package = lib.mkOption {
+        type = lib.types.package;
+        description = lib.mdDoc "Which package to use for the nix-ld.";
+        default = pkgs.nix-ld;
+        defaultText = lib.mdDoc "pkgs.nix-ld";
+      };
+      libraries = lib.mkOption {
+        type = lib.types.listOf lib.types.package;
+        description = lib.mdDoc "Libraries that automatically become available to all programs. The default set includes common libraries.";
+        default = baseLibraries;
+        defaultText = lib.mdDoc "baseLibraries";
+      };
+    };
   };
   config = lib.mkIf config.programs.nix-ld.enable {
-    systemd.tmpfiles.packages = [ pkgs.nix-ld ];
+    systemd.tmpfiles.packages = [ cfg.package ];
+
+    environment.systemPackages = [ nix-ld-libraries ];
+
+    environment.pathsToLink = [ "/share/nix-ld" ];
+
+    environment.variables = {
+      NIX_LD = toString nix-ld-so;
+      NIX_LD_LIBRARY_PATH = "/run/current-system/sw/share/nix-ld/lib";
+    };
   };
 }
diff --git a/nixos/modules/programs/streamdeck-ui.nix b/nixos/modules/programs/streamdeck-ui.nix
index e933b899c55e..113d1d49e151 100644
--- a/nixos/modules/programs/streamdeck-ui.nix
+++ b/nixos/modules/programs/streamdeck-ui.nix
@@ -4,7 +4,8 @@ with lib;
 
 let
   cfg = config.programs.streamdeck-ui;
-in {
+in
+{
   options.programs.streamdeck-ui = {
     enable = mkEnableOption (lib.mdDoc "streamdeck-ui");
 
@@ -13,15 +14,20 @@ in {
       type = types.bool;
       description = lib.mdDoc "Whether streamdeck-ui should be started automatically.";
     };
+
+    package = mkPackageOption pkgs "streamdeck-ui" {
+      default = [ "streamdeck-ui" ];
+    };
+
   };
 
   config = mkIf cfg.enable {
     environment.systemPackages = with pkgs; [
-      streamdeck-ui
-      (mkIf cfg.autoStart (makeAutostartItem { name = "streamdeck-ui"; package = streamdeck-ui; }))
+      cfg.package
+      (mkIf cfg.autoStart (makeAutostartItem { name = "streamdeck-ui"; package = cfg.package; }))
     ];
 
-    services.udev.packages = with pkgs; [ streamdeck-ui ];
+    services.udev.packages = [ cfg.package ];
   };
 
   meta.maintainers = with maintainers; [ majiir ];
diff --git a/nixos/modules/programs/vim.nix b/nixos/modules/programs/vim.nix
index 15983e371f0e..b12a45166d56 100644
--- a/nixos/modules/programs/vim.nix
+++ b/nixos/modules/programs/vim.nix
@@ -19,7 +19,7 @@ in {
       type = types.package;
       default = pkgs.vim;
       defaultText = literalExpression "pkgs.vim";
-      example = literalExpression "pkgs.vimHugeX";
+      example = literalExpression "pkgs.vim-full";
       description = lib.mdDoc ''
         vim package to use.
       '';
diff --git a/nixos/modules/programs/xfs_quota.nix b/nixos/modules/programs/xfs_quota.nix
index a1e9ff941c6b..0fc2958b3f38 100644
--- a/nixos/modules/programs/xfs_quota.nix
+++ b/nixos/modules/programs/xfs_quota.nix
@@ -94,7 +94,7 @@ in
         '';
 
         wantedBy = [ "multi-user.target" ];
-        after = [ ((replaceChars [ "/" ] [ "-" ] opts.fileSystem) + ".mount") ];
+        after = [ ((replaceStrings [ "/" ] [ "-" ] opts.fileSystem) + ".mount") ];
 
         restartTriggers = [ config.environment.etc.projects.source ];
 
diff --git a/nixos/modules/programs/xonsh.nix b/nixos/modules/programs/xonsh.nix
index 3223761f9340..7202ed06c6af 100644
--- a/nixos/modules/programs/xonsh.nix
+++ b/nixos/modules/programs/xonsh.nix
@@ -46,8 +46,8 @@ in
 
   config = mkIf cfg.enable {
 
-    environment.etc.xonshrc.text = ''
-      # /etc/xonshrc: DO NOT EDIT -- this file has been generated automatically.
+    environment.etc."xonsh/xonshrc".text = ''
+      # /etc/xonsh/xonshrc: DO NOT EDIT -- this file has been generated automatically.
 
 
       if not ''${...}.get('__NIXOS_SET_ENVIRONMENT_DONE'):
diff --git a/nixos/modules/security/acme/default.nix b/nixos/modules/security/acme/default.nix
index 4e163901b088..a380bb5484af 100644
--- a/nixos/modules/security/acme/default.nix
+++ b/nixos/modules/security/acme/default.nix
@@ -714,7 +714,7 @@ in {
         default = false;
         description = lib.mdDoc ''
           Whether to use the root user when generating certs. This is not recommended
-          for security + compatiblity reasons. If a service requires root owned certificates
+          for security + compatibility reasons. If a service requires root owned certificates
           consider following the guide on "Using ACME with services demanding root
           owned certificates" in the NixOS manual, and only using this as a fallback
           or for testing.
@@ -765,7 +765,7 @@ in {
       To use the let's encrypt staging server, use security.acme.server =
       "https://acme-staging-v02.api.letsencrypt.org/directory".
     '')
-    (mkRemovedOptionModule [ "security" "acme" "directory" ] "ACME Directory is now hardcoded to /var/lib/acme and its permisisons are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.")
+    (mkRemovedOptionModule [ "security" "acme" "directory" ] "ACME Directory is now hardcoded to /var/lib/acme and its permissions are managed by systemd. See https://github.com/NixOS/nixpkgs/issues/53852 for more info.")
     (mkRemovedOptionModule [ "security" "acme" "preDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
     (mkRemovedOptionModule [ "security" "acme" "activationDelay" ] "This option has been removed. If you want to make sure that something executes before certificates are provisioned, add a RequiredBy=acme-\${cert}.service to the service you want to execute before the cert renewal")
     (mkChangedOptionModule [ "security" "acme" "validMin" ] [ "security" "acme" "defaults" "validMinDays" ] (config: config.security.acme.validMin / (24 * 3600)))
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
index 0d858a458564..24b48338ed77 100644
--- a/nixos/modules/security/apparmor.nix
+++ b/nixos/modules/security/apparmor.nix
@@ -202,7 +202,7 @@ in
           # (indirectly read from /etc/apparmor.d/*, without recursing into sub-directory).
           # Note that this does not remove profiles dynamically generated by libvirt.
           [ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] ++
-          # Optionaly kill the processes which are unconfined but now have a profile loaded
+          # Optionally kill the processes which are unconfined but now have a profile loaded
           # (because AppArmor can only start to confine new processes).
           optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
         ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 21e1749d8503..273bc796341c 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -282,7 +282,7 @@ let
         defaultText = literalExpression "config.security.pam.mount.enable";
         type = types.bool;
         description = lib.mdDoc ''
-          Enable PAM mount (pam_mount) system to mount fileystems on user login.
+          Enable PAM mount (pam_mount) system to mount filesystems on user login.
         '';
       };
 
@@ -305,7 +305,7 @@ let
         default = false;
         type = types.bool;
         description = lib.mdDoc ''
-          Wheather the delay after typing a wrong password should be disabled.
+          Whether the delay after typing a wrong password should be disabled.
         '';
       };
 
@@ -694,7 +694,7 @@ let
           optionalString (cfg.limits != []) ''
             session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}
           '' +
-          optionalString (cfg.showMotd && config.users.motd != null) ''
+          optionalString (cfg.showMotd && (config.users.motd != null || config.users.motdFile != null)) ''
             session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}
           '' +
           optionalString (cfg.enableAppArmor && config.security.apparmor.enable) ''
@@ -775,7 +775,9 @@ let
     };
   }));
 
-  motd = pkgs.writeText "motd" config.users.motd;
+  motd = if isNull config.users.motdFile
+         then pkgs.writeText "motd" config.users.motd
+         else config.users.motdFile;
 
   makePAMService = name: service:
     { name = "pam.d/${name}";
@@ -1199,12 +1201,26 @@ in
       description = lib.mdDoc "Message of the day shown to users when they log in.";
     };
 
+    users.motdFile = mkOption {
+      default = null;
+      example = "/etc/motd";
+      type = types.nullOr types.path;
+      description = lib.mdDoc "A file containing the message of the day shown to users when they log in.";
+    };
   };
 
 
   ###### implementation
 
   config = {
+    assertions = [
+      {
+        assertion = isNull config.users.motd || isNull config.users.motdFile;
+        message = ''
+          Only one of users.motd and users.motdFile can be set.
+        '';
+      }
+    ];
 
     environment.systemPackages =
       # Include the PAM modules in the system path mostly for the manpages.
diff --git a/nixos/modules/security/pam_mount.nix b/nixos/modules/security/pam_mount.nix
index 11cc13a8cbeb..481f1f3d38eb 100644
--- a/nixos/modules/security/pam_mount.nix
+++ b/nixos/modules/security/pam_mount.nix
@@ -24,7 +24,7 @@ in
         type = types.bool;
         default = false;
         description = lib.mdDoc ''
-          Enable PAM mount system to mount fileystems on user login.
+          Enable PAM mount system to mount filesystems on user login.
         '';
       };
 
diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix
index a58c792d8c5f..4b62abd658a4 100644
--- a/nixos/modules/security/wrappers/default.nix
+++ b/nixos/modules/security/wrappers/default.nix
@@ -202,7 +202,7 @@ in
       internal    = true;
       description = lib.mdDoc ''
         This option defines the path to the wrapper programs. It
-        should not be overriden.
+        should not be overridden.
       '';
     };
   };
diff --git a/nixos/modules/services/audio/botamusique.nix b/nixos/modules/services/audio/botamusique.nix
index 4cd900f945c8..5d3f7db12bc9 100644
--- a/nixos/modules/services/audio/botamusique.nix
+++ b/nixos/modules/services/audio/botamusique.nix
@@ -103,9 +103,8 @@ in
         StateDirectory = "botamusique";
         SystemCallArchitectures = "native";
         SystemCallFilter = [
-          "@system-service"
+          "@system-service @resources"
           "~@privileged"
-          "~@resources"
         ];
         UMask = "0077";
         WorkingDirectory = "/var/lib/botamusique";
diff --git a/nixos/modules/services/audio/icecast.nix b/nixos/modules/services/audio/icecast.nix
index f71a13b9626d..63049bd93ab9 100644
--- a/nixos/modules/services/audio/icecast.nix
+++ b/nixos/modules/services/audio/icecast.nix
@@ -48,7 +48,7 @@ in {
 
       hostname = mkOption {
         type = types.nullOr types.str;
-        description = lib.mdDoc "DNS name or IP address that will be used for the stream directory lookups or possibily the playlist generation if a Host header is not provided.";
+        description = lib.mdDoc "DNS name or IP address that will be used for the stream directory lookups or possibly the playlist generation if a Host header is not provided.";
         default = config.networking.domain;
         defaultText = literalExpression "config.networking.domain";
       };
diff --git a/nixos/modules/services/audio/roon-server.nix b/nixos/modules/services/audio/roon-server.nix
index 4764ee3e598f..74cae909f5db 100644
--- a/nixos/modules/services/audio/roon-server.nix
+++ b/nixos/modules/services/audio/roon-server.nix
@@ -40,6 +40,7 @@ in {
       wantedBy = [ "multi-user.target" ];
 
       environment.ROON_DATAROOT = "/var/lib/${name}";
+      environment.ROON_ID_DIR = "/var/lib/${name}";
 
       serviceConfig = {
         ExecStart = "${pkgs.roon-server}/bin/RoonServer";
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
index ea1d393c7b02..ae8e1dd8463b 100644
--- a/nixos/modules/services/backup/borgbackup.nix
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -11,7 +11,11 @@ let
 
   mkExcludeFile = cfg:
     # Write each exclude pattern to a new line
-    pkgs.writeText "excludefile" (concatStringsSep "\n" cfg.exclude);
+    pkgs.writeText "excludefile" (concatMapStrings (s: s + "\n") cfg.exclude);
+
+  mkPatternsFile = cfg:
+    # Write each pattern to a new line
+    pkgs.writeText "patternsfile" (concatMapStrings (s: s + "\n") cfg.patterns);
 
   mkKeepArgs = cfg:
     # If cfg.prune.keep e.g. has a yearly attribute,
@@ -19,7 +23,8 @@ let
     concatStringsSep " "
       (mapAttrsToList (x: y: "--keep-${x}=${toString y}") cfg.prune.keep);
 
-  mkBackupScript = cfg: ''
+  mkBackupScript = name: cfg: pkgs.writeShellScript "${name}-script" (''
+    set -e
     on_exit()
     {
       exitStatus=$?
@@ -46,6 +51,7 @@ let
       borg create $extraArgs \
         --compression ${cfg.compression} \
         --exclude-from ${mkExcludeFile cfg} \
+        --patterns-from ${mkPatternsFile cfg} \
         $extraCreateArgs \
         "::$archiveName$archiveSuffix" \
         ${if cfg.paths == null then "-" else escapeShellArgs cfg.paths}
@@ -61,7 +67,7 @@ let
       ${optionalString (cfg.prune.prefix != null) "--glob-archives ${escapeShellArg "${cfg.prune.prefix}*"}"} \
       $extraPruneArgs
     ${cfg.postPrune}
-  '';
+  '');
 
   mkPassEnv = cfg: with cfg.encryption;
     if passCommand != null then
@@ -73,12 +79,19 @@ let
   mkBackupService = name: cfg:
     let
       userHome = config.users.users.${cfg.user}.home;
-    in nameValuePair "borgbackup-job-${name}" {
+      backupJobName = "borgbackup-job-${name}";
+      backupScript = mkBackupScript backupJobName cfg;
+    in nameValuePair backupJobName {
       description = "BorgBackup job ${name}";
       path = with pkgs; [
         borgbackup openssh
       ];
-      script = mkBackupScript cfg;
+      script = "exec " + optionalString cfg.inhibitsSleep ''\
+        ${pkgs.systemd}/bin/systemd-inhibit \
+            --who="borgbackup" \
+            --what="sleep" \
+            --why="Scheduled backup" \
+        '' + backupScript;
       serviceConfig = {
         User = cfg.user;
         Group = cfg.group;
@@ -341,6 +354,15 @@ in {
             '';
           };
 
+          inhibitsSleep = mkOption {
+            default = false;
+            type = types.bool;
+            example = true;
+            description = lib.mdDoc ''
+              Prevents the system from sleeping while backing up.
+            '';
+          };
+
           user = mkOption {
             type = types.str;
             description = lib.mdDoc ''
@@ -424,6 +446,21 @@ in {
             ];
           };
 
+          patterns = mkOption {
+            type = with types; listOf str;
+            description = lib.mdDoc ''
+              Include/exclude paths matching the given patterns. The first
+              matching patterns is used, so if an include pattern (prefix `+`)
+              matches before an exclude pattern (prefix `-`), the file is
+              backed up. See [{command}`borg help patterns`](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-patterns) for pattern syntax.
+            '';
+            default = [ ];
+            example = [
+              "+ /home/susan"
+              "- /home/*"
+            ];
+          };
+
           readWritePaths = mkOption {
             type = with types; listOf path;
             description = lib.mdDoc ''
diff --git a/nixos/modules/services/backup/zfs-replication.nix b/nixos/modules/services/backup/zfs-replication.nix
index f0267c47364d..ce914003c622 100644
--- a/nixos/modules/services/backup/zfs-replication.nix
+++ b/nixos/modules/services/backup/zfs-replication.nix
@@ -12,7 +12,7 @@ in {
       enable = mkEnableOption (lib.mdDoc "ZFS snapshot replication.");
 
       followDelete = mkOption {
-        description = lib.mdDoc "Remove remote snapshots that don't have a local correspondant.";
+        description = lib.mdDoc "Remove remote snapshots that don't have a local correspondent.";
         default = true;
         type = types.bool;
       };
@@ -30,7 +30,7 @@ in {
       };
 
       localFilesystem = mkOption {
-        description = lib.mdDoc "Local ZFS fileystem from which snapshots should be sent.  Defaults to the attribute name.";
+        description = lib.mdDoc "Local ZFS filesystem from which snapshots should be sent.  Defaults to the attribute name.";
         example = "pool/file/path";
         type = types.str;
       };
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index f8d741e3ad9a..76f147c18aff 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -9,7 +9,7 @@ let
       The znapzend backup plan to use for the source.
 
       The plan specifies how often to backup and for how long to keep the
-      backups. It consists of a series of retention periodes to interval
+      backups. It consists of a series of retention periods to interval
       associations:
 
       ```
@@ -268,7 +268,7 @@ let
 
   mkSrcAttrs = srcCfg: with srcCfg; {
     enabled = onOff enable;
-    # mbuffer is not referenced by its full path to accomodate non-NixOS systems or differing mbuffer versions between source and target
+    # mbuffer is not referenced by its full path to accommodate non-NixOS systems or differing mbuffer versions between source and target
     mbuffer = with mbuffer; if enable then "mbuffer"
         + optionalString (port != null) ":${toString port}" else "off";
     mbuffer_size = mbuffer.size;
@@ -372,7 +372,7 @@ in
         compressed feature which adds the options `-Lce` to
         the {command}`zfs send` command. When this is enabled, make
         sure that both the sending and receiving pool have the same relevant
-        features enabled. Using `-c` will skip unneccessary
+        features enabled. Using `-c` will skip unnecessary
         decompress-compress stages, `-L` is for large block
         support and -e is for embedded data support. see
         {manpage}`znapzend(1)`
diff --git a/nixos/modules/services/blockchain/ethereum/lighthouse.nix b/nixos/modules/services/blockchain/ethereum/lighthouse.nix
index 20a4ead689c8..863e737d908a 100644
--- a/nixos/modules/services/blockchain/ethereum/lighthouse.nix
+++ b/nixos/modules/services/blockchain/ethereum/lighthouse.nix
@@ -51,7 +51,7 @@ in {
               type = types.bool;
               default = false;
               description = lib.mdDoc ''
-                Explictly disables syncing of deposit logs from the execution node.
+                Explicitly disables syncing of deposit logs from the execution node.
                 This overrides any previous option that depends on it.
                 Useful if you intend to run a non-validating beacon node.
               '';
@@ -280,7 +280,7 @@ in {
         ${pkgs.lighthouse}/bin/lighthouse validator_client \
           --network ${cfg.network} \
           --beacon-nodes ${lib.concatStringsSep "," cfg.validator.beaconNodes} \
-          --datadir ${cfg.validator.dataDir}/${cfg.network}
+          --datadir ${cfg.validator.dataDir}/${cfg.network} \
           ${optionalString cfg.validator.metrics.enable ''--metrics --metrics-address ${cfg.validator.metrics.address} --metrics-port ${toString cfg.validator.metrics.port}''} \
           ${cfg.extraArgs} ${cfg.validator.extraArgs}
       '';
diff --git a/nixos/modules/services/cluster/kubernetes/addon-manager.nix b/nixos/modules/services/cluster/kubernetes/addon-manager.nix
index d6b3428908b2..7aa2a8323b1d 100644
--- a/nixos/modules/services/cluster/kubernetes/addon-manager.nix
+++ b/nixos/modules/services/cluster/kubernetes/addon-manager.nix
@@ -22,7 +22,7 @@ in
 
     bootstrapAddons = mkOption {
       description = lib.mdDoc ''
-        Bootstrap addons are like regular addons, but they are applied with cluster-admin rigths.
+        Bootstrap addons are like regular addons, but they are applied with cluster-admin rights.
         They are applied at addon-manager startup only.
       '';
       default = { };
diff --git a/nixos/modules/services/cluster/kubernetes/pki.nix b/nixos/modules/services/cluster/kubernetes/pki.nix
index d68267883e45..26fe0f5e9e09 100644
--- a/nixos/modules/services/cluster/kubernetes/pki.nix
+++ b/nixos/modules/services/cluster/kubernetes/pki.nix
@@ -323,7 +323,7 @@ in
           systemctl restart flannel
         ''}
 
-        echo "Node joined succesfully"
+        echo "Node joined successfully"
       '')];
 
       # isolate etcd on loopback at the master node
diff --git a/nixos/modules/services/continuous-integration/buildbot/master.nix b/nixos/modules/services/continuous-integration/buildbot/master.nix
index 680b21dbf21b..5666199c4845 100644
--- a/nixos/modules/services/continuous-integration/buildbot/master.nix
+++ b/nixos/modules/services/continuous-integration/buildbot/master.nix
@@ -1,4 +1,4 @@
-# NixOS module for Buildbot continous integration server.
+# NixOS module for Buildbot continuous integration server.
 
 { config, lib, options, pkgs, ... }:
 
diff --git a/nixos/modules/services/continuous-integration/github-runner/options.nix b/nixos/modules/services/continuous-integration/github-runner/options.nix
index 796b5a7f1175..72ac0c129900 100644
--- a/nixos/modules/services/continuous-integration/github-runner/options.nix
+++ b/nixos/modules/services/continuous-integration/github-runner/options.nix
@@ -49,7 +49,7 @@ with lib;
       registration token on startup as needed. Make sure the PAT has a scope of
       `admin:org` for organization-wide registrations or a scope of
       `repo` for a single repository. Fine-grained PATs need read and write permission
-      to the "Adminstration" resources.
+      to the "Administration" resources.
 
       Changing this option or the file's content triggers a new runner registration.
     '';
diff --git a/nixos/modules/services/continuous-integration/gitlab-runner.nix b/nixos/modules/services/continuous-integration/gitlab-runner.nix
index 2050e04d55cd..7b1c4da86260 100644
--- a/nixos/modules/services/continuous-integration/gitlab-runner.nix
+++ b/nixos/modules/services/continuous-integration/gitlab-runner.nix
@@ -141,7 +141,7 @@ in
       default = false;
       description = lib.mdDoc ''
         Finish all remaining jobs before stopping.
-        If not set gitlab-runner will stop immediatly without waiting
+        If not set gitlab-runner will stop immediately without waiting
         for jobs to finish, which will lead to failed builds.
       '';
     };
diff --git a/nixos/modules/services/continuous-integration/hail.nix b/nixos/modules/services/continuous-integration/hail.nix
index 76d7356e2472..62e8b8077c07 100644
--- a/nixos/modules/services/continuous-integration/hail.nix
+++ b/nixos/modules/services/continuous-integration/hail.nix
@@ -15,8 +15,8 @@ in {
       default = false;
       description = lib.mdDoc ''
         Enables the Hail Auto Update Service. Hail can automatically deploy artifacts
-        built by a Hydra Continous Integration server. A common use case is to provide
-        continous deployment for single services or a full NixOS configuration.'';
+        built by a Hydra Continuous Integration server. A common use case is to provide
+        continuous deployment for single services or a full NixOS configuration.'';
     };
     profile = mkOption {
       type = types.str;
diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix
index b2c4a5dd8f62..4c2855345368 100644
--- a/nixos/modules/services/databases/firebird.nix
+++ b/nixos/modules/services/databases/firebird.nix
@@ -14,7 +14,7 @@
 #
 # Be careful, virtuoso-opensource also provides a different isql command !
 
-# There are at least two ways to run firebird. superserver has been choosen
+# There are at least two ways to run firebird. superserver has been chosen
 # however there are no strong reasons to prefer this or the other one AFAIK
 # Eg superserver is said to be most efficiently using resources according to
 # http://www.firebirdsql.org/manual/qsg25-classic-or-super.html
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index fe7ef48075a7..6665e7a088fc 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -146,6 +146,7 @@ in
                 Name of the user to ensure.
               '';
             };
+
             ensurePermissions = mkOption {
               type = types.attrsOf types.str;
               default = {};
@@ -167,6 +168,154 @@ in
                 }
               '';
             };
+
+            ensureClauses = mkOption {
+              description = lib.mdDoc ''
+                An attrset of clauses to grant to the user. Under the hood this uses the
+                [ALTER USER syntax](https://www.postgresql.org/docs/current/sql-alteruser.html) for each attrName where
+                the attrValue is true in the attrSet:
+                `ALTER USER user.name WITH attrName`
+              '';
+              example = literalExpression ''
+                {
+                  superuser = true;
+                  createrole = true;
+                  createdb = true;
+                }
+              '';
+              default = {};
+              defaultText = lib.literalMD ''
+                The default, `null`, means that the user created will have the default permissions assigned by PostgreSQL. Subsequent server starts will not set or unset the clause, so imperative changes are preserved.
+              '';
+              type = types.submodule {
+                options = let
+                  defaultText = lib.literalMD ''
+                    `null`: do not set. For newly created roles, use PostgreSQL's default. For existing roles, do not touch this clause.
+                  '';
+                in {
+                  superuser = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user, created by the ensureUser attr, superuser permissions. From the postgres docs:
+
+                      A database superuser bypasses all permission checks,
+                      except the right to log in. This is a dangerous privilege
+                      and should not be used carelessly; it is best to do most
+                      of your work as a role that is not a superuser. To create
+                      a new database superuser, use CREATE ROLE name SUPERUSER.
+                      You must do this as a role that is already a superuser.
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                  createrole = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user, created by the ensureUser attr, createrole permissions. From the postgres docs:
+
+                      A role must be explicitly given permission to create more
+                      roles (except for superusers, since those bypass all
+                      permission checks). To create such a role, use CREATE
+                      ROLE name CREATEROLE. A role with CREATEROLE privilege
+                      can alter and drop other roles, too, as well as grant or
+                      revoke membership in them. However, to create, alter,
+                      drop, or change membership of a superuser role, superuser
+                      status is required; CREATEROLE is insufficient for that.
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                  createdb = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user, created by the ensureUser attr, createdb permissions. From the postgres docs:
+
+                      A role must be explicitly given permission to create
+                      databases (except for superusers, since those bypass all
+                      permission checks). To create such a role, use CREATE
+                      ROLE name CREATEDB.
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                  "inherit" = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user created inherit permissions. From the postgres docs:
+
+                      A role is given permission to inherit the privileges of
+                      roles it is a member of, by default. However, to create a
+                      role without the permission, use CREATE ROLE name
+                      NOINHERIT.
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                  login = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user, created by the ensureUser attr, login permissions. From the postgres docs:
+
+                      Only roles that have the LOGIN attribute can be used as
+                      the initial role name for a database connection. A role
+                      with the LOGIN attribute can be considered the same as a
+                      “database user”. To create a role with login privilege,
+                      use either:
+
+                      CREATE ROLE name LOGIN; CREATE USER name;
+
+                      (CREATE USER is equivalent to CREATE ROLE except that
+                      CREATE USER includes LOGIN by default, while CREATE ROLE
+                      does not.)
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                  replication = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user, created by the ensureUser attr, replication permissions. From the postgres docs:
+
+                      A role must explicitly be given permission to initiate
+                      streaming replication (except for superusers, since those
+                      bypass all permission checks). A role used for streaming
+                      replication must have LOGIN permission as well. To create
+                      such a role, use CREATE ROLE name REPLICATION LOGIN.
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                  bypassrls = mkOption {
+                    type = types.nullOr types.bool;
+                    description = lib.mdDoc ''
+                      Grants the user, created by the ensureUser attr, replication permissions. From the postgres docs:
+
+                      A role must be explicitly given permission to bypass
+                      every row-level security (RLS) policy (except for
+                      superusers, since those bypass all permission checks). To
+                      create such a role, use CREATE ROLE name BYPASSRLS as a
+                      superuser.
+
+                      More information on postgres roles can be found [here](https://www.postgresql.org/docs/current/role-attributes.html)
+                    '';
+                    default = null;
+                    inherit defaultText;
+                  };
+                };
+              };
+            };
           };
         });
         default = [];
@@ -380,12 +529,29 @@ in
               $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"'
             '') cfg.ensureDatabases}
           '' + ''
-            ${concatMapStrings (user: ''
-              $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
-              ${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
-                $PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"'
-              '') user.ensurePermissions)}
-            '') cfg.ensureUsers}
+            ${
+              concatMapStrings
+              (user:
+                let
+                  userPermissions = concatStringsSep "\n"
+                    (mapAttrsToList
+                      (database: permission: ''$PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '')
+                      user.ensurePermissions
+                    );
+
+                  filteredClauses = filterAttrs (name: value: value != null) user.ensureClauses;
+
+                  clauseSqlStatements = attrValues (mapAttrs (n: v: if v then n else "no${n}") filteredClauses);
+
+                  userClauses = ''$PSQL -tAc 'ALTER ROLE "${user.name}" ${concatStringsSep " " clauseSqlStatements}' '';
+                in ''
+                  $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
+                  ${userPermissions}
+                  ${userClauses}
+                ''
+              )
+              cfg.ensureUsers
+            }
           '';
 
         serviceConfig = mkMerge [
diff --git a/nixos/modules/services/databases/surrealdb.nix b/nixos/modules/services/databases/surrealdb.nix
index 02cd11655e1e..050a5336cb4c 100644
--- a/nixos/modules/services/databases/surrealdb.nix
+++ b/nixos/modules/services/databases/surrealdb.nix
@@ -10,6 +10,15 @@ in {
     services.surrealdb = {
       enable = mkEnableOption (lib.mdDoc "A scalable, distributed, collaborative, document-graph database, for the realtime web ");
 
+      package = mkOption {
+        default = pkgs.surrealdb;
+        defaultText = literalExpression "pkgs.surrealdb";
+        type = types.package;
+        description = lib.mdDoc ''
+          Which surrealdb derivation to use.
+        '';
+      };
+
       dbPath = mkOption {
         type = types.str;
         description = lib.mdDoc ''
@@ -57,7 +66,7 @@ in {
   config = mkIf cfg.enable {
 
     # Used to connect to the running service
-    environment.systemPackages = [ pkgs.surrealdb ] ;
+    environment.systemPackages = [ cfg.package ] ;
 
     systemd.services.surrealdb = {
       description = "A scalable, distributed, collaborative, document-graph database, for the realtime web ";
@@ -65,7 +74,7 @@ in {
       after = [ "network.target" ];
 
       script = ''
-        ${pkgs.surrealdb}/bin/surreal start \
+        ${cfg.package}/bin/surreal start \
           --user $(${pkgs.systemd}/bin/systemd-creds cat SURREALDB_USERNAME) \
           --pass $(${pkgs.systemd}/bin/systemd-creds cat SURREALDB_PASSWORD) \
           --bind ${cfg.host}:${toString cfg.port} \
diff --git a/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixos/modules/services/desktops/pipewire/wireplumber.nix
index 32490773b5e9..4b36b99aa7c1 100644
--- a/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -32,6 +32,10 @@ in
         assertion = !config.services.pipewire.media-session.enable;
         message = "WirePlumber and pipewire-media-session can't be enabled at the same time.";
       }
+      {
+        assertion = !config.hardware.bluetooth.hsphfpd.enable;
+        message = "Using Wireplumber conflicts with hsphfpd, as it provides the same functionality. `hardware.bluetooth.hsphfpd.enable` needs be set to false";
+      }
     ];
 
     environment.systemPackages = [ cfg.package ];
diff --git a/nixos/modules/services/display-managers/greetd.nix b/nixos/modules/services/display-managers/greetd.nix
index fa3f8fdf4f1d..96eddc1ce7cd 100644
--- a/nixos/modules/services/display-managers/greetd.nix
+++ b/nixos/modules/services/display-managers/greetd.nix
@@ -45,7 +45,7 @@ in
       default = !(cfg.settings ? initial_session);
       defaultText = literalExpression "!(config.services.greetd.settings ? initial_session)";
       description = lib.mdDoc ''
-        Wether to restart greetd when it terminates (e.g. on failure).
+        Whether to restart greetd when it terminates (e.g. on failure).
         This is usually desirable so a user can always log in, but should be disabled when using 'settings.initial_session' (autologin),
         because every greetd restart will trigger the autologin again.
       '';
diff --git a/nixos/modules/services/games/asf.nix b/nixos/modules/services/games/asf.nix
index d3f7883421ce..7585d56b2d78 100644
--- a/nixos/modules/services/games/asf.nix
+++ b/nixos/modules/services/games/asf.nix
@@ -35,7 +35,7 @@ in
       description = lib.mdDoc ''
         If enabled, starts the ArchisSteamFarm service.
         For configuring the SteamGuard token you will need to use the web-ui, which is enabled by default over on 127.0.0.1:1242.
-        You cannot configure ASF in any way outside of nix, since all the config files get wiped on restart and replaced with the programatically set ones by nix.
+        You cannot configure ASF in any way outside of nix, since all the config files get wiped on restart and replaced with the programnatically set ones by nix.
       '';
       default = false;
     };
@@ -98,7 +98,7 @@ in
     ipcPasswordFile = mkOption {
       type = types.nullOr types.path;
       default = null;
-      description = lib.mdDoc "Path to a file containig the password. The file must be readable by the `asf` user/group.";
+      description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `asf` user/group.";
     };
 
     ipcSettings = mkOption {
@@ -129,7 +129,7 @@ in
           };
           passwordFile = mkOption {
             type = types.path;
-            description = lib.mdDoc "Path to a file containig the password. The file must be readable by the `asf` user/group.";
+            description = lib.mdDoc "Path to a file containing the password. The file must be readable by the `asf` user/group.";
           };
           enabled = mkOption {
             type = types.bool;
diff --git a/nixos/modules/services/games/minetest-server.nix b/nixos/modules/services/games/minetest-server.nix
index 34e0ba8c8e5c..e8c96881673b 100644
--- a/nixos/modules/services/games/minetest-server.nix
+++ b/nixos/modules/services/games/minetest-server.nix
@@ -62,7 +62,7 @@ in
           Path to logfile for logging.
 
           If set to null, logging will be output to stdout which means
-          all output will be catched by systemd.
+          all output will be caught by systemd.
         '';
       };
 
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
index 98f837bd7824..8d7651f97c39 100644
--- a/nixos/modules/services/hardware/fwupd.nix
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -7,13 +7,16 @@ with lib;
 let
   cfg = config.services.fwupd;
 
+  format = pkgs.formats.ini {
+    listToValue = l: lib.concatStringsSep ";" (map (s: generators.mkValueStringDefault {} s) l);
+    mkKeyValue = generators.mkKeyValueDefault {} "=";
+  };
+
   customEtc = {
     "fwupd/daemon.conf" = {
-      source = pkgs.writeText "daemon.conf" ''
-        [fwupd]
-        DisabledDevices=${lib.concatStringsSep ";" cfg.disabledDevices}
-        DisabledPlugins=${lib.concatStringsSep ";" cfg.disabledPlugins}
-      '';
+      source = format.generate "daemon.conf" {
+        fwupd = cfg.daemonSettings;
+      };
     };
     "fwupd/uefi_capsule.conf" = {
       source = pkgs.writeText "uefi_capsule.conf" ''
@@ -67,24 +70,6 @@ in {
         '';
       };
 
-      disabledDevices = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
-        description = lib.mdDoc ''
-          Allow disabling specific devices by their GUID
-        '';
-      };
-
-      disabledPlugins = mkOption {
-        type = types.listOf types.str;
-        default = [];
-        example = [ "udev" ];
-        description = lib.mdDoc ''
-          Allow disabling specific plugins
-        '';
-      };
-
       extraTrustedKeys = mkOption {
         type = types.listOf types.path;
         default = [];
@@ -120,18 +105,49 @@ in {
           Which fwupd package to use.
         '';
       };
+
+      daemonSettings = mkOption {
+        type = types.submodule {
+          freeformType = format.type.nestedTypes.elemType;
+          options = {
+            DisabledDevices = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "2082b5e0-7a64-478a-b1b2-e3404fab6dad" ];
+              description = lib.mdDoc ''
+                List of device GUIDs to be disabled.
+              '';
+            };
+
+            DisabledPlugins = mkOption {
+              type = types.listOf types.str;
+              default = [];
+              example = [ "udev" ];
+              description = lib.mdDoc ''
+                List of plugins to be disabled.
+              '';
+            };
+          };
+        };
+        default = {};
+        description = lib.mdDoc ''
+          Configurations for the fwupd daemon.
+        '';
+      };
     };
   };
 
   imports = [
-    (mkRenamedOptionModule [ "services" "fwupd" "blacklistDevices"] [ "services" "fwupd" "disabledDevices" ])
-    (mkRenamedOptionModule [ "services" "fwupd" "blacklistPlugins"] [ "services" "fwupd" "disabledPlugins" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "blacklistDevices"] [ "services" "fwupd" "daemonSettings" "DisabledDevices" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "blacklistPlugins"] [ "services" "fwupd" "daemonSettings" "DisabledPlugins" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "disabledDevices" ] [ "services" "fwupd" "daemonSettings" "DisabledDevices" ])
+    (mkRenamedOptionModule [ "services" "fwupd" "disabledPlugins" ] [ "services" "fwupd" "daemonSettings" "DisabledPlugins" ])
   ];
 
   ###### implementation
   config = mkIf cfg.enable {
     # Disable test related plug-ins implicitly so that users do not have to care about them.
-    services.fwupd.disabledPlugins = cfg.package.defaultDisabledPlugins;
+    services.fwupd.daemonSettings.DisabledPlugins = cfg.package.defaultDisabledPlugins;
 
     environment.systemPackages = [ cfg.package ];
 
diff --git a/nixos/modules/services/hardware/lirc.nix b/nixos/modules/services/hardware/lirc.nix
index acc43cd4186b..5b1a8d10c729 100644
--- a/nixos/modules/services/hardware/lirc.nix
+++ b/nixos/modules/services/hardware/lirc.nix
@@ -19,7 +19,7 @@ in {
           [lircd]
           nodaemon = False
         '';
-        description = lib.mdDoc "LIRC default options descriped in man:lircd(8) ({file}`lirc_options.conf`)";
+        description = lib.mdDoc "LIRC default options described in man:lircd(8) ({file}`lirc_options.conf`)";
       };
 
       configs = mkOption {
diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix
index fe6dd268df60..60354c7644f7 100644
--- a/nixos/modules/services/hardware/sane.nix
+++ b/nixos/modules/services/hardware/sane.nix
@@ -141,7 +141,7 @@ in
       description = lib.mdDoc ''
         Enable saned network daemon for remote connection to scanners.
 
-        saned would be runned from `scanner` user; to allow
+        saned would be run from `scanner` user; to allow
         access to hardware that doesn't have `scanner` group
         you should add needed groups to this user.
       '';
diff --git a/nixos/modules/services/hardware/usbmuxd.nix b/nixos/modules/services/hardware/usbmuxd.nix
index b4c954906dd3..9466ea26995b 100644
--- a/nixos/modules/services/hardware/usbmuxd.nix
+++ b/nixos/modules/services/hardware/usbmuxd.nix
@@ -13,6 +13,7 @@ in
 
 {
   options.services.usbmuxd = {
+
     enable = mkOption {
       type = types.bool;
       default = false;
@@ -39,6 +40,15 @@ in
         The group usbmuxd should use to run after startup.
       '';
     };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.usbmuxd;
+      defaultText = literalExpression "pkgs.usbmuxd";
+      description = lib.mdDoc "Which package to use for the usbmuxd daemon.";
+      relatedPackages = [ "usbmuxd" "usbmuxd2" ];
+    };
+
   };
 
   config = mkIf cfg.enable {
@@ -68,7 +78,7 @@ in
         # Trigger the udev rule manually. This doesn't require replugging the
         # device when first enabling the option to get it to work
         ExecStartPre = "${pkgs.udev}/bin/udevadm trigger -s usb -a idVendor=${apple}";
-        ExecStart = "${pkgs.usbmuxd}/bin/usbmuxd -U ${cfg.user} -f";
+        ExecStart = "${cfg.package}/bin/usbmuxd -U ${cfg.user} -v";
       };
     };
 
diff --git a/nixos/modules/services/logging/logrotate.nix b/nixos/modules/services/logging/logrotate.nix
index fd41b982678f..1799e9282b3b 100644
--- a/nixos/modules/services/logging/logrotate.nix
+++ b/nixos/modules/services/logging/logrotate.nix
@@ -163,7 +163,7 @@ in
               default = null;
               description = lib.mdDoc ''
                 How often to rotate the logs. Defaults to previously set global setting,
-                which itself defauts to weekly.
+                which itself defaults to weekly.
               '';
             };
 
diff --git a/nixos/modules/services/mail/listmonk.nix b/nixos/modules/services/mail/listmonk.nix
index c4ea6747196c..8b636bd5b1ff 100644
--- a/nixos/modules/services/mail/listmonk.nix
+++ b/nixos/modules/services/mail/listmonk.nix
@@ -8,7 +8,7 @@ let
   # Escaping is done according to https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS
   setDatabaseOption = key: value:
     "UPDATE settings SET value = '${
-      lib.replaceChars [ "'" ] [ "''" ] (builtins.toJSON value)
+      lib.replaceStrings [ "'" ] [ "''" ] (builtins.toJSON value)
     }' WHERE key = '${key}';";
   updateDatabaseConfigSQL = pkgs.writeText "update-database-config.sql"
     (concatStringsSep "\n" (mapAttrsToList setDatabaseOption
diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 5edcd313a91a..0ca87696b143 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -113,7 +113,7 @@ in {
           type = types.str;
           example = "/run/secrets/ldap-bind";
           description = lib.mdDoc ''
-            Path to the file containing the bind password of the servie account
+            Path to the file containing the bind password of the service account
             defined by [](#opt-services.mailman.ldap.bindDn).
           '';
         };
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 5461e89a801d..d01734d61e87 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -234,12 +234,12 @@ let
 
   headerChecks = concatStringsSep "\n" (map (x: "${x.pattern} ${x.action}") cfg.headerChecks) + cfg.extraHeaderChecks;
 
-  aliases = let seperator = if cfg.aliasMapType == "hash" then ":" else ""; in
+  aliases = let separator = if cfg.aliasMapType == "hash" then ":" else ""; in
     optionalString (cfg.postmasterAlias != "") ''
-      postmaster${seperator} ${cfg.postmasterAlias}
+      postmaster${separator} ${cfg.postmasterAlias}
     ''
     + optionalString (cfg.rootAlias != "") ''
-      root${seperator} ${cfg.rootAlias}
+      root${separator} ${cfg.rootAlias}
     ''
     + cfg.extraAliases
   ;
diff --git a/nixos/modules/services/mail/roundcube.nix b/nixos/modules/services/mail/roundcube.nix
index d8adf53e48a9..e05820fb87cf 100644
--- a/nixos/modules/services/mail/roundcube.nix
+++ b/nixos/modules/services/mail/roundcube.nix
@@ -39,7 +39,7 @@ in
       '';
 
       description = lib.mdDoc ''
-        The package which contains roundcube's sources. Can be overriden to create
+        The package which contains roundcube's sources. Can be overridden to create
         an environment which contains roundcube and third-party plugins.
       '';
     };
@@ -92,7 +92,7 @@ in
       default = [];
       example = literalExpression "with pkgs.aspellDicts; [ en fr de ]";
       description = lib.mdDoc ''
-        List of aspell dictionnaries for spell checking. If empty, spell checking is disabled.
+        List of aspell dictionaries for spell checking. If empty, spell checking is disabled.
       '';
     };
 
diff --git a/nixos/modules/services/matrix/mautrix-telegram.nix b/nixos/modules/services/matrix/mautrix-telegram.nix
index 2d9c2dc76c29..5a632fd27e80 100644
--- a/nixos/modules/services/matrix/mautrix-telegram.nix
+++ b/nixos/modules/services/matrix/mautrix-telegram.nix
@@ -105,7 +105,7 @@ in {
           `MAUTRIX_TELEGRAM_TELEGRAM_BOT_TOKEN`.
 
           These environment variables can also be used to set other options by
-          replacing hierachy levels by `.`, converting the name to uppercase
+          replacing hierarchy levels by `.`, converting the name to uppercase
           and prepending `MAUTRIX_TELEGRAM_`.
           For example, the first value above maps to
           {option}`settings.appservice.as_token`.
@@ -140,7 +140,7 @@ in {
       path = [ pkgs.lottieconverter ];
 
       # mautrix-telegram tries to generate a dotfile in the home directory of
-      # the running user if using a postgresql databse:
+      # the running user if using a postgresql database:
       #
       #  File "python3.10/site-packages/asyncpg/connect_utils.py", line 257, in _dot_postgre>
       #    return (pathlib.Path.home() / '.postgresql' / filename).resolve()
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index a80d4cabfec0..b9b581acb34a 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -80,7 +80,7 @@ in {
     (mkRemovedOptionModule [ "services" "matrix-synapse" "user_creation_max_duration" ] "It is no longer supported by synapse." )
     (mkRemovedOptionModule [ "services" "matrix-synapse" "verbose" ] "Use a log config instead." )
 
-    # options that were moved into rfc42 style settigns
+    # options that were moved into rfc42 style settings
     (mkRemovedOptionModule [ "services" "matrix-synapse" "app_service_config_files" ] "Use settings.app_service_config_files instead" )
     (mkRemovedOptionModule [ "services" "matrix-synapse" "database_args" ] "Use settings.database.args instead" )
     (mkRemovedOptionModule [ "services" "matrix-synapse" "database_name" ] "Use settings.database.args.database instead" )
diff --git a/nixos/modules/services/misc/atuin.nix b/nixos/modules/services/misc/atuin.nix
new file mode 100644
index 000000000000..c94852e3aad9
--- /dev/null
+++ b/nixos/modules/services/misc/atuin.nix
@@ -0,0 +1,85 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.services.atuin;
+in
+{
+  options = {
+    services.atuin = {
+      enable = mkEnableOption (mdDoc "Enable server for shell history sync with atuin.");
+
+      openRegistration = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc "Allow new user registrations with the atuin server.";
+      };
+
+      path = mkOption {
+        type = types.str;
+        default = "";
+        description = mdDoc "A path to prepend to all the routes of the server.";
+      };
+
+      host = mkOption {
+        type = types.str;
+        default = "127.0.0.1";
+        description = mdDoc "The host address the atuin server should listen on.";
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8888;
+        description = mdDoc "The port the atuin server should listen on.";
+      };
+
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = mdDoc "Open ports in the firewall for the atuin server.";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    # enable postgres to host atuin db
+    services.postgresql = {
+      enable = true;
+      ensureUsers = [{
+        name = "atuin";
+        ensurePermissions = {
+          "DATABASE atuin" = "ALL PRIVILEGES";
+        };
+      }];
+      ensureDatabases = [ "atuin" ];
+    };
+
+    systemd.services.atuin = {
+      description = "atuin server";
+      after = [ "network.target" "postgresql.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = "${pkgs.atuin}/bin/atuin server start";
+        RuntimeDirectory = "atuin";
+        RuntimeDirectoryMode = "0700";
+        DynamicUser = true;
+      };
+
+      environment = {
+        ATUIN_HOST = cfg.host;
+        ATUIN_PORT = toString cfg.port;
+        ATUIN_OPEN_REGISTRATION = boolToString cfg.openRegistration;
+        ATUIN_DB_URI = "postgresql:///atuin";
+        ATUIN_PATH = cfg.path;
+        ATUIN_CONFIG_DIR = "/run/atuin"; # required to start, but not used as configuration is via environment variables
+      };
+    };
+
+    networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
+
+  };
+}
diff --git a/nixos/modules/services/misc/dysnomia.nix b/nixos/modules/services/misc/dysnomia.nix
index 4d748ec6eb66..0f92265ccbea 100644
--- a/nixos/modules/services/misc/dysnomia.nix
+++ b/nixos/modules/services/misc/dysnomia.nix
@@ -114,7 +114,7 @@ in
       };
 
       components = mkOption {
-        description = lib.mdDoc "An atttribute set in which each key represents a container and each value an attribute set in which each key represents a component and each value a derivation constructing its initial state";
+        description = lib.mdDoc "An attribute set in which each key represents a container and each value an attribute set in which each key represents a component and each value a derivation constructing its initial state";
         default = {};
         type = types.attrsOf types.attrs;
       };
diff --git a/nixos/modules/services/misc/gammu-smsd.nix b/nixos/modules/services/misc/gammu-smsd.nix
index 2c7d90b28ded..83f4efe695a2 100644
--- a/nixos/modules/services/misc/gammu-smsd.nix
+++ b/nixos/modules/services/misc/gammu-smsd.nix
@@ -192,7 +192,7 @@ in {
           password = mkOption {
             type = types.nullOr types.str;
             default = null;
-            description = lib.mdDoc "User password used for connetion to the database";
+            description = lib.mdDoc "User password used for connection to the database";
           };
         };
       };
diff --git a/nixos/modules/services/misc/gitea.nix b/nixos/modules/services/misc/gitea.nix
index d29416eda219..ceb4c1172854 100644
--- a/nixos/modules/services/misc/gitea.nix
+++ b/nixos/modules/services/misc/gitea.nix
@@ -183,7 +183,7 @@ in
         file = mkOption {
           type = types.nullOr types.str;
           default = null;
-          description = lib.mdDoc "Filename to be used for the dump. If `null` a default name is choosen by gitea.";
+          description = lib.mdDoc "Filename to be used for the dump. If `null` a default name is chosen by gitea.";
           example = "gitea-dump";
         };
       };
@@ -487,7 +487,7 @@ in
 
       # In older versions the secret naming for JWT was kind of confusing.
       # The file jwt_secret hold the value for LFS_JWT_SECRET and JWT_SECRET
-      # wasn't persistant at all.
+      # wasn't persistent at all.
       # To fix that, there is now the file oauth2_jwt_secret containing the
       # values for JWT_SECRET and the file jwt_secret gets renamed to
       # lfs_jwt_secret.
diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml
index 40424c5039a2..9816fdac7dd7 100644
--- a/nixos/modules/services/misc/gitlab.xml
+++ b/nixos/modules/services/misc/gitlab.xml
@@ -141,7 +141,7 @@ services.gitlab = {
    </para>
 
    <para>
-    A list of all availabe rake tasks can be obtained by running:
+    A list of all available rake tasks can be obtained by running:
 <screen>
 <prompt>$ </prompt>sudo -u git -H gitlab-rake -T
 </screen>
diff --git a/nixos/modules/services/misc/gpsd.nix b/nixos/modules/services/misc/gpsd.nix
index 1ab8d1bbe062..ec0a8e1eaa1c 100644
--- a/nixos/modules/services/misc/gpsd.nix
+++ b/nixos/modules/services/misc/gpsd.nix
@@ -77,6 +77,14 @@ in
         '';
       };
 
+      listenany = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc ''
+          Listen on all addresses rather than just loopback.
+        '';
+      };
+
     };
 
   };
@@ -106,6 +114,7 @@ in
             -S "${toString cfg.port}"                             \
             ${optionalString cfg.readonly "-b"}                   \
             ${optionalString cfg.nowait "-n"}                     \
+            ${optionalString cfg.listenany "-G"}                  \
             "${cfg.device}"
         '';
       };
diff --git a/nixos/modules/services/misc/jellyfin.nix b/nixos/modules/services/misc/jellyfin.nix
index f49657a07533..2a4483199d7d 100644
--- a/nixos/modules/services/misc/jellyfin.nix
+++ b/nixos/modules/services/misc/jellyfin.nix
@@ -81,7 +81,7 @@ in
         ProtectKernelTunables = !config.boot.isContainer;
         LockPersonality = true;
         PrivateTmp = !config.boot.isContainer;
-        # needed for hardware accelaration
+        # needed for hardware acceleration
         PrivateDevices = false;
         PrivateUsers = true;
         RemoveIPC = true;
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index c88fceb9a92c..f9f0736efcb9 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -609,7 +609,7 @@ in
 
                 By default, pseudo-features `nixos-test`, `benchmark`,
                 and `big-parallel` used in Nixpkgs are set, `kvm`
-                is also included in it is avaliable.
+                is also included in it is available.
               '';
             };
 
@@ -642,7 +642,7 @@ in
         description = lib.mdDoc ''
           Configuration for Nix, see
           <https://nixos.org/manual/nix/stable/#sec-conf-file> or
-          {manpage}`nix.conf(5)` for avalaible options.
+          {manpage}`nix.conf(5)` for available options.
           The value declared here will be translated directly to the key-value pairs Nix expects.
 
           You can use {command}`nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.nix.settings`
diff --git a/nixos/modules/services/misc/podgrab.nix b/nixos/modules/services/misc/podgrab.nix
index c0a124718505..c596122fd31c 100644
--- a/nixos/modules/services/misc/podgrab.nix
+++ b/nixos/modules/services/misc/podgrab.nix
@@ -12,7 +12,7 @@ in
       example = "/run/secrets/password.env";
       description = lib.mdDoc ''
         The path to a file containing the PASSWORD environment variable
-        definition for Podgrab's authentification.
+        definition for Podgrab's authentication.
       '';
     };
 
diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix
index 1dae605e46f2..f60cbe347713 100644
--- a/nixos/modules/services/misc/portunus.nix
+++ b/nixos/modules/services/misc/portunus.nix
@@ -135,7 +135,7 @@ in
         type = types.bool;
         default = false;
         description = lib.mdDoc ''
-          Wether to enable LDAPS protocol.
+          Whether to enable LDAPS protocol.
           This also adds two entries to the `/etc/hosts` file to point [](#opt-services.portunus.domain) to localhost,
           so that CLIs and programs can use ldaps protocol and verify the certificate without opening the firewall port for the protocol.
 
diff --git a/nixos/modules/services/misc/serviio.nix b/nixos/modules/services/misc/serviio.nix
index 57efebb2c03f..18e64030d79d 100644
--- a/nixos/modules/services/misc/serviio.nix
+++ b/nixos/modules/services/misc/serviio.nix
@@ -80,7 +80,7 @@ in {
         23424 # mediabrowser
       ];
       allowedUDPPorts = [
-        1900 # UPnP service discovey
+        1900 # UPnP service discovery
       ];
     };
   };
diff --git a/nixos/modules/services/misc/sourcehut/service.nix b/nixos/modules/services/misc/sourcehut/service.nix
index 37a439ee352b..aae13e0cc2c9 100644
--- a/nixos/modules/services/misc/sourcehut/service.nix
+++ b/nixos/modules/services/misc/sourcehut/service.nix
@@ -71,7 +71,7 @@ let
       # Note that each systemd service gets its own ${runDir}/config.ini file.
       ExecStartPre = mkBefore [("+"+pkgs.writeShellScript "${serviceName}-credentials" ''
         set -x
-        # Replace values begining with a '<' by the content of the file whose name is after.
+        # Replace values beginning with a '<' by the content of the file whose name is after.
         gawk '{ if (match($0,/^([^=]+=)<(.+)/,m)) { getline f < m[2]; print m[1] f } else print $0 }' ${configIni} |
         ${optionalString (!allowStripe) "gawk '!/^stripe-secret-key=/' |"}
         install -o ${srvCfg.user} -g root -m 400 /dev/stdin ${runDir}/config.ini
diff --git a/nixos/modules/services/misc/taskserver/default.nix b/nixos/modules/services/misc/taskserver/default.nix
index aeefd657f4d0..ee4bf42183f9 100644
--- a/nixos/modules/services/misc/taskserver/default.nix
+++ b/nixos/modules/services/misc/taskserver/default.nix
@@ -145,7 +145,7 @@ in {
         in lib.mdDoc ''
           Whether to enable the Taskwarrior server.
 
-          More instructions about NixOS in conjuction with Taskserver can be
+          More instructions about NixOS in conjunction with Taskserver can be
           found [in the NixOS manual](${url}).
         '';
       };
@@ -251,7 +251,7 @@ in {
           client id (such as `task 2.3.0`).
 
           The values `all` or `none` have
-          special meaning. Overidden by any entry in the option
+          special meaning. Overridden by any entry in the option
           {option}`services.taskserver.disallowedClientIDs`.
         '';
       };
diff --git a/nixos/modules/services/monitoring/grafana.nix b/nixos/modules/services/monitoring/grafana.nix
index 9b3068796d8e..9a9a0ab75532 100644
--- a/nixos/modules/services/monitoring/grafana.nix
+++ b/nixos/modules/services/monitoring/grafana.nix
@@ -555,7 +555,7 @@ in {
             auto_assign_org_role = mkOption {
               description = lib.mdDoc "Default role new users will be auto assigned.";
               default = "Viewer";
-              type = types.enum ["Viewer" "Editor"];
+              type = types.enum ["Viewer" "Editor" "Admin"];
             };
           };
 
@@ -1291,7 +1291,10 @@ in {
         SystemCallArchitectures = "native";
         # Upstream grafana is not setting SystemCallFilter for compatibility
         # reasons, see https://github.com/grafana/grafana/pull/40176
-        SystemCallFilter = [ "@system-service" "~@privileged" ];
+        SystemCallFilter = [
+          "@system-service"
+          "~@privileged"
+        ] ++ lib.optional (cfg.settings.server.protocol == "socket") [ "@chown" ];
         UMask = "0027";
       };
       preStart = ''
diff --git a/nixos/modules/services/monitoring/graphite.nix b/nixos/modules/services/monitoring/graphite.nix
index 8d92e60d3bad..65c91b8f79bb 100644
--- a/nixos/modules/services/monitoring/graphite.nix
+++ b/nixos/modules/services/monitoring/graphite.nix
@@ -154,14 +154,14 @@ in {
       };
 
       blacklist = mkOption {
-        description = lib.mdDoc "Any metrics received which match one of the experssions will be dropped.";
+        description = lib.mdDoc "Any metrics received which match one of the expressions will be dropped.";
         default = null;
         type = types.nullOr types.str;
         example = "^some\\.noisy\\.metric\\.prefix\\..*";
       };
 
       whitelist = mkOption {
-        description = lib.mdDoc "Only metrics received which match one of the experssions will be persisted.";
+        description = lib.mdDoc "Only metrics received which match one of the expressions will be persisted.";
         default = null;
         type = types.nullOr types.str;
         example = ".*";
diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
index 8706a18a184d..0c0931d3d295 100644
--- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix
+++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix
@@ -34,7 +34,7 @@ in {
     (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "group" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a group setting.")
     (mkRemovedOptionModule [ "services" "prometheus" "alertmanagerURL" ] ''
       Due to incompatibility, the alertmanagerURL option has been removed,
-      please use 'services.prometheus2.alertmanagers' instead.
+      please use 'services.prometheus.alertmanagers' instead.
     '')
   ];
 
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index f6bae8f9e965..f516b75ab10f 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -3,7 +3,7 @@
 with lib;
 
 let
-  json = pkgs.formats.json { };
+  yaml = pkgs.formats.yaml { };
   cfg = config.services.prometheus;
   checkConfigEnabled =
     (lib.isBool cfg.checkConfig && cfg.checkConfig)
@@ -11,8 +11,6 @@ let
 
   workingDir = "/var/lib/" + cfg.stateDir;
 
-  prometheusYmlOut = "${workingDir}/prometheus-substituted.yaml";
-
   triggerReload = pkgs.writeShellScriptBin "trigger-reload-prometheus" ''
     PATH="${makeBinPath (with pkgs; [ systemd ])}"
     if systemctl -q is-active prometheus.service; then
@@ -38,7 +36,7 @@ let
         promtool ${what} $out
       '' else file;
 
-  generatedPrometheusYml = json.generate "prometheus.yml" promConfig;
+  generatedPrometheusYml = yaml.generate "prometheus.yml" promConfig;
 
   # This becomes the main config file for Prometheus
   promConfig = {
@@ -73,7 +71,8 @@ let
     "--web.listen-address=${cfg.listenAddress}:${builtins.toString cfg.port}"
     "--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
   ] ++ optional (cfg.webExternalUrl != null) "--web.external-url=${cfg.webExternalUrl}"
-    ++ optional (cfg.retentionTime != null) "--storage.tsdb.retention.time=${cfg.retentionTime}";
+    ++ optional (cfg.retentionTime != null) "--storage.tsdb.retention.time=${cfg.retentionTime}"
+    ++ optional (cfg.webConfigFile != null) "--web.config.file=${cfg.webConfigFile}";
 
   filterValidPrometheus = filterAttrsListRecursive (n: v: !(n == "_module" || v == null));
   filterAttrsListRecursive = pred: x:
@@ -1719,6 +1718,15 @@ in
       '';
     };
 
+    webConfigFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = lib.mdDoc ''
+        Specifies which file should be used as web.config.file and be passed on startup.
+        See https://prometheus.io/docs/prometheus/latest/configuration/https/ for valid options.
+      '';
+    };
+
     checkConfig = mkOption {
       type = with types; either bool (enum [ "syntax-only" ]);
       default = true;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.nix b/nixos/modules/services/monitoring/prometheus/exporters.nix
index 2451f46ba7d7..f3fbfb149ad7 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters.nix
@@ -68,6 +68,7 @@ let
     "smartctl"
     "smokeping"
     "sql"
+    "statsd"
     "surfboard"
     "systemd"
     "tor"
diff --git a/nixos/modules/services/monitoring/prometheus/exporters.xml b/nixos/modules/services/monitoring/prometheus/exporters.xml
index 1df88bb61a12..e922e1ace8d0 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters.xml
+++ b/nixos/modules/services/monitoring/prometheus/exporters.xml
@@ -37,7 +37,7 @@
    by default</link>, via http under <literal>/metrics</literal>. In this
    example the firewall should just allow incoming connections to the
    exporter's port on the bridge interface <literal>br0</literal> (this would
-   have to be configured seperately of course). For more information about
+   have to be configured separately of course). For more information about
    configuration see <literal>man configuration.nix</literal> or search through
    the
    <link xlink:href="https://nixos.org/nixos/options.html#prometheus.exporters">available
@@ -179,7 +179,7 @@ in
   # for the exporter's systemd service. One of
   # `serviceOpts.script` and `serviceOpts.serviceConfig.ExecStart`
   # has to be specified here. This will be merged with the default
-  # service confiuration.
+  # service configuration.
   # Note that by default 'DynamicUser' is 'true'.
   serviceOpts = {
     serviceConfig = {
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix
index 9e507423c7d6..674dc9dd4158 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginxlog.nix
@@ -17,7 +17,7 @@ in {
         settings that can all be used here: https://github.com/martin-helmich/prometheus-nginxlog-exporter
 
         The `listen` object is already generated by `port`, `listenAddress` and `metricsEndpoint` and
-        will be merged with the value of `settings` before writting it as JSON.
+        will be merged with the value of `settings` before writing it as JSON.
       '';
     };
 
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix b/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix
new file mode 100644
index 000000000000..d9d732d8c125
--- /dev/null
+++ b/nixos/modules/services/monitoring/prometheus/exporters/statsd.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, options }:
+
+with lib;
+
+let
+  cfg = config.services.prometheus.exporters.statsd;
+in
+{
+  port = 9102;
+  serviceOpts = {
+    serviceConfig = {
+      ExecStart = ''
+        ${pkgs.prometheus-statsd-exporter}/bin/statsd_exporter \
+          --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
+          ${concatStringsSep " \\\n  " cfg.extraFlags}
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/monitoring/thanos.nix b/nixos/modules/services/monitoring/thanos.nix
index 41462da4ff4c..e6d8afc66624 100644
--- a/nixos/modules/services/monitoring/thanos.nix
+++ b/nixos/modules/services/monitoring/thanos.nix
@@ -300,7 +300,7 @@ let
       max-time = mkParamDef types.str "9999-12-31T23:59:59Z" ''
         End of time range limit to serve.
 
-        Thanos Store serves only blocks, which happened eariler than this
+        Thanos Store serves only blocks, which happened earlier than this
         value. Option can be a constant time in RFC3339 format or time duration
         relative to current time, such as -1d or 2h45m. Valid duration units are
         ms, s, m, h, d, w, y.
diff --git a/nixos/modules/services/monitoring/ups.nix b/nixos/modules/services/monitoring/ups.nix
index c7abaeb2973e..bb11b6a1c1d0 100644
--- a/nixos/modules/services/monitoring/ups.nix
+++ b/nixos/modules/services/monitoring/ups.nix
@@ -12,7 +12,7 @@ let
   upsOptions = {name, config, ...}:
   {
     options = {
-      # This can be infered from the UPS model by looking at
+      # This can be inferred from the UPS model by looking at
       # /nix/store/nut/share/driver.list
       driver = mkOption {
         type = types.str;
@@ -228,7 +228,7 @@ in
           "}
         '';
       "nut/upssched.conf".source = cfg.schedulerRules;
-      # These file are containing private informations and thus should not
+      # These file are containing private information and thus should not
       # be stored inside the Nix store.
       /*
       "nut/upsd.conf".source = "";
diff --git a/nixos/modules/services/network-filesystems/kubo.nix b/nixos/modules/services/network-filesystems/kubo.nix
index 51e1282db418..13a062c32128 100644
--- a/nixos/modules/services/network-filesystems/kubo.nix
+++ b/nixos/modules/services/network-filesystems/kubo.nix
@@ -54,7 +54,7 @@ in
 
     services.kubo = {
 
-      enable = mkEnableOption (lib.mdDoc "Interplanetary File System (WARNING: may cause severe network degredation)");
+      enable = mkEnableOption (lib.mdDoc "Interplanetary File System (WARNING: may cause severe network degradation)");
 
       package = mkOption {
         type = types.package;
diff --git a/nixos/modules/services/network-filesystems/moosefs.nix b/nixos/modules/services/network-filesystems/moosefs.nix
index c9a5a43ebcd9..ab82a2a07dd4 100644
--- a/nixos/modules/services/network-filesystems/moosefs.nix
+++ b/nixos/modules/services/network-filesystems/moosefs.nix
@@ -52,7 +52,7 @@ let
   chunkserverCfg = settingsFormat.generate
     "mfschunkserver.cfg" cfg.chunkserver.settings;
 
-  # generic template for all deamons
+  # generic template for all daemons
   systemdService = name: extraConfig: configFile: {
     wantedBy = [ "multi-user.target" ];
     wants = [ "network-online.target" ];
@@ -94,7 +94,7 @@ in {
             Enable Moosefs master daemon.
 
             You need to run `mfsmaster-init` on a freshly installed master server to
-            initialize the `DATA_PATH` direcory.
+            initialize the `DATA_PATH` directory.
           '';
           default = false;
         };
diff --git a/nixos/modules/services/network-filesystems/orangefs/client.nix b/nixos/modules/services/network-filesystems/orangefs/client.nix
index 471e17970ae1..68f23f477af1 100644
--- a/nixos/modules/services/network-filesystems/orangefs/client.nix
+++ b/nixos/modules/services/network-filesystems/orangefs/client.nix
@@ -21,7 +21,7 @@ in {
       fileSystems = mkOption {
         description = lib.mdDoc ''
           The orangefs file systems to be mounted.
-          This option is prefered over using {option}`fileSystems` directly since
+          This option is preferred over using {option}`fileSystems` directly since
           the pvfs client service needs to be running for it to be mounted.
         '';
 
diff --git a/nixos/modules/services/network-filesystems/orangefs/server.nix b/nixos/modules/services/network-filesystems/orangefs/server.nix
index 8e6838c04678..e20e7975ebaa 100644
--- a/nixos/modules/services/network-filesystems/orangefs/server.nix
+++ b/nixos/modules/services/network-filesystems/orangefs/server.nix
@@ -209,7 +209,7 @@ in {
       after = [ "network-online.target" ];
 
       serviceConfig = {
-        # Run as "simple" in forground mode.
+        # Run as "simple" in foreground mode.
         # This is more reliable
         ExecStart = ''
           ${pkgs.orangefs}/bin/pvfs2-server -d \
diff --git a/nixos/modules/services/networking/3proxy.nix b/nixos/modules/services/networking/3proxy.nix
index e643ed941315..ef695a7f49fa 100644
--- a/nixos/modules/services/networking/3proxy.nix
+++ b/nixos/modules/services/networking/3proxy.nix
@@ -158,7 +158,7 @@ in {
                   description = lib.mdDoc ''
                     List of target IP ranges, use empty list for any.
                     May also contain host names instead of addresses.
-                    It's possible to use wildmask in the begginning and in the the end of hostname, e.g. `*badsite.com` or `*badcontent*`.
+                    It's possible to use wildmask in the beginning and in the the end of hostname, e.g. `*badsite.com` or `*badcontent*`.
                     Hostname is only checked if hostname presents in request.
                   '';
                 };
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 0875d8a85140..3933ed5a2315 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -103,16 +103,17 @@ in
 
     openFirewall = mkOption {
       type = types.bool;
-      default = false;
+      default = true;
       description = lib.mdDoc ''
         Whether to open the firewall for UDP port 5353.
+        Disabling this setting also disables discovering of network devices.
       '';
     };
 
     allowPointToPoint = mkOption {
       type = types.bool;
       default = false;
-      description= lib.mdDoc ''
+      description = lib.mdDoc ''
         Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large
         latencies with such links and opens a potential security hole by allowing mDNS access from Internet
         connections.
diff --git a/nixos/modules/services/networking/biboumi.nix b/nixos/modules/services/networking/biboumi.nix
index 896a2350e3d5..1428856764e6 100644
--- a/nixos/modules/services/networking/biboumi.nix
+++ b/nixos/modules/services/networking/biboumi.nix
@@ -45,7 +45,7 @@ in
             default = "/etc/ssl/certs/ca-certificates.crt";
             description = lib.mdDoc ''
               Specifies which file should be used as the list of trusted CA
-              when negociating a TLS session.
+              when negotiating a TLS session.
             '';
           };
           options.db_name = mkOption {
@@ -111,7 +111,7 @@ in
             description = lib.mdDoc ''
               A directory that should contain the policy files,
               used to customize Botan’s behaviour
-              when negociating the TLS connections with the IRC servers.
+              when negotiating the TLS connections with the IRC servers.
             '';
           };
           options.port = mkOption {
diff --git a/nixos/modules/services/networking/bitcoind.nix b/nixos/modules/services/networking/bitcoind.nix
index 6df809a8b764..a86d52b7202d 100644
--- a/nixos/modules/services/networking/bitcoind.nix
+++ b/nixos/modules/services/networking/bitcoind.nix
@@ -95,7 +95,7 @@ let
             }
           '';
           type = types.attrsOf (types.submodule rpcUserOpts);
-          description = lib.mdDoc "RPC user information for JSON-RPC connnections.";
+          description = lib.mdDoc "RPC user information for JSON-RPC connections.";
         };
       };
 
diff --git a/nixos/modules/services/networking/bitlbee.nix b/nixos/modules/services/networking/bitlbee.nix
index 88c04597e2bc..146bffaa6edf 100644
--- a/nixos/modules/services/networking/bitlbee.nix
+++ b/nixos/modules/services/networking/bitlbee.nix
@@ -60,7 +60,7 @@ in
         type = types.str;
         default = "127.0.0.1";
         description = lib.mdDoc ''
-          The interface the BitlBee deamon will be listening to.  If `127.0.0.1`,
+          The interface the BitlBee daemon will be listening to.  If `127.0.0.1`,
           only clients on the local host can connect to it; if `0.0.0.0`, clients
           can access it from any network interface.
         '';
diff --git a/nixos/modules/services/networking/cloudflared.nix b/nixos/modules/services/networking/cloudflared.nix
new file mode 100644
index 000000000000..c8fc9fafee6d
--- /dev/null
+++ b/nixos/modules/services/networking/cloudflared.nix
@@ -0,0 +1,332 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.cloudflared;
+
+  originRequest = {
+    connectTimeout = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "30s";
+      description = lib.mdDoc ''
+        Timeout for establishing a new TCP connection to your origin server. This excludes the time taken to establish TLS, which is controlled by [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#tlstimeout](tlsTimeout).
+      '';
+    };
+
+    tlsTimeout = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "10s";
+      description = lib.mdDoc ''
+        Timeout for completing a TLS handshake to your origin server, if you have chosen to connect Tunnel to an HTTPS server.
+      '';
+    };
+
+    tcpKeepAlive = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "30s";
+      description = lib.mdDoc ''
+        The timeout after which a TCP keepalive packet is sent on a connection between Tunnel and the origin server.
+      '';
+    };
+
+    noHappyEyeballs = mkOption {
+      type = with types; nullOr bool;
+      default = null;
+      example = false;
+      description = lib.mdDoc ''
+        Disable the “happy eyeballs” algorithm for IPv4/IPv6 fallback if your local network has misconfigured one of the protocols.
+      '';
+    };
+
+    keepAliveConnections = mkOption {
+      type = with types; nullOr int;
+      default = null;
+      example = 100;
+      description = lib.mdDoc ''
+        Maximum number of idle keepalive connections between Tunnel and your origin. This does not restrict the total number of concurrent connections.
+      '';
+    };
+
+    keepAliveTimeout = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "1m30s";
+      description = lib.mdDoc ''
+        Timeout after which an idle keepalive connection can be discarded.
+      '';
+    };
+
+    httpHostHeader = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        Sets the HTTP `Host` header on requests sent to the local service.
+      '';
+    };
+
+    originServerName = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        Hostname that `cloudflared` should expect from your origin server certificate.
+      '';
+    };
+
+    caPool = mkOption {
+      type = with types; nullOr (either str path);
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        Path to the certificate authority (CA) for the certificate of your origin. This option should be used only if your certificate is not signed by Cloudflare.
+      '';
+    };
+
+    noTLSVerify = mkOption {
+      type = with types; nullOr bool;
+      default = null;
+      example = false;
+      description = lib.mdDoc ''
+        Disables TLS verification of the certificate presented by your origin. Will allow any certificate from the origin to be accepted.
+      '';
+    };
+
+    disableChunkedEncoding = mkOption {
+      type = with types; nullOr bool;
+      default = null;
+      example = false;
+      description = lib.mdDoc ''
+        Disables chunked transfer encoding. Useful if you are running a WSGI server.
+      '';
+    };
+
+    proxyAddress = mkOption {
+      type = with types; nullOr str;
+      default = null;
+      example = "127.0.0.1";
+      description = lib.mdDoc ''
+        `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen address for that proxy.
+      '';
+    };
+
+    proxyPort = mkOption {
+      type = with types; nullOr int;
+      default = null;
+      example = 0;
+      description = lib.mdDoc ''
+        `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures the listen port for that proxy. If set to zero, an unused port will randomly be chosen.
+      '';
+    };
+
+    proxyType = mkOption {
+      type = with types; nullOr (enum [ "" "socks" ]);
+      default = null;
+      example = "";
+      description = lib.mdDoc ''
+        `cloudflared` starts a proxy server to translate HTTP traffic into TCP when proxying, for example, SSH or RDP. This configures what type of proxy will be started. Valid options are:
+
+        - `""` for the regular proxy
+        - `"socks"` for a SOCKS5 proxy. Refer to the [https://developers.cloudflare.com/cloudflare-one/tutorials/kubectl/](tutorial on connecting through Cloudflare Access using kubectl) for more information.
+      '';
+    };
+  };
+in
+{
+  options.services.cloudflared = {
+    enable = mkEnableOption (lib.mdDoc "Cloudflare Tunnel client daemon (formerly Argo Tunnel)");
+
+    user = mkOption {
+      type = types.str;
+      default = "cloudflared";
+      description = lib.mdDoc "User account under which Cloudflared runs.";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "cloudflared";
+      description = lib.mdDoc "Group under which cloudflared runs.";
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.cloudflared;
+      defaultText = "pkgs.cloudflared";
+      description = lib.mdDoc "The package to use for Cloudflared.";
+    };
+
+    tunnels = mkOption {
+      description = lib.mdDoc ''
+        Cloudflare tunnels.
+      '';
+      type = types.attrsOf (types.submodule ({ name, ... }: {
+        options = {
+          inherit originRequest;
+
+          credentialsFile = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = lib.mdDoc ''
+              Credential file.
+
+              See [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms/#credentials-file](Credentials file).
+            '';
+          };
+
+          warp-routing = {
+            enabled = mkOption {
+              type = with types; nullOr bool;
+              default = null;
+              description = lib.mdDoc ''
+                Enable warp routing.
+
+                See [https://developers.cloudflare.com/cloudflare-one/tutorials/warp-to-tunnel/](Connect from WARP to a private network on Cloudflare using Cloudflare Tunnel).
+              '';
+            };
+          };
+
+          default = mkOption {
+            type = with types; nullOr str;
+            default = null;
+            description = lib.mdDoc ''
+              Catch-all service if no ingress matches.
+
+              See `service`.
+            '';
+            example = "http_status:404";
+          };
+
+          ingress = mkOption {
+            type = with types; attrsOf (either str (submodule ({ hostname, ... }: {
+              options = {
+                inherit originRequest;
+
+                service = mkOption {
+                  type = with types; nullOr str;
+                  default = null;
+                  description = lib.mdDoc ''
+                    Service to pass the traffic.
+
+                    See [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/#supported-protocols](Supported protocols).
+                  '';
+                  example = "http://localhost:80, tcp://localhost:8000, unix:/home/production/echo.sock, hello_world or http_status:404";
+                };
+
+                path = mkOption {
+                  type = with types; nullOr str;
+                  default = null;
+                  description = lib.mdDoc ''
+                    Path filter.
+
+                    If not specified, all paths will be matched.
+                  '';
+                  example = "/*.(jpg|png|css|js)";
+                };
+
+              };
+            })));
+            default = { };
+            description = lib.mdDoc ''
+              Ingress rules.
+
+              See [https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/local-management/ingress/](Ingress rules).
+            '';
+            example = {
+              "*.domain.com" = "http://localhost:80";
+              "*.anotherone.com" = "http://localhost:80";
+            };
+          };
+        };
+      }));
+
+      default = { };
+      example = {
+        "00000000-0000-0000-0000-000000000000" = {
+          credentialsFile = "/tmp/test";
+          ingress = {
+            "*.domain1.com" = {
+              service = "http://localhost:80";
+            };
+          };
+          default = "http_status:404";
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.targets =
+      mapAttrs'
+        (name: tunnel:
+          nameValuePair "cloudflared-tunnel-${name}" ({
+            description = lib.mdDoc "Cloudflare tunnel '${name}' target";
+            requires = [ "cloudflared-tunnel-${name}.service" ];
+            after = [ "cloudflared-tunnel-${name}.service" ];
+            unitConfig.StopWhenUnneeded = true;
+          })
+        )
+        config.services.cloudflared.tunnels;
+
+    systemd.services =
+      mapAttrs'
+        (name: tunnel:
+          let
+            filterConfig = lib.attrsets.filterAttrsRecursive (_: v: ! builtins.elem v [ null [ ] { } ]);
+
+            filterIngressSet = filterAttrs (_: v: builtins.typeOf v == "set");
+            filterIngressStr = filterAttrs (_: v: builtins.typeOf v == "string");
+
+            ingressesSet = filterIngressSet tunnel.ingress;
+            ingressesStr = filterIngressStr tunnel.ingress;
+
+            fullConfig = {
+              tunnel = name;
+              "credentials-file" = tunnel.credentialsFile;
+              ingress =
+                (map
+                  (key: {
+                    hostname = key;
+                  } // getAttr key (filterConfig (filterConfig ingressesSet)))
+                  (attrNames ingressesSet))
+                ++
+                (map
+                  (key: {
+                    hostname = key;
+                    service = getAttr key ingressesStr;
+                  })
+                  (attrNames ingressesStr))
+                ++ [{ service = tunnel.default; }];
+            };
+            mkConfigFile = pkgs.writeText "cloudflared.yml" (builtins.toJSON fullConfig);
+          in
+          nameValuePair "cloudflared-tunnel-${name}" ({
+            after = [ "network.target" ];
+            wantedBy = [ "multi-user.target" ];
+            serviceConfig = {
+              User = cfg.user;
+              Group = cfg.group;
+              ExecStart = "${cfg.package}/bin/cloudflared tunnel --config=${mkConfigFile} --no-autoupdate run";
+              Restart = "always";
+            };
+          })
+        )
+        config.services.cloudflared.tunnels;
+
+    users.users = mkIf (cfg.user == "cloudflared") {
+      cloudflared = {
+        group = cfg.group;
+        isSystemUser = true;
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "cloudflared") {
+      cloudflared = { };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ bbigras ];
+}
diff --git a/nixos/modules/services/networking/consul.nix b/nixos/modules/services/networking/consul.nix
index bee41dcf765d..f1c36138be3e 100644
--- a/nixos/modules/services/networking/consul.nix
+++ b/nixos/modules/services/networking/consul.nix
@@ -142,7 +142,7 @@ in
         };
 
         consulAddr = mkOption {
-          description = lib.mdDoc "Consul api listening adddress";
+          description = lib.mdDoc "Consul api listening address";
           default = "localhost:8500";
           type = types.str;
         };
diff --git a/nixos/modules/services/networking/epmd.nix b/nixos/modules/services/networking/epmd.nix
index 534b80906211..0bc8c71f4eaa 100644
--- a/nixos/modules/services/networking/epmd.nix
+++ b/nixos/modules/services/networking/epmd.nix
@@ -32,7 +32,7 @@ in
         default = "[::]:4369";
         description = lib.mdDoc ''
           the listenStream used by the systemd socket.
-          see https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream= for more informations.
+          see https://www.freedesktop.org/software/systemd/man/systemd.socket.html#ListenStream= for more information.
           use this to change the port epmd will run on.
           if not defined, epmd will use "[::]:4369"
         '';
diff --git a/nixos/modules/services/networking/firefox-syncserver.nix b/nixos/modules/services/networking/firefox-syncserver.nix
index c3d9f43f7457..9733fb16d903 100644
--- a/nixos/modules/services/networking/firefox-syncserver.nix
+++ b/nixos/modules/services/networking/firefox-syncserver.nix
@@ -11,8 +11,10 @@ let
 
   format = pkgs.formats.toml {};
   settings = {
-    database_url = dbURL;
     human_logs = true;
+    syncstorage = {
+      database_url = dbURL;
+    };
     tokenserver = {
       node_type = "mysql";
       database_url = dbURL;
@@ -253,8 +255,7 @@ in
       serviceConfig = {
         User = defaultUser;
         Group = defaultUser;
-        ExecStart = "${cfg.package}/bin/syncstorage --config ${configFile}";
-        Stderr = "journal";
+        ExecStart = "${cfg.package}/bin/syncserver --config ${configFile}";
         EnvironmentFile = lib.mkIf (cfg.secrets != null) "${cfg.secrets}";
 
         # hardening
diff --git a/nixos/modules/services/networking/hans.nix b/nixos/modules/services/networking/hans.nix
index ffb2ee841c64..3ea95b3bdae9 100644
--- a/nixos/modules/services/networking/hans.nix
+++ b/nixos/modules/services/networking/hans.nix
@@ -55,7 +55,7 @@ in
             passwordFile = mkOption {
               type = types.str;
               default = "";
-              description = lib.mdDoc "File that containts password";
+              description = lib.mdDoc "File that contains password";
             };
 
           };
@@ -92,7 +92,7 @@ in
         passwordFile = mkOption {
           type = types.str;
           default = "";
-          description = lib.mdDoc "File that containts password";
+          description = lib.mdDoc "File that contains password";
         };
       };
 
diff --git a/nixos/modules/services/networking/headscale.nix b/nixos/modules/services/networking/headscale.nix
index 0334c5a00bab..29b632ff5d22 100644
--- a/nixos/modules/services/networking/headscale.nix
+++ b/nixos/modules/services/networking/headscale.nix
@@ -268,7 +268,7 @@ in
           type = types.attrsOf types.str;
           default = { };
           description = lib.mdDoc ''
-            Domain map is used to map incomming users (by their email) to
+            Domain map is used to map incoming users (by their email) to
             a namespace. The key can be a string, or regex.
           '';
           example = {
@@ -326,7 +326,7 @@ in
         type = types.nullOr types.path;
         default = null;
         description = lib.mdDoc ''
-          Path to a file containg ACL policies.
+          Path to a file containing ACL policies.
         '';
       };
 
diff --git a/nixos/modules/services/networking/hylafax/systemd.nix b/nixos/modules/services/networking/hylafax/systemd.nix
index 4506bbbc5eb7..df6d0f49eec4 100644
--- a/nixos/modules/services/networking/hylafax/systemd.nix
+++ b/nixos/modules/services/networking/hylafax/systemd.nix
@@ -96,7 +96,7 @@ let
   hardenService =
     # Add some common systemd service hardening settings,
     # but allow each service (here) to override
-    # settings by explicitely setting those to `null`.
+    # settings by explicitly setting those to `null`.
     # More hardening would be nice but makes
     # customizing hylafax setups very difficult.
     # If at all, it should only be added along
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index a02f8df11163..3f6cb97296b5 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -473,7 +473,7 @@ in
         type = with types; nullOr str;
         default = null;
         description = lib.mdDoc ''
-          Router Familiy to trust for first hops.
+          Router Family to trust for first hops.
         '';
       };
 
diff --git a/nixos/modules/services/networking/iperf3.nix b/nixos/modules/services/networking/iperf3.nix
index a70085bb1f5a..0a204524e00f 100644
--- a/nixos/modules/services/networking/iperf3.nix
+++ b/nixos/modules/services/networking/iperf3.nix
@@ -7,7 +7,7 @@ let
     port = mkOption {
       type        = types.ints.u16;
       default     = 5201;
-      description = lib.mdDoc "Server port to listen on for iperf3 client requsts.";
+      description = lib.mdDoc "Server port to listen on for iperf3 client requests.";
     };
     affinity = mkOption {
       type        = types.nullOr types.ints.unsigned;
diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix
index f39b149dd609..945f4113bd47 100644
--- a/nixos/modules/services/networking/kea.nix
+++ b/nixos/modules/services/networking/kea.nix
@@ -47,7 +47,7 @@ in
             type = listOf str;
             default = [];
             description = lib.mdDoc ''
-              List of additonal arguments to pass to the daemon.
+              List of additional arguments to pass to the daemon.
             '';
           };
 
@@ -86,7 +86,7 @@ in
             type = listOf str;
             default = [];
             description = lib.mdDoc ''
-              List of additonal arguments to pass to the daemon.
+              List of additional arguments to pass to the daemon.
             '';
           };
 
@@ -146,7 +146,7 @@ in
             type = listOf str;
             default = [];
             description = lib.mdDoc ''
-              List of additonal arguments to pass to the daemon.
+              List of additional arguments to pass to the daemon.
             '';
           };
 
@@ -207,7 +207,7 @@ in
             type = listOf str;
             default = [];
             description = lib.mdDoc ''
-              List of additonal arguments to pass to the daemon.
+              List of additional arguments to pass to the daemon.
             '';
           };
 
diff --git a/nixos/modules/services/networking/knot.nix b/nixos/modules/services/networking/knot.nix
index ee7ea83456d4..e97195d82919 100644
--- a/nixos/modules/services/networking/knot.nix
+++ b/nixos/modules/services/networking/knot.nix
@@ -43,7 +43,7 @@ in {
         type = types.listOf types.str;
         default = [];
         description = lib.mdDoc ''
-          List of additional command line paramters for knotd
+          List of additional command line parameters for knotd
         '';
       };
 
diff --git a/nixos/modules/services/networking/libreswan.nix b/nixos/modules/services/networking/libreswan.nix
index b5df31c28d7c..785729d8f742 100644
--- a/nixos/modules/services/networking/libreswan.nix
+++ b/nixos/modules/services/networking/libreswan.nix
@@ -106,7 +106,7 @@ in
         type = types.bool;
         default = true;
         description = lib.mdDoc ''
-          Whether to disable send and accept redirects for all nework interfaces.
+          Whether to disable send and accept redirects for all network interfaces.
           See the Libreswan [
           FAQ](https://libreswan.org/wiki/FAQ#Why_is_it_recommended_to_disable_send_redirects_in_.2Fproc.2Fsys.2Fnet_.3F) page for why this is recommended.
         '';
diff --git a/nixos/modules/services/networking/lxd-image-server.nix b/nixos/modules/services/networking/lxd-image-server.nix
index 1099169440a8..d8e32eb997e8 100644
--- a/nixos/modules/services/networking/lxd-image-server.nix
+++ b/nixos/modules/services/networking/lxd-image-server.nix
@@ -87,7 +87,7 @@ in
         };
       };
     })
-    # this is seperate so it can be enabled on mirrored hosts
+    # this is separate so it can be enabled on mirrored hosts
     (mkIf (cfg.nginx.enable) {
       # https://github.com/Avature/lxd-image-server/blob/master/resources/nginx/includes/lxd-image-server.pkg.conf
       services.nginx.virtualHosts = {
diff --git a/nixos/modules/services/networking/mmsd.nix b/nixos/modules/services/networking/mmsd.nix
new file mode 100644
index 000000000000..7e262a9326c1
--- /dev/null
+++ b/nixos/modules/services/networking/mmsd.nix
@@ -0,0 +1,38 @@
+{ pkgs, lib, config, ... }:
+with lib;
+let
+  cfg = config.services.mmsd;
+  dbusServiceFile = pkgs.writeTextDir "share/dbus-1/services/org.ofono.mms.service" ''
+    [D-BUS Service]
+    Name=org.ofono.mms
+    SystemdService=dbus-org.ofono.mms.service
+
+    # Exec= is still required despite SystemdService= being used:
+    # https://github.com/freedesktop/dbus/blob/ef55a3db0d8f17848f8a579092fb05900cc076f5/test/data/systemd-activation/com.example.SystemdActivatable1.service
+    Exec=${pkgs.coreutils}/bin/false mmsd
+  '';
+in
+{
+  options.services.mmsd = {
+    enable = mkEnableOption (mdDoc "Multimedia Messaging Service Daemon");
+    extraArgs = mkOption {
+      type = with types; listOf str;
+      description = mdDoc "Extra arguments passed to `mmsd-tng`";
+      default = [];
+      example = ["--debug"];
+    };
+  };
+  config = mkIf cfg.enable {
+    services.dbus.packages = [ dbusServiceFile ];
+    systemd.user.services.mmsd = {
+      after = [ "ModemManager.service" ];
+      aliases = [ "dbus-org.ofono.mms.service" ];
+      serviceConfig = {
+        Type = "dbus";
+        ExecStart = "${pkgs.mmsd-tng}/bin/mmsdtng " + escapeShellArgs cfg.extraArgs;
+        BusName = "org.ofono.mms";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/mosquitto.nix b/nixos/modules/services/networking/mosquitto.nix
index 6543eb34b4b2..270450cb0c62 100644
--- a/nixos/modules/services/networking/mosquitto.nix
+++ b/nixos/modules/services/networking/mosquitto.nix
@@ -479,7 +479,7 @@ let
         Directories to be scanned for further config files to include.
         Directories will processed in the order given,
         `*.conf` files in the directory will be
-        read in case-sensistive alphabetical order.
+        read in case-sensitive alphabetical order.
       '';
       default = [];
     };
diff --git a/nixos/modules/services/networking/ncdns.nix b/nixos/modules/services/networking/ncdns.nix
index 1d494332095f..cc97beb14e01 100644
--- a/nixos/modules/services/networking/ncdns.nix
+++ b/nixos/modules/services/networking/ncdns.nix
@@ -85,7 +85,7 @@ in
           ```
           bit. IN NS ns1.example.com.
           ```
-          If unset ncdns will generate an internal psuedo-hostname under the
+          If unset ncdns will generate an internal pseudo-hostname under the
           zone, which will resolve to the value of
           {option}`services.ncdns.identity.address`.
           If you are only using ncdns locally you can ignore this.
diff --git a/nixos/modules/services/networking/ndppd.nix b/nixos/modules/services/networking/ndppd.nix
index 6cbc9712be31..98c58d2d5db1 100644
--- a/nixos/modules/services/networking/ndppd.nix
+++ b/nixos/modules/services/networking/ndppd.nix
@@ -43,7 +43,7 @@ let
       timeout = mkOption {
         type = types.int;
         description = lib.mdDoc ''
-          Controls how long to wait for a Neighbor Advertisment Message before
+          Controls how long to wait for a Neighbor Advertisement Message before
           invalidating the entry, in milliseconds.
         '';
         default = 500;
@@ -74,7 +74,7 @@ let
         type = types.nullOr types.str;
         description = lib.mdDoc ''
           This is the target address is to match against. If no netmask
-          is provided, /128 is assumed. The addresses of serveral rules
+          is provided, /128 is assumed. The addresses of several rules
           may or may not overlap.
           Defaults to the name of the attrset.
         '';
diff --git a/nixos/modules/services/networking/nftables.nix b/nixos/modules/services/networking/nftables.nix
index d2d7543e8cf3..8166a8e7110b 100644
--- a/nixos/modules/services/networking/nftables.nix
+++ b/nixos/modules/services/networking/nftables.nix
@@ -37,7 +37,7 @@ in
         # Check out https://wiki.nftables.org/ for better documentation.
         # Table for both IPv4 and IPv6.
         table inet filter {
-          # Block all incomming connections traffic except SSH and "ping".
+          # Block all incoming connections traffic except SSH and "ping".
           chain input {
             type filter hook input priority 0;
 
diff --git a/nixos/modules/services/networking/nsd.nix b/nixos/modules/services/networking/nsd.nix
index 0ded92652093..09f3bdc7ae07 100644
--- a/nixos/modules/services/networking/nsd.nix
+++ b/nixos/modules/services/networking/nsd.nix
@@ -371,7 +371,7 @@ let
         default = null;
         example = "2000::1@1234";
         description = lib.mdDoc ''
-          This address will be used for zone-transfere requests if configured
+          This address will be used for zone-transfer requests if configured
           as a secondary server or notifications in case of a primary server.
           Supply either a plain IPv4 or IPv6 address with an optional port
           number (ip@port).
diff --git a/nixos/modules/services/networking/ostinato.nix b/nixos/modules/services/networking/ostinato.nix
index 40c227ea0c68..dc07313ea901 100644
--- a/nixos/modules/services/networking/ostinato.nix
+++ b/nixos/modules/services/networking/ostinato.nix
@@ -54,7 +54,7 @@ in
           default = "0.0.0.0";
           description = lib.mdDoc ''
             By default, the Drone RPC server will listen on all interfaces and
-            local IPv4 adresses for incoming connections from clients.  Specify
+            local IPv4 addresses for incoming connections from clients.  Specify
             a single IPv4 or IPv6 address if you want to restrict that.
             To listen on any IPv6 address, use ::
           '';
diff --git a/nixos/modules/services/networking/pleroma.nix b/nixos/modules/services/networking/pleroma.nix
index dfd1ed4036ab..188598ea7b86 100644
--- a/nixos/modules/services/networking/pleroma.nix
+++ b/nixos/modules/services/networking/pleroma.nix
@@ -52,7 +52,7 @@ in {
           the right place to store any secret
 
           Have a look to Pleroma section in the NixOS manual for more
-          informations.
+          information.
           '';
       };
 
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index 2d122bcf655a..342638f93bae 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -309,7 +309,7 @@ let
         type = types.int;
         default = 300;
         description = lib.mdDoc ''
-          Timout after which the room is destroyed or unlocked if not
+          Timeout after which the room is destroyed or unlocked if not
           configured, in seconds
        '';
       };
@@ -489,7 +489,7 @@ in
 
           Setting this option to true will prevent you from building a
           NixOS configuration which won't comply with this standard.
-          You can explicitely decide to ignore this standard if you
+          You can explicitly decide to ignore this standard if you
           know what you are doing by setting this option to false.
 
           [1] https://xmpp.org/extensions/xep-0423.html
@@ -649,7 +649,7 @@ in
       extraPluginPaths = mkOption {
         type = types.listOf types.path;
         default = [];
-        description = lib.mdDoc "Addtional path in which to look find plugins/modules";
+        description = lib.mdDoc "Additional path in which to look find plugins/modules";
       };
 
       uploadHttp = mkOption {
@@ -733,7 +733,7 @@ in
 
           Having a server not XEP-0423-compliant might make your XMPP
           experience terrible. See the NixOS manual for further
-          informations.
+          information.
 
           If you know what you're doing, you can disable this warning by
           setting config.services.prosody.xmppComplianceSuite to false.
diff --git a/nixos/modules/services/networking/radicale.nix b/nixos/modules/services/networking/radicale.nix
index a343dab7af25..9ec507fe2ab6 100644
--- a/nixos/modules/services/networking/radicale.nix
+++ b/nixos/modules/services/networking/radicale.nix
@@ -77,7 +77,7 @@ in {
         <https://radicale.org/3.0.html#documentation/authentication-and-rights>.
         This option only works in conjunction with {option}`settings`.
         Setting this will also set {option}`settings.rights.type` and
-        {option}`settings.rights.file` to approriate values.
+        {option}`settings.rights.file` to appropriate values.
       '';
       default = { };
       example = literalExpression ''
diff --git a/nixos/modules/services/networking/rpcbind.nix b/nixos/modules/services/networking/rpcbind.nix
index aa04214debb0..60e78dfec51b 100644
--- a/nixos/modules/services/networking/rpcbind.nix
+++ b/nixos/modules/services/networking/rpcbind.nix
@@ -35,6 +35,16 @@ with lib;
 
     systemd.services.rpcbind = {
       wantedBy = [ "multi-user.target" ];
+      # rpcbind performs a check for /var/run/rpcbind.lock at startup
+      # and will crash if /var/run isn't present. In the stock NixOS
+      # var.conf tmpfiles configuration file, /var/run is symlinked to
+      # /run, so rpcbind can enter a race condition in which /var/run
+      # isn't symlinked yet but tries to interact with the path, so
+      # controlling the order explicitly here ensures that rpcbind can
+      # start successfully. The `wants` instead of `requires` should
+      # avoid creating a strict/brittle dependency.
+      wants = [ "systemd-tmpfiles-setup.service" ];
+      after = [ "systemd-tmpfiles-setup.service" ];
     };
 
     users.users.rpc = {
diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix
index 214b6c6a787a..6c57ddbde2d4 100644
--- a/nixos/modules/services/networking/searx.nix
+++ b/nixos/modules/services/networking/searx.nix
@@ -124,7 +124,7 @@ in
         description = lib.mdDoc ''
           Whether to run searx in uWSGI as a "vassal", instead of using its
           built-in HTTP server. This is the recommended mode for public or
-          large instances, but is unecessary for LAN or local-only use.
+          large instances, but is unnecessary for LAN or local-only use.
 
           ::: {.warning}
           The built-in HTTP server logs all queries by default.
@@ -223,7 +223,7 @@ in
         module = "searx.webapp";
         env = [
           "SEARX_SETTINGS_PATH=${cfg.settingsFile}"
-          # searxng compatiblity https://github.com/searxng/searxng/issues/1519
+          # searxng compatibility https://github.com/searxng/searxng/issues/1519
           "SEARXNG_SETTINGS_PATH=${cfg.settingsFile}"
         ];
         buffer-size = 32768;
diff --git a/nixos/modules/services/networking/stunnel.nix b/nixos/modules/services/networking/stunnel.nix
index 3bd0367a0bb1..4f592fb312d3 100644
--- a/nixos/modules/services/networking/stunnel.nix
+++ b/nixos/modules/services/networking/stunnel.nix
@@ -78,7 +78,7 @@ in
 
       servers = mkOption {
         description = lib.mdDoc ''
-          Define the server configuations.
+          Define the server configurations.
 
           See "SERVICE-LEVEL OPTIONS" in {manpage}`stunnel(8)`.
         '';
diff --git a/nixos/modules/services/networking/supplicant.nix b/nixos/modules/services/networking/supplicant.nix
index 0a48e73932e8..13d84736e2c2 100644
--- a/nixos/modules/services/networking/supplicant.nix
+++ b/nixos/modules/services/networking/supplicant.nix
@@ -13,7 +13,7 @@ let
   serviceName = iface: "supplicant-${if (iface=="WLAN") then "wlan@" else (
                                      if (iface=="LAN") then "lan@" else (
                                      if (iface=="DBUS") then "dbus"
-                                     else (replaceChars [" "] ["-"] iface)))}";
+                                     else (replaceStrings [" "] ["-"] iface)))}";
 
   # TODO: Use proper privilege separation for wpa_supplicant
   supplicantService = iface: suppl:
@@ -27,7 +27,7 @@ let
       driverArg = optionalString (suppl.driver != null) "-D${suppl.driver}";
       bridgeArg = optionalString (suppl.bridge!="") "-b${suppl.bridge}";
       confFileArg = optionalString (suppl.configFile.path!=null) "-c${suppl.configFile.path}";
-      extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceChars [" "] ["-"] iface}" ''
+      extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceStrings [" "] ["-"] iface}" ''
         ${optionalString suppl.userControlled.enable "ctrl_interface=DIR=${suppl.userControlled.socketDir} GROUP=${suppl.userControlled.group}"}
         ${optionalString suppl.configFile.writable "update_config=1"}
         ${suppl.extraConf}
@@ -223,7 +223,7 @@ in
         text = ''
           ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface:
             flip (concatMapStringsSep "\n") (splitString " " iface) (i: ''
-              ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
+              ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceStrings [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))}
 
           ${optionalString (hasAttr "WLAN" cfg) ''
             ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="/run/current-system/systemd/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service"
diff --git a/nixos/modules/services/networking/tailscale.nix b/nixos/modules/services/networking/tailscale.nix
index 26997dd96013..233bfdf9ebf5 100644
--- a/nixos/modules/services/networking/tailscale.nix
+++ b/nixos/modules/services/networking/tailscale.nix
@@ -4,10 +4,7 @@ with lib;
 
 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 ];
 
@@ -38,14 +35,23 @@ in {
       defaultText = literalExpression "pkgs.tailscale";
       description = lib.mdDoc "The package to use for tailscale";
     };
+
+    useRoutingFeatures = mkOption {
+      type = types.enum [ "none" "client" "server" "both" ];
+      default = "none";
+      example = "server";
+      description = lib.mdDoc ''
+        Enables settings required for Tailscale's routing features like subnet routers and exit nodes.
+
+        To use these these features, you will still need to call `sudo tailscale up` with the relevant flags like `--advertise-exit-node` and `--exit-node`.
+
+        When set to `client` or `both`, reverse path filtering will be set to loose instead of strict.
+        When set to `server` or `both`, IP forwarding will be enabled.
+      '';
+    };
   };
 
   config = mkIf cfg.enable {
-    warnings = optional (firewallOn && rpfIsStrict) ''
-      Strict reverse path filtering breaks Tailscale exit node use and some subnet routing setups. Consider setting:
-
-        networking.firewall.checkReversePath = "loose";
-    '';
     environment.systemPackages = [ cfg.package ]; # for the CLI
     systemd.packages = [ cfg.package ];
     systemd.services.tailscaled = {
@@ -75,6 +81,13 @@ in {
       stopIfChanged = false;
     };
 
+    boot.kernel.sysctl = mkIf (cfg.useRoutingFeatures == "server" || cfg.useRoutingFeatures == "both") {
+      "net.ipv4.conf.all.forwarding" = mkDefault true;
+      "net.ipv6.conf.all.forwarding" = mkDefault true;
+    };
+
+    networking.firewall.checkReversePath = mkIf (cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both") "loose";
+
     networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ];
 
     systemd.network.networks."50-tailscale" = mkIf isNetworkd {
diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix
index fa24c70e63de..c85dd03867f7 100644
--- a/nixos/modules/services/networking/unbound.nix
+++ b/nixos/modules/services/networking/unbound.nix
@@ -245,7 +245,7 @@ in {
         NotifyAccess = "main";
         Type = "notify";
 
-        # FIXME: Which of these do we actualy need, can we drop the chroot flag?
+        # FIXME: Which of these do we actually need, can we drop the chroot flag?
         AmbientCapabilities = [
           "CAP_NET_BIND_SERVICE"
           "CAP_NET_RAW"
diff --git a/nixos/modules/services/networking/unifi.nix b/nixos/modules/services/networking/unifi.nix
index d30f7c89633b..d220aa9fbbe4 100644
--- a/nixos/modules/services/networking/unifi.nix
+++ b/nixos/modules/services/networking/unifi.nix
@@ -24,8 +24,8 @@ in
 
     services.unifi.jrePackage = mkOption {
       type = types.package;
-      default = pkgs.jre8;
-      defaultText = literalExpression "pkgs.jre8";
+      default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3") then pkgs.jdk11 else pkgs.jre8;
+      defaultText = literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8'';
       description = lib.mdDoc ''
         The JRE package to use. Check the release notes to ensure it is supported.
       '';
@@ -76,7 +76,7 @@ in
       default = null;
       example = 4096;
       description = lib.mdDoc ''
-        Set the maximimum heap size for the JVM in MB. If this option isn't set, the
+        Set the maximum heap size for the JVM in MB. If this option isn't set, the
         JVM will decide this value at runtime.
       '';
     };
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index 5fee7b66a4dc..b1f0f7403243 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -168,7 +168,7 @@ in
 
           The default is a file containing the users from {option}`userlist`.
 
-          If explicitely set to null userlist_file will not be set in vsftpd's config file.
+          If explicitly set to null userlist_file will not be set in vsftpd's config file.
         '';
       };
 
diff --git a/nixos/modules/services/networking/wireguard.nix b/nixos/modules/services/networking/wireguard.nix
index ce5616672c16..1d6556f626be 100644
--- a/nixos/modules/services/networking/wireguard.nix
+++ b/nixos/modules/services/networking/wireguard.nix
@@ -303,7 +303,7 @@ let
           set -e
 
           # If the parent dir does not already exist, create it.
-          # Otherwise, does nothing, keeping existing permisions intact.
+          # Otherwise, does nothing, keeping existing permissions intact.
           mkdir -p --mode 0755 "${dirOf values.privateKeyFile}"
 
           if [ ! -f "${values.privateKeyFile}" ]; then
@@ -315,7 +315,7 @@ let
 
   peerUnitServiceName = interfaceName: publicKey: dynamicRefreshEnabled:
     let
-      keyToUnitName = replaceChars
+      keyToUnitName = replaceStrings
         [ "/" "-"    " "     "+"     "="      ]
         [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ];
       unitName = keyToUnitName publicKey;
diff --git a/nixos/modules/services/networking/yggdrasil.xml b/nixos/modules/services/networking/yggdrasil.xml
index bc9da84fa431..a7b8c469529a 100644
--- a/nixos/modules/services/networking/yggdrasil.xml
+++ b/nixos/modules/services/networking/yggdrasil.xml
@@ -30,7 +30,7 @@ An annotated example of a simple configuration:
     settings = {
       Peers = [
         # Yggdrasil will automatically connect and "peer" with other nodes it
-        # discovers via link-local multicast annoucements. Unless this is the
+        # discovers via link-local multicast announcements. Unless this is the
         # case (it probably isn't) a node needs peers within the existing
         # network that it can tunnel to.
         "tcp://1.2.3.4:1024"
@@ -78,7 +78,7 @@ in {
   }];
 
   services.radvd = {
-    # Annouce the 300::/8 prefix to eth0.
+    # Announce the 300::/8 prefix to eth0.
     enable = true;
     config = ''
       interface eth0
diff --git a/nixos/modules/services/printing/ipp-usb.nix b/nixos/modules/services/printing/ipp-usb.nix
index 0425eb913731..8ed2ff826871 100644
--- a/nixos/modules/services/printing/ipp-usb.nix
+++ b/nixos/modules/services/printing/ipp-usb.nix
@@ -7,7 +7,7 @@
   config = lib.mkIf config.services.ipp-usb.enable {
     systemd.services.ipp-usb = {
       description = "Daemon for IPP over USB printer support";
-      after = [ "cups.service" "avahi-deamon.service" ];
+      after = [ "cups.service" "avahi-daemon.service" ];
       wants = [ "avahi-daemon.service" ];
       serviceConfig = {
         ExecStart = [ "${pkgs.ipp-usb}/bin/ipp-usb" ];
diff --git a/nixos/modules/services/security/fail2ban.nix b/nixos/modules/services/security/fail2ban.nix
index e208eed008ae..3b124a4f0e08 100644
--- a/nixos/modules/services/security/fail2ban.nix
+++ b/nixos/modules/services/security/fail2ban.nix
@@ -161,7 +161,7 @@ in
         type = types.str;
         example = "2 4 16 128";
         description = lib.mdDoc ''
-          "bantime-increment.multipliers" used to calculate next value of ban time instead of formula, coresponding
+          "bantime-increment.multipliers" used to calculate next value of ban time instead of formula, corresponding
           previously ban count and given "bantime.factor" (for multipliers default is 1);
           following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count,
           always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours
@@ -174,7 +174,7 @@ in
         example = true;
         description = lib.mdDoc ''
           "bantime-increment.overalljails"  (if true) specifies the search of IP in the database will be executed
-          cross over all jails, if false (dafault), only current jail of the ban IP will be searched
+          cross over all jails, if false (default), only current jail of the ban IP will be searched
         '';
       };
 
diff --git a/nixos/modules/services/security/shibboleth-sp.nix b/nixos/modules/services/security/shibboleth-sp.nix
index 6626ea213625..e7897c3324cf 100644
--- a/nixos/modules/services/security/shibboleth-sp.nix
+++ b/nixos/modules/services/security/shibboleth-sp.nix
@@ -27,13 +27,13 @@ in {
       fastcgi.shibAuthorizerPort = mkOption {
         type = types.int;
         default = 9100;
-        description = lib.mdDoc "Port for shibauthorizer FastCGI proccess to bind to";
+        description = lib.mdDoc "Port for shibauthorizer FastCGI process to bind to";
       };
 
       fastcgi.shibResponderPort = mkOption {
         type = types.int;
         default = 9101;
-        description = lib.mdDoc "Port for shibauthorizer FastCGI proccess to bind to";
+        description = lib.mdDoc "Port for shibauthorizer FastCGI process to bind to";
       };
     };
   };
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index b85b78f269a1..2aa2964f8818 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -146,7 +146,7 @@ let
     ]))];
     description = lib.mdDoc (descriptionGeneric optionName);
   };
-  optionBandwith = optionName: mkOption {
+  optionBandwidth = optionName: mkOption {
     type = with types; nullOr (either int str);
     default = null;
     description = lib.mdDoc (descriptionGeneric optionName);
@@ -205,7 +205,7 @@ in
     (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "isolationOptions" ] "Use services.tor.settings.TransPort instead.")
     (mkRemovedOptionModule [ "services" "tor" "client" "transparentProxy" "listenAddress" ] "Use services.tor.settings.TransPort instead.")
     (mkRenamedOptionModule [ "services" "tor" "controlPort" ] [ "services" "tor" "settings" "ControlPort" ])
-    (mkRemovedOptionModule [ "services" "tor" "extraConfig" ] "Plese use services.tor.settings instead.")
+    (mkRemovedOptionModule [ "services" "tor" "extraConfig" ] "Please use services.tor.settings instead.")
     (mkRenamedOptionModule [ "services" "tor" "hiddenServices" ] [ "services" "tor" "relay" "onionServices" ])
     (mkRenamedOptionModule [ "services" "tor" "relay" "accountingMax" ] [ "services" "tor" "settings" "AccountingMax" ])
     (mkRenamedOptionModule [ "services" "tor" "relay" "accountingStart" ] [ "services" "tor" "settings" "AccountingStart" ])
@@ -546,7 +546,7 @@ in
             };
           options.Address = optionString "Address";
           options.AssumeReachable = optionBool "AssumeReachable";
-          options.AccountingMax = optionBandwith "AccountingMax";
+          options.AccountingMax = optionBandwidth "AccountingMax";
           options.AccountingStart = optionString "AccountingStart";
           options.AuthDirHasIPv6Connectivity = optionBool "AuthDirHasIPv6Connectivity";
           options.AuthDirListBadExits = optionBool "AuthDirListBadExits";
@@ -559,8 +559,8 @@ in
             default = [".onion" ".exit"];
             example = [".onion"];
           };
-          options.BandwidthBurst = optionBandwith "BandwidthBurst";
-          options.BandwidthRate = optionBandwith "BandwidthRate";
+          options.BandwidthBurst = optionBandwidth "BandwidthBurst";
+          options.BandwidthRate = optionBandwidth "BandwidthRate";
           options.BridgeAuthoritativeDir = optionBool "BridgeAuthoritativeDir";
           options.BridgeRecordUsageByCountry = optionBool "BridgeRecordUsageByCountry";
           options.BridgeRelay = optionBool "BridgeRelay" // { default = false; };
@@ -709,7 +709,7 @@ in
           options.LogMessageDomains = optionBool "LogMessageDomains";
           options.LongLivedPorts = optionPorts "LongLivedPorts";
           options.MainloopStats = optionBool "MainloopStats";
-          options.MaxAdvertisedBandwidth = optionBandwith "MaxAdvertisedBandwidth";
+          options.MaxAdvertisedBandwidth = optionBandwidth "MaxAdvertisedBandwidth";
           options.MaxCircuitDirtiness = optionInt "MaxCircuitDirtiness";
           options.MaxClientCircuitsPending = optionInt "MaxClientCircuitsPending";
           options.NATDPort = optionIsolablePorts "NATDPort";
@@ -719,8 +719,8 @@ in
           options.OfflineMasterKey = optionBool "OfflineMasterKey";
           options.OptimisticData = optionBool "OptimisticData"; # default is null and like "auto"
           options.PaddingStatistics = optionBool "PaddingStatistics";
-          options.PerConnBWBurst = optionBandwith "PerConnBWBurst";
-          options.PerConnBWRate = optionBandwith "PerConnBWRate";
+          options.PerConnBWBurst = optionBandwidth "PerConnBWBurst";
+          options.PerConnBWRate = optionBandwidth "PerConnBWRate";
           options.PidFile = optionPath "PidFile";
           options.ProtocolWarnings = optionBool "ProtocolWarnings";
           options.PublishHidServDescriptors = optionBool "PublishHidServDescriptors";
@@ -732,8 +732,8 @@ in
           options.ReducedExitPolicy = optionBool "ReducedExitPolicy";
           options.RefuseUnknownExits = optionBool "RefuseUnknownExits"; # default is null and like "auto"
           options.RejectPlaintextPorts = optionPorts "RejectPlaintextPorts";
-          options.RelayBandwidthBurst = optionBandwith "RelayBandwidthBurst";
-          options.RelayBandwidthRate = optionBandwith "RelayBandwidthRate";
+          options.RelayBandwidthBurst = optionBandwidth "RelayBandwidthBurst";
+          options.RelayBandwidthRate = optionBandwidth "RelayBandwidthRate";
           #options.RunAsDaemon
           options.Sandbox = optionBool "Sandbox";
           options.ServerDNSAllowBrokenConfig = optionBool "ServerDNSAllowBrokenConfig";
diff --git a/nixos/modules/services/security/usbguard.nix b/nixos/modules/services/security/usbguard.nix
index 1b1fa84c4fa3..1d846b194077 100644
--- a/nixos/modules/services/security/usbguard.nix
+++ b/nixos/modules/services/security/usbguard.nix
@@ -118,9 +118,9 @@ in
         description = lib.mdDoc ''
           The  USBGuard  daemon  modifies  some attributes of controller
           devices like the default authorization state of new child device
-          instances. Using this setting, you can controll whether the daemon
+          instances. Using this setting, you can control whether the daemon
           will try to restore the attribute values to the state before
-          modificaton on shutdown.
+          modification on shutdown.
         '';
       };
 
diff --git a/nixos/modules/services/security/vaultwarden/default.nix b/nixos/modules/services/security/vaultwarden/default.nix
index 3ef0bfb090ac..aaa3f5507f77 100644
--- a/nixos/modules/services/security/vaultwarden/default.nix
+++ b/nixos/modules/services/security/vaultwarden/default.nix
@@ -162,8 +162,8 @@ in {
 
     webVaultPackage = mkOption {
       type = package;
-      default = pkgs.vaultwarden-vault;
-      defaultText = literalExpression "pkgs.vaultwarden-vault";
+      default = pkgs.vaultwarden.webvault;
+      defaultText = literalExpression "pkgs.vaultwarden.webvault";
       description = lib.mdDoc "Web vault package to use.";
     };
   };
diff --git a/nixos/modules/services/system/cloud-init.nix b/nixos/modules/services/system/cloud-init.nix
index 30b2ca568e12..d75070dea434 100644
--- a/nixos/modules/services/system/cloud-init.nix
+++ b/nixos/modules/services/system/cloud-init.nix
@@ -27,7 +27,7 @@ in
 
           This configuration is not completely compatible with the
           NixOS way of doing configuration, as configuration done by
-          cloud-init might be overriden by a subsequent nixos-rebuild
+          cloud-init might be overridden by a subsequent nixos-rebuild
           call. However, some parts of cloud-init fall outside of
           NixOS's responsibility, like filesystem resizing and ssh
           public key provisioning, and cloud-init is useful for that
diff --git a/nixos/modules/services/system/kerberos/default.nix b/nixos/modules/services/system/kerberos/default.nix
index 0c9e44a45c15..4ed48e463741 100644
--- a/nixos/modules/services/system/kerberos/default.nix
+++ b/nixos/modules/services/system/kerberos/default.nix
@@ -51,7 +51,7 @@ in
   ###### interface
   options = {
     services.kerberos_server = {
-      enable = lib.mkEnableOption (lib.mdDoc "the kerberos authentification server");
+      enable = lib.mkEnableOption (lib.mdDoc "the kerberos authentication server");
 
       realms = mkOption {
         type = types.attrsOf (types.submodule realm);
diff --git a/nixos/modules/services/torrent/deluge.nix b/nixos/modules/services/torrent/deluge.nix
index 70fad4d7d76c..de3d077daec9 100644
--- a/nixos/modules/services/torrent/deluge.nix
+++ b/nixos/modules/services/torrent/deluge.nix
@@ -66,7 +66,7 @@ in {
             `true`. String values must be quoted, integer and
             boolean values must not. See
             <https://git.deluge-torrent.org/deluge/tree/deluge/core/preferencesmanager.py#n41>
-            for the availaible options.
+            for the available options.
           '';
         };
 
@@ -117,7 +117,7 @@ in {
             when {option}`services.deluge.declarative` is set to
             `true`.
             See <https://dev.deluge-torrent.org/wiki/UserGuide/Authentication> for
-            more informations.
+            more information.
           '';
         };
 
diff --git a/nixos/modules/services/torrent/magnetico.nix b/nixos/modules/services/torrent/magnetico.nix
index b681c58dfe2a..b813f1205119 100644
--- a/nixos/modules/services/torrent/magnetico.nix
+++ b/nixos/modules/services/torrent/magnetico.nix
@@ -143,7 +143,7 @@ in {
         The path to the file holding the credentials to access the web
         interface. If unset no authentication will be required.
 
-        The file must constain user names and password hashes in the format
+        The file must contain user names and password hashes in the format
         `username:hash `, one for each line.  Usernames must
         start with a lowecase ([a-z]) ASCII character, might contain
         non-consecutive underscores except at the end, and consists of
diff --git a/nixos/modules/services/torrent/rtorrent.nix b/nixos/modules/services/torrent/rtorrent.nix
index 935c11e3eb00..627439e1079b 100644
--- a/nixos/modules/services/torrent/rtorrent.nix
+++ b/nixos/modules/services/torrent/rtorrent.nix
@@ -82,7 +82,7 @@ in {
       type = types.lines;
       default = "";
       description = lib.mdDoc ''
-        The content of {file}`rtorrent.rc`. The [modernized configuration template](https://rtorrent-docs.readthedocs.io/en/latest/cookbook.html#modernized-configuration-template) with the values specified in this module will be prepended using mkBefore. You can use mkForce to overwrite the config completly.
+        The content of {file}`rtorrent.rc`. The [modernized configuration template](https://rtorrent-docs.readthedocs.io/en/latest/cookbook.html#modernized-configuration-template) with the values specified in this module will be prepended using mkBefore. You can use mkForce to overwrite the config completely.
       '';
     };
   };
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 9b53f5de143d..437823384833 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -44,7 +44,7 @@ in
           (each time the service starts).
 
           See [Transmission's Wiki](https://github.com/transmission/transmission/wiki/Editing-Configuration-Files)
-          for documentation of settings not explicitely covered by this module.
+          for documentation of settings not explicitly covered by this module.
         '';
         default = {};
         type = types.submodule {
@@ -174,6 +174,8 @@ in
         };
       };
 
+      package = mkPackageOption pkgs "transmission" {};
+
       downloadDirPermissions = mkOption {
         type = with types; nullOr str;
         default = null;
@@ -287,7 +289,7 @@ in
           install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' /dev/stdin \
            '${cfg.home}/${settingsDir}/settings.json'
         '')];
-        ExecStart="${pkgs.transmission}/bin/transmission-daemon -f -g ${cfg.home}/${settingsDir} ${escapeShellArgs cfg.extraFlags}";
+        ExecStart="${cfg.package}/bin/transmission-daemon -f -g ${cfg.home}/${settingsDir} ${escapeShellArgs cfg.extraFlags}";
         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
         User = cfg.user;
         Group = cfg.group;
@@ -353,7 +355,7 @@ in
         PrivateUsers = true;
         ProtectClock = true;
         ProtectControlGroups = true;
-        # ProtectHome=true would not allow BindPaths= to work accross /home,
+        # ProtectHome=true would not allow BindPaths= to work across /home,
         # and ProtectHome=tmpfs would break statfs(),
         # preventing transmission-daemon to report the available free space.
         # However, RootDirectory= is used, so this is not a security concern
@@ -385,7 +387,7 @@ in
     };
 
     # It's useful to have transmission in path, e.g. for remote control
-    environment.systemPackages = [ pkgs.transmission ];
+    environment.systemPackages = [ cfg.package ];
 
     users.users = optionalAttrs (cfg.user == "transmission") ({
       transmission = {
@@ -457,7 +459,7 @@ in
     ];
 
     security.apparmor.policies."bin.transmission-daemon".profile = ''
-      include "${pkgs.transmission.apparmor}/bin.transmission-daemon"
+      include "${cfg.package.apparmor}/bin.transmission-daemon"
     '';
     security.apparmor.includes."local/bin.transmission-daemon" = ''
       r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
diff --git a/nixos/modules/services/video/unifi-video.nix b/nixos/modules/services/video/unifi-video.nix
index 450e92dd9a37..93d4d5060b70 100644
--- a/nixos/modules/services/video/unifi-video.nix
+++ b/nixos/modules/services/video/unifi-video.nix
@@ -159,7 +159,7 @@ in
       default = 1024;
       example = 4096;
       description = lib.mdDoc ''
-        Set the maximimum heap size for the JVM in MB.
+        Set the maximum heap size for the JVM in MB.
       '';
     };
 
diff --git a/nixos/modules/services/web-apps/bookstack.nix b/nixos/modules/services/web-apps/bookstack.nix
index 40bb377e2c88..d846c98577c8 100644
--- a/nixos/modules/services/web-apps/bookstack.nix
+++ b/nixos/modules/services/web-apps/bookstack.nix
@@ -359,7 +359,7 @@ in {
     };
 
     systemd.services.bookstack-setup = {
-      description = "Preperation tasks for BookStack";
+      description = "Preparation tasks for BookStack";
       before = [ "phpfpm-bookstack.service" ];
       after = optional db.createLocally "mysql.service";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/web-apps/healthchecks.nix b/nixos/modules/services/web-apps/healthchecks.nix
index 7da6dce1f954..b3fdb681e2f3 100644
--- a/nixos/modules/services/web-apps/healthchecks.nix
+++ b/nixos/modules/services/web-apps/healthchecks.nix
@@ -98,7 +98,7 @@ in
       description = lib.mdDoc ''
         Environment variables which are read by healthchecks `(local)_settings.py`.
 
-        Settings which are explictly covered in options bewlow, are type-checked and/or transformed
+        Settings which are explicitly covered in options bewlow, are type-checked and/or transformed
         before added to the environment, everything else is passed as a string.
 
         See <https://healthchecks.io/docs/self_hosted_configuration/>
diff --git a/nixos/modules/services/web-apps/ihatemoney/default.nix b/nixos/modules/services/web-apps/ihatemoney/default.nix
index b0da0acfcf8a..a61aa445f82c 100644
--- a/nixos/modules/services/web-apps/ihatemoney/default.nix
+++ b/nixos/modules/services/web-apps/ihatemoney/default.nix
@@ -68,7 +68,7 @@ in
         example = {
           http = ":8000";
         };
-        description = lib.mdDoc "Additionnal configuration of the UWSGI vassal running ihatemoney. It should notably specify on which interfaces and ports the vassal should listen.";
+        description = lib.mdDoc "Additional configuration of the UWSGI vassal running ihatemoney. It should notably specify on which interfaces and ports the vassal should listen.";
       };
       defaultSender = {
         name = mkOption {
diff --git a/nixos/modules/services/web-apps/invidious.nix b/nixos/modules/services/web-apps/invidious.nix
index a153aa3fb0c3..61c52ee03dc6 100644
--- a/nixos/modules/services/web-apps/invidious.nix
+++ b/nixos/modules/services/web-apps/invidious.nix
@@ -171,7 +171,7 @@ in
       description = lib.mdDoc ''
         A file including Invidious settings.
 
-        It gets merged with the setttings specified in {option}`services.invidious.settings`
+        It gets merged with the settings specified in {option}`services.invidious.settings`
         and can be used to store secrets like `hmac_key` outside of the nix store.
       '';
     };
diff --git a/nixos/modules/services/web-apps/invoiceplane.nix b/nixos/modules/services/web-apps/invoiceplane.nix
index 99e7b1f96eac..8be1fd3055d0 100644
--- a/nixos/modules/services/web-apps/invoiceplane.nix
+++ b/nixos/modules/services/web-apps/invoiceplane.nix
@@ -74,7 +74,7 @@ let
           type = types.path;
           default = "/var/lib/invoiceplane/${name}";
           description = lib.mdDoc ''
-            This directory is used for uploads of attachements and cache.
+            This directory is used for uploads of attachments and cache.
             The directory passed here is automatically created and permissions
             adjusted as required.
           '';
diff --git a/nixos/modules/services/web-apps/jitsi-meet.nix b/nixos/modules/services/web-apps/jitsi-meet.nix
index a42e249189f6..5b0934b2fb76 100644
--- a/nixos/modules/services/web-apps/jitsi-meet.nix
+++ b/nixos/modules/services/web-apps/jitsi-meet.nix
@@ -28,7 +28,7 @@ let
     '');
 
   # Essential config - it's probably not good to have these as option default because
-  # types.attrs doesn't do merging. Let's merge explicitly, can still be overriden if
+  # types.attrs doesn't do merging. Let's merge explicitly, can still be overridden if
   # user desires.
   defaultCfg = {
     hosts = {
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index a6cd7432db24..b6e2309555f2 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -1,7 +1,9 @@
-{ config, lib, pkgs, ... }:
+{ lib, pkgs, config, options, ... }:
 
 let
   cfg = config.services.mastodon;
+  opt = options.services.mastodon;
+
   # We only want to create a database if we're actually going to connect to it.
   databaseActuallyCreateLocally = cfg.database.createLocally && cfg.database.host == "/run/postgresql";
 
@@ -23,7 +25,6 @@ let
     REDIS_HOST = cfg.redis.host;
     REDIS_PORT = toString(cfg.redis.port);
     DB_HOST = cfg.database.host;
-    DB_PORT = toString(cfg.database.port);
     DB_NAME = cfg.database.name;
     LOCAL_DOMAIN = cfg.localDomain;
     SMTP_SERVER = cfg.smtp.host;
@@ -37,7 +38,8 @@ let
 
     TRUSTED_PROXY_IP = cfg.trustedProxy;
   }
-  // (if cfg.smtp.authenticate then { SMTP_LOGIN  = cfg.smtp.user; } else {})
+  // lib.optionalAttrs (cfg.database.host != "/run/postgresql" && cfg.database.port != null) { DB_PORT = toString cfg.database.port; }
+  // lib.optionalAttrs cfg.smtp.authenticate { SMTP_LOGIN  = cfg.smtp.user; }
   // cfg.extraConfig;
 
   systemCallsList = [ "@cpu-emulation" "@debug" "@keyring" "@ipc" "@mount" "@obsolete" "@privileged" "@setuid" ];
@@ -314,8 +316,13 @@ in {
         };
 
         port = lib.mkOption {
-          type = lib.types.port;
-          default = 5432;
+          type = lib.types.nullOr lib.types.port;
+          default = if cfg.database.createLocally then null else 5432;
+          defaultText = lib.literalExpression ''
+            if config.${opt.database.createLocally}
+            then null
+            else 5432
+          '';
           description = lib.mdDoc "Database host port.";
         };
 
@@ -333,8 +340,8 @@ in {
 
         passwordFile = lib.mkOption {
           type = lib.types.nullOr lib.types.path;
-          default = "/var/lib/mastodon/secrets/db-password";
-          example = "/run/keys/mastodon-db-password";
+          default = null;
+          example = "/var/lib/mastodon/secrets/db-password";
           description = lib.mdDoc ''
             A file containing the password corresponding to
             {option}`database.user`.
@@ -468,7 +475,18 @@ in {
     assertions = [
       {
         assertion = databaseActuallyCreateLocally -> (cfg.user == cfg.database.user);
-        message = ''For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user and services.mastodon.database.user must be identical.'';
+        message = ''
+          For local automatic database provisioning (services.mastodon.database.createLocally == true) with peer
+            authentication (services.mastodon.database.host == "/run/postgresql") to work services.mastodon.user
+            and services.mastodon.database.user must be identical.
+        '';
+      }
+      {
+        assertion = !databaseActuallyCreateLocally -> (cfg.database.host != "/run/postgresql");
+        message = ''
+          <option>services.mastodon.database.host</option> needs to be set if
+            <option>services.mastodon.database.createLocally</option> is not enabled.
+        '';
       }
       {
         assertion = cfg.smtp.authenticate -> (cfg.smtp.user != null);
@@ -512,10 +530,11 @@ in {
         OTP_SECRET="$(cat ${cfg.otpSecretFile})"
         VAPID_PRIVATE_KEY="$(cat ${cfg.vapidPrivateKeyFile})"
         VAPID_PUBLIC_KEY="$(cat ${cfg.vapidPublicKeyFile})"
+      '' + lib.optionalString (cfg.database.passwordFile != null) ''
         DB_PASS="$(cat ${cfg.database.passwordFile})"
-      '' + (if cfg.smtp.authenticate then ''
+      '' + lib.optionalString cfg.smtp.authenticate ''
         SMTP_PASSWORD="$(cat ${cfg.smtp.passwordFile})"
-      '' else "") + ''
+      '' + ''
         EOF
       '';
       environment = env;
@@ -530,7 +549,16 @@ in {
     };
 
     systemd.services.mastodon-init-db = lib.mkIf cfg.automaticMigrations {
-      script = ''
+      script = lib.optionalString (!databaseActuallyCreateLocally) ''
+        umask 077
+
+        export PGPASSFILE
+        PGPASSFILE=$(mktemp)
+        cat > $PGPASSFILE <<EOF
+        ${cfg.database.host}:${toString cfg.database.port}:${cfg.database.name}:${cfg.database.user}:$(cat ${cfg.database.passwordFile})
+        EOF
+
+      '' + ''
         if [ `psql ${cfg.database.name} -c \
                 "select count(*) from pg_class c \
                 join pg_namespace s on s.oid = c.relnamespace \
@@ -541,9 +569,15 @@ in {
         else
           rails db:migrate
         fi
+      '' +  lib.optionalString (!databaseActuallyCreateLocally) ''
+        rm $PGPASSFILE
+        unset PGPASSFILE
       '';
       path = [ cfg.package pkgs.postgresql ];
-      environment = env;
+      environment = env // lib.optionalAttrs (!databaseActuallyCreateLocally) {
+        PGHOST = cfg.database.host;
+        PGUSER = cfg.database.user;
+      };
       serviceConfig = {
         Type = "oneshot";
         EnvironmentFile = [ "/var/lib/mastodon/.secrets_env" ];
diff --git a/nixos/modules/services/web-apps/matomo.nix b/nixos/modules/services/web-apps/matomo.nix
index 117d540ba36b..0435d21ce8a2 100644
--- a/nixos/modules/services/web-apps/matomo.nix
+++ b/nixos/modules/services/web-apps/matomo.nix
@@ -174,7 +174,7 @@ in {
           CURRENT_PACKAGE=$(readlink ${dataDir}/current-package)
           NEW_PACKAGE=${cfg.package}
           if [ "$CURRENT_PACKAGE" != "$NEW_PACKAGE" ]; then
-            # keeping tmp arround between upgrades seems to bork stuff, so delete it
+            # keeping tmp around between upgrades seems to bork stuff, so delete it
             rm -rf ${dataDir}/tmp
           fi
         elif [ -e ${dataDir}/tmp ]; then
diff --git a/nixos/modules/services/web-apps/mattermost.nix b/nixos/modules/services/web-apps/mattermost.nix
index 99042821f5eb..56a53198b3fb 100644
--- a/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixos/modules/services/web-apps/mattermost.nix
@@ -170,7 +170,7 @@ in
         type = types.attrs;
         default = { };
         description = lib.mdDoc ''
-          Addtional configuration options as Nix attribute set in config.json schema.
+          Additional configuration options as Nix attribute set in config.json schema.
         '';
       };
 
diff --git a/nixos/modules/services/web-apps/mediawiki.nix b/nixos/modules/services/web-apps/mediawiki.nix
index e332847f5a28..07f296748629 100644
--- a/nixos/modules/services/web-apps/mediawiki.nix
+++ b/nixos/modules/services/web-apps/mediawiki.nix
@@ -129,7 +129,7 @@ let
 
       ## Set $wgCacheDirectory to a writable directory on the web server
       ## to make your wiki go slightly faster. The directory should not
-      ## be publically accessible from the web.
+      ## be publicly accessible from the web.
       $wgCacheDirectory = "${cacheDir}";
 
       # Site language code, should be one of the list in ./languages/data/Names.php
diff --git a/nixos/modules/services/web-apps/miniflux.nix b/nixos/modules/services/web-apps/miniflux.nix
index 34a108cebd2b..7cc8ce10ffe0 100644
--- a/nixos/modules/services/web-apps/miniflux.nix
+++ b/nixos/modules/services/web-apps/miniflux.nix
@@ -21,6 +21,13 @@ in
     services.miniflux = {
       enable = mkEnableOption (lib.mdDoc "miniflux and creates a local postgres database for it");
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.miniflux;
+        defaultText = literalExpression "pkgs.miniflux";
+        description = lib.mdDoc "Miniflux package to use.";
+      };
+
       config = mkOption {
         type = types.attrsOf types.str;
         example = literalExpression ''
@@ -89,7 +96,7 @@ in
       after = [ "network.target" "postgresql.service" "miniflux-dbsetup.service" ];
 
       serviceConfig = {
-        ExecStart = "${pkgs.miniflux}/bin/miniflux";
+        ExecStart = "${cfg.package}/bin/miniflux";
         User = dbUser;
         DynamicUser = true;
         RuntimeDirectory = "miniflux";
@@ -122,6 +129,6 @@ in
 
       environment = cfg.config;
     };
-    environment.systemPackages = [ pkgs.miniflux ];
+    environment.systemPackages = [ cfg.package ];
   };
 }
diff --git a/nixos/modules/services/web-apps/netbox.nix b/nixos/modules/services/web-apps/netbox.nix
index 800af234e278..e028f16004ef 100644
--- a/nixos/modules/services/web-apps/netbox.nix
+++ b/nixos/modules/services/web-apps/netbox.nix
@@ -135,7 +135,7 @@ in {
       type = types.path;
       default = "";
       description = lib.mdDoc ''
-        Path to the Configuration-File for LDAP-Authentification, will be loaded as `ldap_config.py`.
+        Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
         See the [documentation](https://netbox.readthedocs.io/en/stable/installation/6-ldap/#configuration) for possible options.
       '';
     };
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index da621573f2a2..90801e996817 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -16,7 +16,7 @@ let
         # disable default openssl extension
         (lib.filter (e: e.pname != "php-openssl") enabled)
         # use OpenSSL 1.1 for RC4 Nextcloud encryption if user
-        # has acknowledged the brokeness of the ciphers (RC4).
+        # has acknowledged the brokenness of the ciphers (RC4).
         # TODO: remove when https://github.com/nextcloud/server/issues/32003 is fixed.
         ++ (if cfg.enableBrokenCiphersForSSE then [ cfg.phpPackage.extensions.openssl-legacy ] else [ cfg.phpPackage.extensions.openssl ])
         ++ optional cfg.enableImagemagick imagick
@@ -76,7 +76,7 @@ in {
         * setting `listen.owner` & `listen.group` in the phpfpm-pool to a different value
 
       Further details about this can be found in the `Nextcloud`-section of the NixOS-manual
-      (which can be openend e.g. by running `nixos-help`).
+      (which can be opened e.g. by running `nixos-help`).
     '')
     (mkRemovedOptionModule [ "services" "nextcloud" "disableImagemagick" ] ''
       Use services.nextcloud.nginx.enableImagemagick instead.
@@ -388,7 +388,7 @@ in {
         default = [];
         description = lib.mdDoc ''
           Trusted domains, from which the nextcloud installation will be
-          acessible.  You don't need to add
+          accessible.  You don't need to add
           `services.nextcloud.hostname` here.
         '';
       };
@@ -698,7 +698,7 @@ in {
 
             services.nextcloud.enableBrokenCiphersForSSE = false;
 
-          If you need to use server-side encryption you can ignore this waring.
+          If you need to use server-side encryption you can ignore this warning.
           Otherwise you'd have to disable server-side encryption first in order
           to be able to safely disable this option and get rid of this warning.
           See <https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/encryption_configuration.html#disabling-encryption> on how to achieve this.
@@ -758,7 +758,7 @@ in {
 
         nextcloud-setup = let
           c = cfg.config;
-          writePhpArrary = a: "[${concatMapStringsSep "," (val: ''"${toString val}"'') a}]";
+          writePhpArray = a: "[${concatMapStringsSep "," (val: ''"${toString val}"'') a}]";
           requiresReadSecretFunction = c.dbpassFile != null || c.objectstore.s3.enable;
           objectstoreConfig = let s3 = c.objectstore.s3; in optionalString s3.enable ''
             'objectstore' => [
@@ -838,8 +838,8 @@ in {
                 ''
               }
               'dbtype' => '${c.dbtype}',
-              'trusted_domains' => ${writePhpArrary ([ cfg.hostName ] ++ c.extraTrustedDomains)},
-              'trusted_proxies' => ${writePhpArrary (c.trustedProxies)},
+              'trusted_domains' => ${writePhpArray ([ cfg.hostName ] ++ c.extraTrustedDomains)},
+              'trusted_proxies' => ${writePhpArray (c.trustedProxies)},
               ${optionalString (c.defaultPhoneRegion != null) "'default_phone_region' => '${c.defaultPhoneRegion}',"}
               ${optionalString (nextcloudGreaterOrEqualThan "23") "'profile.enabled' => ${boolToString cfg.globalProfiles},"}
               ${objectstoreConfig}
diff --git a/nixos/modules/services/web-apps/nextcloud.xml b/nixos/modules/services/web-apps/nextcloud.xml
index ca57692fc16a..4207c4008d5b 100644
--- a/nixos/modules/services/web-apps/nextcloud.xml
+++ b/nixos/modules/services/web-apps/nextcloud.xml
@@ -283,7 +283,7 @@
 
   <para>
    If major-releases will be abandoned by upstream, we should check first if those are needed
-   in NixOS for a safe upgrade-path before removing those. In that case we shold keep those
+   in NixOS for a safe upgrade-path before removing those. In that case we should keep those
    packages, but mark them as insecure in an expression like this (in
    <literal>&lt;nixpkgs/pkgs/servers/nextcloud/default.nix&gt;</literal>):
 <programlisting>/* ... */
diff --git a/nixos/modules/services/web-apps/onlyoffice.nix b/nixos/modules/services/web-apps/onlyoffice.nix
index 1478e8da87a1..79ed3e43dd18 100644
--- a/nixos/modules/services/web-apps/onlyoffice.nix
+++ b/nixos/modules/services/web-apps/onlyoffice.nix
@@ -54,7 +54,7 @@ in
     postgresName = mkOption {
       type = types.str;
       default = "onlyoffice";
-      description = lib.mdDoc "The name of databse OnlyOffice should user.";
+      description = lib.mdDoc "The name of database OnlyOffice should user.";
     };
 
     postgresPasswordFile = mkOption {
diff --git a/nixos/modules/services/web-apps/outline.nix b/nixos/modules/services/web-apps/outline.nix
index 701930393f01..b72dd8243bb4 100644
--- a/nixos/modules/services/web-apps/outline.nix
+++ b/nixos/modules/services/web-apps/outline.nix
@@ -465,7 +465,7 @@ in
         options = {
           host = lib.mkOption {
             type = lib.types.str;
-            description = lib.mdDoc "Host name or IP adress of the SMTP server.";
+            description = lib.mdDoc "Host name or IP address of the SMTP server.";
           };
           port = lib.mkOption {
             type = lib.types.port;
diff --git a/nixos/modules/services/web-apps/peering-manager.nix b/nixos/modules/services/web-apps/peering-manager.nix
index 0db2e8e4aeda..666b82621268 100644
--- a/nixos/modules/services/web-apps/peering-manager.nix
+++ b/nixos/modules/services/web-apps/peering-manager.nix
@@ -130,7 +130,7 @@ in {
     ldapConfigPath = mkOption {
       type = types.path;
       description = lib.mdDoc ''
-        Path to the Configuration-File for LDAP-Authentification, will be loaded as `ldap_config.py`.
+        Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`.
         See the [documentation](https://peering-manager.readthedocs.io/en/stable/setup/6-ldap/#configuration) for possible options.
       '';
     };
diff --git a/nixos/modules/services/web-apps/pgpkeyserver-lite.nix b/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
index 0ab39b079315..dd51bacd75ea 100644
--- a/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
+++ b/nixos/modules/services/web-apps/pgpkeyserver-lite.nix
@@ -41,7 +41,7 @@ in
         defaultText = literalExpression "head config.${sksOpt.hkpAddress}";
         type = types.str;
         description = lib.mdDoc ''
-          Wich ip address the sks-keyserver is listening on.
+          Which IP address the sks-keyserver is listening on.
         '';
       };
 
diff --git a/nixos/modules/services/web-apps/snipe-it.nix b/nixos/modules/services/web-apps/snipe-it.nix
index 6da44f1bdf34..314a69a73a87 100644
--- a/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixos/modules/services/web-apps/snipe-it.nix
@@ -381,7 +381,7 @@ in {
     };
 
     systemd.services.snipe-it-setup = {
-      description = "Preperation tasks for snipe-it";
+      description = "Preparation tasks for snipe-it";
       before = [ "phpfpm-snipe-it.service" ];
       after = optional db.createLocally "mysql.service";
       wantedBy = [ "multi-user.target" ];
diff --git a/nixos/modules/services/web-apps/sogo.nix b/nixos/modules/services/web-apps/sogo.nix
index ca1f426623f5..5e5d9472829d 100644
--- a/nixos/modules/services/web-apps/sogo.nix
+++ b/nixos/modules/services/web-apps/sogo.nix
@@ -49,7 +49,7 @@ in {
         Replacement-filepath mapping for sogo.conf.
         Every key is replaced with the contents of the file specified as value.
 
-        In the example, every occurence of LDAP_BINDPW will be replaced with the text of the
+        In the example, every occurrence of LDAP_BINDPW will be replaced with the text of the
         specified file.
       '';
       type = attrsOf str;
diff --git a/nixos/modules/services/web-apps/wiki-js.nix b/nixos/modules/services/web-apps/wiki-js.nix
index c5627a28b849..b6e5b4594f1d 100644
--- a/nixos/modules/services/web-apps/wiki-js.nix
+++ b/nixos/modules/services/web-apps/wiki-js.nix
@@ -17,7 +17,7 @@ in {
       default = null;
       example = "/root/wiki-js.env";
       description = lib.mdDoc ''
-        Environment fiel to inject e.g. secrets into the configuration.
+        Environment file to inject e.g. secrets into the configuration.
       '';
     };
 
diff --git a/nixos/modules/services/web-servers/agate.nix b/nixos/modules/services/web-servers/agate.nix
index 9d635c64a44e..a0c8a8c94ee5 100644
--- a/nixos/modules/services/web-servers/agate.nix
+++ b/nixos/modules/services/web-servers/agate.nix
@@ -43,7 +43,7 @@ in
         type = types.listOf types.str;
         description = lib.mdDoc ''
           Domain name of this Gemini server, enables checking hostname and port
-          in requests. (multiple occurences means basic vhosts)
+          in requests. (multiple occurrences means basic vhosts)
         '';
       };
 
diff --git a/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix b/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
index 0d60d533c994..7b87f9ef4bde 100644
--- a/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/vhost-options.nix
@@ -61,7 +61,7 @@ in
 
       description = lib.mdDoc ''
         Listen addresses for this virtual host.
-        Compared to `listen` this only sets the addreses
+        Compared to `listen` this only sets the addresses
         and the ports are chosen automatically.
       '';
       default = [ "*" ];
diff --git a/nixos/modules/services/web-servers/keter/default.nix b/nixos/modules/services/web-servers/keter/default.nix
index 42ab6640b4c9..9adbe65de69f 100644
--- a/nixos/modules/services/web-servers/keter/default.nix
+++ b/nixos/modules/services/web-servers/keter/default.nix
@@ -140,7 +140,7 @@ Keep an old app running and swap the ports when the new one is booted.
 
       # On deploy this will load our app, by moving it into the incoming dir
       # If the bundle content changes, this will run again.
-      # Because the bundle content contains the nix path to the exectuable,
+      # Because the bundle content contains the nix path to the executable,
       # we inherit nix based cache busting.
       systemd.services.load-keter-bundle = {
         description = "load keter bundle into incoming folder";
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 85c76ed59d66..953f31632934 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -241,7 +241,7 @@ let
 
   configPath = if cfg.enableReload
     then "/etc/nginx/nginx.conf"
-    else configFile;
+    else finalConfigFile;
 
   execCommand = "${cfg.package}/bin/nginx -c '${configPath}'";
 
@@ -318,7 +318,9 @@ let
           ${acmeLocation}
           ${optionalString (vhost.root != null) "root ${vhost.root};"}
           ${optionalString (vhost.globalRedirect != null) ''
-            return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
+            location / {
+              return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri;
+            }
           ''}
           ${optionalString hasSSL ''
             ssl_certificate ${vhost.sslCertificate};
@@ -391,6 +393,38 @@ let
   );
 
   mkCertOwnershipAssertion = import ../../../security/acme/mk-cert-ownership-assertion.nix;
+
+  snakeOilCert = pkgs.runCommand "nginx-config-validate-cert" { nativeBuildInputs = [ pkgs.openssl.bin ]; } ''
+    mkdir $out
+    openssl genrsa -des3 -passout pass:xxxxx -out server.pass.key 2048
+    openssl rsa -passin pass:xxxxx -in server.pass.key -out $out/server.key
+    openssl req -new -key $out/server.key -out server.csr \
+    -subj "/C=UK/ST=Warwickshire/L=Leamington/O=OrgName/OU=IT Department/CN=example.com"
+    openssl x509 -req -days 1 -in server.csr -signkey $out/server.key -out $out/server.crt
+  '';
+  validatedConfigFile = pkgs.runCommand "validated-nginx.conf" { nativeBuildInputs = [ cfg.package ]; } ''
+    # nginx absolutely wants to read the certificates even when told to only validate config, so let's provide fake certs
+    sed ${configFile} \
+    -e "s|ssl_certificate .*;|ssl_certificate ${snakeOilCert}/server.crt;|g" \
+    -e "s|ssl_trusted_certificate .*;|ssl_trusted_certificate ${snakeOilCert}/server.crt;|g" \
+    -e "s|ssl_certificate_key .*;|ssl_certificate_key ${snakeOilCert}/server.key;|g" \
+    > conf
+
+    LD_PRELOAD=${pkgs.libredirect}/lib/libredirect.so \
+      NIX_REDIRECTS="/etc/resolv.conf=/dev/null" \
+      nginx -t -c $(readlink -f ./conf) > out 2>&1 || true
+    if ! grep -q "syntax is ok" out; then
+      echo nginx config validation failed.
+      echo config was ${configFile}.
+      echo 'in case of false positive, set `services.nginx.validateConfig` to false.'
+      echo nginx output:
+      cat out
+      exit 1
+    fi
+    cp ${configFile} $out
+  '';
+
+  finalConfigFile = if cfg.validateConfig then validatedConfigFile else configFile;
 in
 
 {
@@ -489,6 +523,15 @@ in
         '';
       };
 
+      validateConfig = mkOption {
+        default = pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform;
+        defaultText = literalExpression "pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform";
+        type = types.bool;
+        description = lib.mdDoc ''
+          Validate the generated nginx config at build time. The check is not very robust and can be disabled in case of false positives. This is notably the case when cross-compiling or when using `include` with files outside of the store.
+        '';
+      };
+
       additionalModules = mkOption {
         default = [];
         type = types.listOf (types.attrsOf types.anything);
@@ -1027,7 +1070,7 @@ in
     };
 
     environment.etc."nginx/nginx.conf" = mkIf cfg.enableReload {
-      source = configFile;
+      source = finalConfigFile;
     };
 
     # This service waits for all certificates to be available
@@ -1046,7 +1089,7 @@ in
       # certs are updated _after_ config has been reloaded.
       before = sslTargets;
       after = sslServices;
-      restartTriggers = optionals (cfg.enableReload) [ configFile ];
+      restartTriggers = optionals (cfg.enableReload) [ finalConfigFile ];
       # Block reloading if not all certs exist yet.
       # Happens when config changes add new vhosts/certs.
       unitConfig.ConditionPathExists = optionals (sslServices != []) (map (certName: certs.${certName}.directory + "/fullchain.pem") dependentCertNames);
diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix
index 2bdc046c0d8f..089decb5f433 100644
--- a/nixos/modules/services/web-servers/nginx/vhost-options.nix
+++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix
@@ -54,8 +54,8 @@ with lib;
 
       description = lib.mdDoc ''
         Listen addresses for this virtual host.
-        Compared to `listen` this only sets the addreses
-        and the ports are choosen automatically.
+        Compared to `listen` this only sets the addresses
+        and the ports are chosen automatically.
 
         Note: This option overrides `enableIPv6`
       '';
diff --git a/nixos/modules/services/web-servers/ttyd.nix b/nixos/modules/services/web-servers/ttyd.nix
index affd5bbeea3c..e0a8b5179e06 100644
--- a/nixos/modules/services/web-servers/ttyd.nix
+++ b/nixos/modules/services/web-servers/ttyd.nix
@@ -163,7 +163,7 @@ in
     assertions =
       [ { assertion = cfg.enableSSL
             -> cfg.certFile != null && cfg.keyFile != null && cfg.caFile != null;
-          message = "SSL is enabled for ttyd, but no certFile, keyFile or caFile has been specefied."; }
+          message = "SSL is enabled for ttyd, but no certFile, keyFile or caFile has been specified."; }
         { assertion = ! (cfg.interface != null && cfg.socket != null);
           message = "Cannot set both interface and socket for ttyd."; }
         { assertion = (cfg.username != null) == (cfg.passwordFile != null);
diff --git a/nixos/modules/services/web-servers/zope2.nix b/nixos/modules/services/web-servers/zope2.nix
index a80fe882f1a7..a17fe6bc2082 100644
--- a/nixos/modules/services/web-servers/zope2.nix
+++ b/nixos/modules/services/web-servers/zope2.nix
@@ -95,7 +95,7 @@ in
           };
         }
       '';
-      description = lib.mdDoc "zope2 instances to be created automaticaly by the system.";
+      description = lib.mdDoc "zope2 instances to be created automatically by the system.";
     };
   };
 
diff --git a/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixos/modules/services/x11/desktop-managers/plasma5.nix
index a6ab3053087c..9fcb408c287d 100644
--- a/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -384,6 +384,11 @@ in
         ++ lib.optionals config.services.samba.enable [ kdenetwork-filesharing pkgs.samba ]
         ++ lib.optional config.services.xserver.wacom.enable pkgs.wacomtablet;
 
+      # Extra services for D-Bus activation
+      services.dbus.packages = [
+        plasma5.kactivitymanagerd
+      ];
+
       environment.pathsToLink = [
         # FIXME: modules should link subdirs of `/share` rather than relying on this
         "/share"
@@ -446,6 +451,9 @@ in
 
       xdg.portal.enable = true;
       xdg.portal.extraPortals = [ plasma5.xdg-desktop-portal-kde ];
+      # xdg-desktop-portal-kde expects PipeWire to be running.
+      # This does not, by default, replace PulseAudio.
+      services.pipewire.enable = mkDefault true;
 
       # Update the start menu for each user that is currently logged in
       system.userActivationScripts.plasmaSetup = activationScript;
@@ -577,6 +585,8 @@ in
       hardware.bluetooth.enable = true;
       hardware.pulseaudio.enable = true;
       networking.networkmanager.enable = true;
+      # Required for autorotate
+      hardware.sensor.iio.enable = lib.mkDefault true;
 
       # Recommendations can be found here:
       #  - https://invent.kde.org/plasma-mobile/plasma-phone-settings/-/tree/master/etc/xdg
diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix
index e86a18ff618e..a3f03d7a19a6 100644
--- a/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixos/modules/services/x11/display-managers/sddm.nix
@@ -123,7 +123,7 @@ in
           };
         };
         description = lib.mdDoc ''
-          Extra settings merged in and overwritting defaults in sddm.conf.
+          Extra settings merged in and overwriting defaults in sddm.conf.
         '';
       };
 
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index 0d30b9b5e68d..f77036360e02 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -171,7 +171,7 @@ let cfg = config.services.xserver.libinput;
           lib.mdDoc ''
             Enables or disables drag lock during tapping behavior. When enabled, a finger up during tap-
             and-drag will not immediately release the button. If the finger is set down again within the
-            timeout, the draging process continues.
+            timeout, the dragging process continues.
           '';
       };
 
diff --git a/nixos/modules/services/x11/imwheel.nix b/nixos/modules/services/x11/imwheel.nix
index 03cbdbfb09a4..133e64c65cdd 100644
--- a/nixos/modules/services/x11/imwheel.nix
+++ b/nixos/modules/services/x11/imwheel.nix
@@ -37,8 +37,8 @@ in
             Window class translation rules.
             /etc/X11/imwheelrc is generated based on this config
             which means this config is global for all users.
-            See [offical man pages](http://imwheel.sourceforge.net/imwheel.1.html)
-            for more informations.
+            See [official man pages](http://imwheel.sourceforge.net/imwheel.1.html)
+            for more information.
           '';
         };
       };
diff --git a/nixos/modules/services/x11/window-managers/katriawm.nix b/nixos/modules/services/x11/window-managers/katriawm.nix
new file mode 100644
index 000000000000..106631792ff4
--- /dev/null
+++ b/nixos/modules/services/x11/window-managers/katriawm.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mdDoc mkEnableOption mkIf mkPackageOption singleton;
+  cfg = config.services.xserver.windowManager.katriawm;
+in
+{
+  ###### interface
+  options = {
+    services.xserver.windowManager.katriawm = {
+      enable = mkEnableOption (mdDoc "katriawm");
+      package = mkPackageOption pkgs "katriawm" {};
+    };
+  };
+
+  ###### implementation
+  config = mkIf cfg.enable {
+    services.xserver.windowManager.session = singleton {
+      name = "katriawm";
+      start = ''
+        ${cfg.package}/bin/katriawm &
+        waitPID=$!
+      '';
+    };
+    environment.systemPackages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/services/x11/xautolock.nix b/nixos/modules/services/x11/xautolock.nix
index 8200057660e5..5b8b748a086b 100644
--- a/nixos/modules/services/x11/xautolock.nix
+++ b/nixos/modules/services/x11/xautolock.nix
@@ -71,7 +71,7 @@ in
           type = types.nullOr types.str;
 
           description = lib.mdDoc ''
-            The script to use when nothing has happend for as long as {option}`killtime`
+            The script to use when nothing has happened for as long as {option}`killtime`
           '';
         };
 
diff --git a/nixos/modules/system/activation/bootspec.cue b/nixos/modules/system/activation/bootspec.cue
new file mode 100644
index 000000000000..3fc9ca381df7
--- /dev/null
+++ b/nixos/modules/system/activation/bootspec.cue
@@ -0,0 +1,17 @@
+#V1: {
+	init:           string
+	initrd?:        string
+	initrdSecrets?: string
+	kernel:         string
+	kernelParams: [...string]
+	label:    string
+	toplevel: string
+	specialisation?: {
+		[=~"^"]: #V1
+	}
+	extensions?: {...}
+}
+
+Document: {
+	v1: #V1
+}
diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix
new file mode 100644
index 000000000000..da76bf9084af
--- /dev/null
+++ b/nixos/modules/system/activation/bootspec.nix
@@ -0,0 +1,124 @@
+# Note that these schemas are defined by RFC-0125.
+# This document is considered a stable API, and is depended upon by external tooling.
+# Changes to the structure of the document, or the semantics of the values should go through an RFC.
+#
+# See: https://github.com/NixOS/rfcs/pull/125
+{ config
+, pkgs
+, lib
+, ...
+}:
+let
+  cfg = config.boot.bootspec;
+  children = lib.mapAttrs (childName: childConfig: childConfig.configuration.system.build.toplevel) config.specialisation;
+  schemas = {
+    v1 = rec {
+      filename = "boot.json";
+      json =
+        pkgs.writeText filename
+          (builtins.toJSON
+          {
+            v1 = {
+              kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
+              kernelParams = config.boot.kernelParams;
+              initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
+              initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets";
+              label = "NixOS ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})";
+
+              inherit (cfg) extensions;
+            };
+          });
+
+      generator =
+        let
+          # NOTE: Be careful to not introduce excess newlines at the end of the
+          # injectors, as that may affect the pipes and redirects.
+
+          # Inject toplevel and init into the bootspec.
+          # This can only be done here because we *cannot* depend on $out
+          # referring to the toplevel, except by living in the toplevel itself.
+          toplevelInjector = lib.escapeShellArgs [
+            "${pkgs.jq}/bin/jq"
+            ''
+              .v1.toplevel = $toplevel |
+              .v1.init = $init
+            ''
+            "--sort-keys"
+            "--arg" "toplevel" "${placeholder "out"}"
+            "--arg" "init" "${placeholder "out"}/init"
+          ] + " < ${json}";
+
+          # We slurp all specialisations and inject them as values, such that
+          # `.specialisations.${name}` embeds the specialisation's bootspec
+          # document.
+          specialisationInjector =
+            let
+              specialisationLoader = (lib.mapAttrsToList
+                (childName: childToplevel: lib.escapeShellArgs [ "--slurpfile" childName "${childToplevel}/bootspec/${filename}" ])
+                children);
+            in
+            lib.escapeShellArgs [
+              "${pkgs.jq}/bin/jq"
+              "--sort-keys"
+              ".v1.specialisation = ($ARGS.named | map_values(. | first | .v1))"
+            ] + " ${lib.concatStringsSep " " specialisationLoader}";
+        in
+        ''
+          mkdir -p $out/bootspec
+
+          ${toplevelInjector} | ${specialisationInjector} > $out/bootspec/${filename}
+        '';
+
+      validator = pkgs.writeCueValidator ./bootspec.cue {
+        document = "Document"; # Universal validator for any version as long the schema is correctly set.
+      };
+    };
+  };
+in
+{
+  options.boot.bootspec = {
+    enable = lib.mkEnableOption (lib.mdDoc "Enable generation of RFC-0125 bootspec in $system/bootspec, e.g. /run/current-system/bootspec");
+
+    extensions = lib.mkOption {
+      type = lib.types.attrs;
+      default = { };
+      description = lib.mdDoc ''
+        User-defined data that extends the bootspec document.
+
+        To reduce incompatibility and prevent names from clashing
+        between applications, it is **highly recommended** to use a
+        unique namespace for your extensions.
+      '';
+    };
+
+    # This will be run as a part of the `systemBuilder` in ./top-level.nix. This
+    # means `$out` points to the output of `config.system.build.toplevel` and can
+    # be used for a variety of things (though, for now, it's only used to report
+    # the path of the `toplevel` itself and the `init` executable).
+    writer = lib.mkOption {
+      internal = true;
+      default = schemas.v1.generator;
+    };
+
+    validator = lib.mkOption {
+      internal = true;
+      default = schemas.v1.validator;
+    };
+
+    filename = lib.mkOption {
+      internal = true;
+      default = schemas.v1.filename;
+    };
+  };
+
+  config = lib.mkIf (cfg.enable) {
+    warnings = [
+      ''RFC-0125 is not merged yet, this is a feature preview of bootspec.
+        The schema is not definitive and features are not guaranteed to be stable until RFC-0125 is merged.
+        See:
+        - https://github.com/NixOS/nixpkgs/pull/172237 to track merge status in nixpkgs.
+        - https://github.com/NixOS/rfcs/pull/125 to track RFC status.
+      ''
+    ];
+  };
+}
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 64e97b510b06..0bb3628ceed9 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -79,6 +79,11 @@ let
 
       echo -n "$extraDependencies" > $out/extra-dependencies
 
+      ${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) ''
+        ${config.boot.bootspec.writer}
+        ${config.boot.bootspec.validator} "$out/bootspec/${config.boot.bootspec.filename}"
+      ''}
+
       ${config.system.extraSystemBuilderCmds}
     '';
 
diff --git a/nixos/modules/system/boot/loader/external/external.md b/nixos/modules/system/boot/loader/external/external.md
new file mode 100644
index 000000000000..ba1dfd4d9b9a
--- /dev/null
+++ b/nixos/modules/system/boot/loader/external/external.md
@@ -0,0 +1,26 @@
+# External Bootloader Backends {#sec-bootloader-external}
+
+NixOS has support for several bootloader backends by default: systemd-boot, grub, uboot, etc.
+The built-in bootloader backend support is generic and supports most use cases.
+Some users may prefer to create advanced workflows around managing the bootloader and bootable entries.
+
+You can replace the built-in bootloader support with your own tooling using the "external" bootloader option.
+
+Imagine you have created a new package called FooBoot.
+FooBoot provides a program at `${pkgs.fooboot}/bin/fooboot-install` which takes the system closure's path as its only argument and configures the system's bootloader.
+
+You can enable FooBoot like this:
+
+```nix
+{ pkgs, ... }: {
+  boot.loader.external = {
+    enable = true;
+    installHook = "${pkgs.fooboot}/bin/fooboot-install";
+  };
+}
+```
+
+## Developing Custom Bootloader Backends
+
+Bootloaders should use [RFC-0125](https://github.com/NixOS/rfcs/pull/125)'s Bootspec format and synthesis tools to identify the key properties for bootable system generations.
+
diff --git a/nixos/modules/system/boot/loader/external/external.nix b/nixos/modules/system/boot/loader/external/external.nix
new file mode 100644
index 000000000000..5cf478e6c83c
--- /dev/null
+++ b/nixos/modules/system/boot/loader/external/external.nix
@@ -0,0 +1,38 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.boot.loader.external;
+in
+{
+  meta = {
+    maintainers = with maintainers; [ cole-h grahamc raitobezarius ];
+    # Don't edit the docbook xml directly, edit the md and generate it:
+    # `pandoc external.md -t docbook --top-level-division=chapter --extract-media=media -f markdown+smart > external.xml`
+    doc = ./external.xml;
+  };
+
+  options.boot.loader.external = {
+    enable = mkEnableOption (lib.mdDoc "use an external tool to install your bootloader");
+
+    installHook = mkOption {
+      type = with types; path;
+      description = lib.mdDoc ''
+        The full path to a program of your choosing which performs the bootloader installation process.
+
+        The program will be called with an argument pointing to the output of the system's toplevel.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    boot.loader = {
+      grub.enable = mkDefault false;
+      systemd-boot.enable = mkDefault false;
+      supportsInitrdSecrets = mkDefault false;
+    };
+
+    system.build.installBootLoader = cfg.installHook;
+  };
+}
diff --git a/nixos/modules/system/boot/loader/external/external.xml b/nixos/modules/system/boot/loader/external/external.xml
new file mode 100644
index 000000000000..39ab2156bc8c
--- /dev/null
+++ b/nixos/modules/system/boot/loader/external/external.xml
@@ -0,0 +1,41 @@
+<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-bootloader-external">
+  <title>External Bootloader Backends</title>
+  <para>
+    NixOS has support for several bootloader backends by default:
+    systemd-boot, grub, uboot, etc. The built-in bootloader backend
+    support is generic and supports most use cases. Some users may
+    prefer to create advanced workflows around managing the bootloader
+    and bootable entries.
+  </para>
+  <para>
+    You can replace the built-in bootloader support with your own
+    tooling using the <quote>external</quote> bootloader option.
+  </para>
+  <para>
+    Imagine you have created a new package called FooBoot. FooBoot
+    provides a program at
+    <literal>${pkgs.fooboot}/bin/fooboot-install</literal> which takes
+    the system closure’s path as its only argument and configures the
+    system’s bootloader.
+  </para>
+  <para>
+    You can enable FooBoot like this:
+  </para>
+  <programlisting language="nix">
+{ pkgs, ... }: {
+  boot.loader.external = {
+    enable = true;
+    installHook = &quot;${pkgs.fooboot}/bin/fooboot-install&quot;;
+  };
+}
+</programlisting>
+  <section xml:id="developing-custom-bootloader-backends">
+    <title>Developing Custom Bootloader Backends</title>
+    <para>
+      Bootloaders should use
+      <link xlink:href="https://github.com/NixOS/rfcs/pull/125">RFC-0125</link>’s
+      Bootspec format and synthesis tools to identify the key properties
+      for bootable system generations.
+    </para>
+  </section>
+</chapter>
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index a67b10608aa7..1d266b5a37d5 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -38,7 +38,7 @@ let
   grubConfig = args:
     let
       efiSysMountPoint = if args.efiSysMountPoint == null then args.path else args.efiSysMountPoint;
-      efiSysMountPoint' = replaceChars [ "/" ] [ "-" ] efiSysMountPoint;
+      efiSysMountPoint' = replaceStrings [ "/" ] [ "-" ] efiSysMountPoint;
     in
     pkgs.writeText "grub-config.xml" (builtins.toXML
     { splashImage = f cfg.splashImage;
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index 28c76fb169f1..95dcdfd7fbe1 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -205,8 +205,9 @@ let
       # Copy ld manually since it isn't detected correctly
       cp -pv ${pkgs.stdenv.cc.libc.out}/lib/ld*.so.? $out/lib
 
-      # Copy all of the needed libraries
-      find $out/bin $out/lib -type f | while read BIN; do
+      # Copy all of the needed libraries in a consistent order so
+      # duplicates are resolved the same way.
+      find $out/bin $out/lib -type f | sort | while read BIN; do
         echo "Copying libs for executable $BIN"
         for LIB in $(${findLibs}/bin/find-libs $BIN); do
           TGT="$out/lib/$(basename $LIB)"
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index 4b4f4cc801ab..0f14f2b501c2 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -503,6 +503,10 @@ in
           assertion = !cfgZfs.forceImportAll || cfgZfs.forceImportRoot;
           message = "If you enable boot.zfs.forceImportAll, you must also enable boot.zfs.forceImportRoot";
         }
+        {
+          assertion = cfgZfs.allowHibernation -> !cfgZfs.forceImportRoot && !cfgZfs.forceImportAll;
+          message = "boot.zfs.allowHibernation while force importing is enabled will cause data corruption";
+        }
       ];
 
       boot = {
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 4c211b747614..4d47a56ccca3 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1377,12 +1377,12 @@ in
       # networkmanager falls back to "/proc/sys/net/ipv6/conf/default/use_tempaddr"
       "net.ipv6.conf.default.use_tempaddr" = tempaddrValues.${cfg.tempAddresses}.sysctl;
     } // listToAttrs (forEach interfaces
-        (i: nameValuePair "net.ipv4.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" i.proxyARP))
+        (i: nameValuePair "net.ipv4.conf.${replaceStrings ["."] ["/"] i.name}.proxy_arp" i.proxyARP))
       // listToAttrs (forEach interfaces
         (i: let
           opt = i.tempAddress;
           val = tempaddrValues.${opt}.sysctl;
-         in nameValuePair "net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr" val));
+         in nameValuePair "net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr" val));
 
     security.wrappers = {
       ping = {
@@ -1495,7 +1495,7 @@ in
           in
           ''
             # override to ${msg} for ${i.name}
-            ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceChars ["."] ["/"] i.name}.use_tempaddr=${val}"
+            ACTION=="add", SUBSYSTEM=="net", RUN+="${pkgs.procps}/bin/sysctl net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr=${val}"
           '') (filter (i: i.tempAddress != cfg.tempAddresses) interfaces);
       })
     ] ++ lib.optional (cfg.wlanInterfaces != {})
diff --git a/nixos/modules/virtualisation/appvm.nix b/nixos/modules/virtualisation/appvm.nix
index b23b321095cf..9fe2995d37a0 100644
--- a/nixos/modules/virtualisation/appvm.nix
+++ b/nixos/modules/virtualisation/appvm.nix
@@ -20,7 +20,7 @@ in {
       user = mkOption {
         type = types.str;
         description = lib.mdDoc ''
-          AppVM user login. Currenly only AppVMs are supported for a single user only.
+          AppVM user login. Currently only AppVMs are supported for a single user only.
         '';
       };
     };
diff --git a/nixos/modules/virtualisation/hyperv-guest.nix b/nixos/modules/virtualisation/hyperv-guest.nix
index 7c73e32be695..cba4f92abe82 100644
--- a/nixos/modules/virtualisation/hyperv-guest.nix
+++ b/nixos/modules/virtualisation/hyperv-guest.nix
@@ -56,8 +56,6 @@ in {
     systemd = {
       packages = [ config.boot.kernelPackages.hyperv-daemons.lib ];
 
-      services.hv-vss.unitConfig.ConditionPathExists = [ "/dev/vmbus/hv_vss" ];
-
       targets.hyperv-daemons = {
         wantedBy = [ "multi-user.target" ];
       };
diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix
index a71b69341051..4963d9f3f9e4 100644
--- a/nixos/modules/virtualisation/lxc-container.nix
+++ b/nixos/modules/virtualisation/lxc-container.nix
@@ -51,8 +51,8 @@ in
 {
   imports = [
     ../installer/cd-dvd/channel.nix
-    ../profiles/minimal.nix
     ../profiles/clone-config.nix
+    ../profiles/minimal.nix
   ];
 
   options = {
@@ -199,5 +199,11 @@ in
     # Containers should be light-weight, so start sshd on demand.
     services.openssh.enable = mkDefault true;
     services.openssh.startWhenNeeded = mkDefault true;
+
+    # As this is intended as a standalone image, undo some of the minimal profile stuff
+    environment.noXlibs = false;
+    documentation.enable = true;
+    documentation.nixos.enable = true;
+    services.logrotate.enable = true;
   };
 }
diff --git a/nixos/modules/virtualisation/proxmox-image.nix b/nixos/modules/virtualisation/proxmox-image.nix
index 42c52c12edf0..6a4220fd265c 100644
--- a/nixos/modules/virtualisation/proxmox-image.nix
+++ b/nixos/modules/virtualisation/proxmox-image.nix
@@ -28,7 +28,7 @@ with lib;
         default = "local-lvm:vm-9999-disk-0";
         example = "ceph:vm-123-disk-0";
         description = lib.mdDoc ''
-          Configuration for the default virtio disk. It can be used as a cue for PVE to autodetect the target sotrage.
+          Configuration for the default virtio disk. It can be used as a cue for PVE to autodetect the target storage.
           This parameter is required by PVE even if it isn't used.
         '';
       };
diff --git a/nixos/modules/virtualisation/vmware-host.nix b/nixos/modules/virtualisation/vmware-host.nix
index e1d695640be1..4b2dc28aeac7 100644
--- a/nixos/modules/virtualisation/vmware-host.nix
+++ b/nixos/modules/virtualisation/vmware-host.nix
@@ -120,7 +120,7 @@ in
     # Services
 
     systemd.services."vmware-authdlauncher" = {
-      description = "VMware Authentification Daemon";
+      description = "VMware Authentication Daemon";
       serviceConfig = {
         Type = "forking";
         ExecStart = [ "${cfg.package}/bin/vmware-authdlauncher" ];
diff --git a/nixos/tests/acme.nix b/nixos/tests/acme.nix
index d540bc6ec31b..64bc99f6d325 100644
--- a/nixos/tests/acme.nix
+++ b/nixos/tests/acme.nix
@@ -277,7 +277,7 @@ in {
           };
         };
 
-      # Test compatiblity with Caddy
+      # Test compatibility with Caddy
       # It only supports useACMEHost, hence not using mkServerConfigs
       } // (let
         baseCaddyConfig = { nodes, config, ... }: {
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index b372ae20480b..1956d3c9e8c7 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -80,6 +80,7 @@ in {
   apparmor = handleTest ./apparmor.nix {};
   atd = handleTest ./atd.nix {};
   atop = handleTest ./atop.nix {};
+  atuin = handleTest ./atuin.nix {};
   auth-mysql = handleTest ./auth-mysql.nix {};
   avahi = handleTest ./avahi.nix {};
   avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
@@ -365,7 +366,7 @@ in {
   mailhog = handleTest ./mailhog.nix {};
   man = handleTest ./man.nix {};
   mariadb-galera = handleTest ./mysql/mariadb-galera.nix {};
-  mastodon = handleTestOn ["x86_64-linux" "i686-linux" "aarch64-linux"] ./web-apps/mastodon.nix {};
+  mastodon = discoverTests (import ./web-apps/mastodon { inherit handleTestOn; });
   matomo = handleTest ./matomo.nix {};
   matrix-appservice-irc = handleTest ./matrix/appservice-irc.nix {};
   matrix-conduit = handleTest ./matrix/conduit.nix {};
@@ -434,6 +435,7 @@ in {
   nginx = handleTest ./nginx.nix {};
   nginx-auth = handleTest ./nginx-auth.nix {};
   nginx-etag = handleTest ./nginx-etag.nix {};
+  nginx-globalredirect = handleTest ./nginx-globalredirect.nix {};
   nginx-http3 = handleTest ./nginx-http3.nix {};
   nginx-modsecurity = handleTest ./nginx-modsecurity.nix {};
   nginx-njs = handleTest ./nginx-njs.nix {};
diff --git a/nixos/tests/atuin.nix b/nixos/tests/atuin.nix
new file mode 100644
index 000000000000..85213d1e53ea
--- /dev/null
+++ b/nixos/tests/atuin.nix
@@ -0,0 +1,65 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }:
+
+let
+  testPort = 8888;
+  testUser = "testerman";
+  testPass = "password";
+  testEmail = "test.testerman@test.com";
+in
+with lib;
+{
+  name = "atuin";
+  meta.maintainers = with pkgs.lib.maintainers; [ devusb ];
+
+  nodes = {
+    server =
+      { ... }:
+      {
+        services.atuin = {
+          enable = true;
+          port = testPort;
+          host = "0.0.0.0";
+          openFirewall = true;
+          openRegistration = true;
+        };
+      };
+
+    client =
+      { ... }:
+      { };
+
+  };
+
+  testScript = with pkgs; ''
+    start_all()
+
+    # wait for atuin server startup
+    server.wait_for_unit("atuin.service")
+    server.wait_for_open_port(${toString testPort})
+
+    # configure atuin client on server node
+    server.execute("mkdir -p ~/.config/atuin")
+    server.execute("echo 'sync_address = \"http://localhost:${toString testPort}\"' > ~/.config/atuin/config.toml")
+
+    # register with atuin server on server node
+    server.succeed("${atuin}/bin/atuin register -u ${testUser} -p ${testPass} -e ${testEmail}")
+    _, key = server.execute("${atuin}/bin/atuin key")
+
+    # store test record in atuin server and sync
+    server.succeed("ATUIN_SESSION=$(${atuin}/bin/atuin uuid) ${atuin}/bin/atuin history start 'shazbot'")
+    server.succeed("${atuin}/bin/atuin sync")
+
+    # configure atuin client on client node
+    client.execute("mkdir -p ~/.config/atuin")
+    client.execute("echo 'sync_address = \"http://server:${toString testPort}\"' > ~/.config/atuin/config.toml")
+
+    # log in to atuin server on client node
+    client.succeed(f"${atuin}/bin/atuin login -u ${testUser} -p ${testPass} -k {key}")
+
+    # pull records from atuin server
+    client.succeed("${atuin}/bin/atuin sync -f")
+
+    # check for test record
+    client.succeed("ATUIN_SESSION=$(${atuin}/bin/atuin uuid) ${atuin}/bin/atuin history list | grep shazbot")
+  '';
+})
diff --git a/nixos/tests/bootspec.nix b/nixos/tests/bootspec.nix
new file mode 100644
index 000000000000..13360bb1eaa2
--- /dev/null
+++ b/nixos/tests/bootspec.nix
@@ -0,0 +1,144 @@
+{ system ? builtins.currentSystem,
+  config ? {},
+  pkgs ? import ../.. { inherit system config; }
+}:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+with pkgs.lib;
+
+let
+  baseline = {
+    virtualisation.useBootLoader = true;
+  };
+  grub = {
+    boot.loader.grub.enable = true;
+  };
+  systemd-boot = {
+    boot.loader.systemd-boot.enable = true;
+  };
+  uefi = {
+    virtualisation.useEFIBoot = true;
+    boot.loader.efi.canTouchEfiVariables = true;
+    boot.loader.grub.efiSupport = true;
+    environment.systemPackages = [ pkgs.efibootmgr ];
+  };
+  standard = {
+    boot.bootspec.enable = true;
+
+    imports = [
+      baseline
+      systemd-boot
+      uefi
+    ];
+  };
+in
+{
+  basic = makeTest {
+    name = "systemd-boot-with-bootspec";
+    meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+
+    nodes.machine = standard;
+
+    testScript = ''
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      machine.succeed("test -e /run/current-system/bootspec/boot.json")
+    '';
+  };
+
+  grub = makeTest {
+    name = "grub-with-bootspec";
+    meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+
+    nodes.machine = {
+      boot.bootspec.enable = true;
+
+      imports = [
+        baseline
+        grub
+        uefi
+      ];
+    };
+
+    testScript = ''
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      machine.succeed("test -e /run/current-system/bootspec/boot.json")
+    '';
+  };
+
+  legacy-boot = makeTest {
+    name = "legacy-boot-with-bootspec";
+    meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+
+    nodes.machine = {
+      boot.bootspec.enable = true;
+
+      imports = [
+        baseline
+        grub
+      ];
+    };
+
+    testScript = ''
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      machine.succeed("test -e /run/current-system/bootspec/boot.json")
+    '';
+  };
+
+  # Check that specialisations create corresponding entries in bootspec.
+  specialisation = makeTest {
+    name = "bootspec-with-specialisation";
+    meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+
+    nodes.machine = {
+      imports = [ standard ];
+      environment.systemPackages = [ pkgs.jq ];
+      specialisation.something.configuration = {};
+    };
+
+    testScript = ''
+      import json
+
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      machine.succeed("test -e /run/current-system/bootspec/boot.json")
+      machine.succeed("test -e /run/current-system/specialisation/something/bootspec/boot.json")
+
+      sp_in_parent = json.loads(machine.succeed("jq -r '.v1.specialisation.something' /run/current-system/bootspec/boot.json"))
+      sp_in_fs = json.loads(machine.succeed("cat /run/current-system/specialisation/something/bootspec/boot.json"))
+
+      assert sp_in_parent == sp_in_fs['v1'], "Bootspecs of the same specialisation are different!"
+    '';
+  };
+
+  # Check that extensions are propagated.
+  extensions = makeTest {
+    name = "bootspec-with-extensions";
+    meta.maintainers = with pkgs.lib.maintainers; [ raitobezarius ];
+
+    nodes.machine = { config, ... }: {
+      imports = [ standard ];
+      environment.systemPackages = [ pkgs.jq ];
+      boot.bootspec.extensions = {
+        osRelease = config.environment.etc."os-release".source;
+      };
+    };
+
+    testScript = ''
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      current_os_release = machine.succeed("cat /etc/os-release")
+      bootspec_os_release = machine.succeed("cat $(jq -r '.v1.extensions.osRelease' /run/current-system/bootspec/boot.json)")
+
+      assert current_os_release == bootspec_os_release, "Filename referenced by extension has unexpected contents"
+    '';
+  };
+
+}
diff --git a/nixos/tests/borgbackup.nix b/nixos/tests/borgbackup.nix
index d3cd6c66bfeb..9afe4d537da4 100644
--- a/nixos/tests/borgbackup.nix
+++ b/nixos/tests/borgbackup.nix
@@ -99,6 +99,18 @@ in {
           environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
         };
 
+        sleepInhibited = {
+          inhibitsSleep = true;
+          # Blocks indefinitely while "backing up" so that we can try to suspend the local system while it's hung
+          dumpCommand = pkgs.writeScript "sleepInhibited" ''
+            cat /dev/zero
+          '';
+          repo = remoteRepo;
+          encryption.mode = "none";
+          startAt = [ ];
+          environment.BORG_RSH = "ssh -oStrictHostKeyChecking=no -i /root/id_ed25519";
+        };
+
       };
     };
 
@@ -204,5 +216,13 @@ in {
         client.wait_for_unit("network.target")
         client.systemctl("start --wait borgbackup-job-commandFail")
         client.succeed("systemctl is-failed borgbackup-job-commandFail")
+
+    with subtest("sleepInhibited"):
+        server.wait_for_unit("sshd.service")
+        client.wait_for_unit("network.target")
+        client.fail("systemd-inhibit --list | grep -q borgbackup")
+        client.systemctl("start borgbackup-job-sleepInhibited")
+        client.wait_until_succeeds("systemd-inhibit --list | grep -q borgbackup")
+        client.systemctl("stop borgbackup-job-sleepInhibited")
   '';
 })
diff --git a/nixos/tests/cockroachdb.nix b/nixos/tests/cockroachdb.nix
index d793842f0ab2..5b1e1a7dee1f 100644
--- a/nixos/tests/cockroachdb.nix
+++ b/nixos/tests/cockroachdb.nix
@@ -8,7 +8,7 @@
 # Cluster joins that are outside this window will fail, and nodes that skew
 # outside the window after joining will promptly get kicked out.
 #
-# To accomodate this, we use QEMU/virtio infrastructure and load the 'ptp_kvm'
+# To accommodate this, we use QEMU/virtio infrastructure and load the 'ptp_kvm'
 # driver inside a guest. This driver allows the host machine to pass its clock
 # through to the guest as a hardware clock that appears as a Precision Time
 # Protocol (PTP) Clock device, generally /dev/ptp0. PTP devices can be measured
diff --git a/nixos/tests/common/ec2.nix b/nixos/tests/common/ec2.nix
index 64b0a91ac1f7..6ed420e0aae7 100644
--- a/nixos/tests/common/ec2.nix
+++ b/nixos/tests/common/ec2.nix
@@ -46,7 +46,7 @@ with pkgs.lib;
         # Note: we use net=169.0.0.0/8 rather than
         # net=169.254.0.0/16 to prevent dhcpcd from getting horribly
         # confused. (It would get a DHCP lease in the 169.254.*
-        # range, which it would then configure and prompty delete
+        # range, which it would then configure and promptly delete
         # again when it deletes link-local addresses.) Ideally we'd
         # turn off the DHCP server, but qemu does not have an option
         # to do that.
diff --git a/nixos/tests/grafana/basic.nix b/nixos/tests/grafana/basic.nix
index f6566d449709..8bf4caad7fbf 100644
--- a/nixos/tests/grafana/basic.nix
+++ b/nixos/tests/grafana/basic.nix
@@ -25,6 +25,22 @@ let
   extraNodeConfs = {
     sqlite = {};
 
+    socket = { config, ... }: {
+      services.grafana.settings.server = {
+        protocol = "socket";
+        socket = "/run/grafana/sock";
+        socket_gid = config.users.groups.nginx.gid;
+      };
+
+      users.users.grafana.extraGroups = [ "nginx" ];
+
+      services.nginx = {
+        enable = true;
+        recommendedProxySettings = true;
+        virtualHosts."_".locations."/".proxyPass = "http://unix:/run/grafana/sock";
+      };
+    };
+
     declarativePlugins = {
       services.grafana.declarativePlugins = [ pkgs.grafanaPlugins.grafana-clock-panel ];
     };
@@ -92,6 +108,17 @@ in {
         )
         sqlite.shutdown()
 
+    with subtest("Successful API query as admin user with sqlite db listening on socket"):
+        socket.wait_for_unit("grafana.service")
+        socket.wait_for_open_port(80)
+        print(socket.succeed(
+            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1/api/org/users -i"
+        ))
+        socket.succeed(
+            "curl -sSfN -u testadmin:snakeoilpwd http://127.0.0.1/api/org/users | grep admin\@localhost"
+        )
+        socket.shutdown()
+
     with subtest("Successful API query as admin user with postgresql db"):
         postgresql.wait_for_unit("grafana.service")
         postgresql.wait_for_unit("postgresql.service")
diff --git a/nixos/tests/graphite.nix b/nixos/tests/graphite.nix
index c534d45428e1..de6cd8a50e17 100644
--- a/nixos/tests/graphite.nix
+++ b/nixos/tests/graphite.nix
@@ -13,7 +13,7 @@ import ./make-test-python.nix ({ pkgs, ... } :
             '';
           };
           carbon.enableCache = true;
-          seyren.enable = false;  # Implicitely requires openssl-1.0.2u which is marked insecure
+          seyren.enable = false;  # Implicitly requires openssl-1.0.2u which is marked insecure
         };
       };
   };
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index 0bbeffd18cf0..8d58de75eabc 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -120,7 +120,7 @@ in {
     start_all()
 
     # Parse the package path out of the systemd unit, as we cannot
-    # access the final package, that is overriden inside the module,
+    # access the final package, that is overridden inside the module,
     # by any other means.
     pattern = re.compile(r"path=(?P<path>[\/a-z0-9-.]+)\/bin\/hass")
     response = hass.execute("systemctl show -p ExecStart home-assistant.service")[1]
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 9b3c8a762991..398ad8de19cf 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -57,7 +57,7 @@ let
 
         hardware.enableAllFirmware = lib.mkForce false;
 
-        ${replaceChars ["\n"] ["\n  "] extraConfig}
+        ${replaceStrings ["\n"] ["\n  "] extraConfig}
       }
     '';
 
diff --git a/nixos/tests/networking-proxy.nix b/nixos/tests/networking-proxy.nix
index fcb2558cf3b0..330bac2588a5 100644
--- a/nixos/tests/networking-proxy.nix
+++ b/nixos/tests/networking-proxy.nix
@@ -37,7 +37,7 @@ in import ./make-test-python.nix ({ pkgs, ...} : {
       default-config //
       {
         networking.proxy = {
-          # useless because overriden by the next options
+          # useless because overridden by the next options
           default = "http://user:pass@host:port";
           # advanced proxy setup
           httpProxy = "123-http://user:pass@http-host:port";
diff --git a/nixos/tests/nginx-globalredirect.nix b/nixos/tests/nginx-globalredirect.nix
new file mode 100644
index 000000000000..5f5f4f344d82
--- /dev/null
+++ b/nixos/tests/nginx-globalredirect.nix
@@ -0,0 +1,24 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "nginx-globalredirect";
+
+  nodes = {
+    webserver = { pkgs, lib, ... }: {
+      services.nginx = {
+        enable = true;
+        virtualHosts.localhost = {
+          globalRedirect = "other.example.com";
+          # Add an exception
+          locations."/noredirect".return = "200 'foo'";
+        };
+      };
+    };
+  };
+
+  testScript = ''
+    webserver.wait_for_unit("nginx")
+    webserver.wait_for_open_port(80)
+
+    webserver.succeed("curl --fail -si http://localhost/alf | grep '^Location:.*/alf'")
+    webserver.fail("curl --fail -si http://localhost/noredirect | grep '^Location:'")
+  '';
+})
diff --git a/nixos/tests/nginx-modsecurity.nix b/nixos/tests/nginx-modsecurity.nix
index 5ceee3787297..3c41da3e8d9b 100644
--- a/nixos/tests/nginx-modsecurity.nix
+++ b/nixos/tests/nginx-modsecurity.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   nodes.machine = { config, lib, pkgs, ... }: {
     services.nginx = {
       enable = true;
-      additionalModules = [ pkgs.nginxModules.modsecurity-nginx ];
+      additionalModules = [ pkgs.nginxModules.modsecurity ];
       virtualHosts.localhost =
         let modsecurity_conf = pkgs.writeText "modsecurity.conf" ''
           SecRuleEngine On
diff --git a/nixos/tests/nginx.nix b/nixos/tests/nginx.nix
index d9d073822a14..73f1133bd6ca 100644
--- a/nixos/tests/nginx.nix
+++ b/nixos/tests/nginx.nix
@@ -61,7 +61,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
 
       specialisation.reloadWithErrorsSystem.configuration = {
         services.nginx.package = pkgs.nginxMainline;
-        services.nginx.virtualHosts."!@$$(#*%".locations."~@#*$*!)".proxyPass = ";;;";
+        services.nginx.virtualHosts."hello".extraConfig = "access_log /does/not/exist.log;";
       };
     };
   };
diff --git a/nixos/tests/nix-ld.nix b/nixos/tests/nix-ld.nix
index ae5297ab87ea..8733f5b0c397 100644
--- a/nixos/tests/nix-ld.nix
+++ b/nixos/tests/nix-ld.nix
@@ -12,9 +12,6 @@ import ./make-test-python.nix ({ lib, pkgs, ...} :
   };
   testScript = ''
     start_all()
-    path = "${pkgs.stdenv.cc}/nix-support/dynamic-linker"
-    with open(path) as f:
-        real_ld = f.read().strip()
-    machine.succeed(f"NIX_LD={real_ld} hello")
+    machine.succeed("hello")
  '';
 })
diff --git a/nixos/tests/ntfy-sh.nix b/nixos/tests/ntfy-sh.nix
index c0c289b904b6..9a36fcdf3922 100644
--- a/nixos/tests/ntfy-sh.nix
+++ b/nixos/tests/ntfy-sh.nix
@@ -1,4 +1,5 @@
 import ./make-test-python.nix {
+  name = "ntfy-sh";
 
   nodes.machine = { ... }: {
     services.ntfy-sh.enable = true;
diff --git a/nixos/tests/os-prober.nix b/nixos/tests/os-prober.nix
index 1c89cf8c1c67..8f3e2494047c 100644
--- a/nixos/tests/os-prober.nix
+++ b/nixos/tests/os-prober.nix
@@ -1,7 +1,7 @@
 import ./make-test-python.nix ({pkgs, lib, ...}:
 let
   # A filesystem image with a (presumably) bootable debian
-  debianImage = pkgs.vmTools.diskImageFuns.debian9i386 {
+  debianImage = pkgs.vmTools.diskImageFuns.debian11i386 {
     # os-prober cannot detect systems installed on disks without a partition table
     # so we create the disk ourselves
     createRootFS = with pkgs; ''
diff --git a/nixos/tests/pgadmin4-standalone.nix b/nixos/tests/pgadmin4-standalone.nix
index 442570c5306b..5aa17fcb5bb9 100644
--- a/nixos/tests/pgadmin4-standalone.nix
+++ b/nixos/tests/pgadmin4-standalone.nix
@@ -1,5 +1,5 @@
 import ./make-test-python.nix ({ pkgs, lib, ... }:
-  # This is seperate from pgadmin4 since we don't want both running at once
+  # This is separate from pgadmin4 since we don't want both running at once
 
   {
     name = "pgadmin4-standalone";
diff --git a/nixos/tests/pgadmin4.nix b/nixos/tests/pgadmin4.nix
index bec2554a3f04..2a2b5aaa2841 100644
--- a/nixos/tests/pgadmin4.nix
+++ b/nixos/tests/pgadmin4.nix
@@ -106,7 +106,7 @@ import ./make-test-python.nix ({ pkgs, lib, buildDeps ? [ ], pythonEnv ? [ ], ..
            && sed -i 's|driver_local.maximize_window()||' web/regression/runtests.py"
       )
 
-      # Don't bother to test LDAP or kerberos authentification
+      # Don't bother to test LDAP or kerberos authentication
       with subtest("run browser test"):
           machine.succeed(
                'cd ${pgadmin4SrcDir}/pgadmin4-${pkgs.pgadmin4.version}/web \
diff --git a/nixos/tests/postgresql.nix b/nixos/tests/postgresql.nix
index 7864f5d6ff32..7e0a82c38828 100644
--- a/nixos/tests/postgresql.nix
+++ b/nixos/tests/postgresql.nix
@@ -130,8 +130,97 @@ let
     '';
 
   };
+
+  mk-ensure-clauses-test = postgresql-name: postgresql-package: makeTest {
+    name = postgresql-name;
+    meta = with pkgs.lib.maintainers; {
+      maintainers = [ zagy ];
+    };
+
+    machine = {...}:
+      {
+        services.postgresql = {
+          enable = true;
+          package = postgresql-package;
+          ensureUsers = [
+            {
+              name = "all-clauses";
+              ensureClauses = {
+                superuser = true;
+                createdb = true;
+                createrole = true;
+                "inherit" = true;
+                login = true;
+                replication = true;
+                bypassrls = true;
+              };
+            }
+            {
+              name = "default-clauses";
+            }
+          ];
+        };
+      };
+
+    testScript = let
+      getClausesQuery = user: pkgs.lib.concatStringsSep " "
+        [
+          "SELECT row_to_json(row)"
+          "FROM ("
+          "SELECT"
+            "rolsuper,"
+            "rolinherit,"
+            "rolcreaterole,"
+            "rolcreatedb,"
+            "rolcanlogin,"
+            "rolreplication,"
+            "rolbypassrls"
+          "FROM pg_roles"
+          "WHERE rolname = '${user}'"
+          ") row;"
+        ];
+    in ''
+      import json
+      machine.start()
+      machine.wait_for_unit("postgresql")
+
+      with subtest("All user permissions are set according to the ensureClauses attr"):
+          clauses = json.loads(
+            machine.succeed(
+                "sudo -u postgres psql -tc \"${getClausesQuery "all-clauses"}\""
+            )
+          )
+          print(clauses)
+          assert clauses['rolsuper'], 'expected user with clauses to have superuser clause'
+          assert clauses['rolinherit'], 'expected user with clauses to have inherit clause'
+          assert clauses['rolcreaterole'], 'expected user with clauses to have create role clause'
+          assert clauses['rolcreatedb'], 'expected user with clauses to have create db clause'
+          assert clauses['rolcanlogin'], 'expected user with clauses to have login clause'
+          assert clauses['rolreplication'], 'expected user with clauses to have replication clause'
+          assert clauses['rolbypassrls'], 'expected user with clauses to have bypassrls clause'
+
+      with subtest("All user permissions default when ensureClauses is not provided"):
+          clauses = json.loads(
+            machine.succeed(
+                "sudo -u postgres psql -tc \"${getClausesQuery "default-clauses"}\""
+            )
+          )
+          assert not clauses['rolsuper'], 'expected user with no clauses set to have default superuser clause'
+          assert clauses['rolinherit'], 'expected user with no clauses set to have default inherit clause'
+          assert not clauses['rolcreaterole'], 'expected user with no clauses set to have default create role clause'
+          assert not clauses['rolcreatedb'], 'expected user with no clauses set to have default create db clause'
+          assert clauses['rolcanlogin'], 'expected user with no clauses set to have default login clause'
+          assert not clauses['rolreplication'], 'expected user with no clauses set to have default replication clause'
+          assert not clauses['rolbypassrls'], 'expected user with no clauses set to have default bypassrls clause'
+
+      machine.shutdown()
+    '';
+  };
 in
-  (mapAttrs' (name: package: { inherit name; value=make-postgresql-test name package false;}) postgresql-versions) // {
+  concatMapAttrs (name: package: {
+    ${name} = make-postgresql-test name package false;
+    ${name + "-clauses"} = mk-ensure-clauses-test name package;
+  }) postgresql-versions
+  // {
     postgresql_11-backup-all = make-postgresql-test "postgresql_11-backup-all" postgresql-versions.postgresql_11 true;
   }
-
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 8b40d7e41c00..5f50a3f87d5d 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -6,7 +6,7 @@
 let
   inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
   inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
-    removeSuffix replaceChars singleton splitString;
+    removeSuffix replaceStrings singleton splitString;
 
   /*
     * The attrset `exporterTests` contains one attribute
@@ -182,7 +182,7 @@ let
         enable = true;
         extraFlags = [ "--web.collectd-push-path /collectd" ];
       };
-      exporterTest = let postData = replaceChars [ "\n" ] [ "" ] ''
+      exporterTest = let postData = replaceStrings [ "\n" ] [ "" ] ''
         [{
           "values":[23],
           "dstypes":["gauge"],
@@ -1172,6 +1172,25 @@ let
       '';
     };
 
+    statsd = {
+      exporterConfig = {
+        enable = true;
+      };
+      exporterTest = ''
+        wait_for_unit("prometheus-statsd-exporter.service")
+        wait_for_open_port(9102)
+        succeed("curl http://localhost:9102/metrics | grep 'statsd_exporter_build_info{'")
+        succeed(
+          "echo 'test.udp:1|c' > /dev/udp/localhost/9125",
+          "curl http://localhost:9102/metrics | grep 'test_udp 1'",
+        )
+        succeed(
+          "echo 'test.tcp:1|c' > /dev/tcp/localhost/9125",
+          "curl http://localhost:9102/metrics | grep 'test_tcp 1'",
+        )
+      '';
+    };
+
     surfboard = {
       exporterConfig = {
         enable = true;
@@ -1244,10 +1263,8 @@ let
       exporterConfig.enable = true;
       exporterConfig.controllers = [{ }];
       exporterTest = ''
-        wait_for_unit("prometheus-unpoller-exporter.service")
-        wait_for_open_port(9130)
-        succeed(
-            "curl -sSf localhost:9130/metrics | grep 'unpoller_build_info{.\\+} 1'"
+        wait_until_succeeds(
+            'journalctl -eu prometheus-unpoller-exporter.service -o cat | grep "Connection Error"'
         )
       '';
     };
diff --git a/nixos/tests/sourcehut.nix b/nixos/tests/sourcehut.nix
index d52fbddd20f3..9caa1bcd98f5 100644
--- a/nixos/tests/sourcehut.nix
+++ b/nixos/tests/sourcehut.nix
@@ -35,7 +35,7 @@ let
           };
 
           security.sudo.wheelNeedsPassword = false;
-          nix.trustedUsers = [ "root" "build" ];
+          nix.settings.trusted-users = [ "root" "build" ];
           documentation.nixos.enable = false;
 
           # builds.sr.ht-image-specific network settings
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index 408019666da3..c0e1d0585b93 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -87,6 +87,9 @@ let
                 testRunner = pkgs.writers.writePython3Bin "test-runner"
                   {
                     libraries = [ pkgs.python3Packages.selenium ];
+                    flakeIgnore = [
+                      "E501"
+                    ];
                   } ''
 
                   from selenium.webdriver.common.by import By
@@ -106,25 +109,25 @@ let
 
                   wait.until(EC.title_contains("Create Account"))
 
-                  driver.find_element(By.CSS_SELECTOR, 'input#email').send_keys(
-                    '${userEmail}'
+                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_email').send_keys(
+                      '${userEmail}'
                   )
-                  driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
-                    'A Cat'
+                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_name').send_keys(
+                      'A Cat'
                   )
-                  driver.find_element(By.CSS_SELECTOR, 'input#masterPassword').send_keys(
-                    '${userPassword}'
+                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_master-password').send_keys(
+                      '${userPassword}'
                   )
-                  driver.find_element(By.CSS_SELECTOR, 'input#masterPasswordRetype').send_keys(
-                    '${userPassword}'
+                  driver.find_element(By.CSS_SELECTOR, 'input#register-form_input_confirm-master-password').send_keys(
+                      '${userPassword}'
                   )
 
-                  driver.find_element(By.XPATH, "//button[contains(., 'Submit')]").click()
+                  driver.find_element(By.XPATH, "//button[contains(., 'Create Account')]").click()
 
                   wait.until_not(EC.title_contains("Create Account"))
 
-                  driver.find_element(By.CSS_SELECTOR, 'input#masterPassword').send_keys(
-                    '${userPassword}'
+                  driver.find_element(By.CSS_SELECTOR, 'input#login_input_master-password').send_keys(
+                      '${userPassword}'
                   )
                   driver.find_element(By.XPATH, "//button[contains(., 'Log In')]").click()
 
@@ -133,10 +136,10 @@ let
                   driver.find_element(By.XPATH, "//button[contains(., 'Add Item')]").click()
 
                   driver.find_element(By.CSS_SELECTOR, 'input#name').send_keys(
-                    'secrets'
+                      'secrets'
                   )
                   driver.find_element(By.CSS_SELECTOR, 'input#loginPassword').send_keys(
-                    '${storedPassword}'
+                      '${storedPassword}'
                   )
 
                   driver.find_element(By.XPATH, "//button[contains(., 'Save')]").click()
@@ -161,7 +164,7 @@ let
       with subtest("configure the cli"):
           client.succeed("bw --nointeraction config server http://server")
 
-      with subtest("can't login to nonexistant account"):
+      with subtest("can't login to nonexistent account"):
           client.fail(
               "bw --nointeraction --raw login ${userEmail} ${userPassword}"
           )
diff --git a/nixos/tests/web-apps/mastodon.nix b/nixos/tests/web-apps/mastodon.nix
deleted file mode 100644
index f10cb8cdc677..000000000000
--- a/nixos/tests/web-apps/mastodon.nix
+++ /dev/null
@@ -1,130 +0,0 @@
-import ../make-test-python.nix ({pkgs, ...}:
-let
-  cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
-    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500
-    mkdir -p $out
-    cp key.pem cert.pem $out
-  '';
-
-  hosts = ''
-    192.168.2.101 mastodon.local
-  '';
-
-in
-{
-  name = "mastodon";
-  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ];
-
-  nodes = {
-    server = { pkgs, ... }: {
-
-      virtualisation.memorySize = 2048;
-
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.2.101"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = hosts;
-        firewall.allowedTCPPorts = [ 80 443 ];
-      };
-
-      security = {
-        pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
-      };
-
-      services.redis.servers.mastodon = {
-        enable = true;
-        bind = "127.0.0.1";
-        port = 31637;
-      };
-
-      services.mastodon = {
-        enable = true;
-        configureNginx = true;
-        localDomain = "mastodon.local";
-        enableUnixSocket = false;
-        smtp = {
-          createLocally = false;
-          fromAddress = "mastodon@mastodon.local";
-        };
-        extraConfig = {
-          EMAIL_DOMAIN_ALLOWLIST = "example.com";
-        };
-      };
-
-      services.nginx = {
-        virtualHosts."mastodon.local" = {
-          enableACME = pkgs.lib.mkForce false;
-          sslCertificate = "${cert pkgs}/cert.pem";
-          sslCertificateKey = "${cert pkgs}/key.pem";
-        };
-      };
-    };
-
-    client = { pkgs, ... }: {
-      environment.systemPackages = [ pkgs.jq ];
-      networking = {
-        interfaces.eth1 = {
-          ipv4.addresses = [
-            { address = "192.168.2.102"; prefixLength = 24; }
-          ];
-        };
-        extraHosts = hosts;
-      };
-
-      security = {
-        pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
-      };
-    };
-  };
-
-  testScript = ''
-    start_all()
-
-    server.wait_for_unit("nginx.service")
-    server.wait_for_unit("redis-mastodon.service")
-    server.wait_for_unit("postgresql.service")
-    server.wait_for_unit("mastodon-sidekiq.service")
-    server.wait_for_unit("mastodon-streaming.service")
-    server.wait_for_unit("mastodon-web.service")
-    server.wait_for_open_port(55000)
-    server.wait_for_open_port(55001)
-
-    # Check that mastodon-media-auto-remove is scheduled
-    server.succeed("systemctl status mastodon-media-auto-remove.timer")
-
-    # Check Mastodon version from remote client
-    client.succeed("curl --fail https://mastodon.local/api/v1/instance | jq -r '.version' | grep '${pkgs.mastodon.version}'")
-
-    # Check access from remote client
-    client.succeed("curl --fail https://mastodon.local/about | grep 'Mastodon hosted on mastodon.local'")
-    client.succeed("curl --fail $(curl https://mastodon.local/api/v1/instance 2> /dev/null | jq -r .thumbnail) --output /dev/null")
-
-    # Simple check tootctl commands
-    # Check Mastodon version
-    server.succeed("mastodon-tootctl version | grep '${pkgs.mastodon.version}'")
-
-    # Manage accounts
-    server.succeed("mastodon-tootctl email_domain_blocks add example.com")
-    server.succeed("mastodon-tootctl email_domain_blocks list | grep example.com")
-    server.fail("mastodon-tootctl email_domain_blocks list | grep mastodon.local")
-    server.fail("mastodon-tootctl accounts create alice --email=alice@example.com")
-    server.succeed("mastodon-tootctl email_domain_blocks remove example.com")
-    server.succeed("mastodon-tootctl accounts create bob --email=bob@example.com")
-    server.succeed("mastodon-tootctl accounts approve bob")
-    server.succeed("mastodon-tootctl accounts delete bob")
-
-    # Manage IP access
-    server.succeed("mastodon-tootctl ip_blocks add 192.168.0.0/16 --severity=no_access")
-    server.succeed("mastodon-tootctl ip_blocks export | grep 192.168.0.0/16")
-    server.fail("mastodon-tootctl ip_blocks export | grep 172.16.0.0/16")
-    client.fail("curl --fail https://mastodon.local/about")
-    server.succeed("mastodon-tootctl ip_blocks remove 192.168.0.0/16")
-    client.succeed("curl --fail https://mastodon.local/about")
-
-    server.shutdown()
-    client.shutdown()
-  '';
-})
diff --git a/nixos/tests/web-apps/mastodon/default.nix b/nixos/tests/web-apps/mastodon/default.nix
new file mode 100644
index 000000000000..411ebfcd731b
--- /dev/null
+++ b/nixos/tests/web-apps/mastodon/default.nix
@@ -0,0 +1,9 @@
+{ system ? builtins.currentSystem, handleTestOn }:
+let
+  supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
+
+in
+{
+  standard = handleTestOn supportedSystems ./standard.nix { inherit system; };
+  remote-postgresql = handleTestOn supportedSystems ./remote-postgresql.nix { inherit system; };
+}
diff --git a/nixos/tests/web-apps/mastodon/remote-postgresql.nix b/nixos/tests/web-apps/mastodon/remote-postgresql.nix
new file mode 100644
index 000000000000..2fd3983e13ec
--- /dev/null
+++ b/nixos/tests/web-apps/mastodon/remote-postgresql.nix
@@ -0,0 +1,160 @@
+import ../../make-test-python.nix ({pkgs, ...}:
+let
+  cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500
+    mkdir -p $out
+    cp key.pem cert.pem $out
+  '';
+
+  hosts = ''
+    192.168.2.103 mastodon.local
+  '';
+
+in
+{
+  name = "mastodon-remote-postgresql";
+  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ];
+
+  nodes = {
+    database = {
+      networking = {
+        interfaces.eth1 = {
+          ipv4.addresses = [
+            { address = "192.168.2.102"; prefixLength = 24; }
+          ];
+        };
+        extraHosts = hosts;
+        firewall.allowedTCPPorts = [ 5432 ];
+      };
+
+      services.postgresql = {
+        enable = true;
+        enableTCPIP = true;
+        authentication = ''
+          hostnossl mastodon_local mastodon_test 192.168.2.201/32 md5
+        '';
+        initialScript = pkgs.writeText "postgresql_init.sql" ''
+          CREATE ROLE mastodon_test LOGIN PASSWORD 'SoDTZcISc3f1M1LJsRLT';
+          CREATE DATABASE mastodon_local TEMPLATE template0 ENCODING UTF8;
+          GRANT ALL PRIVILEGES ON DATABASE mastodon_local TO mastodon_test;
+        '';
+      };
+    };
+
+    nginx = {
+      networking = {
+        interfaces.eth1 = {
+          ipv4.addresses = [
+            { address = "192.168.2.103"; prefixLength = 24; }
+          ];
+        };
+        extraHosts = hosts;
+        firewall.allowedTCPPorts = [ 80 443 ];
+      };
+
+      security = {
+        pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
+      };
+
+      services.nginx = {
+        enable = true;
+        recommendedProxySettings = true;
+        virtualHosts."mastodon.local" = {
+          root = "/var/empty";
+          forceSSL = true;
+          enableACME = pkgs.lib.mkForce false;
+          sslCertificate = "${cert pkgs}/cert.pem";
+          sslCertificateKey = "${cert pkgs}/key.pem";
+          locations."/" = {
+            tryFiles = "$uri @proxy";
+          };
+          locations."@proxy" = {
+            proxyPass = "http://192.168.2.201:55001";
+            proxyWebsockets = true;
+          };
+          locations."/api/v1/streaming/" = {
+            proxyPass = "http://192.168.2.201:55002";
+            proxyWebsockets = true;
+          };
+        };
+      };
+    };
+
+    server = { pkgs, ... }: {
+      virtualisation.memorySize = 2048;
+
+      environment = {
+        etc = {
+          "mastodon/password-posgressql-db".text = ''
+            SoDTZcISc3f1M1LJsRLT
+          '';
+        };
+      };
+
+      networking = {
+        interfaces.eth1 = {
+          ipv4.addresses = [
+            { address = "192.168.2.201"; prefixLength = 24; }
+          ];
+        };
+        extraHosts = hosts;
+        firewall.allowedTCPPorts = [ 55001 55002 ];
+      };
+
+      services.mastodon = {
+        enable = true;
+        configureNginx = false;
+        localDomain = "mastodon.local";
+        enableUnixSocket = false;
+        database = {
+          createLocally = false;
+          host = "192.168.2.102";
+          port = 5432;
+          name = "mastodon_local";
+          user = "mastodon_test";
+          passwordFile = "/etc/mastodon/password-posgressql-db";
+        };
+        smtp = {
+          createLocally = false;
+          fromAddress = "mastodon@mastodon.local";
+        };
+        extraConfig = {
+          BIND = "0.0.0.0";
+          EMAIL_DOMAIN_ALLOWLIST = "example.com";
+          RAILS_SERVE_STATIC_FILES = "true";
+          TRUSTED_PROXY_IP = "192.168.2.103";
+        };
+      };
+    };
+
+    client = { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+      networking = {
+        interfaces.eth1 = {
+          ipv4.addresses = [
+            { address = "192.168.2.202"; prefixLength = 24; }
+          ];
+        };
+        extraHosts = hosts;
+      };
+
+      security = {
+        pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
+      };
+    };
+  };
+
+  testScript = import ./script.nix {
+    inherit pkgs;
+    extraInit = ''
+      nginx.wait_for_unit("nginx.service")
+      nginx.wait_for_open_port(443)
+      database.wait_for_unit("postgresql.service")
+      database.wait_for_open_port(5432)
+    '';
+    extraShutdown = ''
+      nginx.shutdown()
+      database.shutdown()
+    '';
+  };
+})
diff --git a/nixos/tests/web-apps/mastodon/script.nix b/nixos/tests/web-apps/mastodon/script.nix
new file mode 100644
index 000000000000..cdb1d4379b64
--- /dev/null
+++ b/nixos/tests/web-apps/mastodon/script.nix
@@ -0,0 +1,54 @@
+{ pkgs
+, extraInit ? ""
+, extraShutdown ? ""
+}:
+
+''
+  start_all()
+
+  ${extraInit}
+
+  server.wait_for_unit("redis-mastodon.service")
+  server.wait_for_unit("mastodon-sidekiq.service")
+  server.wait_for_unit("mastodon-streaming.service")
+  server.wait_for_unit("mastodon-web.service")
+  server.wait_for_open_port(55000)
+  server.wait_for_open_port(55001)
+
+  # Check that mastodon-media-auto-remove is scheduled
+  server.succeed("systemctl status mastodon-media-auto-remove.timer")
+
+  # Check Mastodon version from remote client
+  client.succeed("curl --fail https://mastodon.local/api/v1/instance | jq -r '.version' | grep '${pkgs.mastodon.version}'")
+
+  # Check access from remote client
+  client.succeed("curl --fail https://mastodon.local/about | grep 'Mastodon hosted on mastodon.local'")
+  client.succeed("curl --fail $(curl https://mastodon.local/api/v1/instance 2> /dev/null | jq -r .thumbnail) --output /dev/null")
+
+  # Simple check tootctl commands
+  # Check Mastodon version
+  server.succeed("mastodon-tootctl version | grep '${pkgs.mastodon.version}'")
+
+  # Manage accounts
+  server.succeed("mastodon-tootctl email_domain_blocks add example.com")
+  server.succeed("mastodon-tootctl email_domain_blocks list | grep example.com")
+  server.fail("mastodon-tootctl email_domain_blocks list | grep mastodon.local")
+  server.fail("mastodon-tootctl accounts create alice --email=alice@example.com")
+  server.succeed("mastodon-tootctl email_domain_blocks remove example.com")
+  server.succeed("mastodon-tootctl accounts create bob --email=bob@example.com")
+  server.succeed("mastodon-tootctl accounts approve bob")
+  server.succeed("mastodon-tootctl accounts delete bob")
+
+  # Manage IP access
+  server.succeed("mastodon-tootctl ip_blocks add 192.168.0.0/16 --severity=no_access")
+  server.succeed("mastodon-tootctl ip_blocks export | grep 192.168.0.0/16")
+  server.fail("mastodon-tootctl ip_blocks export | grep 172.16.0.0/16")
+  client.fail("curl --fail https://mastodon.local/about")
+  server.succeed("mastodon-tootctl ip_blocks remove 192.168.0.0/16")
+  client.succeed("curl --fail https://mastodon.local/about")
+
+  server.shutdown()
+  client.shutdown()
+
+  ${extraShutdown}
+''
diff --git a/nixos/tests/web-apps/mastodon/standard.nix b/nixos/tests/web-apps/mastodon/standard.nix
new file mode 100644
index 000000000000..14311afea3f7
--- /dev/null
+++ b/nixos/tests/web-apps/mastodon/standard.nix
@@ -0,0 +1,92 @@
+import ../../make-test-python.nix ({pkgs, ...}:
+let
+  cert = pkgs: pkgs.runCommand "selfSignedCerts" { buildInputs = [ pkgs.openssl ]; } ''
+    openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj '/CN=mastodon.local' -days 36500
+    mkdir -p $out
+    cp key.pem cert.pem $out
+  '';
+
+  hosts = ''
+    192.168.2.101 mastodon.local
+  '';
+
+in
+{
+  name = "mastodon-standard";
+  meta.maintainers = with pkgs.lib.maintainers; [ erictapen izorkin turion ];
+
+  nodes = {
+    server = { pkgs, ... }: {
+
+      virtualisation.memorySize = 2048;
+
+      networking = {
+        interfaces.eth1 = {
+          ipv4.addresses = [
+            { address = "192.168.2.101"; prefixLength = 24; }
+          ];
+        };
+        extraHosts = hosts;
+        firewall.allowedTCPPorts = [ 80 443 ];
+      };
+
+      security = {
+        pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
+      };
+
+      services.redis.servers.mastodon = {
+        enable = true;
+        bind = "127.0.0.1";
+        port = 31637;
+      };
+
+      services.mastodon = {
+        enable = true;
+        configureNginx = true;
+        localDomain = "mastodon.local";
+        enableUnixSocket = false;
+        smtp = {
+          createLocally = false;
+          fromAddress = "mastodon@mastodon.local";
+        };
+        extraConfig = {
+          EMAIL_DOMAIN_ALLOWLIST = "example.com";
+        };
+      };
+
+      services.nginx = {
+        virtualHosts."mastodon.local" = {
+          enableACME = pkgs.lib.mkForce false;
+          sslCertificate = "${cert pkgs}/cert.pem";
+          sslCertificateKey = "${cert pkgs}/key.pem";
+        };
+      };
+    };
+
+    client = { pkgs, ... }: {
+      environment.systemPackages = [ pkgs.jq ];
+      networking = {
+        interfaces.eth1 = {
+          ipv4.addresses = [
+            { address = "192.168.2.102"; prefixLength = 24; }
+          ];
+        };
+        extraHosts = hosts;
+      };
+
+      security = {
+        pki.certificateFiles = [ "${cert pkgs}/cert.pem" ];
+      };
+    };
+  };
+
+  testScript = import ./script.nix {
+    inherit pkgs;
+    extraInit = ''
+      server.wait_for_unit("nginx.service")
+      server.wait_for_open_port(443)
+      server.wait_for_unit("postgresql.service")
+      server.wait_for_open_port(5432)
+    '';
+  };
+})