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/from_md/release-notes/rl-2111.section.xml113
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md25
-rw-r--r--nixos/lib/make-zfs-image.nix2
-rw-r--r--nixos/lib/qemu-common.nix2
-rwxr-xr-xnixos/lib/test-driver/test-driver.py72
-rw-r--r--nixos/lib/testing-python.nix4
-rw-r--r--nixos/lib/utils.nix2
-rw-r--r--nixos/maintainers/scripts/ec2/amazon-image.nix5
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-amis.sh21
-rw-r--r--nixos/modules/config/swap.nix19
-rw-r--r--nixos/modules/hardware/all-firmware.nix2
-rw-r--r--nixos/modules/hardware/video/nvidia.nix2
-rw-r--r--nixos/modules/installer/cd-dvd/iso-image.nix2
-rw-r--r--nixos/modules/installer/tools/nix-fallback-paths.nix10
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl8
-rw-r--r--nixos/modules/misc/locate.nix4
-rw-r--r--nixos/modules/module-list.nix8
-rw-r--r--nixos/modules/profiles/base.nix1
-rw-r--r--nixos/modules/profiles/minimal.nix2
-rw-r--r--nixos/modules/programs/cnping.nix21
-rw-r--r--nixos/modules/programs/sway.nix2
-rw-r--r--nixos/modules/programs/zsh/zsh.nix17
-rw-r--r--nixos/modules/security/doas.nix2
-rw-r--r--nixos/modules/security/pam.nix374
-rw-r--r--nixos/modules/services/cluster/kubernetes/kubelet.nix2
-rw-r--r--nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix77
-rw-r--r--nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix6
-rw-r--r--nixos/modules/services/databases/clickhouse.nix17
-rw-r--r--nixos/modules/services/databases/hbase.nix44
-rw-r--r--nixos/modules/services/databases/influxdb2.nix2
-rw-r--r--nixos/modules/services/logging/logstash.nix13
-rw-r--r--nixos/modules/services/mail/dovecot.nix3
-rw-r--r--nixos/modules/services/mail/postfix.nix4
-rw-r--r--nixos/modules/services/matrix/mjolnir.nix242
-rw-r--r--nixos/modules/services/matrix/mjolnir.xml134
-rw-r--r--nixos/modules/services/matrix/pantalaimon-options.nix70
-rw-r--r--nixos/modules/services/matrix/pantalaimon.nix70
-rw-r--r--nixos/modules/services/misc/ananicy.nix107
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix66
-rw-r--r--nixos/modules/services/misc/plex.nix24
-rw-r--r--nixos/modules/services/misc/xmrig.nix10
-rw-r--r--nixos/modules/services/monitoring/cadvisor.nix2
-rw-r--r--nixos/modules/services/monitoring/collectd.nix4
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix93
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/nginx.nix2
-rw-r--r--nixos/modules/services/monitoring/zabbix-server.nix7
-rw-r--r--nixos/modules/services/network-filesystems/webdav.nix107
-rw-r--r--nixos/modules/services/networking/antennas.nix80
-rw-r--r--nixos/modules/services/networking/networkmanager.nix1
-rw-r--r--nixos/modules/services/networking/nomad.nix19
-rw-r--r--nixos/modules/services/networking/sabnzbd.nix9
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix10
-rw-r--r--nixos/modules/services/networking/teamspeak3.nix22
-rw-r--r--nixos/modules/services/networking/tinc.nix4
-rw-r--r--nixos/modules/services/security/tor.nix2
-rw-r--r--nixos/modules/services/ttys/getty.nix15
-rw-r--r--nixos/modules/services/web-apps/hledger-web.nix2
-rw-r--r--nixos/modules/services/web-apps/matomo.nix20
-rw-r--r--nixos/modules/services/web-apps/openwebrx.nix33
-rw-r--r--nixos/modules/services/web-apps/plausible.nix92
-rw-r--r--nixos/modules/services/web-apps/zabbix.nix3
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix2
-rw-r--r--nixos/modules/services/x11/clight.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/pantheon.xml6
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/startx.nix3
-rw-r--r--nixos/modules/services/x11/display-managers/sx.nix5
-rw-r--r--nixos/modules/services/x11/hardware/libinput.nix15
-rw-r--r--nixos/modules/services/x11/xserver.nix24
-rw-r--r--nixos/modules/system/boot/binfmt.nix1
-rw-r--r--nixos/modules/system/boot/kernel.nix2
-rw-r--r--nixos/modules/system/boot/networkd.nix44
-rw-r--r--nixos/modules/system/etc/etc.nix5
-rw-r--r--nixos/modules/tasks/auto-upgrade.nix1
-rw-r--r--nixos/modules/tasks/network-interfaces.nix6
-rw-r--r--nixos/modules/virtualisation/azure-agent.nix2
-rw-r--r--nixos/modules/virtualisation/lxc-container.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-guest-agent.nix4
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix12
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix2
-rw-r--r--nixos/modules/virtualisation/vmware-guest.nix2
-rw-r--r--nixos/tests/airsonic.nix4
-rw-r--r--nixos/tests/all-tests.nix13
-rw-r--r--nixos/tests/boot.nix2
-rw-r--r--nixos/tests/cage.nix1
-rw-r--r--nixos/tests/cagebreak.nix1
-rw-r--r--nixos/tests/cassandra.nix1
-rw-r--r--nixos/tests/ceph-multi-node.nix1
-rw-r--r--nixos/tests/ceph-single-node-bluestore.nix1
-rw-r--r--nixos/tests/ceph-single-node.nix1
-rw-r--r--nixos/tests/common/wayland-cage.nix13
-rw-r--r--nixos/tests/containers-bridge.nix1
-rw-r--r--nixos/tests/containers-ephemeral.nix1
-rw-r--r--nixos/tests/containers-extra_veth.nix1
-rw-r--r--nixos/tests/containers-hosts.nix1
-rw-r--r--nixos/tests/containers-imperative.nix1
-rw-r--r--nixos/tests/containers-ip.nix1
-rw-r--r--nixos/tests/containers-macvlans.nix2
-rw-r--r--nixos/tests/containers-physical_interfaces.nix4
-rw-r--r--nixos/tests/containers-portforward.nix1
-rw-r--r--nixos/tests/containers-tmpfs.nix1
-rw-r--r--nixos/tests/custom-ca.nix2
-rw-r--r--nixos/tests/doas.nix8
-rw-r--r--nixos/tests/domination.nix26
-rw-r--r--nixos/tests/enlightenment.nix1
-rw-r--r--nixos/tests/fcitx/default.nix1
-rw-r--r--nixos/tests/fenics.nix1
-rw-r--r--nixos/tests/firefox.nix3
-rw-r--r--nixos/tests/gerrit.nix1
-rw-r--r--nixos/tests/gnome-xorg.nix1
-rw-r--r--nixos/tests/gnome.nix1
-rw-r--r--nixos/tests/graphite.nix1
-rw-r--r--nixos/tests/hadoop/hadoop.nix2
-rw-r--r--nixos/tests/hadoop/hdfs.nix1
-rw-r--r--nixos/tests/hibernate.nix1
-rw-r--r--nixos/tests/installed-tests/fwupd.nix1
-rw-r--r--nixos/tests/installer.nix19
-rw-r--r--nixos/tests/jitsi-meet.nix1
-rw-r--r--nixos/tests/kafka.nix1
-rw-r--r--nixos/tests/kernel-generic.nix1
-rw-r--r--nixos/tests/kexec.nix9
-rw-r--r--nixos/tests/keycloak.nix1
-rw-r--r--nixos/tests/libinput.nix38
-rw-r--r--nixos/tests/libresprite.nix30
-rw-r--r--nixos/tests/lxd-image.nix2
-rw-r--r--nixos/tests/matrix/mjolnir.nix165
-rw-r--r--nixos/tests/matrix/pantalaimon.nix65
-rw-r--r--nixos/tests/metabase.nix1
-rw-r--r--nixos/tests/misc.nix5
-rw-r--r--nixos/tests/mysql/mysql.nix4
-rw-r--r--nixos/tests/networking-proxy.nix1
-rw-r--r--nixos/tests/nginx-etag.nix1
-rw-r--r--nixos/tests/nixops/default.nix1
-rw-r--r--nixos/tests/opensmtpd-rspamd.nix1
-rw-r--r--nixos/tests/pantheon.nix1
-rw-r--r--nixos/tests/paperless-ng.nix1
-rw-r--r--nixos/tests/plasma5-systemd-start.nix1
-rw-r--r--nixos/tests/plasma5.nix1
-rw-r--r--nixos/tests/pleroma.nix1
-rw-r--r--nixos/tests/privacyidea.nix1
-rw-r--r--nixos/tests/prometheus-exporters.nix2
-rw-r--r--nixos/tests/prometheus.nix12
-rw-r--r--nixos/tests/rspamd.nix5
-rw-r--r--nixos/tests/seafile.nix2
-rw-r--r--nixos/tests/signal-desktop.nix1
-rw-r--r--nixos/tests/spark/default.nix1
-rw-r--r--nixos/tests/sway.nix1
-rw-r--r--nixos/tests/sympa.nix1
-rw-r--r--nixos/tests/systemd-cryptenroll.nix1
-rw-r--r--nixos/tests/systemd.nix1
-rw-r--r--nixos/tests/tinydns.nix16
-rw-r--r--nixos/tests/vault-postgresql.nix1
-rw-r--r--nixos/tests/vault.nix1
-rw-r--r--nixos/tests/vaultwarden.nix1
-rw-r--r--nixos/tests/vscodium.nix92
-rw-r--r--nixos/tests/xfce.nix1
156 files changed, 2257 insertions, 623 deletions
diff --git a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
index e5e7c112c617b..f1d803136aa00 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
@@ -17,6 +17,15 @@
     <itemizedlist>
       <listitem>
         <para>
+          Nix has been updated to version 2.4, reference its
+          <link xlink:href="https://discourse.nixos.org/t/nix-2-4-released/15822">release
+          notes</link> for more information on what has changed. The
+          previous version of Nix, 2.3.16, remains available for the
+          time being in the <literal>nix_2_3</literal> package.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           <literal>iptables</literal> now uses
           <literal>nf_tables</literal> backend.
         </para>
@@ -142,6 +151,27 @@
           </listitem>
         </itemizedlist>
       </listitem>
+      <listitem>
+        <para>
+          OpenSSH was updated to version 8.8p1
+        </para>
+        <itemizedlist spacing="compact">
+          <listitem>
+            <para>
+              This breaks connections to old SSH daemons as ssh-rsa host
+              keys and ssh-rsa public keys that were signed with SHA-1
+              are disabled by default now
+            </para>
+          </listitem>
+          <listitem>
+            <para>
+              These can be re-enabled, see the
+              <link xlink:href="https://www.openssh.com/txt/release-8.8">OpenSSH
+              changelog</link> for details
+            </para>
+          </listitem>
+        </itemizedlist>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-21.11-new-services">
@@ -449,6 +479,16 @@
           RandomX benchmark.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          Auto nice daemons
+          <link xlink:href="https://github.com/Nefelim4ag/Ananicy">ananicy</link>
+          and
+          <link xlink:href="https://gitlab.com/ananicy-cpp/ananicy-cpp/">ananicy-cpp</link>.
+          Available as
+          <link linkend="opt-services.ananicy.enable">services.ananicy</link>.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-21.11-incompatibilities">
@@ -584,7 +624,7 @@ Superuser created successfully.
       <listitem>
         <para>
           The <literal>staticjinja</literal> package has been upgraded
-          from 1.0.4 to 4.1.0
+          from 1.0.4 to 4.1.1
         </para>
       </listitem>
       <listitem>
@@ -1260,6 +1300,21 @@ Superuser created successfully.
       </listitem>
       <listitem>
         <para>
+          <literal>nix.daemonNiceLevel</literal> and
+          <literal>nix.daemonIONiceLevel</literal> have been removed in
+          favour of the new options
+          <link xlink:href="options.html#opt-nix.daemonCPUSchedPolicy"><literal>nix.daemonCPUSchedPolicy</literal></link>,
+          <link xlink:href="options.html#opt-nix.daemonIOSchedClass"><literal>nix.daemonIOSchedClass</literal></link>
+          and
+          <link xlink:href="options.html#opt-nix.daemonIOSchedPriority"><literal>nix.daemonIOSchedPriority</literal></link>.
+          Please refer to the options documentation and the
+          <literal>sched(7)</literal> and
+          <literal>ioprio_set(2)</literal> man pages for guidance on how
+          to use them.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           The <literal>coursier</literal> package’s binary was renamed
           from <literal>coursier</literal> to <literal>cs</literal>.
           Completions which haven’t worked for a while should now work
@@ -1277,6 +1332,14 @@ Superuser created successfully.
       </listitem>
       <listitem>
         <para>
+          The <literal>fluidsynth_1</literal> attribute has been
+          removed, as this legacy version is no longer needed in
+          nixpkgs. The actively maintained 2.x series is available as
+          <literal>fluidsynth</literal> unchanged.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           Nextcloud 20 (<literal>pkgs.nextcloud20</literal>) has been
           dropped because it was EOLed by upstream in 2021-10.
         </para>
@@ -1295,6 +1358,24 @@ Superuser created successfully.
           <literal>services.ddclient.passwordFile</literal>.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The default GNAT version has been changed: The
+          <literal>gnat</literal> attribute now points to
+          <literal>gnat11</literal> instead of <literal>gnat9</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          <literal>retroArchCores</literal> has been removed. This means
+          that using <literal>nixpkgs.config.retroarch</literal> to
+          customize RetroArch cores is not supported anymore. Instead,
+          use package overrides, for example:
+          <literal>retroarch.override { cores = with libretro; [ citra snes9x ]; };</literal>.
+          Also, <literal>retroarchFull</literal> derivation is available
+          for those who want to have all RetroArch cores available.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
   <section xml:id="sec-release-21.11-notable-changes">
@@ -1805,6 +1886,17 @@ Superuser created successfully.
       </listitem>
       <listitem>
         <para>
+          The option
+          <literal>services.prometheus.environmentFile</literal> has
+          been removed since it was causing
+          <link xlink:href="https://github.com/NixOS/nixpkgs/issues/126083">issues</link>
+          and Prometheus now has native support for secret files, i.e.
+          <literal>basic_auth.password_file</literal> and
+          <literal>authorization.credentials_file</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
           Dokuwiki now supports caddy! However
         </para>
         <itemizedlist spacing="compact">
@@ -1876,6 +1968,25 @@ Superuser created successfully.
           upgrade guide</link>.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          <literal>julia</literal> now refers to
+          <literal>julia-stable</literal> instead of
+          <literal>julia-lts</literal>. In practice this means it has
+          been upgraded from <literal>1.0.4</literal> to
+          <literal>1.5.4</literal>.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          RetroArch has been upgraded from version
+          <literal>1.8.5</literal> to <literal>1.9.13.2</literal>. Since
+          the previous release was quite old, if you’re having issues
+          after the upgrade, please delete your
+          <literal>$XDG_CONFIG_HOME/retroarch/retroarch.cfg</literal>
+          file.
+        </para>
+      </listitem>
     </itemizedlist>
   </section>
 </section>
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 144e083401578..275ee7142d0ec 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -6,6 +6,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 ## Highlights {#sec-release-21.11-highlights}
 
+- Nix has been updated to version 2.4, reference its [release notes](https://discourse.nixos.org/t/nix-2-4-released/15822) for more information on what has changed. The previous version of Nix, 2.3.16, remains available for the time being in the `nix_2_3` package.
+
 - `iptables` now uses `nf_tables` backend.
 
 - PHP now defaults to PHP 8.0, updated from 7.4.
@@ -44,6 +46,10 @@ In addition to numerous new and upgraded packages, this release has the followin
   - building LXD images from configurations is now directly possible with just nixpkgs
   - hydra is now building nixOS LXD images that can be used standalone with full nixos-rebuild support
 
+- OpenSSH was updated to version 8.8p1
+  - This breaks connections to old SSH daemons as ssh-rsa host keys and ssh-rsa public keys that were signed with SHA-1 are disabled by default now
+  - These can be re-enabled, see the [OpenSSH changelog](https://www.openssh.com/txt/release-8.8) for details
+
 ## New Services {#sec-release-21.11-new-services}
 
 - [btrbk](https://digint.ch/btrbk/index.html), a backup tool for btrfs subvolumes, taking advantage of btrfs specific capabilities to create atomic snapshots and transfer them incrementally to your backup locations. Available as [services.btrbk](options.html#opt-services.brtbk.instances).
@@ -133,6 +139,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - [xmrig](https://github.com/xmrig/xmrig), a high performance, open source, cross platform RandomX, KawPow, CryptoNight and AstroBWT unified CPU/GPU miner and RandomX benchmark.
 
+- Auto nice daemons [ananicy](https://github.com/Nefelim4ag/Ananicy) and [ananicy-cpp](https://gitlab.com/ananicy-cpp/ananicy-cpp/). Available as [services.ananicy](#opt-services.ananicy.enable).
+
 ## Backward Incompatibilities {#sec-release-21.11-incompatibilities}
 
 - The NixOS VM test framework, `pkgs.nixosTest`/`make-test-python.nix`, now requires detaching commands such as `succeed("foo &")` and `succeed("foo | xclip -i")` to close stdout.
@@ -196,7 +204,7 @@ In addition to numerous new and upgraded packages, this release has the followin
     Superuser created successfully.
     ```
 
-- The `staticjinja` package has been upgraded from 1.0.4 to 4.1.0
+- The `staticjinja` package has been upgraded from 1.0.4 to 4.1.1
 
 - Firefox v91 does not support addons with invalid signature anymore. Firefox ESR needs to be used for nix addon support.
 
@@ -380,11 +388,15 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `boot.kernelParams` now only accepts one command line parameter per string. This change is aimed to reduce common mistakes like "param = 12", which would be parsed as 3 parameters.
 
+- `nix.daemonNiceLevel` and `nix.daemonIONiceLevel` have been removed in favour of the new options [`nix.daemonCPUSchedPolicy`](options.html#opt-nix.daemonCPUSchedPolicy), [`nix.daemonIOSchedClass`](options.html#opt-nix.daemonIOSchedClass) and [`nix.daemonIOSchedPriority`](options.html#opt-nix.daemonIOSchedPriority). Please refer to the options documentation and the `sched(7)` and `ioprio_set(2)` man pages for guidance on how to use them.
+
 - The `coursier` package's binary was renamed from `coursier` to `cs`. Completions which haven't worked for a while should now work with the renamed binary. To keep using `coursier`, you can create a shell alias.
 
 - The `services.mosquitto` module has been rewritten to support multiple listeners and per-listener configuration.
   Module configurations from previous releases will no longer work and must be updated.
 
+- The `fluidsynth_1` attribute has been removed, as this legacy version is no longer needed in nixpkgs. The actively maintained 2.x series is available as `fluidsynth` unchanged.
+
 - Nextcloud 20 (`pkgs.nextcloud20`) has been dropped because it was EOLed by upstream in 2021-10.
 
 - The `virtualisation.pathsInNixDB` option was renamed
@@ -392,6 +404,11 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - The `services.ddclient.password` option was removed, and replaced with `services.ddclient.passwordFile`.
 
+- The default GNAT version has been changed: The `gnat` attribute now points to `gnat11`
+  instead of `gnat9`.
+
+- `retroArchCores` has been removed. This means that using `nixpkgs.config.retroarch` to customize RetroArch cores is not supported anymore. Instead, use package overrides, for example: `retroarch.override { cores = with libretro; [ citra snes9x ]; };`. Also, `retroarchFull` derivation is available for those who want to have all RetroArch cores available.
+
 ## Other Notable Changes {#sec-release-21.11-notable-changes}
 
 
@@ -508,6 +525,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - A new option `services.prometheus.enableReload` has been added which can be enabled to reload the prometheus service when its config file changes instead of restarting.
 
+- The option `services.prometheus.environmentFile` has been removed since it was causing [issues](https://github.com/NixOS/nixpkgs/issues/126083) and Prometheus now has native support for secret files, i.e. `basic_auth.password_file` and `authorization.credentials_file`.
+
 - Dokuwiki now supports caddy! However
   - the nginx option has been removed, in the new configuration, please use the `dokuwiki.webserver = "nginx"` instead.
   - The "${hostname}" option has been deprecated, please use `dokuwiki.sites = [ "${hostname}" ]` instead
@@ -520,3 +539,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 - `security.pam.services.<name>.makeHomeDir` now uses `umask=0077` instead of `umask=0022` when creating the home directory.
 
 - Loki has had another release. Some default values have been changed for the configuration and some configuration options have been renamed. For more details, please check [the upgrade guide](https://grafana.com/docs/loki/latest/upgrading/#240).
+
+- `julia` now refers to `julia-stable` instead of `julia-lts`. In practice this means it has been upgraded from `1.0.4` to `1.5.4`.
+
+- RetroArch has been upgraded from version `1.8.5` to `1.9.13.2`. Since the previous release was quite old, if you're having issues after the upgrade, please delete your `$XDG_CONFIG_HOME/retroarch/retroarch.cfg` file.
diff --git a/nixos/lib/make-zfs-image.nix b/nixos/lib/make-zfs-image.nix
index 40648ca24d4d3..a84732aa11712 100644
--- a/nixos/lib/make-zfs-image.nix
+++ b/nixos/lib/make-zfs-image.nix
@@ -241,7 +241,7 @@ let
     pkgs.vmTools.override {
       rootModules =
         [ "zfs" "9p" "9pnet_virtio" "virtio_pci" "virtio_blk" ] ++
-          (pkgs.lib.optional (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) "rtc_cmos");
+          (pkgs.lib.optional pkgs.stdenv.hostPlatform.isx86 "rtc_cmos");
       kernel = modulesTree;
     }
   ).runInLinuxVM (
diff --git a/nixos/lib/qemu-common.nix b/nixos/lib/qemu-common.nix
index 84f9060acd631..1a1f7531feb0b 100644
--- a/nixos/lib/qemu-common.nix
+++ b/nixos/lib/qemu-common.nix
@@ -17,7 +17,7 @@ rec {
       ''-netdev vde,id=vlan${toString nic},sock="$QEMU_VDE_SOCKET_${toString net}"''
     ];
 
-  qemuSerialDevice = if pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64 then "ttyS0"
+  qemuSerialDevice = if pkgs.stdenv.hostPlatform.isx86 then "ttyS0"
         else if (with pkgs.stdenv.hostPlatform; isAarch32 || isAarch64 || isPower) then "ttyAMA0"
         else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'";
 
diff --git a/nixos/lib/test-driver/test-driver.py b/nixos/lib/test-driver/test-driver.py
index a7c0484060f2f..643446f313e3a 100755
--- a/nixos/lib/test-driver/test-driver.py
+++ b/nixos/lib/test-driver/test-driver.py
@@ -4,6 +4,7 @@ from queue import Queue, Empty
 from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List, Iterable
 from xml.sax.saxutils import XMLGenerator
 from colorama import Style
+from pathlib import Path
 import queue
 import io
 import threading
@@ -11,7 +12,6 @@ import argparse
 import base64
 import codecs
 import os
-import pathlib
 import ptpython.repl
 import pty
 import re
@@ -239,8 +239,8 @@ class StartCommand:
 
     def cmd(
         self,
-        monitor_socket_path: pathlib.Path,
-        shell_socket_path: pathlib.Path,
+        monitor_socket_path: Path,
+        shell_socket_path: Path,
         allow_reboot: bool = False,  # TODO: unused, legacy?
     ) -> str:
         display_opts = ""
@@ -272,8 +272,8 @@ class StartCommand:
 
     @staticmethod
     def build_environment(
-        state_dir: pathlib.Path,
-        shared_dir: pathlib.Path,
+        state_dir: Path,
+        shared_dir: Path,
     ) -> dict:
         # We make a copy to not update the current environment
         env = dict(os.environ)
@@ -288,10 +288,10 @@ class StartCommand:
 
     def run(
         self,
-        state_dir: pathlib.Path,
-        shared_dir: pathlib.Path,
-        monitor_socket_path: pathlib.Path,
-        shell_socket_path: pathlib.Path,
+        state_dir: Path,
+        shared_dir: Path,
+        monitor_socket_path: Path,
+        shell_socket_path: Path,
     ) -> subprocess.Popen:
         return subprocess.Popen(
             self.cmd(monitor_socket_path, shell_socket_path),
@@ -334,7 +334,7 @@ class LegacyStartCommand(StartCommand):
         self,
         netBackendArgs: Optional[str] = None,
         netFrontendArgs: Optional[str] = None,
-        hda: Optional[Tuple[pathlib.Path, str]] = None,
+        hda: Optional[Tuple[Path, str]] = None,
         cdrom: Optional[str] = None,
         usb: Optional[str] = None,
         bios: Optional[str] = None,
@@ -394,11 +394,11 @@ class Machine:
     the machine lifecycle with the help of a start script / command."""
 
     name: str
-    tmp_dir: pathlib.Path
-    shared_dir: pathlib.Path
-    state_dir: pathlib.Path
-    monitor_path: pathlib.Path
-    shell_path: pathlib.Path
+    tmp_dir: Path
+    shared_dir: Path
+    state_dir: Path
+    monitor_path: Path
+    shell_path: Path
 
     start_command: StartCommand
     keep_vm_state: bool
@@ -421,7 +421,7 @@ class Machine:
 
     def __init__(
         self,
-        tmp_dir: pathlib.Path,
+        tmp_dir: Path,
         start_command: StartCommand,
         name: str = "machine",
         keep_vm_state: bool = False,
@@ -463,7 +463,7 @@ class Machine:
         hda = None
         if args.get("hda"):
             hda_arg: str = args.get("hda", "")
-            hda_arg_path: pathlib.Path = pathlib.Path(hda_arg)
+            hda_arg_path: Path = Path(hda_arg)
             hda = (hda_arg_path, args.get("hdaInterface", ""))
         return LegacyStartCommand(
             netBackendArgs=args.get("netBackendArgs"),
@@ -814,12 +814,12 @@ class Machine:
         """Copy a file from the host into the guest via the `shared_dir` shared
         among all the VMs (using a temporary directory).
         """
-        host_src = pathlib.Path(source)
-        vm_target = pathlib.Path(target)
+        host_src = Path(source)
+        vm_target = Path(target)
         with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td:
-            shared_temp = pathlib.Path(shared_td)
+            shared_temp = Path(shared_td)
             host_intermediate = shared_temp / host_src.name
-            vm_shared_temp = pathlib.Path("/tmp/shared") / shared_temp.name
+            vm_shared_temp = Path("/tmp/shared") / shared_temp.name
             vm_intermediate = vm_shared_temp / host_src.name
 
             self.succeed(make_command(["mkdir", "-p", vm_shared_temp]))
@@ -836,11 +836,11 @@ class Machine:
         all the VMs (using a temporary directory).
         """
         # Compute the source, target, and intermediate shared file names
-        out_dir = pathlib.Path(os.environ.get("out", os.getcwd()))
-        vm_src = pathlib.Path(source)
+        out_dir = Path(os.environ.get("out", os.getcwd()))
+        vm_src = Path(source)
         with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td:
-            shared_temp = pathlib.Path(shared_td)
-            vm_shared_temp = pathlib.Path("/tmp/shared") / shared_temp.name
+            shared_temp = Path(shared_td)
+            vm_shared_temp = Path("/tmp/shared") / shared_temp.name
             vm_intermediate = vm_shared_temp / vm_src.name
             intermediate = shared_temp / vm_src.name
             # Copy the file to the shared directory inside VM
@@ -911,12 +911,12 @@ class Machine:
 
         self.log("starting vm")
 
-        def clear(path: pathlib.Path) -> pathlib.Path:
+        def clear(path: Path) -> Path:
             if path.exists():
                 path.unlink()
             return path
 
-        def create_socket(path: pathlib.Path) -> socket.socket:
+        def create_socket(path: Path) -> socket.socket:
             s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
             s.bind(str(path))
             s.listen(1)
@@ -1061,7 +1061,7 @@ class VLan:
     """
 
     nr: int
-    socket_dir: pathlib.Path
+    socket_dir: Path
 
     process: subprocess.Popen
     pid: int
@@ -1070,7 +1070,7 @@ class VLan:
     def __repr__(self) -> str:
         return f"<Vlan Nr. {self.nr}>"
 
-    def __init__(self, nr: int, tmp_dir: pathlib.Path):
+    def __init__(self, nr: int, tmp_dir: Path):
         self.nr = nr
         self.socket_dir = tmp_dir / f"vde{self.nr}.ctl"
 
@@ -1123,7 +1123,7 @@ class Driver:
     ):
         self.tests = tests
 
-        tmp_dir = pathlib.Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
+        tmp_dir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
         tmp_dir.mkdir(mode=0o700, exist_ok=True)
 
         with rootlog.nested("start all VLans"):
@@ -1183,9 +1183,11 @@ class Driver:
             serial_stdout_on=self.serial_stdout_on,
             Machine=Machine,  # for typing
         )
-        machine_symbols = {
-            m.name: self.machines[idx] for idx, m in enumerate(self.machines)
-        }
+        machine_symbols = {m.name: m for m in self.machines}
+        # If there's exactly one machine, make it available under the name
+        # "machine", even if it's not called that.
+        if len(self.machines) == 1:
+            (machine_symbols["machine"],) = self.machines
         vlan_symbols = {
             f"vlan{v.nr}": self.vlans[idx] for idx, v in enumerate(self.vlans)
         }
@@ -1230,7 +1232,7 @@ class Driver:
             "Using legacy create_machine(), please instantiate the"
             "Machine class directly, instead"
         )
-        tmp_dir = pathlib.Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
+        tmp_dir = Path(os.environ.get("TMPDIR", tempfile.gettempdir()))
         tmp_dir.mkdir(mode=0o700, exist_ok=True)
 
         if args.get("startCommand"):
@@ -1316,7 +1318,7 @@ if __name__ == "__main__":
         action=EnvDefault,
         envvar="testScript",
         help="the test script to run",
-        type=pathlib.Path,
+        type=Path,
     )
 
     args = arg_parser.parse_args()
diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix
index cce017a6441db..4306d102b2d64 100644
--- a/nixos/lib/testing-python.nix
+++ b/nixos/lib/testing-python.nix
@@ -134,7 +134,9 @@ rec {
       vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
       vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
 
-      nodeHostNames = map (c: c.config.system.name) (lib.attrValues nodes);
+      nodeHostNames = let
+        nodesList = map (c: c.config.system.name) (lib.attrValues nodes);
+      in nodesList ++ lib.optional (lib.length nodesList == 1) "machine";
 
       # TODO: This is an implementation error and needs fixing
       # the testing famework cannot legitimately restrict hostnames further
diff --git a/nixos/lib/utils.nix b/nixos/lib/utils.nix
index 439b627dc3850..f1fa9f07a9742 100644
--- a/nixos/lib/utils.nix
+++ b/nixos/lib/utils.nix
@@ -10,7 +10,7 @@ rec {
   # Check whenever fileSystem is needed for boot.  NOTE: Make sure
   # pathsNeededForBoot is closed under the parent relationship, i.e. if /a/b/c
   # is in the list, put /a and /a/b in as well.
-  pathsNeededForBoot = [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/var/lib/nixos" "/etc" ];
+  pathsNeededForBoot = [ "/" "/nix" "/nix/store" "/var" "/var/log" "/var/lib" "/var/lib/nixos" "/etc" "/usr" ];
   fsNeededForBoot = fs: fs.neededForBoot || elem fs.mountPoint pathsNeededForBoot;
 
   # Check whenever `b` depends on `a` as a fileSystem
diff --git a/nixos/maintainers/scripts/ec2/amazon-image.nix b/nixos/maintainers/scripts/ec2/amazon-image.nix
index fcb369e87ff98..6358ec68f7cf6 100644
--- a/nixos/maintainers/scripts/ec2/amazon-image.nix
+++ b/nixos/maintainers/scripts/ec2/amazon-image.nix
@@ -4,6 +4,7 @@ with lib;
 
 let
   cfg = config.amazonImage;
+  amiBootMode = if config.ec2.efi then "uefi" else "legacy-bios";
 
 in {
 
@@ -106,10 +107,12 @@ in {
          --arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
          --arg root_logical_bytes "$(${pkgs.qemu}/bin/qemu-img info --output json "$rootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
          --arg boot_logical_bytes "$(${pkgs.qemu}/bin/qemu-img info --output json "$bootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
+         --arg boot_mode "${amiBootMode}" \
          --arg root "$rootDisk" \
          --arg boot "$bootDisk" \
         '{}
           | .label = $system_label
+          | .boot_mode = $boot_mode
           | .system = $system
           | .disks.boot.logical_bytes = $boot_logical_bytes
           | .disks.boot.file = $boot
@@ -145,9 +148,11 @@ in {
          --arg system_label ${lib.escapeShellArg config.system.nixos.label} \
          --arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
          --arg logical_bytes "$(${pkgs.qemu}/bin/qemu-img info --output json "$diskImage" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
+         --arg boot_mode "${amiBootMode}" \
          --arg file "$diskImage" \
           '{}
           | .label = $system_label
+          | .boot_mode = $boot_mode
           | .system = $system
           | .logical_bytes = $logical_bytes
           | .file = $file
diff --git a/nixos/maintainers/scripts/ec2/create-amis.sh b/nixos/maintainers/scripts/ec2/create-amis.sh
index 712d9b548ff09..797fe03e20952 100755
--- a/nixos/maintainers/scripts/ec2/create-amis.sh
+++ b/nixos/maintainers/scripts/ec2/create-amis.sh
@@ -1,6 +1,9 @@
 #!/usr/bin/env nix-shell
 #!nix-shell -p awscli -p jq -p qemu -i bash
 # shellcheck shell=bash
+#
+# Future Deprecation?
+# This entire thing should probably be replaced with a generic terraform config
 
 # Uploads and registers NixOS images built from the
 # <nixos/release.nix> amazonImage attribute. Images are uploaded and
@@ -65,13 +68,18 @@ read_image_info() {
 # We handle a single image per invocation, store all attributes in
 # globals for convenience.
 zfs_disks=$(read_image_info .disks)
-image_label="$(read_image_info .label)${zfs_disks:+-ZFS}"
+is_zfs_image=
+if jq -e .boot <<< "$zfs_disks"; then
+  is_zfs_image=1
+  zfs_boot=".disks.boot"
+fi
+image_label="$(read_image_info .label)${is_zfs_image:+-ZFS}"
 image_system=$(read_image_info .system)
-image_files=( $(read_image_info "${zfs_disks:+.disks.root}.file") )
+image_files=( $(read_image_info ".disks.root.file") )
 
-image_logical_bytes=$(read_image_info "${zfs_disks:+.disks.boot}.logical_bytes")
+image_logical_bytes=$(read_image_info "${zfs_boot:-.disks.root}.logical_bytes")
 
-if [[ -n "$zfs_disks" ]]; then
+if [[ -n "$is_zfs_image" ]]; then
   image_files+=( $(read_image_info .disks.boot.file) )
 fi
 
@@ -192,7 +200,7 @@ upload_image() {
     for image_file in "${image_files[@]}"; do
         local aws_path=${image_file#/}
 
-        if [[ -n "$zfs_disks" ]]; then
+        if [[ -n "$is_zfs_image" ]]; then
             local suffix=${image_file%.*}
             suffix=${suffix##*.}
         fi
@@ -239,7 +247,7 @@ upload_image() {
             "DeviceName=/dev/xvda,Ebs={SnapshotId=$snapshot_id,VolumeSize=$image_logical_gigabytes,DeleteOnTermination=true,VolumeType=gp3}"
         )
 
-        if [[ -n "$zfs_disks" ]]; then
+        if [[ -n "$is_zfs_image" ]]; then
             local root_snapshot_id=$(read_state "$region.$image_label.root.$image_system" snapshot_id)
 
             local root_image_logical_bytes=$(read_image_info ".disks.root.logical_bytes")
@@ -270,6 +278,7 @@ upload_image() {
                 --region "$region" \
                 --architecture $amazon_arch \
                 --block-device-mappings "${block_device_mappings[@]}" \
+                --boot-mode $(read_image_info .boot_mode) \
                 "${extra_flags[@]}" \
                 | jq -r '.ImageId'
               )
diff --git a/nixos/modules/config/swap.nix b/nixos/modules/config/swap.nix
index ff2ae1da31bda..2b94b954cb80f 100644
--- a/nixos/modules/config/swap.nix
+++ b/nixos/modules/config/swap.nix
@@ -47,6 +47,15 @@ let
         '';
       };
 
+      allowDiscards = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Whether to allow TRIM requests to the underlying device. This option
+          has security implications; please read the LUKS documentation before
+          activating it.
+        '';
+      };
     };
 
   };
@@ -194,7 +203,6 @@ in
     ];
 
     # Create missing swapfiles.
-    # FIXME: support changing the size of existing swapfiles.
     systemd.services =
       let
 
@@ -214,17 +222,14 @@ in
                 ${optionalString (sw.size != null) ''
                   currentSize=$(( $(stat -c "%s" "${sw.device}" 2>/dev/null || echo 0) / 1024 / 1024 ))
                   if [ "${toString sw.size}" != "$currentSize" ]; then
-                    fallocate -l ${toString sw.size}M "${sw.device}" ||
-                      dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size}
-                    if [ "${toString sw.size}" -lt "$currentSize" ]; then
-                      truncate --size "${toString sw.size}M" "${sw.device}"
-                    fi
+                    dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size}
                     chmod 0600 ${sw.device}
                     ${optionalString (!sw.randomEncryption.enable) "mkswap ${sw.realDevice}"}
                   fi
                 ''}
                 ${optionalString sw.randomEncryption.enable ''
-                  cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} ${optionalString (sw.discardPolicy != null) "--allow-discards"} ${sw.device} ${sw.deviceName}
+                  cryptsetup plainOpen -c ${sw.randomEncryption.cipher} -d ${sw.randomEncryption.source} \
+                    ${optionalString sw.randomEncryption.allowDiscards "--allow-discards"} ${sw.device} ${sw.deviceName}
                   mkswap ${sw.realDevice}
                 ''}
               '';
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
index bdf90816740c8..ce87f9e8be8a4 100644
--- a/nixos/modules/hardware/all-firmware.nix
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -83,7 +83,7 @@ in {
         b43Firmware_5_1_138
         b43Firmware_6_30_163_46
         b43FirmwareCutter
-      ] ++ optional (pkgs.stdenv.hostPlatform.isi686 || pkgs.stdenv.hostPlatform.isx86_64) facetimehd-firmware;
+      ] ++ optional pkgs.stdenv.hostPlatform.isx86 facetimehd-firmware;
     })
     (mkIf cfg.wirelessRegulatoryDatabase {
       hardware.firmware = [ pkgs.wireless-regdb ];
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
index 917c6d3013e75..5b379505608a4 100644
--- a/nixos/modules/hardware/video/nvidia.nix
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -294,7 +294,7 @@ in
     hardware.opengl.extraPackages32 = optional offloadCfg.enable nvidia_x11.lib32;
 
     environment.systemPackages = [ nvidia_x11.bin ]
-      ++ optionals nvidiaSettings [ nvidia_x11.settings ]
+      ++ optionals cfg.nvidiaSettings [ nvidia_x11.settings ]
       ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ];
 
     systemd.packages = optional cfg.powerManagement.enable nvidia_x11.out;
diff --git a/nixos/modules/installer/cd-dvd/iso-image.nix b/nixos/modules/installer/cd-dvd/iso-image.nix
index 4812cacabaf3e..30610b4f42608 100644
--- a/nixos/modules/installer/cd-dvd/iso-image.nix
+++ b/nixos/modules/installer/cd-dvd/iso-image.nix
@@ -467,7 +467,7 @@ let
       throw "Unsupported architecture";
 
   # Syslinux (and isolinux) only supports x86-based architectures.
-  canx86BiosBoot = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
+  canx86BiosBoot = pkgs.stdenv.hostPlatform.isx86;
 
 in
 
diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix
index cb509b7340bdf..065cea470fbb6 100644
--- a/nixos/modules/installer/tools/nix-fallback-paths.nix
+++ b/nixos/modules/installer/tools/nix-fallback-paths.nix
@@ -1,7 +1,7 @@
 {
-  x86_64-linux = "/nix/store/nzp4m3cmm7wawk031byh8jg4cdzjq212-nix-2.3.16";
-  i686-linux = "/nix/store/zsaza9pwim617ak15fsc31lv65b9w3in-nix-2.3.16";
-  aarch64-linux = "/nix/store/7f6z40gyd405yd50qkyzwilnqw106bx8-nix-2.3.16";
-  x86_64-darwin = "/nix/store/c43kyri67ia8mibs0id5ara7gqwlkybf-nix-2.3.16";
-  aarch64-darwin = "/nix/store/6jwhak3cvsgnbqs540n27g8pxnk427fr-nix-2.3.16";
+  x86_64-linux = "/nix/store/hapw7q1fkjxvprnkcgw9ppczavg4daj2-nix-2.4";
+  i686-linux = "/nix/store/8qlvh8pp5j8wgrzj3is2jlbhgrwgsiy9-nix-2.4";
+  aarch64-linux = "/nix/store/h48lkygcqj4hdibbdnpl67q7ks6vkrd6-nix-2.4";
+  x86_64-darwin = "/nix/store/c3mvzszvyzakvcp9spnjvsb8m2bpjk7m-nix-2.4";
+  aarch64-darwin = "/nix/store/hbfqs62r0hga2yr4zi5kc7fzhf71bq9n-nix-2.4";
 }
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 7bc55e67134b6..fe8c4fb1a6b5f 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -91,6 +91,11 @@ sub hasCPUFeature {
 }
 
 
+sub cpuManufacturer {
+    my $id = shift;
+    return $cpuinfo =~ /^vendor_id\s*:.* $id$/m;
+}
+
 
 # Determine CPU governor to use
 if (-e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors") {
@@ -111,6 +116,9 @@ if (-e "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors") {
 push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
 push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
 
+push @attrs, "hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "AuthenticAMD";
+push @attrs, "hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;" if cpuManufacturer "GenuineIntel";
+
 
 # Look at the PCI devices and add necessary modules.  Note that most
 # modules are auto-detected so we don't need to list them here.
diff --git a/nixos/modules/misc/locate.nix b/nixos/modules/misc/locate.nix
index 2f2986c2fec5a..3638bebed931b 100644
--- a/nixos/modules/misc/locate.nix
+++ b/nixos/modules/misc/locate.nix
@@ -149,7 +149,7 @@ in {
 
     prunePaths = mkOption {
       type = listOf path;
-      default = ["/tmp" "/var/tmp" "/var/cache" "/var/lock" "/var/run" "/var/spool" "/nix/store"];
+      default = [ "/tmp" "/var/tmp" "/var/cache" "/var/lock" "/var/run" "/var/spool" "/nix/store" "/nix/var/log/nix" ];
       description = ''
         Which paths to exclude from indexing
       '';
@@ -157,7 +157,7 @@ in {
 
     pruneNames = mkOption {
       type = listOf str;
-      default = [];
+      default = [ ".bzr" ".cache" ".git" ".hg" ".svn" ];
       description = ''
         Directory components which should exclude paths containing them from indexing
       '';
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 8021682e3ffe9..9eca0b8d65f23 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -129,6 +129,7 @@
   ./programs/cdemu.nix
   ./programs/chromium.nix
   ./programs/clickshare.nix
+  ./programs/cnping.nix
   ./programs/command-not-found/command-not-found.nix
   ./programs/criu.nix
   ./programs/dconf.nix
@@ -485,6 +486,9 @@
   ./services/mail/roundcube.nix
   ./services/mail/sympa.nix
   ./services/mail/nullmailer.nix
+  ./services/matrix/mjolnir.nix
+  ./services/matrix/pantalaimon.nix
+  ./services/misc/ananicy.nix
   ./services/misc/airsonic.nix
   ./services/misc/ankisyncd.nix
   ./services/misc/apache-kafka.nix
@@ -607,6 +611,7 @@
   ./services/misc/uhub.nix
   ./services/misc/weechat.nix
   ./services/misc/xmr-stak.nix
+  ./services/misc/xmrig.nix
   ./services/misc/zigbee2mqtt.nix
   ./services/misc/zoneminder.nix
   ./services/misc/zookeeper.nix
@@ -679,12 +684,14 @@
   ./services/network-filesystems/tahoe.nix
   ./services/network-filesystems/diod.nix
   ./services/network-filesystems/u9fs.nix
+  ./services/network-filesystems/webdav.nix
   ./services/network-filesystems/yandex-disk.nix
   ./services/network-filesystems/xtreemfs.nix
   ./services/network-filesystems/ceph.nix
   ./services/networking/3proxy.nix
   ./services/networking/adguardhome.nix
   ./services/networking/amuled.nix
+  ./services/networking/antennas.nix
   ./services/networking/aria2.nix
   ./services/networking/asterisk.nix
   ./services/networking/atftpd.nix
@@ -1014,6 +1021,7 @@
   ./services/web-apps/pgpkeyserver-lite.nix
   ./services/web-apps/matomo.nix
   ./services/web-apps/moinmoin.nix
+  ./services/web-apps/openwebrx.nix
   ./services/web-apps/restya-board.nix
   ./services/web-apps/sogo.nix
   ./services/web-apps/rss-bridge.nix
diff --git a/nixos/modules/profiles/base.nix b/nixos/modules/profiles/base.nix
index 3b67d628f9fd7..33dd80d7c5abd 100644
--- a/nixos/modules/profiles/base.nix
+++ b/nixos/modules/profiles/base.nix
@@ -40,6 +40,7 @@
     # Tools to create / manipulate filesystems.
     pkgs.ntfsprogs # for resizing NTFS partitions
     pkgs.dosfstools
+    pkgs.mtools
     pkgs.xfsprogs.bin
     pkgs.jfsutils
     pkgs.f2fs-tools
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index f044e6f39ea5a..e79b927238419 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -14,4 +14,6 @@ with lib;
   documentation.enable = mkDefault false;
 
   documentation.nixos.enable = mkDefault false;
+
+  programs.command-not-found.enable = mkDefault false;
 }
diff --git a/nixos/modules/programs/cnping.nix b/nixos/modules/programs/cnping.nix
new file mode 100644
index 0000000000000..d208d2b070407
--- /dev/null
+++ b/nixos/modules/programs/cnping.nix
@@ -0,0 +1,21 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.cnping;
+in
+{
+  options = {
+    programs.cnping = {
+      enable = mkEnableOption "Whether to install a setcap wrapper for cnping";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers.cnping = {
+      source = "${pkgs.cnping}/bin/cnping";
+      capabilities = "cap_net_raw+ep";
+    };
+  };
+}
diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix
index caf329c2536a3..c64e01a20cb3d 100644
--- a/nixos/modules/programs/sway.nix
+++ b/nixos/modules/programs/sway.nix
@@ -123,6 +123,8 @@ in {
     ];
     environment = {
       systemPackages = [ swayPackage ] ++ cfg.extraPackages;
+      # Needed for the default wallpaper:
+      pathsToLink = [ "/share/backgrounds/sway" ];
       etc = {
         "sway/config".source = mkOptionDefault "${swayPackage}/etc/sway/config";
         "sway/config.d/nixos.conf".source = pkgs.writeText "nixos.conf" ''
diff --git a/nixos/modules/programs/zsh/zsh.nix b/nixos/modules/programs/zsh/zsh.nix
index dc6c958ca88b2..e5c5b08f8d4da 100644
--- a/nixos/modules/programs/zsh/zsh.nix
+++ b/nixos/modules/programs/zsh/zsh.nix
@@ -283,21 +283,8 @@ in
     # see https://github.com/NixOS/nixpkgs/issues/132732
     environment.etc.zinputrc.text = builtins.readFile ./zinputrc;
 
-    environment.systemPackages =
-      let
-        completions =
-          if lib.versionAtLeast (lib.getVersion config.nix.package) "2.4pre"
-          then
-            pkgs.nix-zsh-completions.overrideAttrs
-              (_: {
-                postInstall = ''
-                  rm $out/share/zsh/site-functions/_nix
-                '';
-              })
-          else pkgs.nix-zsh-completions;
-      in
-      [ pkgs.zsh ]
-      ++ optional cfg.enableCompletion completions;
+    environment.systemPackages = [ pkgs.zsh ]
+      ++ optional cfg.enableCompletion pkgs.nix-zsh-completions;
 
     environment.pathsToLink = optional cfg.enableCompletion "/share/zsh";
 
diff --git a/nixos/modules/security/doas.nix b/nixos/modules/security/doas.nix
index 9a3daf4f504c5..2a814f17e454f 100644
--- a/nixos/modules/security/doas.nix
+++ b/nixos/modules/security/doas.nix
@@ -15,7 +15,7 @@ let
     (optionalString rule.noLog "nolog")
     (optionalString rule.persist "persist")
     (optionalString rule.keepEnv "keepenv")
-    "setenv { SSH_AUTH_SOCK ${concatStringsSep " " rule.setEnv} }"
+    "setenv { SSH_AUTH_SOCK TERMINFO TERMINFO_DIRS ${concatStringsSep " " rule.setEnv} }"
   ];
 
   mkArgs = rule:
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 7c8db9db00313..70bce783a90b6 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -197,6 +197,46 @@ let
         '';
       };
 
+      ttyAudit = {
+        enable = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Enable or disable TTY auditing for specified users
+          '';
+        };
+
+        enablePattern = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            For each user matching one of comma-separated
+            glob patterns, enable TTY auditing
+          '';
+        };
+
+        disablePattern = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          description = ''
+            For each user matching one of comma-separated
+            glob patterns, disable TTY auditing
+          '';
+        };
+
+        openOnly = mkOption {
+          type = types.bool;
+          default = false;
+          description = ''
+            Set the TTY audit flag when opening the session,
+            but do not restore it when closing the session.
+            Using this option is necessary for some services
+            that don't fork() to run the authenticated session,
+            such as sudo.
+          '';
+        };
+      };
+
       forwardXAuth = mkOption {
         default = false;
         type = types.bool;
@@ -370,46 +410,64 @@ let
       # Samba stuff to the Samba module.  This requires that the PAM
       # module provides the right hooks.
       text = mkDefault
-        (''
-          # Account management.
-          account required pam_unix.so
-          ${optionalString use_ldap
-              "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
-          ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false)
-              "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"}
-          ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess)
-              "account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"}
-          ${optionalString config.krb5.enable
-              "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
-          ${optionalString cfg.googleOsLoginAccountVerification ''
+        (
+          ''
+            # Account management.
+            account required pam_unix.so
+          '' +
+          optionalString use_ldap ''
+            account sufficient ${pam_ldap}/lib/security/pam_ldap.so
+          '' +
+          optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false) ''
+            account sufficient ${pkgs.sssd}/lib/security/pam_sss.so
+          '' +
+          optionalString (config.services.sssd.enable && cfg.sssdStrictAccess) ''
+            account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so
+          '' +
+          optionalString config.krb5.enable ''
+            account sufficient ${pam_krb5}/lib/security/pam_krb5.so
+          '' +
+          optionalString cfg.googleOsLoginAccountVerification ''
             account [success=ok ignore=ignore default=die] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so
             account [success=ok default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so
-          ''}
-
-          # Authentication management.
-          ${optionalString cfg.googleOsLoginAuthentication
-              "auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so"}
-          ${optionalString cfg.rootOK
-              "auth sufficient pam_rootok.so"}
-          ${optionalString cfg.requireWheel
-              "auth required pam_wheel.so use_uid"}
-          ${optionalString cfg.logFailures
-              "auth required pam_faillock.so"}
-          ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth)
-              "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}"}
-          ${let p11 = config.security.pam.p11; in optionalString cfg.p11Auth
-              "auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so"}
-          ${let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth
-              "auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"}"}
-          ${optionalString cfg.usbAuth
-              "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
-          ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
-              "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
-          ${let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth
-              "auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}"}
-          ${optionalString cfg.fprintAuth
-              "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"}
-        '' +
+          '' +
+          ''
+
+            # Authentication management.
+          '' +
+          optionalString cfg.googleOsLoginAuthentication ''
+            auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so
+          '' +
+          optionalString cfg.rootOK ''
+            auth sufficient pam_rootok.so
+          '' +
+          optionalString cfg.requireWheel ''
+            auth required pam_wheel.so use_uid
+          '' +
+          optionalString cfg.logFailures ''
+            auth required pam_faillock.so
+          '' +
+          optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) ''
+            auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}
+          '' +
+          (let p11 = config.security.pam.p11; in optionalString cfg.p11Auth ''
+            auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so
+          '') +
+          (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth ''
+            auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"}
+          '') +
+          optionalString cfg.usbAuth ''
+            auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so
+          '' +
+          (let oath = config.security.pam.oath; in optionalString cfg.oathAuth ''
+            auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}
+          '') +
+          (let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth ''
+            auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}
+          '') +
+          optionalString cfg.fprintAuth ''
+            auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so
+          '' +
           # Modules in this block require having the password set in PAM_AUTHTOK.
           # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
           # after it succeeds. Certain modules need to run after pam_unix
@@ -417,109 +475,151 @@ let
           # earlier point and it will run again with 'sufficient' further down.
           # We use try_first_pass the second time to avoid prompting password twice
           (optionalString (cfg.unixAuth &&
-          (config.security.pam.enableEcryptfs
-            || cfg.pamMount
-            || cfg.enableKwallet
-            || cfg.enableGnomeKeyring
-            || cfg.googleAuthenticator.enable
-            || cfg.gnupg.enable
-            || cfg.duoSecurity.enable)) ''
-              auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth
-              ${optionalString config.security.pam.enableEcryptfs
-                "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
-              ${optionalString cfg.pamMount
-                "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive"}
-              ${optionalString cfg.enableKwallet
-                ("auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so" +
-                 " kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5")}
-              ${optionalString cfg.enableGnomeKeyring
-                "auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"}
-              ${optionalString cfg.gnupg.enable
-                "auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"
-                + optionalString cfg.gnupg.storeOnly " store-only"
-               }
-              ${optionalString cfg.googleAuthenticator.enable
-                "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
-              ${optionalString cfg.duoSecurity.enable
-                "auth required ${pkgs.duo-unix}/lib/security/pam_duo.so"}
-            '') + ''
-          ${optionalString cfg.unixAuth
-              "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass"}
-          ${optionalString cfg.otpwAuth
-              "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
-          ${optionalString use_ldap
-              "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
-          ${optionalString config.services.sssd.enable
-              "auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"}
-          ${optionalString config.krb5.enable ''
+            (config.security.pam.enableEcryptfs
+              || cfg.pamMount
+              || cfg.enableKwallet
+              || cfg.enableGnomeKeyring
+              || cfg.googleAuthenticator.enable
+              || cfg.gnupg.enable
+              || cfg.duoSecurity.enable))
+            (
+              ''
+                auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth
+              '' +
+              optionalString config.security.pam.enableEcryptfs ''
+                auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap
+              '' +
+              optionalString cfg.pamMount ''
+                auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
+              '' +
+              optionalString cfg.enableKwallet ''
+               auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5
+              '' +
+              optionalString cfg.enableGnomeKeyring ''
+                auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so
+              '' +
+              optionalString cfg.gnupg.enable ''
+                auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly " store-only"}
+              '' +
+              optionalString cfg.googleAuthenticator.enable ''
+                auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp
+              '' +
+              optionalString cfg.duoSecurity.enable ''
+                auth required ${pkgs.duo-unix}/lib/security/pam_duo.so
+              ''
+            )) +
+          optionalString cfg.unixAuth ''
+            auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass
+          '' +
+          optionalString cfg.otpwAuth ''
+            auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so
+          '' +
+          optionalString use_ldap ''
+            auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass
+          '' +
+          optionalString config.services.sssd.enable ''
+            auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass
+          '' +
+          optionalString config.krb5.enable ''
             auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
             auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
             auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass
-          ''}
-          auth required pam_deny.so
-
-          # Password management.
-          password sufficient pam_unix.so nullok sha512
-          ${optionalString config.security.pam.enableEcryptfs
-              "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
-          ${optionalString cfg.pamMount
-              "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
-          ${optionalString use_ldap
-              "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
-          ${optionalString config.services.sssd.enable
-              "password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"}
-          ${optionalString config.krb5.enable
-              "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"}
-          ${optionalString cfg.enableGnomeKeyring
-              "password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok"}
-
-          # Session management.
-          ${optionalString cfg.setEnvironment ''
+          '' +
+          ''
+            auth required pam_deny.so
+
+            # Password management.
+            password sufficient pam_unix.so nullok sha512
+          '' +
+          optionalString config.security.pam.enableEcryptfs ''
+            password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
+          '' +
+          optionalString cfg.pamMount ''
+            password optional ${pkgs.pam_mount}/lib/security/pam_mount.so
+          '' +
+          optionalString use_ldap ''
+            password sufficient ${pam_ldap}/lib/security/pam_ldap.so
+          '' +
+          optionalString config.services.sssd.enable ''
+            password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok
+          '' +
+          optionalString config.krb5.enable ''
+            password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
+          '' +
+          optionalString cfg.enableGnomeKeyring ''
+            password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok
+          '' +
+          ''
+
+            # Session management.
+          '' +
+          optionalString cfg.setEnvironment ''
             session required pam_env.so conffile=/etc/pam/environment readenv=0
-          ''}
-          session required pam_unix.so
-          ${optionalString cfg.setLoginUid
-              "session ${
-                if config.boot.isContainer then "optional" else "required"
-              } pam_loginuid.so"}
-          ${optionalString cfg.makeHomeDir
-              "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0077"}
-          ${optionalString cfg.updateWtmp
-              "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
-          ${optionalString config.security.pam.enableEcryptfs
-              "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
-          ${optionalString cfg.pamMount
-              "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive"}
-          ${optionalString use_ldap
-              "session optional ${pam_ldap}/lib/security/pam_ldap.so"}
-          ${optionalString config.services.sssd.enable
-              "session optional ${pkgs.sssd}/lib/security/pam_sss.so"}
-          ${optionalString config.krb5.enable
-              "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
-          ${optionalString cfg.otpwAuth
-              "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
-          ${optionalString cfg.startSession
-              "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
-          ${optionalString cfg.forwardXAuth
-              "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
-          ${optionalString (cfg.limits != [])
-              "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"}
-          ${optionalString (cfg.showMotd && config.users.motd != null)
-              "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"}
-          ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable)
-              "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
-          ${optionalString (cfg.enableKwallet)
-              ("session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so" +
-               " kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5")}
-          ${optionalString (cfg.enableGnomeKeyring)
-              "session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"}
-          ${optionalString cfg.gnupg.enable
-              "session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"
-              + optionalString cfg.gnupg.noAutostart " no-autostart"
-           }
-          ${optionalString (config.virtualisation.lxc.lxcfs.enable)
-               "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"}
-        '');
+          '' +
+          ''
+            session required pam_unix.so
+          '' +
+          optionalString cfg.setLoginUid ''
+            session ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so
+          '' +
+          optionalString cfg.ttyAudit.enable ''
+            session required ${pkgs.pam}/lib/security/pam_tty_audit.so
+                open_only=${toString cfg.ttyAudit.openOnly}
+                ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"}
+                ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"}
+          '' +
+          optionalString cfg.makeHomeDir ''
+            session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0077
+          '' +
+          optionalString cfg.updateWtmp ''
+            session required ${pkgs.pam}/lib/security/pam_lastlog.so silent
+          '' +
+          optionalString config.security.pam.enableEcryptfs ''
+            session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
+          '' +
+          optionalString cfg.pamMount ''
+            session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
+          '' +
+          optionalString use_ldap ''
+            session optional ${pam_ldap}/lib/security/pam_ldap.so
+          '' +
+          optionalString config.services.sssd.enable ''
+            session optional ${pkgs.sssd}/lib/security/pam_sss.so
+          '' +
+          optionalString config.krb5.enable ''
+            session optional ${pam_krb5}/lib/security/pam_krb5.so
+          '' +
+          optionalString cfg.otpwAuth ''
+            session optional ${pkgs.otpw}/lib/security/pam_otpw.so
+          '' +
+          optionalString cfg.startSession ''
+            session optional ${pkgs.systemd}/lib/security/pam_systemd.so
+          '' +
+          optionalString cfg.forwardXAuth ''
+            session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99
+          '' +
+          optionalString (cfg.limits != []) ''
+            session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}
+          '' +
+          optionalString (cfg.showMotd && config.users.motd != null) ''
+            session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}
+          '' +
+          optionalString (cfg.enableAppArmor && config.security.apparmor.enable) ''
+            session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug
+          '' +
+          optionalString (cfg.enableKwallet) ''
+            session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5
+          '' +
+          optionalString (cfg.enableGnomeKeyring) ''
+            session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start
+          '' +
+          optionalString cfg.gnupg.enable ''
+            session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"}
+          '' +
+          optionalString (config.virtualisation.lxc.lxcfs.enable) ''
+            session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all
+          ''
+        );
     };
 
   };
diff --git a/nixos/modules/services/cluster/kubernetes/kubelet.nix b/nixos/modules/services/cluster/kubernetes/kubelet.nix
index 3a2a0ed363d6f..eb0cb1f3dbc0c 100644
--- a/nixos/modules/services/cluster/kubernetes/kubelet.nix
+++ b/nixos/modules/services/cluster/kubernetes/kubelet.nix
@@ -258,6 +258,8 @@ in
         "net.bridge.bridge-nf-call-ip6tables" = 1;
       };
 
+      systemd.enableUnifiedCgroupHierarchy = false; # true breaks node memory metrics
+
       systemd.services.kubelet = {
         description = "Kubernetes Kubelet Service";
         wantedBy = [ "kubernetes.target" ];
diff --git a/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix b/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
index d53d68bdcf97e..80c88714bfc17 100644
--- a/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
+++ b/nixos/modules/services/continuous-integration/hercules-ci-agent/common.nix
@@ -1,10 +1,10 @@
 /*
 
-This file is for options that NixOS and nix-darwin have in common.
+  This file is for options that NixOS and nix-darwin have in common.
 
-Platform-specific code is in the respective default.nix files.
+  Platform-specific code is in the respective default.nix files.
 
- */
+*/
 
 { config, lib, options, pkgs, ... }:
 let
@@ -27,6 +27,16 @@ let
   settingsModule = { config, ... }: {
     freeformType = format.type;
     options = {
+      apiBaseUrl = mkOption {
+        description = ''
+          API base URL that the agent will connect to.
+
+          When using Hercules CI Enterprise, set this to the URL where your
+          Hercules CI server is reachable.
+        '';
+        type = types.str;
+        default = "https://hercules-ci.com";
+      };
       baseDirectory = mkOption {
         type = types.path;
         default = "/var/lib/hercules-ci-agent";
@@ -55,6 +65,25 @@ let
         type = types.either types.ints.positive (types.enum [ "auto" ]);
         default = "auto";
       };
+      labels = mkOption {
+        description = ''
+          A key-value map of user data.
+
+          This data will be available to organization members in the dashboard and API.
+
+          The values can be of any TOML type that corresponds to a JSON type, but arrays
+          can not contain tables/objects due to limitations of the TOML library. Values
+          involving arrays of non-primitive types may not be representable currently.
+        '';
+        type = format.type;
+        defaultText = literalExpression ''
+          {
+            agent.source = "..."; # One of "nixpkgs", "flake", "override"
+            lib.version = "...";
+            pkgs.version = "...";
+          }
+        '';
+      };
       workDirectory = mkOption {
         description = ''
           The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
@@ -66,6 +95,8 @@ let
       staticSecretsDirectory = mkOption {
         description = ''
           This is the default directory to look for statically configured secrets like <literal>cluster-join-token.key</literal>.
+
+          See also <literal>clusterJoinTokenPath</literal> and <literal>binaryCachesPath</literal> for fine-grained configuration.
         '';
         type = types.path;
         default = config.baseDirectory + "/secrets";
@@ -74,24 +105,48 @@ let
       clusterJoinTokenPath = mkOption {
         description = ''
           Location of the cluster-join-token.key file.
+
+          You can retrieve the contents of the file when creating a new agent via
+          <link xlink:href="https://hercules-ci.com/dashboard">https://hercules-ci.com/dashboard</link>.
+
+          As this value is confidential, it should not be in the store, but
+          installed using other means, such as agenix, NixOps
+          <literal>deployment.keys</literal>, or manual installation.
+
+          The contents of the file are used for authentication between the agent and the API.
         '';
         type = types.path;
         default = config.staticSecretsDirectory + "/cluster-join-token.key";
         defaultText = literalExpression ''staticSecretsDirectory + "/cluster-join-token.key"'';
-        # internal: It's a bit too detailed to show by default in the docs,
-        # but useful to define explicitly to allow reuse by other modules.
-        internal = true;
       };
       binaryCachesPath = mkOption {
         description = ''
-          Location of the binary-caches.json file.
+          Path to a JSON file containing binary cache secret keys.
+
+          As these values are confidential, they should not be in the store, but
+          copied over using other means, such as agenix, NixOps
+          <literal>deployment.keys</literal>, or manual installation.
+
+          The format is described on <link xlink:href="https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/">https://docs.hercules-ci.com/hercules-ci-agent/binary-caches-json/</link>.
         '';
         type = types.path;
         default = config.staticSecretsDirectory + "/binary-caches.json";
         defaultText = literalExpression ''staticSecretsDirectory + "/binary-caches.json"'';
-        # internal: It's a bit too detailed to show by default in the docs,
-        # but useful to define explicitly to allow reuse by other modules.
-        internal = true;
+      };
+      secretsJsonPath = mkOption {
+        description = ''
+          Path to a JSON file containing secrets for effects.
+
+          As these values are confidential, they should not be in the store, but
+          copied over using other means, such as agenix, NixOps
+          <literal>deployment.keys</literal>, or manual installation.
+
+          The format is described on <link xlink:href="https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/">https://docs.hercules-ci.com/hercules-ci-agent/secrets-json/</link>.
+
+        '';
+        type = types.path;
+        default = config.staticSecretsDirectory + "/secrets.json";
+        defaultText = literalExpression ''staticSecretsDirectory + "/secrets.json"'';
       };
     };
   };
@@ -177,7 +232,7 @@ in
 
       These are written as options instead of let binding to allow sharing with
       default.nix on both NixOS and nix-darwin.
-     */
+    */
     tomlFile = mkOption {
       type = types.path;
       internal = true;
diff --git a/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix b/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix
index 06c174e7d376e..968bc8f1e54ed 100644
--- a/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix
+++ b/nixos/modules/services/continuous-integration/hercules-ci-agent/default.nix
@@ -1,10 +1,10 @@
 /*
 
-This file is for NixOS-specific options and configs.
+  This file is for NixOS-specific options and configs.
 
-Code that is shared with nix-darwin goes in common.nix.
+  Code that is shared with nix-darwin goes in common.nix.
 
- */
+*/
 
 { pkgs, config, lib, ... }:
 let
diff --git a/nixos/modules/services/databases/clickhouse.nix b/nixos/modules/services/databases/clickhouse.nix
index f2f4e9d25542d..3a161d56107eb 100644
--- a/nixos/modules/services/databases/clickhouse.nix
+++ b/nixos/modules/services/databases/clickhouse.nix
@@ -13,6 +13,15 @@ with lib;
 
       enable = mkEnableOption "ClickHouse database server";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.clickhouse;
+        defaultText = "pkgs.clickhouse";
+        description = ''
+          ClickHouse package to use.
+        '';
+      };
+
     };
 
   };
@@ -45,21 +54,21 @@ with lib;
         AmbientCapabilities = "CAP_SYS_NICE";
         StateDirectory = "clickhouse";
         LogsDirectory = "clickhouse";
-        ExecStart = "${pkgs.clickhouse}/bin/clickhouse-server --config-file=${pkgs.clickhouse}/etc/clickhouse-server/config.xml";
+        ExecStart = "${cfg.package}/bin/clickhouse-server --config-file=${cfg.package}/etc/clickhouse-server/config.xml";
       };
     };
 
     environment.etc = {
       "clickhouse-server/config.xml" = {
-        source = "${pkgs.clickhouse}/etc/clickhouse-server/config.xml";
+        source = "${cfg.package}/etc/clickhouse-server/config.xml";
       };
 
       "clickhouse-server/users.xml" = {
-        source = "${pkgs.clickhouse}/etc/clickhouse-server/users.xml";
+        source = "${cfg.package}/etc/clickhouse-server/users.xml";
       };
     };
 
-    environment.systemPackages = [ pkgs.clickhouse ];
+    environment.systemPackages = [ cfg.package ];
 
     # startup requires a `/etc/localtime` which only if exists if `time.timeZone != null`
     time.timeZone = mkDefault "UTC";
diff --git a/nixos/modules/services/databases/hbase.nix b/nixos/modules/services/databases/hbase.nix
index 9132b7ed35698..181be2d6b0b87 100644
--- a/nixos/modules/services/databases/hbase.nix
+++ b/nixos/modules/services/databases/hbase.nix
@@ -5,18 +5,27 @@ with lib;
 let
   cfg = config.services.hbase;
 
-  configFile = pkgs.writeText "hbase-site.xml" ''
-    <configuration>
-      <property>
-        <name>hbase.rootdir</name>
-        <value>file://${cfg.dataDir}/hbase</value>
-      </property>
-      <property>
-        <name>hbase.zookeeper.property.dataDir</name>
-        <value>${cfg.dataDir}/zookeeper</value>
-      </property>
-    </configuration>
-  '';
+  defaultConfig = {
+    "hbase.rootdir" = "file://${cfg.dataDir}/hbase";
+    "hbase.zookeeper.property.dataDir" = "${cfg.dataDir}/zookeeper";
+  };
+
+  buildProperty = configAttr:
+    (builtins.concatStringsSep "\n"
+      (lib.mapAttrsToList
+        (name: value: ''
+          <property>
+            <name>${name}</name>
+            <value>${builtins.toString value}</value>
+          </property>
+        '')
+        configAttr));
+
+  configFile = pkgs.writeText "hbase-site.xml"
+    ''<configuration>
+        ${buildProperty (defaultConfig // cfg.settings)}
+      </configuration>
+    '';
 
   configDir = pkgs.runCommand "hbase-config-dir" { preferLocalBuild = true; } ''
     mkdir -p $out
@@ -85,6 +94,14 @@ in {
         '';
       };
 
+      settings = mkOption {
+        type = with lib.types; attrsOf (oneOf [ str int bool ]);
+        default = defaultConfig;
+        description = ''
+          configurations in hbase-site.xml, see <link xlink:href="https://github.com/apache/hbase/blob/master/hbase-server/src/test/resources/hbase-site.xml"/> for details.
+        '';
+      };
+
     };
 
   };
@@ -103,7 +120,8 @@ in {
       wantedBy = [ "multi-user.target" ];
 
       environment = {
-        JAVA_HOME = "${pkgs.jre}";
+        # JRE 15 removed option `UseConcMarkSweepGC` which is needed.
+        JAVA_HOME = "${pkgs.jre8}";
         HBASE_LOG_DIR = cfg.logDir;
       };
 
diff --git a/nixos/modules/services/databases/influxdb2.nix b/nixos/modules/services/databases/influxdb2.nix
index 01b9c49348475..15f008cbc6d6c 100644
--- a/nixos/modules/services/databases/influxdb2.nix
+++ b/nixos/modules/services/databases/influxdb2.nix
@@ -17,7 +17,7 @@ in
       };
       settings = mkOption {
         default = { };
-        description = "configuration options for influxdb2, see https://docs.influxdata.com/influxdb/v2.0/reference/config-options for details.";
+        description = ''configuration options for influxdb2, see <link xlink:href="https://docs.influxdata.com/influxdb/v2.0/reference/config-options"/> for details.'';
         type = format.type;
       };
     };
diff --git a/nixos/modules/services/logging/logstash.nix b/nixos/modules/services/logging/logstash.nix
index 044d5330231ec..a08203dffe789 100644
--- a/nixos/modules/services/logging/logstash.nix
+++ b/nixos/modules/services/logging/logstash.nix
@@ -23,12 +23,16 @@ let
 
   logstashSettingsYml = pkgs.writeText "logstash.yml" cfg.extraSettings;
 
+  logstashJvmOptionsFile = pkgs.writeText "jvm.options" cfg.extraJvmOptions;
+
   logstashSettingsDir = pkgs.runCommand "logstash-settings" {
+      inherit logstashJvmOptionsFile;
       inherit logstashSettingsYml;
       preferLocalBuild = true;
     } ''
     mkdir -p $out
     ln -s $logstashSettingsYml $out/logstash.yml
+    ln -s $logstashJvmOptionsFile $out/jvm.options
   '';
 in
 
@@ -152,6 +156,15 @@ in
         '';
       };
 
+      extraJvmOptions = mkOption {
+        type = types.lines;
+        default = "";
+        description = "Extra JVM options, one per line (jvm.options format).";
+        example = ''
+          -Xms2g
+          -Xmx2g
+        '';
+      };
 
     };
   };
diff --git a/nixos/modules/services/mail/dovecot.nix b/nixos/modules/services/mail/dovecot.nix
index 223f3bef77dbb..c39827c5b867d 100644
--- a/nixos/modules/services/mail/dovecot.nix
+++ b/nixos/modules/services/mail/dovecot.nix
@@ -103,11 +103,12 @@ let
 
         plugin {
           quota_rule = *:storage=${cfg.quotaGlobalPerUser}
-          quota = maildir:User quota # per virtual mail user quota # BUG/FIXME broken, we couldn't get this working
+          quota = count:User quota # per virtual mail user quota
           quota_status_success = DUNNO
           quota_status_nouser = DUNNO
           quota_status_overquota = "552 5.2.2 Mailbox is full"
           quota_grace = 10%%
+          quota_vsizes = yes
         }
       ''
     )
diff --git a/nixos/modules/services/mail/postfix.nix b/nixos/modules/services/mail/postfix.nix
index 6fc09682e0c02..23d3574ae27c5 100644
--- a/nixos/modules/services/mail/postfix.nix
+++ b/nixos/modules/services/mail/postfix.nix
@@ -294,7 +294,7 @@ in
       };
 
       submissionOptions = mkOption {
-        type = types.attrs;
+        type = with types; attrsOf str;
         default = {
           smtpd_tls_security_level = "encrypt";
           smtpd_sasl_auth_enable = "yes";
@@ -312,7 +312,7 @@ in
       };
 
       submissionsOptions = mkOption {
-        type = types.attrs;
+        type = with types; attrsOf str;
         default = {
           smtpd_sasl_auth_enable = "yes";
           smtpd_client_restrictions = "permit_sasl_authenticated,reject";
diff --git a/nixos/modules/services/matrix/mjolnir.nix b/nixos/modules/services/matrix/mjolnir.nix
new file mode 100644
index 0000000000000..278924b05cf28
--- /dev/null
+++ b/nixos/modules/services/matrix/mjolnir.nix
@@ -0,0 +1,242 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.mjolnir;
+
+  yamlConfig = {
+    inherit (cfg) dataPath managementRoom protectedRooms;
+
+    accessToken = "@ACCESS_TOKEN@"; # will be replaced in "generateConfig"
+    homeserverUrl =
+      if cfg.pantalaimon.enable then
+        "http://${cfg.pantalaimon.options.listenAddress}:${toString cfg.pantalaimon.options.listenPort}"
+      else
+        cfg.homeserverUrl;
+
+    rawHomeserverUrl = cfg.homeserverUrl;
+
+    pantalaimon = {
+      inherit (cfg.pantalaimon) username;
+
+      use = cfg.pantalaimon.enable;
+      password = "@PANTALAIMON_PASSWORD@"; # will be replaced in "generateConfig"
+    };
+  };
+
+  moduleConfigFile = pkgs.writeText "module-config.yaml" (
+    generators.toYAML { } (filterAttrs (_: v: v != null)
+      (fold recursiveUpdate { } [ yamlConfig cfg.settings ])));
+
+  # these config files will be merged one after the other to build the final config
+  configFiles = [
+    "${pkgs.mjolnir}/share/mjolnir/config/default.yaml"
+    moduleConfigFile
+  ];
+
+  # this will generate the default.yaml file with all configFiles as inputs and
+  # replace all secret strings using replace-secret
+  generateConfig = pkgs.writeShellScript "mjolnir-generate-config" (
+    let
+      yqEvalStr = concatImapStringsSep " * " (pos: _: "select(fileIndex == ${toString (pos - 1)})") configFiles;
+      yqEvalArgs = concatStringsSep " " configFiles;
+    in
+    ''
+      set -euo pipefail
+
+      umask 077
+
+      # mjolnir will try to load a config from "./config/default.yaml" in the working directory
+      # -> let's place the generated config there
+      mkdir -p ${cfg.dataPath}/config
+
+      # merge all config files into one, overriding settings of the previous one with the next config
+      # e.g. "eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' filea.yaml fileb.yaml" will merge filea.yaml with fileb.yaml
+      ${pkgs.yq-go}/bin/yq eval-all -P '${yqEvalStr}' ${yqEvalArgs} > ${cfg.dataPath}/config/default.yaml
+
+      ${optionalString (cfg.accessTokenFile != null) ''
+        ${pkgs.replace-secret}/bin/replace-secret '@ACCESS_TOKEN@' '${cfg.accessTokenFile}' ${cfg.dataPath}/config/default.yaml
+      ''}
+      ${optionalString (cfg.pantalaimon.passwordFile != null) ''
+        ${pkgs.replace-secret}/bin/replace-secret '@PANTALAIMON_PASSWORD@' '${cfg.pantalaimon.passwordFile}' ${cfg.dataPath}/config/default.yaml
+      ''}
+    ''
+  );
+in
+{
+  options.services.mjolnir = {
+    enable = mkEnableOption "Mjolnir, a moderation tool for Matrix";
+
+    homeserverUrl = mkOption {
+      type = types.str;
+      default = "https://matrix.org";
+      description = ''
+        Where the homeserver is located (client-server URL).
+
+        If <literal>pantalaimon.enable</literal> is <literal>true</literal>, this option will become the homeserver to which <literal>pantalaimon</literal> connects.
+        The listen address of <literal>pantalaimon</literal> will then become the <literal>homeserverUrl</literal> of <literal>mjolnir</literal>.
+      '';
+    };
+
+    accessTokenFile = mkOption {
+      type = with types; nullOr path;
+      default = null;
+      description = ''
+        File containing the matrix access token for the <literal>mjolnir</literal> user.
+      '';
+    };
+
+    pantalaimon = mkOption {
+      description = ''
+        <literal>pantalaimon</literal> options (enables E2E Encryption support).
+
+        This will create a <literal>pantalaimon</literal> instance with the name "mjolnir".
+      '';
+      default = { };
+      type = types.submodule {
+        options = {
+          enable = mkEnableOption ''
+            If true, accessToken is ignored and the username/password below will be
+            used instead. The access token of the bot will be stored in the dataPath.
+          '';
+
+          username = mkOption {
+            type = types.str;
+            description = "The username to login with.";
+          };
+
+          passwordFile = mkOption {
+            type = with types; nullOr path;
+            default = null;
+            description = ''
+              File containing the matrix password for the <literal>mjolnir</literal> user.
+            '';
+          };
+
+          options = mkOption {
+            type = types.submodule (import ./pantalaimon-options.nix);
+            default = { };
+            description = ''
+              passthrough additional options to the <literal>pantalaimon</literal> service.
+            '';
+          };
+        };
+      };
+    };
+
+    dataPath = mkOption {
+      type = types.path;
+      default = "/var/lib/mjolnir";
+      description = ''
+        The directory the bot should store various bits of information in.
+      '';
+    };
+
+    managementRoom = mkOption {
+      type = types.str;
+      default = "#moderators:example.org";
+      description = ''
+        The room ID where people can use the bot. The bot has no access controls, so
+        anyone in this room can use the bot - secure your room!
+        This should be a room alias or room ID - not a matrix.to URL.
+        Note: <literal>mjolnir</literal> is fairly verbose - expect a lot of messages from it.
+      '';
+    };
+
+    protectedRooms = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      example = literalExpression ''
+        [
+          "https://matrix.to/#/#yourroom:example.org"
+          "https://matrix.to/#/#anotherroom:example.org"
+        ]
+      '';
+      description = ''
+        A list of rooms to protect (matrix.to URLs).
+      '';
+    };
+
+    settings = mkOption {
+      default = { };
+      type = (pkgs.formats.yaml { }).type;
+      example = literalExpression ''
+        {
+          autojoinOnlyIfManager = true;
+          automaticallyRedactForReasons = [ "spam" "advertising" ];
+        }
+      '';
+      description = ''
+        Additional settings (see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">mjolnir default config</link> for available settings). These settings will override settings made by the module config.
+      '';
+    };
+  };
+
+  config = mkIf config.services.mjolnir.enable {
+    assertions = [
+      {
+        assertion = !(cfg.pantalaimon.enable && cfg.pantalaimon.passwordFile == null);
+        message = "Specify pantalaimon.passwordFile";
+      }
+      {
+        assertion = !(cfg.pantalaimon.enable && cfg.accessTokenFile != null);
+        message = "Do not specify accessTokenFile when using pantalaimon";
+      }
+      {
+        assertion = !(!cfg.pantalaimon.enable && cfg.accessTokenFile == null);
+        message = "Specify accessTokenFile when not using pantalaimon";
+      }
+    ];
+
+    services.pantalaimon-headless.instances."mjolnir" = mkIf cfg.pantalaimon.enable
+      {
+        homeserver = cfg.homeserverUrl;
+      } // cfg.pantalaimon.options;
+
+    systemd.services.mjolnir = {
+      description = "mjolnir - a moderation tool for Matrix";
+      wants = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ];
+      after = [ "network-online.target" ] ++ optionals (cfg.pantalaimon.enable) [ "pantalaimon-mjolnir.service" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = ''${pkgs.mjolnir}/bin/mjolnir'';
+        ExecStartPre = [ generateConfig ];
+        WorkingDirectory = cfg.dataPath;
+        StateDirectory = "mjolnir";
+        StateDirectoryMode = "0700";
+        ProtectSystem = "strict";
+        ProtectHome = true;
+        PrivateTmp = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        User = "mjolnir";
+        Restart = "on-failure";
+
+        /* TODO: wait for #102397 to be resolved. Then load secrets from $CREDENTIALS_DIRECTORY+"/NAME"
+        DynamicUser = true;
+        LoadCredential = [] ++
+          optionals (cfg.accessTokenFile != null) [
+            "access_token:${cfg.accessTokenFile}"
+          ] ++
+          optionals (cfg.pantalaimon.passwordFile != null) [
+            "pantalaimon_password:${cfg.pantalaimon.passwordFile}"
+          ];
+        */
+      };
+    };
+
+    users = {
+      users.mjolnir = {
+        group = "mjolnir";
+        isSystemUser = true;
+      };
+      groups.mjolnir = { };
+    };
+  };
+
+  meta = {
+    doc = ./mjolnir.xml;
+    maintainers = with maintainers; [ jojosch ];
+  };
+}
diff --git a/nixos/modules/services/matrix/mjolnir.xml b/nixos/modules/services/matrix/mjolnir.xml
new file mode 100644
index 0000000000000..d462ddf7b01be
--- /dev/null
+++ b/nixos/modules/services/matrix/mjolnir.xml
@@ -0,0 +1,134 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-mjolnir">
+ <title>Mjolnir (Matrix Moderation Tool)</title>
+ <para>
+  This chapter will show you how to set up your own, self-hosted
+  <link xlink:href="https://github.com/matrix-org/mjolnir">Mjolnir</link>
+  instance.
+ </para>
+ <para>
+  As an all-in-one moderation tool, it can protect your server from
+  malicious invites, spam messages, and whatever else you don't want.
+  In addition to server-level protection, Mjolnir is great for communities
+  wanting to protect their rooms without having to use their personal
+  accounts for moderation.
+ </para>
+ <para>
+  The bot by default includes support for bans, redactions, anti-spam,
+  server ACLs, room directory changes, room alias transfers, account
+  deactivation, room shutdown, and more.
+ </para>
+ <para>
+  See the <link xlink:href="https://github.com/matrix-org/mjolnir#readme">README</link>
+  page and the <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/docs/moderators.md">Moderator's guide</link>
+  for additional instructions on how to setup and use Mjolnir.
+ </para>
+ <para>
+  For <link linkend="opt-services.mjolnir.settings">additional settings</link>
+  see <link xlink:href="https://github.com/matrix-org/mjolnir/blob/main/config/default.yaml">the default configuration</link>.
+ </para>
+ <section xml:id="module-services-mjolnir-setup">
+  <title>Mjolnir Setup</title>
+  <para>
+   First create a new Room which will be used as a management room for Mjolnir. In
+   this room, Mjolnir will log possible errors and debugging information. You'll
+   need to set this Room-ID in <link linkend="opt-services.mjolnir.managementRoom">services.mjolnir.managementRoom</link>.
+  </para>
+  <para>
+   Next, create a new user for Mjolnir on your homeserver, if not present already.
+  </para>
+  <para>
+   The Mjolnir Matrix user expects to be free of any rate limiting.
+   See <link xlink:href="https://github.com/matrix-org/synapse/issues/6286">Synapse #6286</link>
+   for an example on how to achieve this.
+  </para>
+  <para>
+   If you want Mjolnir to be able to deactivate users, move room aliases, shutdown rooms, etc.
+   you'll need to make the Mjolnir user a Matrix server admin.
+  </para>
+  <para>
+   Now invite the Mjolnir user to the management room.
+  </para>
+  <para>
+   It is recommended to use <link xlink:href="https://github.com/matrix-org/pantalaimon">Pantalaimon</link>,
+   so your management room can be encrypted. This also applies if you are looking to moderate an encrypted room.
+  </para>
+  <para>
+   To enable the Pantalaimon E2E Proxy for mjolnir, enable
+   <link linkend="opt-services.mjolnir.pantalaimon.enable">services.mjolnir.pantalaimon</link>. This will
+   autoconfigure a new Pantalaimon instance, which will connect to the homeserver
+   set in <link linkend="opt-services.mjolnir.homeserverUrl">services.mjolnir.homeserverUrl</link> and Mjolnir itself
+   will be configured to connect to the new Pantalaimon instance.
+  </para>
+<programlisting>
+{
+  services.mjolnir = {
+    enable = true;
+    <link linkend="opt-services.mjolnir.homeserverUrl">homeserverUrl</link> = "https://matrix.domain.tld";
+    <link linkend="opt-services.mjolnir.pantalaimon">pantalaimon</link> = {
+       <link linkend="opt-services.mjolnir.pantalaimon.enable">enable</link> = true;
+       <link linkend="opt-services.mjolnir.pantalaimon.username">username</link> = "mjolnir";
+       <link linkend="opt-services.mjolnir.pantalaimon.passwordFile">passwordFile</link> = "/run/secrets/mjolnir-password";
+    };
+    <link linkend="opt-services.mjolnir.protectedRooms">protectedRooms</link> = [
+      "https://matrix.to/#/!xxx:domain.tld"
+    ];
+    <link linkend="opt-services.mjolnir.managementRoom">managementRoom</link> = "!yyy:domain.tld";
+  };
+}
+</programlisting>
+ <section xml:id="module-services-mjolnir-setup-ems">
+  <title>Element Matrix Services (EMS)</title>
+  <para>
+   If you are using a managed <link xlink:href="https://ems.element.io/">"Element Matrix Services (EMS)"</link>
+   server, you will need to consent to the terms and conditions. Upon startup, an error
+   log entry with a URL to the consent page will be generated.
+  </para>
+ </section>
+ </section>
+
+ <section xml:id="module-services-mjolnir-matrix-synapse-antispam">
+  <title>Synapse Antispam Module</title>
+  <para>
+   A Synapse module is also available to apply the same rulesets the bot
+   uses across an entire homeserver.
+  </para>
+  <para>
+   To use the Antispam Module, add <package>matrix-synapse-plugins.matrix-synapse-mjolnir-antispam</package>
+   to the Synapse plugin list and enable the <literal>mjolnir.AntiSpam</literal> module.
+  </para>
+<programlisting>
+{
+  services.matrix-synapse = {
+    plugins = with pkgs; [
+      matrix-synapse-plugins.matrix-synapse-mjolnir-antispam
+    ];
+    extraConfig = ''
+      modules:
+        - module: mjolnir.AntiSpam
+          config:
+            # Prevent servers/users in the ban lists from inviting users on this
+            # server to rooms. Default true.
+            block_invites: true
+            # Flag messages sent by servers/users in the ban lists as spam. Currently
+            # this means that spammy messages will appear as empty to users. Default
+            # false.
+            block_messages: false
+            # Remove users from the user directory search by filtering matrix IDs and
+            # display names by the entries in the user ban list. Default false.
+            block_usernames: false
+            # The room IDs of the ban lists to honour. Unlike other parts of Mjolnir,
+            # this list cannot be room aliases or permalinks. This server is expected
+            # to already be joined to the room - Mjolnir will not automatically join
+            # these rooms.
+            ban_lists:
+              - "!roomid:example.org"
+    '';
+  };
+}
+</programlisting>
+ </section>
+</chapter>
diff --git a/nixos/modules/services/matrix/pantalaimon-options.nix b/nixos/modules/services/matrix/pantalaimon-options.nix
new file mode 100644
index 0000000000000..035c57540d09d
--- /dev/null
+++ b/nixos/modules/services/matrix/pantalaimon-options.nix
@@ -0,0 +1,70 @@
+{ config, lib, name, ... }:
+
+with lib;
+{
+  options = {
+    dataPath = mkOption {
+      type = types.path;
+      default = "/var/lib/pantalaimon-${name}";
+      description = ''
+        The directory where <literal>pantalaimon</literal> should store its state such as the database file.
+      '';
+    };
+
+    logLevel = mkOption {
+      type = types.enum [ "info" "warning" "error" "debug" ];
+      default = "warning";
+      description = ''
+        Set the log level of the daemon.
+      '';
+    };
+
+    homeserver = mkOption {
+      type = types.str;
+      example = "https://matrix.org";
+      description = ''
+        The URI of the homeserver that the <literal>pantalaimon</literal> proxy should
+        forward requests to, without the matrix API path but including
+        the http(s) schema.
+      '';
+    };
+
+    ssl = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether or not SSL verification should be enabled for outgoing
+        connections to the homeserver.
+      '';
+    };
+
+    listenAddress = mkOption {
+      type = types.str;
+      default = "localhost";
+      description = ''
+        The address where the daemon will listen to client connections
+        for this homeserver.
+      '';
+    };
+
+    listenPort = mkOption {
+      type = types.port;
+      default = 8009;
+      description = ''
+        The port where the daemon will listen to client connections for
+        this homeserver. Note that the listen address/port combination
+        needs to be unique between different homeservers.
+      '';
+    };
+
+    extraSettings = mkOption {
+      type = types.attrs;
+      default = { };
+      description = ''
+        Extra configuration options. See
+        <link xlink:href="https://github.com/matrix-org/pantalaimon/blob/master/docs/man/pantalaimon.5.md">pantalaimon(5)</link>
+        for available options.
+      '';
+    };
+  };
+}
diff --git a/nixos/modules/services/matrix/pantalaimon.nix b/nixos/modules/services/matrix/pantalaimon.nix
new file mode 100644
index 0000000000000..63b40099ca5dd
--- /dev/null
+++ b/nixos/modules/services/matrix/pantalaimon.nix
@@ -0,0 +1,70 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.pantalaimon-headless;
+
+  iniFmt = pkgs.formats.ini { };
+
+  mkConfigFile = name: instanceConfig: iniFmt.generate "pantalaimon.conf" {
+    Default = {
+      LogLevel = instanceConfig.logLevel;
+      Notifications = false;
+    };
+
+    ${name} = (recursiveUpdate
+      {
+        Homeserver = instanceConfig.homeserver;
+        ListenAddress = instanceConfig.listenAddress;
+        ListenPort = instanceConfig.listenPort;
+        SSL = instanceConfig.ssl;
+
+        # Set some settings to prevent user interaction for headless operation
+        IgnoreVerification = true;
+        UseKeyring = false;
+      }
+      instanceConfig.extraSettings
+    );
+  };
+
+  mkPantalaimonService = name: instanceConfig:
+    nameValuePair "pantalaimon-${name}" {
+      description = "pantalaimon instance ${name} - E2EE aware proxy daemon for matrix clients";
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      serviceConfig = {
+        ExecStart = ''${pkgs.pantalaimon-headless}/bin/pantalaimon --config ${mkConfigFile name instanceConfig} --data-path ${instanceConfig.dataPath}'';
+        Restart = "on-failure";
+        DynamicUser = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        ProtectHome = true;
+        ProtectSystem = "strict";
+        StateDirectory = "pantalaimon-${name}";
+      };
+    };
+in
+{
+  options.services.pantalaimon-headless.instances = mkOption {
+    default = { };
+    type = types.attrsOf (types.submodule (import ./pantalaimon-options.nix));
+    description = ''
+      Declarative instance config.
+
+      Note: to use pantalaimon interactively, e.g. for a Matrix client which does not
+      support End-to-end encryption (like <literal>fractal</literal>), refer to the home-manager module.
+    '';
+  };
+
+  config = mkIf (config.services.pantalaimon-headless.instances != { })
+    {
+      systemd.services = mapAttrs' mkPantalaimonService config.services.pantalaimon-headless.instances;
+    };
+
+  meta = {
+    maintainers = with maintainers; [ jojosch ];
+  };
+}
diff --git a/nixos/modules/services/misc/ananicy.nix b/nixos/modules/services/misc/ananicy.nix
new file mode 100644
index 0000000000000..f76f534fb4507
--- /dev/null
+++ b/nixos/modules/services/misc/ananicy.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.ananicy;
+  configFile = pkgs.writeText "ananicy.conf" (generators.toKeyValue { } cfg.settings);
+  extraRules = pkgs.writeText "extraRules" cfg.extraRules;
+  servicename = if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then "ananicy-cpp" else "ananicy";
+in
+{
+  options = {
+    services.ananicy = {
+      enable = mkEnableOption "Ananicy, an auto nice daemon";
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.ananicy;
+        defaultText = literalExpression "pkgs.ananicy";
+        example = literalExpression "pkgs.ananicy-cpp";
+        description = ''
+          Which ananicy package to use.
+        '';
+      };
+
+      settings = mkOption {
+        type = with types; attrsOf (oneOf [ int bool str ]);
+        default = { };
+        example = {
+          apply_nice = false;
+        };
+        description = ''
+          See <link xlink:href="https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf"/>
+        '';
+      };
+
+      extraRules = mkOption {
+        type = types.str;
+        default = "";
+        description = ''
+          Extra rules in json format on separate lines. See:
+          <link xlink:href="https://github.com/Nefelim4ag/Ananicy#configuration"/>
+          <link xlink:href="https://gitlab.com/ananicy-cpp/ananicy-cpp/#global-configuration"/>
+        '';
+        example = literalExpression ''
+          '''
+            { "name": "eog", "type": "Image-View" }
+            { "name": "fdupes", "type": "BG_CPUIO" }
+          '''
+        '';
+
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ cfg.package ];
+      etc."ananicy.d".source = pkgs.runCommandLocal "ananicyfiles" { } ''
+        mkdir -p $out
+        # ananicy-cpp does not include rules or settings on purpose
+        cp -r ${pkgs.ananicy}/etc/ananicy.d/* $out
+        rm $out/ananicy.conf
+        cp ${configFile} $out/ananicy.conf
+        ${optionalString (cfg.extraRules != "") "cp ${extraRules} $out/nixRules.rules"}
+      '';
+    };
+
+    # ananicy and ananicy-cpp have different default settings
+    services.ananicy.settings =
+      let
+        mkOD = mkOptionDefault;
+      in
+      {
+        cgroup_load = mkOD true;
+        type_load = mkOD true;
+        rule_load = mkOD true;
+        apply_nice = mkOD true;
+        apply_ioclass = mkOD true;
+        apply_ionice = mkOD true;
+        apply_sched = mkOD true;
+        apply_oom_score_adj = mkOD true;
+        apply_cgroup = mkOD true;
+      } // (if ((lib.getName cfg.package) == (lib.getName pkgs.ananicy-cpp)) then {
+        # https://gitlab.com/ananicy-cpp/ananicy-cpp/-/blob/master/src/config.cpp#L12
+        loglevel = mkOD "warn"; # default is info but its spammy
+        cgroup_realtime_workaround = mkOD true;
+      } else {
+        # https://github.com/Nefelim4ag/Ananicy/blob/master/ananicy.d/ananicy.conf
+        check_disks_schedulers = mkOD true;
+        check_freq = mkOD 5;
+      });
+
+    systemd = {
+      # https://gitlab.com/ananicy-cpp/ananicy-cpp/#cgroups applies to both ananicy and -cpp
+      enableUnifiedCgroupHierarchy = mkDefault false;
+      packages = [ cfg.package ];
+      services."${servicename}" = {
+        wantedBy = [ "default.target" ];
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ artturin ];
+  };
+}
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index d049370047778..4ea45888e5fc5 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -184,34 +184,51 @@ in
         '';
       };
 
-      daemonNiceLevel = mkOption {
-        type = types.int;
-        default = 0;
+      daemonCPUSchedPolicy = mkOption {
+        type = types.enum ["other" "batch" "idle"];
+        default = "other";
+        example = "batch";
         description = ''
-          Nix daemon process priority. This priority propagates to build processes.
-          0 is the default Unix process priority, 19 is the lowest. Note that nix
-          bypasses nix-daemon when running as root and this option does not have
-          any effect in such a case.
-
-          Please note that if used on a recent Linux kernel with group scheduling,
-          setting the nice level will only have an effect relative to other threads
-          in the same task group. Therefore this option is only useful if
-          autogrouping has been disabled (see the kernel.sched_autogroup_enabled
-          sysctl) and no systemd unit uses any of the per-service CPU accounting
-          features of systemd. Otherwise the Nix daemon process may be placed in a
-          separate task group and the nice level setting will have no effect.
-          Refer to the man pages sched(7) and systemd.resource-control(5) for
-          details.
-        '';
+          Nix daemon process CPU scheduling policy. This policy propagates to
+          build processes. other is the default scheduling policy for regular
+          tasks. The batch policy is similar to other, but optimised for
+          non-interactive tasks. idle is for extremely low-priority tasks
+          that should only be run when no other task requires CPU time.
+
+          Please note that while using the idle policy may greatly improve
+          responsiveness of a system performing expensive builds, it may also
+          slow down and potentially starve crucial configuration updates
+          during load.
+      '';
+      };
+
+      daemonIOSchedClass = mkOption {
+        type = types.enum ["best-effort" "idle"];
+        default = "best-effort";
+        example = "idle";
+        description = ''
+          Nix daemon process I/O scheduling class. This class propagates to
+          build processes. best-effort is the default class for regular tasks.
+          The idle class is for extremely low-priority tasks that should only
+          perform I/O when no other task does.
+
+          Please note that while using the idle scheduling class can improve
+          responsiveness of a system performing expensive builds, it might also
+          slow down or starve crucial configuration updates during load.
+      '';
       };
 
-      daemonIONiceLevel = mkOption {
+      daemonIOSchedPriority = mkOption {
         type = types.int;
         default = 0;
+        example = 1;
         description = ''
-          Nix daemon process I/O priority. This priority propagates to build processes.
-          0 is the default Unix process I/O priority, 7 is the lowest.
-        '';
+          Nix daemon process I/O scheduling priority. This priority propagates
+          to build processes. The supported priorities depend on the
+          scheduling policy: With idle, priorities are not used in scheduling
+          decisions. best-effort supports values in the range 0 (high) to 7
+          (low).
+      '';
       };
 
       buildMachines = mkOption {
@@ -587,8 +604,9 @@ in
         unitConfig.RequiresMountsFor = "/nix/store";
 
         serviceConfig =
-          { Nice = cfg.daemonNiceLevel;
-            IOSchedulingPriority = cfg.daemonIONiceLevel;
+          { CPUSchedulingPolicy = cfg.daemonCPUSchedPolicy;
+            IOSchedulingClass = cfg.daemonIOSchedClass;
+            IOSchedulingPriority = cfg.daemonIOSchedPriority;
             LimitNOFILE = 4096;
           };
 
diff --git a/nixos/modules/services/misc/plex.nix b/nixos/modules/services/misc/plex.nix
index 5f99ee866a507..2ae4e80d5c3fd 100644
--- a/nixos/modules/services/misc/plex.nix
+++ b/nixos/modules/services/misc/plex.nix
@@ -65,6 +65,29 @@ in
         '';
       };
 
+      extraScanners = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          A list of paths to extra scanners to install in Plex's scanners
+          directory.
+
+          Every time the systemd unit for Plex starts up, all of the symlinks
+          in Plex's scanners directory will be cleared and this module will
+          symlink all of the paths specified here to that directory.
+        '';
+        example = literalExpression ''
+          [
+            (fetchFromGitHub {
+              owner = "ZeroQI";
+              repo = "Absolute-Series-Scanner";
+              rev = "773a39f502a1204b0b0255903cee4ed02c46fde0";
+              sha256 = "4l+vpiDdC8L/EeJowUgYyB3JPNTZ1sauN8liFAcK+PY=";
+            })
+          ]
+        '';
+      };
+
       package = mkOption {
         type = types.package;
         default = pkgs.plex;
@@ -113,6 +136,7 @@ in
         # Configuration for our FHS userenv script
         PLEX_DATADIR=cfg.dataDir;
         PLEX_PLUGINS=concatMapStringsSep ":" builtins.toString cfg.extraPlugins;
+        PLEX_SCANNERS=concatMapStringsSep ":" builtins.toString cfg.extraScanners;
 
         # The following variables should be set by the FHS userenv script:
         #   PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR
diff --git a/nixos/modules/services/misc/xmrig.nix b/nixos/modules/services/misc/xmrig.nix
index 15c7d2a7b14ca..cf01bb119e894 100644
--- a/nixos/modules/services/misc/xmrig.nix
+++ b/nixos/modules/services/misc/xmrig.nix
@@ -51,6 +51,8 @@ with lib;
   };
 
   config = mkIf cfg.enable {
+    boot.kernelModules = [ "msr" ];
+
     systemd.services.xmrig = {
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
@@ -58,14 +60,16 @@ with lib;
       serviceConfig = {
         ExecStartPre = "${cfg.package}/bin/xmrig --config=${configFile} --dry-run";
         ExecStart = "${cfg.package}/bin/xmrig --config=${configFile}";
-        DynamicUser = true;
+        # https://xmrig.com/docs/miner/randomx-optimization-guide/msr
+        # If you use recent XMRig with root privileges (Linux) or admin
+        # privileges (Windows) the miner configure all MSR registers
+        # automatically.
+        DynamicUser = lib.mkDefault false;
       };
     };
   };
 
   meta = with lib; {
-    description = "XMRig Mining Software Service";
-    license = licenses.gpl3Only;
     maintainers = with maintainers; [ ratsclub ];
   };
 }
diff --git a/nixos/modules/services/monitoring/cadvisor.nix b/nixos/modules/services/monitoring/cadvisor.nix
index da051dbe4655b..dfbf07efcaea9 100644
--- a/nixos/modules/services/monitoring/cadvisor.nix
+++ b/nixos/modules/services/monitoring/cadvisor.nix
@@ -111,6 +111,8 @@ in {
         wantedBy = [ "multi-user.target" ];
         after = [ "network.target" "docker.service" "influxdb.service" ];
 
+        path = optionals config.boot.zfs.enabled [ pkgs.zfs ];
+
         postStart = mkBefore ''
           until ${pkgs.curl.bin}/bin/curl -s -o /dev/null 'http://${cfg.listenAddress}:${toString cfg.port}/containers/'; do
             sleep 1;
diff --git a/nixos/modules/services/monitoring/collectd.nix b/nixos/modules/services/monitoring/collectd.nix
index ad0cf4735ad43..6af04d22f0f70 100644
--- a/nixos/modules/services/monitoring/collectd.nix
+++ b/nixos/modules/services/monitoring/collectd.nix
@@ -57,7 +57,7 @@ in {
       description = ''
         Build a minimal collectd package with only the configured `services.collectd.plugins`
       '';
-      type = types.bool;
+      type = bool;
     };
 
     user = mkOption {
@@ -98,7 +98,7 @@ in {
       description = ''
         Attribute set of plugin names to plugin config segments
       '';
-      type = types.attrsOf types.str;
+      type = attrsOf lines;
     };
 
     extraConfig = mkOption {
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index cdda6c5ce3aeb..f20b8dde1abd0 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -9,13 +9,6 @@ let
 
   prometheusYmlOut = "${workingDir}/prometheus-substituted.yaml";
 
-  writeConfig = pkgs.writeShellScriptBin "write-prometheus-config" ''
-    PATH="${makeBinPath (with pkgs; [ coreutils envsubst ])}"
-    touch '${prometheusYmlOut}'
-    chmod 600 '${prometheusYmlOut}'
-    envsubst -o '${prometheusYmlOut}' -i '${prometheusYml}'
-  '';
-
   triggerReload = pkgs.writeShellScriptBin "trigger-reload-prometheus" ''
     PATH="${makeBinPath (with pkgs; [ systemd ])}"
     if systemctl -q is-active prometheus.service; then
@@ -76,8 +69,8 @@ let
     "--storage.tsdb.path=${workingDir}/data/"
     "--config.file=${
       if cfg.enableReload
-      then prometheusYmlOut
-      else "/run/prometheus/prometheus-substituted.yaml"
+      then "/etc/prometheus/prometheus.yaml"
+      else prometheusYml
     }"
     "--web.listen-address=${cfg.listenAddress}:${builtins.toString cfg.port}"
     "--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
@@ -1561,6 +1554,8 @@ in
 
   imports = [
     (mkRenamedOptionModule [ "services" "prometheus2" ] [ "services" "prometheus" ])
+    (mkRemovedOptionModule [ "services" "prometheus" "environmentFile" ]
+      "It has been removed since it was causing issues (https://github.com/NixOS/nixpkgs/issues/126083) and Prometheus now has native support for secret files, i.e. `basic_auth.password_file` and `authorization.credentials_file`.")
   ];
 
   options.services.prometheus = {
@@ -1625,51 +1620,6 @@ in
         (<literal>switch-to-configuration</literal>) that changes the prometheus
         configuration only finishes successully when prometheus has finished
         loading the new configuration.
-
-        Note that prometheus will also get reloaded when the location of the
-        <option>environmentFile</option> changes but not when its contents
-        changes. So when you change it contents make sure to reload prometheus
-        manually or include the hash of <option>environmentFile</option> in its
-        name.
-      '';
-    };
-
-    environmentFile = mkOption {
-      type = types.nullOr types.path;
-      default = null;
-      example = "/root/prometheus.env";
-      description = ''
-        Environment file as defined in <citerefentry>
-        <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
-        </citerefentry>.
-
-        Secrets may be passed to the service without adding them to the
-        world-readable Nix store, by specifying placeholder variables as
-        the option value in Nix and setting these variables accordingly in the
-        environment file.
-
-        Environment variables from this file will be interpolated into the
-        config file using envsubst with this syntax:
-        <literal>$ENVIRONMENT ''${VARIABLE}</literal>
-
-        <programlisting>
-          # Example scrape config entry handling an OAuth bearer token
-          {
-            job_name = "home_assistant";
-            metrics_path = "/api/prometheus";
-            scheme = "https";
-            bearer_token = "\''${HOME_ASSISTANT_BEARER_TOKEN}";
-            [...]
-          }
-        </programlisting>
-
-        <programlisting>
-          # Content of the environment file
-          HOME_ASSISTANT_BEARER_TOKEN=someoauthbearertoken
-        </programlisting>
-
-        Note that this file needs to be available on the host on which
-        <literal>Prometheus</literal> is running.
       '';
     };
 
@@ -1830,13 +1780,12 @@ in
       uid = config.ids.uids.prometheus;
       group = "prometheus";
     };
+    environment.etc."prometheus/prometheus.yaml" = mkIf cfg.enableReload {
+      source = prometheusYml;
+    };
     systemd.services.prometheus = {
       wantedBy = [ "multi-user.target" ];
       after = [ "network.target" ];
-      preStart = mkIf (!cfg.enableReload) ''
-        ${lib.getBin pkgs.envsubst}/bin/envsubst -o "/run/prometheus/prometheus-substituted.yaml" \
-                                                 -i "${prometheusYml}"
-      '';
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/prometheus" +
           optionalString (length cmdlineArgs != 0) (" \\\n  " +
@@ -1844,7 +1793,6 @@ in
         ExecReload = mkIf cfg.enableReload "+${reload}/bin/reload-prometheus";
         User = "prometheus";
         Restart = "always";
-        EnvironmentFile = mkIf (cfg.environmentFile != null && !cfg.enableReload) [ cfg.environmentFile ];
         RuntimeDirectory = "prometheus";
         RuntimeDirectoryMode = "0700";
         WorkingDirectory = workingDir;
@@ -1852,18 +1800,6 @@ in
         StateDirectoryMode = "0700";
       };
     };
-    systemd.services.prometheus-config-write = mkIf cfg.enableReload {
-      wantedBy = [ "prometheus.service" ];
-      before = [ "prometheus.service" ];
-      serviceConfig = {
-        Type = "oneshot";
-        User = "prometheus";
-        StateDirectory = cfg.stateDir;
-        StateDirectoryMode = "0700";
-        EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
-        ExecStart = "${writeConfig}/bin/write-prometheus-config";
-      };
-    };
     # prometheus-config-reload will activate after prometheus. However, what we
     # don't want is that on startup it immediately reloads prometheus because
     # prometheus itself might have just started.
@@ -1873,26 +1809,19 @@ in
     # harmless message and then stay active (RemainAfterExit).
     #
     # Then, when the config file has changed, switch-to-configuration notices
-    # that this service has changed and needs to be reloaded
-    # (reloadIfChanged). The reload command then actually writes the new config
-    # and reloads prometheus.
+    # that this service has changed (restartTriggers) and needs to be reloaded
+    # (reloadIfChanged). The reload command then reloads prometheus.
     systemd.services.prometheus-config-reload = mkIf cfg.enableReload {
       wantedBy = [ "prometheus.service" ];
       after = [ "prometheus.service" ];
       reloadIfChanged = true;
+      restartTriggers = [ prometheusYml ];
       serviceConfig = {
         Type = "oneshot";
-        User = "prometheus";
-        StateDirectory = cfg.stateDir;
-        StateDirectoryMode = "0700";
-        EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
         RemainAfterExit = true;
         TimeoutSec = 60;
         ExecStart = "${pkgs.logger}/bin/logger 'prometheus-config-reload will only reload prometheus when reloaded itself.'";
-        ExecReload = [
-          "${writeConfig}/bin/write-prometheus-config"
-          "+${triggerReload}/bin/trigger-reload-prometheus"
-        ];
+        ExecReload = [ "${triggerReload}/bin/trigger-reload-prometheus" ];
       };
     };
   };
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
index 5ee8c346be1dc..3cdd7866bd4db 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/nginx.nix
@@ -47,7 +47,7 @@ in
       ExecStart = ''
         ${pkgs.prometheus-nginx-exporter}/bin/nginx-prometheus-exporter \
           --nginx.scrape-uri '${cfg.scrapeUri}' \
-          --nginx.ssl-verify ${toString cfg.sslVerify} \
+          --nginx.ssl-verify ${boolToString cfg.sslVerify} \
           --web.listen-address ${cfg.listenAddress}:${toString cfg.port} \
           --web.telemetry-path ${cfg.telemetryPath} \
           --prometheus.const-labels ${concatStringsSep "," cfg.constLabels} \
diff --git a/nixos/modules/services/monitoring/zabbix-server.nix b/nixos/modules/services/monitoring/zabbix-server.nix
index 9b0fd9dbff13e..0141c073da25d 100644
--- a/nixos/modules/services/monitoring/zabbix-server.nix
+++ b/nixos/modules/services/monitoring/zabbix-server.nix
@@ -250,7 +250,12 @@ in
     };
 
     security.wrappers = {
-      fping.source = "${pkgs.fping}/bin/fping";
+      fping =
+        { setuid = true;
+          owner = "root";
+          group = "root";
+          source = "${pkgs.fping}/bin/fping";
+        };
     };
 
     systemd.services.zabbix-server = {
diff --git a/nixos/modules/services/network-filesystems/webdav.nix b/nixos/modules/services/network-filesystems/webdav.nix
new file mode 100644
index 0000000000000..4086a0f5d5620
--- /dev/null
+++ b/nixos/modules/services/network-filesystems/webdav.nix
@@ -0,0 +1,107 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.services.webdav;
+  format = pkgs.formats.yaml { };
+in
+{
+  options = {
+    services.webdav = {
+      enable = mkEnableOption "WebDAV server";
+
+      user = mkOption {
+        type = types.str;
+        default = "webdav";
+        description = "User account under which WebDAV runs.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "webdav";
+        description = "Group under which WebDAV runs.";
+      };
+
+      settings = mkOption {
+        type = format.type;
+        default = { };
+        description = ''
+          Attrset that is converted and passed as config file. Available options
+          can be found at
+          <link xlink:href="https://github.com/hacdias/webdav">here</link>.
+
+          This program supports reading username and password configuration
+          from environment variables, so it's strongly recommended to store
+          username and password in a separate
+          <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#EnvironmentFile=">EnvironmentFile</link>.
+          This prevents adding secrets to the world-readable Nix store.
+        '';
+        example = literalExpression ''
+          {
+              address = "0.0.0.0";
+              port = 8080;
+              scope = "/srv/public";
+              modify = true;
+              auth = true;
+              users = [
+                {
+                  username = "{env}ENV_USERNAME";
+                  password = "{env}ENV_PASSWORD";
+                }
+              ];
+          }
+        '';
+      };
+
+      configFile = mkOption {
+        type = types.path;
+        default = format.generate "webdav.yaml" cfg.settings;
+        defaultText = "Config file generated from services.webdav.settings";
+        description = ''
+          Path to config file. If this option is set, it will override any
+          configuration done in options.services.webdav.settings.
+        '';
+        example = "/etc/webdav/config.yaml";
+      };
+
+      environmentFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Environment file as defined in <citerefentry>
+          <refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum>
+          </citerefentry>.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.users = mkIf (cfg.user == "webdav") {
+      webdav = {
+        description = "WebDAV daemon user";
+        isSystemUser = true;
+        group = cfg.group;
+      };
+    };
+
+    users.groups = mkIf (cfg.group == "webdav") {
+      webdav = { };
+    };
+
+    systemd.services.webdav = {
+      description = "WebDAV server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart = "${pkgs.webdav}/bin/webdav -c ${cfg.configFile}";
+        Restart = "on-failure";
+        User = cfg.user;
+        Group = cfg.group;
+        EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
+      };
+    };
+  };
+
+  meta.maintainers = with maintainers; [ pengmeiyu ];
+}
diff --git a/nixos/modules/services/networking/antennas.nix b/nixos/modules/services/networking/antennas.nix
new file mode 100644
index 0000000000000..ef98af22f20f2
--- /dev/null
+++ b/nixos/modules/services/networking/antennas.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let cfg = config.services.antennas;
+in
+
+{
+  options = {
+    services.antennas = {
+      enable = mkEnableOption "Antennas";
+
+      tvheadendUrl = mkOption {
+        type        = types.str;
+        default     = "http://localhost:9981";
+        description = "URL of Tvheadend.";
+      };
+
+      antennasUrl = mkOption {
+        type        = types.str;
+        default     = "http://127.0.0.1:5004";
+        description = "URL of Antennas.";
+      };
+
+      tunerCount = mkOption {
+        type        = types.int;
+        default     = 6;
+        description = "Numbers of tuners in tvheadend.";
+      };
+
+      deviceUUID = mkOption {
+        type        = types.str;
+        default     = "2f70c0d7-90a3-4429-8275-cbeeee9cd605";
+        description = "Device tuner UUID. Change this if you are running multiple instances.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.antennas = {
+      description = "Antennas HDHomeRun emulator for Tvheadend. ";
+      wantedBy    = [ "multi-user.target" ];
+
+      # Config
+      environment = {
+        TVHEADEND_URL = cfg.tvheadendUrl;
+        ANTENNAS_URL = cfg.antennasUrl;
+        TUNER_COUNT = toString cfg.tunerCount;
+        DEVICE_UUID = cfg.deviceUUID;
+      };
+
+      serviceConfig = {
+         ExecStart = "${pkgs.antennas}/bin/antennas";
+
+        # Antennas expects all resources like html and config to be relative to it's working directory
+        WorkingDirectory = "${pkgs.antennas}/libexec/antennas/deps/antennas/";
+
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DynamicUser = true;
+        LockPersonality = true;
+        ProcSubset = "pid";
+        PrivateDevices = true;
+        PrivateUsers = true;
+        PrivateTmp = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 74daf0ae9fafe..73e63e2ee99b5 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -527,7 +527,6 @@ in {
 
       {
         networkmanager.connectionConfig = {
-          "ipv6.ip6-privacy" = 2;
           "ethernet.cloned-mac-address" = cfg.ethernet.macAddress;
           "wifi.cloned-mac-address" = cfg.wifi.macAddress;
           "wifi.powersave" =
diff --git a/nixos/modules/services/networking/nomad.nix b/nixos/modules/services/networking/nomad.nix
index 3bd15bd5c808d..43333af5e2fea 100644
--- a/nixos/modules/services/networking/nomad.nix
+++ b/nixos/modules/services/networking/nomad.nix
@@ -51,7 +51,7 @@ in
 
       extraSettingsPaths = mkOption {
         type = types.listOf types.path;
-        default = [];
+        default = [ ];
         description = ''
           Additional settings paths used to configure nomad. These can be files or directories.
         '';
@@ -60,9 +60,21 @@ in
         '';
       };
 
+      extraSettingsPlugins = mkOption {
+        type = types.listOf (types.either types.package types.path);
+        default = [ ];
+        description = ''
+          Additional plugins dir used to configure nomad.
+        '';
+        example = literalExpression ''
+          [ "<pluginDir>" "pkgs.<plugins-name>"]
+        '';
+      };
+
+
       settings = mkOption {
         type = format.type;
-        default = {};
+        default = { };
         description = ''
           Configuration for Nomad. See the <link xlink:href="https://www.nomadproject.io/docs/configuration">documentation</link>
           for supported values.
@@ -128,7 +140,8 @@ in
           DynamicUser = cfg.dropPrivileges;
           ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
           ExecStart = "${cfg.package}/bin/nomad agent -config=/etc/nomad.json" +
-            concatMapStrings (path: " -config=${path}") cfg.extraSettingsPaths;
+            concatMapStrings (path: " -config=${path}") cfg.extraSettingsPaths +
+            concatMapStrings (path: " -plugin-dir=${path}/bin") cfg.extraSettingsPlugins;
           KillMode = "process";
           KillSignal = "SIGINT";
           LimitNOFILE = 65536;
diff --git a/nixos/modules/services/networking/sabnzbd.nix b/nixos/modules/services/networking/sabnzbd.nix
index 43566dfd25c5f..54eeba1a9ec17 100644
--- a/nixos/modules/services/networking/sabnzbd.nix
+++ b/nixos/modules/services/networking/sabnzbd.nix
@@ -17,6 +17,13 @@ in
     services.sabnzbd = {
       enable = mkEnableOption "the sabnzbd server";
 
+      package = mkOption {
+        type = types.package;
+        default = pkgs.sabnzbd;
+        defaultText = "pkgs.sabnzbd";
+        description = "The sabnzbd executable package run by the service.";
+      };
+
       configFile = mkOption {
         type = types.path;
         default = "/var/lib/sabnzbd/sabnzbd.ini";
@@ -63,7 +70,7 @@ in
           GuessMainPID = "no";
           User = "${cfg.user}";
           Group = "${cfg.group}";
-          ExecStart = "${sabnzbd}/bin/sabnzbd -d -f ${cfg.configFile}";
+          ExecStart = "${lib.getBin cfg.package}/bin/sabnzbd -d -f ${cfg.configFile}";
         };
     };
   };
diff --git a/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
index 8ae62931a8f90..cca61b9ce930e 100644
--- a/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
+++ b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
@@ -569,6 +569,16 @@ in {
         these sections offer more flexibility.
       '';
 
+      ca_id = mkOptionalStrParam ''
+        Identity in CA certificate to accept for authentication. The specified
+        identity must be contained in one (intermediate) CA of the remote peer
+        trustchain, either as subject or as subjectAltName. This has the same
+        effect as specifying <literal>cacerts</literal> to force clients under
+        a CA to specific connections; it does not require the CA certificate
+        to be available locally, and can be received from the peer during the
+        IKE exchange.
+      '';
+
       cacerts = mkCommaSepListParam [] ''
         List of CA certificates to accept for
         authentication. The certificates may use a relative path from the
diff --git a/nixos/modules/services/networking/teamspeak3.nix b/nixos/modules/services/networking/teamspeak3.nix
index fadb32dcd7770..c0ed08282aaf3 100644
--- a/nixos/modules/services/networking/teamspeak3.nix
+++ b/nixos/modules/services/networking/teamspeak3.nix
@@ -43,7 +43,7 @@ in
       voiceIP = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "0.0.0.0";
+        example = "[::]";
         description = ''
           IP on which the server instance will listen for incoming voice connections. Defaults to any IP.
         '';
@@ -60,7 +60,7 @@ in
       fileTransferIP = mkOption {
         type = types.nullOr types.str;
         default = null;
-        example = "0.0.0.0";
+        example = "[::]";
         description = ''
           IP on which the server instance will listen for incoming file transfer connections. Defaults to any IP.
         '';
@@ -91,6 +91,18 @@ in
         '';
       };
 
+      openFirewall = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for the TeamSpeak3 server.";
+      };
+
+      openFirewallServerQuery = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Open ports in the firewall for the TeamSpeak3 serverquery (administration) system. Requires openFirewall.";
+      };
+
     };
 
   };
@@ -115,6 +127,12 @@ in
       "d '${cfg.logPath}' - ${user} ${group} - -"
     ];
 
+    networking.firewall = mkIf cfg.openFirewall {
+      allowedTCPPorts = [ cfg.fileTransferPort ] ++ optionals (cfg.openFirewallServerQuery) [ cfg.queryPort (cfg.queryPort + 11) ];
+      # subsequent vServers will use the incremented voice port, let's just open the next 10
+      allowedUDPPortRanges = [ { from = cfg.defaultVoicePort; to = cfg.defaultVoicePort + 10; } ];
+    };
+
     systemd.services.teamspeak3-server = {
       description = "Teamspeak3 voice communication server daemon";
       after = [ "network.target" ];
diff --git a/nixos/modules/services/networking/tinc.nix b/nixos/modules/services/networking/tinc.nix
index 1d77503d68bc4..9db433fa0735c 100644
--- a/nixos/modules/services/networking/tinc.nix
+++ b/nixos/modules/services/networking/tinc.nix
@@ -289,13 +289,13 @@ in
             };
 
             chroot = mkOption {
-              default = true;
+              default = false;
               type = types.bool;
               description = ''
                 Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security.
                 The chroot is performed after all the initialization is done, after writing pid files and opening network sockets.
 
-                Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
+                Note that this currently breaks dns resolution and tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment.
               '';
             };
 
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index c94b248d5f101..c3e3248ee8ab1 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -1012,6 +1012,7 @@ in
         # Tor cannot currently bind privileged port when PrivateUsers=true,
         # see https://gitlab.torproject.org/legacy/trac/-/issues/20930
         PrivateUsers = !bindsPrivilegedPort;
+        ProcSubset = "pid";
         ProtectClock = true;
         ProtectControlGroups = true;
         ProtectHome = true;
@@ -1019,6 +1020,7 @@ in
         ProtectKernelLogs = true;
         ProtectKernelModules = true;
         ProtectKernelTunables = true;
+        ProtectProc = "invisible";
         ProtectSystem = "strict";
         RemoveIPC = true;
         RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" "AF_NETLINK" ];
diff --git a/nixos/modules/services/ttys/getty.nix b/nixos/modules/services/ttys/getty.nix
index 8c5b6e5e0cbc1..7021a2c80f857 100644
--- a/nixos/modules/services/ttys/getty.nix
+++ b/nixos/modules/services/ttys/getty.nix
@@ -24,6 +24,7 @@ in
 
   imports = [
     (mkRenamedOptionModule [ "services" "mingetty" ] [ "services" "getty" ])
+    (mkRemovedOptionModule [ "services" "getty" "serialSpeed" ] ''set non-standard baudrates with `boot.kernelParams` i.e. boot.kernelParams = ["console=ttyS2,1500000"];'')
   ];
 
   options = {
@@ -92,17 +93,6 @@ in
         '';
       };
 
-      serialSpeed = mkOption {
-        type = types.listOf types.int;
-        default = [ 115200 57600 38400 9600 ];
-        example = [ 38400 9600 ];
-        description = ''
-            Bitrates to allow for agetty's listening on serial ports. Listing more
-            bitrates gives more interoperability but at the cost of long delays
-            for getting a sync on the line.
-        '';
-      };
-
     };
 
   };
@@ -124,10 +114,9 @@ in
       };
 
     systemd.services."serial-getty@" =
-      let speeds = concatStringsSep "," (map toString config.services.getty.serialSpeed); in
       { serviceConfig.ExecStart = [
           "" # override upstream default with an empty ExecStart
-          (gettyCmd "%I --keep-baud ${speeds} $TERM")
+          (gettyCmd "%I --keep-baud $TERM")
         ];
         restartIfChanged = false;
       };
diff --git a/nixos/modules/services/web-apps/hledger-web.nix b/nixos/modules/services/web-apps/hledger-web.nix
index 9c66589dffd1b..4f6a34e6d2fe5 100644
--- a/nixos/modules/services/web-apps/hledger-web.nix
+++ b/nixos/modules/services/web-apps/hledger-web.nix
@@ -118,7 +118,7 @@ in {
         ++ extraOptions);
     in {
       description = "hledger-web - web-app for the hledger accounting tool.";
-      documentation = [ https://hledger.org/hledger-web.html ];
+      documentation = [ "https://hledger.org/hledger-web.html" ];
       wantedBy = [ "multi-user.target" ];
       after = [ "networking.target" ];
       serviceConfig = mkMerge [
diff --git a/nixos/modules/services/web-apps/matomo.nix b/nixos/modules/services/web-apps/matomo.nix
index b0d281cfb6ed3..eba55e7e9befa 100644
--- a/nixos/modules/services/web-apps/matomo.nix
+++ b/nixos/modules/services/web-apps/matomo.nix
@@ -24,6 +24,7 @@ in {
     (mkRemovedOptionModule [ "services" "piwik" "phpfpmProcessManagerConfig" ] "Use services.phpfpm.pools.<name>.settings")
     (mkRemovedOptionModule [ "services" "matomo" "phpfpmProcessManagerConfig" ] "Use services.phpfpm.pools.<name>.settings")
     (mkRenamedOptionModule [ "services" "piwik" "nginx" ] [ "services" "matomo" "nginx" ])
+    (mkRenamedOptionModule [ "services" "matomo" "periodicArchiveProcessingUrl" ] [ "services" "matomo" "hostname" ])
   ];
 
   options = {
@@ -77,7 +78,7 @@ in {
         '';
       };
 
-      periodicArchiveProcessingUrl = mkOption {
+      hostname = mkOption {
         type = types.str;
         default = "${user}.${fqdn}";
         example = "matomo.yourdomain.org";
@@ -170,6 +171,19 @@ in {
         fi
         chown -R ${user}:${user} ${dataDir}
         chmod -R ug+rwX,o-rwx ${dataDir}
+
+        if [ -e ${dataDir}/current-package ]; then
+          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
+            rm -rf ${dataDir}/tmp
+          fi
+        elif [ -e ${dataDir}/tmp ]; then
+          # upgrade from 4.4.1
+          rm -rf ${dataDir}/tmp
+        fi
+        ln -sfT ${cfg.package} ${dataDir}/current-package
         '';
       script = ''
             # Use User-Private Group scheme to protect Matomo data, but allow administration / backup via 'matomo' group
@@ -202,7 +216,7 @@ in {
         UMask = "0007";
         CPUSchedulingPolicy = "idle";
         IOSchedulingClass = "idle";
-        ExecStart = "${cfg.package}/bin/matomo-console core:archive --url=https://${cfg.periodicArchiveProcessingUrl}";
+        ExecStart = "${cfg.package}/bin/matomo-console core:archive --url=https://${cfg.hostname}";
       };
     };
 
@@ -258,7 +272,7 @@ in {
       # References:
       # https://fralef.me/piwik-hardening-with-nginx-and-php-fpm.html
       # https://github.com/perusio/piwik-nginx
-      "${user}.${fqdn}" = mkMerge [ cfg.nginx {
+      "${cfg.hostname}" = mkMerge [ cfg.nginx {
         # don't allow to override the root easily, as it will almost certainly break Matomo.
         # disadvantage: not shown as default in docs.
         root = mkForce "${cfg.package}/share";
diff --git a/nixos/modules/services/web-apps/openwebrx.nix b/nixos/modules/services/web-apps/openwebrx.nix
new file mode 100644
index 0000000000000..51005cd1e4972
--- /dev/null
+++ b/nixos/modules/services/web-apps/openwebrx.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.openwebrx;
+in
+{
+  options.services.openwebrx = with lib; {
+    enable = mkEnableOption "OpenWebRX Web interface for Software-Defined Radios on http://localhost:8073";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.openwebrx;
+      description = "OpenWebRX package to use for the service";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.openwebrx = {
+      wantedBy = [ "multi-user.target" ];
+      path = with pkgs; [
+        csdr
+        alsaUtils
+        netcat
+      ];
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/openwebrx";
+        Restart = "always";
+        DynamicUser = true;
+        # openwebrx uses /var/lib/openwebrx by default
+        StateDirectory = [ "openwebrx" ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/web-apps/plausible.nix b/nixos/modules/services/web-apps/plausible.nix
index b56848b79d21c..b6c48186a1d32 100644
--- a/nixos/modules/services/web-apps/plausible.nix
+++ b/nixos/modules/services/web-apps/plausible.nix
@@ -5,23 +5,18 @@ with lib;
 let
   cfg = config.services.plausible;
 
-  # FIXME consider using LoadCredential as soon as it actually works.
-  envSecrets = ''
-    ADMIN_USER_PWD="$(<${cfg.adminUser.passwordFile})"
-    export ADMIN_USER_PWD # separate export to make `set -e` work
-
-    SECRET_KEY_BASE="$(<${cfg.server.secretKeybaseFile})"
-    export SECRET_KEY_BASE # separate export to make `set -e` work
-
-    ${optionalString (cfg.mail.smtp.passwordFile != null) ''
-      SMTP_USER_PWD="$(<${cfg.mail.smtp.passwordFile})"
-      export SMTP_USER_PWD # separate export to make `set -e` work
-    ''}
-  '';
 in {
   options.services.plausible = {
     enable = mkEnableOption "plausible";
 
+    releaseCookiePath = mkOption {
+      default = null;
+      type = with types; nullOr (either str path);
+      description = ''
+        The path to the file with release cookie. (used for remote connection to the running node).
+      '';
+    };
+
     adminUser = {
       name = mkOption {
         default = "admin";
@@ -184,13 +179,17 @@ in {
       enable = true;
     };
 
+    services.epmd.enable = true;
+
+    environment.systemPackages = [ pkgs.plausible ];
+
     systemd.services = mkMerge [
       {
         plausible = {
           inherit (pkgs.plausible.meta) description;
           documentation = [ "https://plausible.io/docs/self-hosting" ];
           wantedBy = [ "multi-user.target" ];
-          after = optional cfg.database.postgres.setup "plausible-postgres.service";
+          after = optionals cfg.database.postgres.setup [ "postgresql.service" "plausible-postgres.service" ];
           requires = optional cfg.database.clickhouse.setup "clickhouse.service"
             ++ optionals cfg.database.postgres.setup [
               "postgresql.service"
@@ -200,7 +199,7 @@ in {
           environment = {
             # NixOS specific option to avoid that it's trying to write into its store-path.
             # See also https://github.com/lau/tzdata#data-directory-and-releases
-            TZDATA_DIR = "/var/lib/plausible/elixir_tzdata";
+            STORAGE_DIR = "/var/lib/plausible/elixir_tzdata";
 
             # Configuration options from
             # https://plausible.io/docs/self-hosting-configuration
@@ -208,6 +207,8 @@ in {
             DISABLE_REGISTRATION = boolToString cfg.server.disableRegistration;
 
             RELEASE_TMP = "/var/lib/plausible/tmp";
+            # Home is needed to connect to the node with iex
+            HOME = "/var/lib/plausible";
 
             ADMIN_USER_NAME = cfg.adminUser.name;
             ADMIN_USER_EMAIL = cfg.adminUser.email;
@@ -231,28 +232,33 @@ in {
 
           path = [ pkgs.plausible ]
             ++ optional cfg.database.postgres.setup config.services.postgresql.package;
+          script = ''
+            export CONFIG_DIR=$CREDENTIALS_DIRECTORY
+
+            # setup
+            ${pkgs.plausible}/createdb.sh
+            ${pkgs.plausible}/migrate.sh
+            ${optionalString cfg.adminUser.activate ''
+              if ! ${pkgs.plausible}/init-admin.sh | grep 'already exists'; then
+                psql -d plausible <<< "UPDATE users SET email_verified=true;"
+              fi
+            ''}
+            ${optionalString (cfg.releaseCookiePath != null) ''
+              export RELEASE_COOKIE="$(< $CREDENTIALS_DIRECTORY/RELEASE_COOKIE )"
+            ''}
+            plausible start
+          '';
 
           serviceConfig = {
             DynamicUser = true;
             PrivateTmp = true;
             WorkingDirectory = "/var/lib/plausible";
             StateDirectory = "plausible";
-            ExecStartPre = "@${pkgs.writeShellScript "plausible-setup" ''
-              set -eu -o pipefail
-              ${envSecrets}
-              ${pkgs.plausible}/createdb.sh
-              ${pkgs.plausible}/migrate.sh
-              ${optionalString cfg.adminUser.activate ''
-                if ! ${pkgs.plausible}/init-admin.sh | grep 'already exists'; then
-                  psql -d plausible <<< "UPDATE users SET email_verified=true;"
-                fi
-              ''}
-            ''} plausible-setup";
-            ExecStart = "@${pkgs.writeShellScript "plausible" ''
-              set -eu -o pipefail
-              ${envSecrets}
-              plausible start
-            ''} plausible";
+            LoadCredential = [
+              "ADMIN_USER_PWD:${cfg.adminUser.passwordFile}"
+              "SECRET_KEY_BASE:${cfg.server.secretKeybaseFile}"
+            ] ++ lib.optionals (cfg.mail.smtp.passwordFile != null) [ "SMTP_USER_PWD:${cfg.mail.smtp.passwordFile}"]
+            ++ lib.optionals (cfg.releaseCookiePath != null) [ "RELEASE_COOKIE:${cfg.releaseCookiePath}"];
           };
         };
       }
@@ -260,20 +266,22 @@ in {
         # `plausible' requires the `citext'-extension.
         plausible-postgres = {
           after = [ "postgresql.service" ];
-          bindsTo = [ "postgresql.service" ];
-          requiredBy = [ "plausible.service" ];
           partOf = [ "plausible.service" ];
-          serviceConfig.Type = "oneshot";
-          unitConfig.ConditionPathExists = "!/var/lib/plausible/.db-setup";
-          script = ''
-            mkdir -p /var/lib/plausible/
+          serviceConfig = {
+            Type = "oneshot";
+            User = config.services.postgresql.superUser;
+            RemainAfterExit = true;
+          };
+          script = with cfg.database.postgres; ''
             PSQL() {
-              /run/wrappers/bin/sudo -Hu postgres ${config.services.postgresql.package}/bin/psql --port=5432 "$@"
+              ${config.services.postgresql.package}/bin/psql --port=5432 "$@"
             }
-            PSQL -tAc "CREATE ROLE plausible WITH LOGIN;"
-            PSQL -tAc "CREATE DATABASE plausible WITH OWNER plausible;"
-            PSQL -d plausible -tAc "CREATE EXTENSION IF NOT EXISTS citext;"
-            touch /var/lib/plausible/.db-setup
+            # check if the database already exists
+            if ! PSQL -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw ${dbname} ; then
+              PSQL -tAc "CREATE ROLE plausible WITH LOGIN;"
+              PSQL -tAc "CREATE DATABASE ${dbname} WITH OWNER plausible;"
+              PSQL -d ${dbname} -tAc "CREATE EXTENSION IF NOT EXISTS citext;"
+            fi
           '';
         };
       })
diff --git a/nixos/modules/services/web-apps/zabbix.nix b/nixos/modules/services/web-apps/zabbix.nix
index 21567896a89e7..ff50b95254f90 100644
--- a/nixos/modules/services/web-apps/zabbix.nix
+++ b/nixos/modules/services/web-apps/zabbix.nix
@@ -21,7 +21,8 @@ let
     $DB['PORT'] = '${toString cfg.database.port}';
     $DB['DATABASE'] = '${cfg.database.name}';
     $DB['USER'] = '${cfg.database.user}';
-    $DB['PASSWORD'] = ${if cfg.database.passwordFile != null then "file_get_contents('${cfg.database.passwordFile}')" else "''"};
+    # NOTE: file_get_contents adds newline at the end of returned string
+    $DB['PASSWORD'] = ${if cfg.database.passwordFile != null then "trim(file_get_contents('${cfg.database.passwordFile}'), \"\\r\\n\")" else "''"};
     // Schema name. Used for IBM DB2 and PostgreSQL.
     $DB['SCHEMA'] = ''';
     $ZBX_SERVER = '${cfg.server.address}';
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index be589e42ddd6a..5717b86b3bea6 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -896,7 +896,7 @@ in
         PrivateMounts = true;
         # System Call Filtering
         SystemCallArchitectures = "native";
-        SystemCallFilter = "~@cpu-emulation @debug @keyring @ipc @mount @obsolete @privileged @setuid";
+        SystemCallFilter = "~@cpu-emulation @debug @keyring @ipc @mount @obsolete @privileged @setuid @mincore";
       };
     };
 
diff --git a/nixos/modules/services/x11/clight.nix b/nixos/modules/services/x11/clight.nix
index 873f425fb8be4..d994a658cbaa5 100644
--- a/nixos/modules/services/x11/clight.nix
+++ b/nixos/modules/services/x11/clight.nix
@@ -71,6 +71,14 @@ in {
   };
 
   config = mkIf cfg.enable {
+    assertions = let
+      inRange = v: l: r: v >= l && v <= r;
+    in [
+      { assertion = config.location.provider == "manual" ->
+          inRange config.location.latitude (-90) 90 && inRange config.location.longitude (-180) 180;
+        message = "You must specify a valid latitude and longitude if manually providing location"; }
+    ];
+
     boot.kernelModules = [ "i2c_dev" ];
     environment.systemPackages = with pkgs; [ clight clightd ];
     services.dbus.packages = with pkgs; [ clight clightd ];
diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.xml b/nixos/modules/services/x11/desktop-managers/pantheon.xml
index 70cd7410c1e0b..64933349e7988 100644
--- a/nixos/modules/services/x11/desktop-managers/pantheon.xml
+++ b/nixos/modules/services/x11/desktop-managers/pantheon.xml
@@ -107,12 +107,6 @@ switchboard-with-plugs.override {
      <para>
       AppCenter has been available since 20.03, but it is of little use. This is because there is no functioning PackageKit backend for Nix 2.0. The Flatpak backend will not work before <link xlink:href="https://github.com/elementary/appcenter/issues/1076">flag for Flatpak-only</link> is provided. See this <link xlink:href="https://github.com/NixOS/nixpkgs/issues/70214">issue</link>.
      </para>
-     <para>
-      To use AppCenter on NixOS, add <literal>pantheon.appcenter</literal> to <xref linkend="opt-environment.systemPackages" />, <link linkend="module-services-flatpak">enable Flatpak support</link> and optionally add the <literal>appcenter</literal> Flatpak remote:
-     </para>
-<screen>
-<prompt>$ </prompt>flatpak remote-add --if-not-exists appcenter https://flatpak.elementary.io/repo.flatpakrepo
-</screen>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index 1c9a5f978c543..9a7532b476415 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -146,7 +146,7 @@ in
       };
 
       background = mkOption {
-        type = types.path;
+        type = types.either types.path (types.strMatching "^#[0-9]\{6\}$");
         # Manual cannot depend on packages, we are actually setting the default in config below.
         defaultText = literalExpression "pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom.gnomeFilePath";
         description = ''
diff --git a/nixos/modules/services/x11/display-managers/startx.nix b/nixos/modules/services/x11/display-managers/startx.nix
index 6cd46cdf96498..a48566ae06841 100644
--- a/nixos/modules/services/x11/display-managers/startx.nix
+++ b/nixos/modules/services/x11/display-managers/startx.nix
@@ -35,10 +35,7 @@ in
   config = mkIf cfg.enable {
     services.xserver = {
       exportConfiguration = true;
-      displayManager.job.execCmd = "";
-      displayManager.lightdm.enable = lib.mkForce false;
     };
-    systemd.services.display-manager.enable = false;
 
     # Other displayManagers log to /dev/null because they're services and put
     # Xorg's stdout in the journal
diff --git a/nixos/modules/services/x11/display-managers/sx.nix b/nixos/modules/services/x11/display-managers/sx.nix
index 73d27390a5803..e309773643009 100644
--- a/nixos/modules/services/x11/display-managers/sx.nix
+++ b/nixos/modules/services/x11/display-managers/sx.nix
@@ -26,13 +26,8 @@ in {
     environment.systemPackages = [ pkgs.sx ];
     services.xserver = {
       exportConfiguration = true;
-      displayManager = {
-        job.execCmd = "";
-        lightdm.enable = mkForce false;
-      };
       logFile = mkDefault null;
     };
-    systemd.services.display-manager.enable = false;
   };
 
   meta.maintainers = with maintainers; [ figsoda ];
diff --git a/nixos/modules/services/x11/hardware/libinput.nix b/nixos/modules/services/x11/hardware/libinput.nix
index e2fb7d0918e36..efdb7c61dfaeb 100644
--- a/nixos/modules/services/x11/hardware/libinput.nix
+++ b/nixos/modules/services/x11/hardware/libinput.nix
@@ -13,7 +13,7 @@ let cfg = config.services.xserver.libinput;
         example = "/dev/input/event0";
         description =
           ''
-            Path for ${deviceType} device.  Set to null to apply to any
+            Path for ${deviceType} device.  Set to <literal>null</literal> to apply to any
             auto-detected ${deviceType}.
           '';
       };
@@ -24,8 +24,8 @@ let cfg = config.services.xserver.libinput;
         example = "flat";
         description =
           ''
-            Sets  the pointer acceleration profile to the given profile.
-            Permitted values are adaptive, flat.
+            Sets the pointer acceleration profile to the given profile.
+            Permitted values are <literal>adaptive</literal>, <literal>flat</literal>.
             Not all devices support this option or all profiles.
             If a profile is unsupported, the default profile for this is used.
             <literal>flat</literal>: Pointer motion is accelerated by a constant
@@ -38,12 +38,14 @@ let cfg = config.services.xserver.libinput;
       accelSpeed = mkOption {
         type = types.nullOr types.str;
         default = null;
+        example = "-0.5";
         description = "Cursor acceleration (how fast speed increases from minSpeed to maxSpeed).";
       };
 
       buttonMapping = mkOption {
         type = types.nullOr types.str;
         default = null;
+        example = "1 6 3 4 5 0 7";
         description =
           ''
             Sets the logical button mapping for this device, see XSetPointerMapping(3). The string  must
@@ -58,9 +60,10 @@ let cfg = config.services.xserver.libinput;
       calibrationMatrix = mkOption {
         type = types.nullOr types.str;
         default = null;
+        example = "0.5 0 0 0 0.8 0.1 0 0 1";
         description =
           ''
-            A  string  of  9 space-separated floating point numbers.  Sets the calibration matrix to the
+            A string of 9 space-separated floating point numbers. Sets the calibration matrix to the
             3x3 matrix where the first row is (abc), the second row is (def) and the third row is (ghi).
           '';
       };
@@ -68,6 +71,7 @@ let cfg = config.services.xserver.libinput;
       clickMethod = mkOption {
         type = types.nullOr (types.enum [ "none" "buttonareas" "clickfinger" ]);
         default = null;
+        example = "buttonareas";
         description =
           ''
             Enables a click method. Permitted values are <literal>none</literal>,
@@ -166,8 +170,9 @@ let cfg = config.services.xserver.libinput;
       transformationMatrix = mkOption {
         type = types.nullOr types.str;
         default = null;
+        example = "0.5 0 0 0 0.8 0.1 0 0 1";
         description = ''
-          A  string  of  9 space-separated floating point numbers.  Sets the transformation matrix to
+          A string of 9 space-separated floating point numbers. Sets the transformation matrix to
           the 3x3 matrix where the first row is (abc), the second row is (def) and the third row is (ghi).
         '';
       };
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index cb620f10b13fc..24d9257344235 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -588,11 +588,22 @@ in
   config = mkIf cfg.enable {
 
     services.xserver.displayManager.lightdm.enable =
-      let dmconf = cfg.displayManager;
-          default = !(dmconf.gdm.enable
-                    || dmconf.sddm.enable
-                    || dmconf.xpra.enable );
-      in mkIf (default) true;
+      let dmConf = cfg.displayManager;
+          default = !(dmConf.gdm.enable
+                    || dmConf.sddm.enable
+                    || dmConf.xpra.enable
+                    || dmConf.sx.enable
+                    || dmConf.startx.enable);
+      in mkIf (default) (mkDefault true);
+
+    # so that the service won't be enabled when only startx is used
+    systemd.services.display-manager.enable  =
+      let dmConf = cfg.displayManager;
+          noDmUsed = !(dmConf.gdm.enable
+                    || dmConf.sddm.enable
+                    || dmConf.xpra.enable
+                    || dmConf.lightdm.enable);
+      in mkIf (noDmUsed) (mkDefault false);
 
     hardware.opengl.enable = mkDefault true;
 
@@ -702,7 +713,8 @@ in
             rm -f /tmp/.X0-lock
           '';
 
-        script = "${cfg.displayManager.job.execCmd}";
+        # TODO: move declaring the systemd service to its own mkIf
+        script = mkIf (config.systemd.services.display-manager.enable == true) "${cfg.displayManager.job.execCmd}";
 
         # Stop restarting if the display manager stops (crashes) 2 times
         # in one minute. Starting X typically takes 3-4s.
diff --git a/nixos/modules/system/boot/binfmt.nix b/nixos/modules/system/boot/binfmt.nix
index 2408ecc80d22b..fdb4d0e4c7fb3 100644
--- a/nixos/modules/system/boot/binfmt.nix
+++ b/nixos/modules/system/boot/binfmt.nix
@@ -248,6 +248,7 @@ in {
         description = ''
           List of systems to emulate. Will also configure Nix to
           support your new systems.
+          Warning: the builder can execute all emulated systems within the same build, which introduces impurities in the case of cross compilation.
         '';
         type = types.listOf types.str;
       };
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 4a9da93945191..d147155d796c1 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -243,7 +243,7 @@ in
             "hid_generic" "hid_lenovo" "hid_apple" "hid_roccat"
             "hid_logitech_hidpp" "hid_logitech_dj" "hid_microsoft"
 
-          ] ++ optionals (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+          ] ++ optionals pkgs.stdenv.hostPlatform.isx86 [
             # Misc. x86 keyboard stuff.
             "pcips2" "atkbd" "i8042"
 
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 662dfe2db9895..2e17bdf6bb659 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -131,6 +131,7 @@ let
           "fou"
           "xfrm"
           "ifb"
+          "batadv"
         ])
         (assertByteFormat "MTUBytes")
         (assertMacAddress "MACAddress")
@@ -381,6 +382,29 @@ let
         (assertInt "Table")
         (assertMinimum "Table" 0)
       ];
+
+      sectionBatmanAdvanced = checkUnitConfig "BatmanAdvanced" [
+        (assertOnlyFields [
+          "GatewayMode"
+          "Aggregation"
+          "BridgeLoopAvoidance"
+          "DistributedArpTable"
+          "Fragmentation"
+          "HopPenalty"
+          "OriginatorIntervalSec"
+          "GatewayBandwithDown"
+          "GatewayBandwithUp"
+          "RoutingAlgorithm"
+        ])
+        (assertValueOneOf "GatewayMode" ["off" "client" "server"])
+        (assertValueOneOf "Aggregation" boolValues)
+        (assertValueOneOf "BridgeLoopAvoidance" boolValues)
+        (assertValueOneOf "DistributedArpTable" boolValues)
+        (assertValueOneOf "Fragmentation" boolValues)
+        (assertInt "HopPenalty")
+        (assertRange "HopPenalty" 0 255)
+        (assertValueOneOf "RoutingAlgorithm" ["batman-v" "batman-iv"])
+      ];
     };
 
     network = {
@@ -473,6 +497,7 @@ let
           "IgnoreCarrierLoss"
           "Xfrm"
           "KeepConfiguration"
+          "BatmanAdvanced"
         ])
         # Note: For DHCP the values both, none, v4, v6 are deprecated
         (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"])
@@ -1056,6 +1081,21 @@ let
       '';
     };
 
+    batmanAdvancedConfig = mkOption {
+      default = {};
+      example = {
+        GatewayMode = "server";
+        RoutingAlgorithm = "batman-v";
+      };
+      type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionBatmanAdvanced;
+      description = ''
+        Each attribute in this set specifies an option in the
+        <literal>[BatmanAdvanced]</literal> section of the unit. See
+        <citerefentry><refentrytitle>systemd.netdev</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
   };
 
   addressOptions = {
@@ -1507,6 +1547,10 @@ let
           [VRF]
           ${attrsToSection def.vrfConfig}
         ''
+        + optionalString (def.batmanAdvancedConfig != { }) ''
+          [BatmanAdvanced]
+          ${attrsToSection def.batmanAdvancedConfig}
+        ''
         + def.extraConfig;
     };
 
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 8f14f04a1f64d..6cc8c341e6dfa 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -85,7 +85,7 @@ in
       '';
 
       type = with types; attrsOf (submodule (
-        { name, config, ... }:
+        { name, config, options, ... }:
         { options = {
 
             enable = mkOption {
@@ -172,7 +172,8 @@ in
             target = mkDefault name;
             source = mkIf (config.text != null) (
               let name' = "etc-" + baseNameOf name;
-              in mkDefault (pkgs.writeText name' config.text));
+              in mkDerivedConfig options.text (pkgs.writeText name')
+            );
           };
 
         }));
diff --git a/nixos/modules/tasks/auto-upgrade.nix b/nixos/modules/tasks/auto-upgrade.nix
index b19b688a1fb8a..b931b27ad8170 100644
--- a/nixos/modules/tasks/auto-upgrade.nix
+++ b/nixos/modules/tasks/auto-upgrade.nix
@@ -139,6 +139,7 @@ in {
         gzip
         gitMinimal
         config.nix.package.out
+        config.programs.ssh.package
       ];
 
       script = let
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 4e20ec1184641..49901cda848df 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -417,7 +417,11 @@ in
         network node hostname (uname --nodename) the option
         boot.kernel.sysctl."kernel.hostname" can be used as a workaround (but
         the 64 character limit still applies).
+
+        WARNING: Do not use underscores (_) or you may run into unexpected issues.
       '';
+       # warning until the issues in https://github.com/NixOS/nixpkgs/pull/138978
+       # are resolved
     };
 
     networking.fqdn = mkOption {
@@ -1233,6 +1237,8 @@ in
       "net.ipv4.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
       "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
+      # networkmanager falls back to "/proc/sys/net/ipv6/conf/default/use_tempaddr"
+      "net.ipv6.conf.default.use_tempaddr" = tempaddrValues.${cfg.tempAddresses}.sysctl;
     } // listToAttrs (flip concatMap (filter (i: i.proxyARP) interfaces)
         (i: [(nameValuePair "net.ipv4.conf.${replaceChars ["."] ["/"] i.name}.proxy_arp" true)]))
       // listToAttrs (forEach interfaces
diff --git a/nixos/modules/virtualisation/azure-agent.nix b/nixos/modules/virtualisation/azure-agent.nix
index 41f3fa0e6642e..bd8c7f8c1eea3 100644
--- a/nixos/modules/virtualisation/azure-agent.nix
+++ b/nixos/modules/virtualisation/azure-agent.nix
@@ -76,7 +76,7 @@ in
 
   config = mkIf cfg.enable {
     assertions = [ {
-      assertion = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
+      assertion = pkgs.stdenv.hostPlatform.isx86;
       message = "Azure not currently supported on ${pkgs.stdenv.hostPlatform.system}";
     } {
       assertion = config.networking.networkmanager.enable == false;
diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix
index c7d5ee1fd1178..9816cc2332fbd 100644
--- a/nixos/modules/virtualisation/lxc-container.nix
+++ b/nixos/modules/virtualisation/lxc-container.nix
@@ -61,7 +61,7 @@ in
         description = "Templates for LXD";
         type = types.attrsOf (types.submodule (templateSubmodule));
         default = {};
-        example = literalExample ''
+        example = literalExpression ''
           {
             # create /etc/hostname on container creation
             "hostname" = {
diff --git a/nixos/modules/virtualisation/qemu-guest-agent.nix b/nixos/modules/virtualisation/qemu-guest-agent.nix
index 37a93a29976ba..39273e523e8f3 100644
--- a/nixos/modules/virtualisation/qemu-guest-agent.nix
+++ b/nixos/modules/virtualisation/qemu-guest-agent.nix
@@ -14,8 +14,8 @@ in {
       };
       package = mkOption {
         type = types.package;
-        default = pkgs.qemu.ga;
-        defaultText = literalExpression "pkgs.qemu.ga";
+        default = pkgs.qemu_kvm.ga;
+        defaultText = literalExpression "pkgs.qemu_kvm.ga";
         description = "The QEMU guest agent package.";
       };
   };
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index a8db42f5ed6fa..493c407222f7c 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -97,7 +97,7 @@ let
     imap1 (idx: drive: drive // { device = driveDeviceName idx; });
 
   efiPrefix =
-    if (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then "${pkgs.OVMF.fd}/FV/OVMF"
+    if pkgs.stdenv.hostPlatform.isx86 then "${pkgs.OVMF.fd}/FV/OVMF"
     else if pkgs.stdenv.isAarch64 then "${pkgs.OVMF.fd}/FV/AAVMF"
     else throw "No EFI firmware available for platform";
   efiFirmware = "${efiPrefix}_CODE.fd";
@@ -296,7 +296,7 @@ in
     virtualisation.memorySize =
       mkOption {
         type = types.ints.positive;
-        default = 384;
+        default = 1024;
         description =
           ''
             The memory size in megabytes of the virtual machine.
@@ -306,7 +306,7 @@ in
     virtualisation.msize =
       mkOption {
         type = types.ints.positive;
-        default = 16384;
+        default = pkgs.vmTools.default9PMsizeBytes;
         description =
           ''
             The msize (maximum packet size) option passed to 9p file systems, in
@@ -318,7 +318,7 @@ in
     virtualisation.diskSize =
       mkOption {
         type = types.nullOr types.ints.positive;
-        default = 512;
+        default = 1024;
         description =
           ''
             The disk size in megabytes of the virtual machine.
@@ -833,7 +833,7 @@ in
 
     # FIXME: Consolidate this one day.
     virtualisation.qemu.options = mkMerge [
-      (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [
+      (mkIf pkgs.stdenv.hostPlatform.isx86 [
         "-usb" "-device usb-tablet,bus=usb-bus.0"
       ])
       (mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [
@@ -845,7 +845,7 @@ in
         ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
       ])
       (mkIf cfg.useEFIBoot [
-        "-drive if=pflash,format=raw,unit=0,readonly,file=${efiFirmware}"
+        "-drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}"
         "-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS"
       ])
       (mkIf (cfg.bios != null) [
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
index 486951983d303..f702fb4e525c2 100644
--- a/nixos/modules/virtualisation/virtualbox-guest.nix
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -33,7 +33,7 @@ in
 
   config = mkIf cfg.enable (mkMerge [{
     assertions = [{
-      assertion = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
+      assertion = pkgs.stdenv.hostPlatform.isx86;
       message = "Virtualbox not currently supported on ${pkgs.stdenv.hostPlatform.system}";
     }];
 
diff --git a/nixos/modules/virtualisation/vmware-guest.nix b/nixos/modules/virtualisation/vmware-guest.nix
index 480a9703cef34..09b26eeb33a92 100644
--- a/nixos/modules/virtualisation/vmware-guest.nix
+++ b/nixos/modules/virtualisation/vmware-guest.nix
@@ -23,7 +23,7 @@ in
 
   config = mkIf cfg.enable {
     assertions = [ {
-      assertion = pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64;
+      assertion = pkgs.stdenv.hostPlatform.isx86;
       message = "VMWare guest is not currently supported on ${pkgs.stdenv.hostPlatform.system}";
     } ];
 
diff --git a/nixos/tests/airsonic.nix b/nixos/tests/airsonic.nix
index 59bd84877c61c..d8df092c2ecfa 100644
--- a/nixos/tests/airsonic.nix
+++ b/nixos/tests/airsonic.nix
@@ -11,10 +11,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         enable = true;
         maxMemory = 800;
       };
-
-      # Airsonic is a Java application, and unfortunately requires a significant
-      # amount of memory.
-      virtualisation.memorySize = 1024;
     };
 
   testScript = ''
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index a48dcda94d7ed..b8219416dc42a 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -33,7 +33,7 @@ in
   avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
   babeld = handleTest ./babeld.nix {};
   bazarr = handleTest ./bazarr.nix {};
-  bcachefs = handleTestOn ["x86_64-linux"] ./bcachefs.nix {}; # linux-4.18.2018.10.12 is unsupported on aarch64
+  bcachefs = handleTestOn ["x86_64-linux" "aarch64-linux"] ./bcachefs.nix {};
   beanstalkd = handleTest ./beanstalkd.nix {};
   bees = handleTest ./bees.nix {};
   bind = handleTest ./bind.nix {};
@@ -109,6 +109,7 @@ in
   docker-tools-overlay = handleTestOn ["x86_64-linux"] ./docker-tools-overlay.nix {};
   documize = handleTest ./documize.nix {};
   dokuwiki = handleTest ./dokuwiki.nix {};
+  domination = handleTest ./domination.nix {};
   dovecot = handleTest ./dovecot.nix {};
   ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {};
   ec2-nixops = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-nixops or {};
@@ -218,6 +219,7 @@ in
   kerberos = handleTest ./kerberos/default.nix {};
   kernel-generic = handleTest ./kernel-generic.nix {};
   kernel-latest-ath-user-regd = handleTest ./kernel-latest-ath-user-regd.nix {};
+  kexec = handleTest ./kexec.nix {};
   keycloak = discoverTests (import ./keycloak.nix);
   keymap = handleTest ./keymap.nix {};
   knot = handleTest ./knot.nix {};
@@ -226,9 +228,11 @@ in
   kubernetes = handleTestOn ["x86_64-linux"] ./kubernetes {};
   latestKernel.login = handleTest ./login.nix { latestKernel = true; };
   leaps = handleTest ./leaps.nix {};
+  libinput = handleTest ./libinput.nix {};
   libreddit = handleTest ./libreddit.nix {};
-  lidarr = handleTest ./lidarr.nix {};
+  libresprite = handleTest ./libresprite.nix {};
   libreswan = handleTest ./libreswan.nix {};
+  lidarr = handleTest ./lidarr.nix {};
   lightdm = handleTest ./lightdm.nix {};
   limesurvey = handleTest ./limesurvey.nix {};
   litestream = handleTest ./litestream.nix {};
@@ -260,6 +264,7 @@ in
   miniflux = handleTest ./miniflux.nix {};
   minio = handleTest ./minio.nix {};
   misc = handleTest ./misc.nix {};
+  mjolnir = handleTest ./matrix/mjolnir.nix {};
   mod_perl = handleTest ./mod_perl.nix {};
   moinmoin = handleTest ./moinmoin.nix {};
   mongodb = handleTest ./mongodb.nix {};
@@ -340,6 +345,7 @@ in
   packagekit = handleTest ./packagekit.nix {};
   pam-oath-login = handleTest ./pam-oath-login.nix {};
   pam-u2f = handleTest ./pam-u2f.nix {};
+  pantalaimon = handleTest ./matrix/pantalaimon.nix {};
   pantheon = handleTest ./pantheon.nix {};
   paperless-ng = handleTest ./paperless-ng.nix {};
   parsedmarc = handleTest ./parsedmarc {};
@@ -403,6 +409,7 @@ in
   samba-wsdd = handleTest ./samba-wsdd.nix {};
   sanoid = handleTest ./sanoid.nix {};
   sddm = handleTest ./sddm.nix {};
+  seafile = handleTest ./seafile.nix {};
   searx = handleTest ./searx.nix {};
   service-runner = handleTest ./service-runner.nix {};
   shadow = handleTest ./shadow.nix {};
@@ -482,7 +489,7 @@ in
   victoriametrics = handleTest ./victoriametrics.nix {};
   vikunja = handleTest ./vikunja.nix {};
   virtualbox = handleTestOn ["x86_64-linux"] ./virtualbox.nix {};
-  vscodium = handleTest ./vscodium.nix {};
+  vscodium = discoverTests (import ./vscodium.nix);
   wasabibackend = handleTest ./wasabibackend.nix {};
   wiki-js = handleTest ./wiki-js.nix {};
   wireguard = handleTest ./wireguard {};
diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix
index e8440598a822c..9945a1dcd62f7 100644
--- a/nixos/tests/boot.nix
+++ b/nixos/tests/boot.nix
@@ -36,7 +36,7 @@ let
             machine = create_machine(${machineConfig})
             machine.start()
             machine.wait_for_unit("multi-user.target")
-            machine.succeed("nix verify -r --no-trust /run/current-system")
+            machine.succeed("nix store verify --no-trust -r --option experimental-features nix-command /run/current-system")
 
             with subtest("Check whether the channel got installed correctly"):
                 machine.succeed("nix-instantiate --dry-run '<nixpkgs>' -A hello")
diff --git a/nixos/tests/cage.nix b/nixos/tests/cage.nix
index e6bef374d3037..83bae3deeeab2 100644
--- a/nixos/tests/cage.nix
+++ b/nixos/tests/cage.nix
@@ -17,7 +17,6 @@ import ./make-test-python.nix ({ pkgs, ...} :
       program = "${pkgs.xterm}/bin/xterm -cm -pc -fa Monospace -fs 24";
     };
 
-    virtualisation.memorySize = 1024;
     # Need to switch to a different GPU driver than the default one (-vga std) so that Cage can launch:
     virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
   };
diff --git a/nixos/tests/cagebreak.nix b/nixos/tests/cagebreak.nix
index 242e59f5d7aba..c6c2c632b61ac 100644
--- a/nixos/tests/cagebreak.nix
+++ b/nixos/tests/cagebreak.nix
@@ -35,7 +35,6 @@ in
     programs.xwayland.enable = true;
     environment.systemPackages = [ pkgs.cagebreak pkgs.wayland-utils ];
 
-    virtualisation.memorySize = 1024;
     # Need to switch to a different GPU driver than the default one (-vga std) so that Cagebreak can launch:
     virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
   };
diff --git a/nixos/tests/cassandra.nix b/nixos/tests/cassandra.nix
index bef3105f0a9eb..a19d525c3431e 100644
--- a/nixos/tests/cassandra.nix
+++ b/nixos/tests/cassandra.nix
@@ -41,7 +41,6 @@ let
       ];
     };
     services.cassandra = cassandraCfg ipAddress // extra;
-    virtualisation.memorySize = 1024;
   };
 in
 {
diff --git a/nixos/tests/ceph-multi-node.nix b/nixos/tests/ceph-multi-node.nix
index 33736e27b984d..29e7c279d69ac 100644
--- a/nixos/tests/ceph-multi-node.nix
+++ b/nixos/tests/ceph-multi-node.nix
@@ -37,7 +37,6 @@ let
 
   generateHost = { pkgs, cephConfig, networkConfig, ... }: {
     virtualisation = {
-      memorySize = 1024;
       emptyDiskImages = [ 20480 ];
       vlans = [ 1 ];
     };
diff --git a/nixos/tests/ceph-single-node-bluestore.nix b/nixos/tests/ceph-single-node-bluestore.nix
index f706d4d56fcfd..acaae4cf300e8 100644
--- a/nixos/tests/ceph-single-node-bluestore.nix
+++ b/nixos/tests/ceph-single-node-bluestore.nix
@@ -34,7 +34,6 @@ let
 
   generateHost = { pkgs, cephConfig, networkConfig, ... }: {
     virtualisation = {
-      memorySize = 1024;
       emptyDiskImages = [ 20480 20480 20480 ];
       vlans = [ 1 ];
     };
diff --git a/nixos/tests/ceph-single-node.nix b/nixos/tests/ceph-single-node.nix
index d1d56ea6708cc..4fe5dc59ff8f3 100644
--- a/nixos/tests/ceph-single-node.nix
+++ b/nixos/tests/ceph-single-node.nix
@@ -34,7 +34,6 @@ let
 
   generateHost = { pkgs, cephConfig, networkConfig, ... }: {
     virtualisation = {
-      memorySize = 1024;
       emptyDiskImages = [ 20480 20480 20480 ];
       vlans = [ 1 ];
     };
diff --git a/nixos/tests/common/wayland-cage.nix b/nixos/tests/common/wayland-cage.nix
new file mode 100644
index 0000000000000..fd0700941392b
--- /dev/null
+++ b/nixos/tests/common/wayland-cage.nix
@@ -0,0 +1,13 @@
+{ ... }:
+
+{
+  imports = [ ./user-account.nix ];
+  services.cage = {
+    enable = true;
+    user = "alice";
+  };
+
+  virtualisation = {
+    qemu.options = [ "-vga virtio" ];
+  };
+}
diff --git a/nixos/tests/containers-bridge.nix b/nixos/tests/containers-bridge.nix
index a1b0c9469d83d..b8661fd7997c9 100644
--- a/nixos/tests/containers-bridge.nix
+++ b/nixos/tests/containers-bridge.nix
@@ -15,7 +15,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     { pkgs, ... }:
     { imports = [ ../modules/installer/cd-dvd/channel.nix ];
       virtualisation.writableStore = true;
-      virtualisation.memorySize = 768;
 
       networking.bridges = {
         br0 = {
diff --git a/nixos/tests/containers-ephemeral.nix b/nixos/tests/containers-ephemeral.nix
index fabf0593f23af..db1631cf5b5d1 100644
--- a/nixos/tests/containers-ephemeral.nix
+++ b/nixos/tests/containers-ephemeral.nix
@@ -5,7 +5,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   };
 
   machine = { pkgs, ... }: {
-    virtualisation.memorySize = 768;
     virtualisation.writableStore = true;
 
     containers.webserver = {
diff --git a/nixos/tests/containers-extra_veth.nix b/nixos/tests/containers-extra_veth.nix
index 172409f56e89e..b8f3d9844064c 100644
--- a/nixos/tests/containers-extra_veth.nix
+++ b/nixos/tests/containers-extra_veth.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     { pkgs, ... }:
     { imports = [ ../modules/installer/cd-dvd/channel.nix ];
       virtualisation.writableStore = true;
-      virtualisation.memorySize = 768;
       virtualisation.vlans = [];
 
       networking.useDHCP = false;
diff --git a/nixos/tests/containers-hosts.nix b/nixos/tests/containers-hosts.nix
index 1f24ed1f3c2c4..3c6a15710027a 100644
--- a/nixos/tests/containers-hosts.nix
+++ b/nixos/tests/containers-hosts.nix
@@ -7,7 +7,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   machine =
     { lib, ... }:
     {
-      virtualisation.memorySize = 256;
       virtualisation.vlans = [];
 
       networking.bridges.br0.interfaces = [];
diff --git a/nixos/tests/containers-imperative.nix b/nixos/tests/containers-imperative.nix
index 34103ef7586b5..a126a5480c035 100644
--- a/nixos/tests/containers-imperative.nix
+++ b/nixos/tests/containers-imperative.nix
@@ -14,7 +14,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
       nix.binaryCaches = []; # don't try to access cache.nixos.org
 
       virtualisation.writableStore = true;
-      virtualisation.memorySize = 1024;
       # Make sure we always have all the required dependencies for creating a
       # container available within the VM, because we don't have network access.
       virtualisation.additionalPaths = let
diff --git a/nixos/tests/containers-ip.nix b/nixos/tests/containers-ip.nix
index 8fc42dab62410..91fdda0392a9b 100644
--- a/nixos/tests/containers-ip.nix
+++ b/nixos/tests/containers-ip.nix
@@ -22,7 +22,6 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }: {
       imports = [ ../modules/installer/cd-dvd/channel.nix ];
       virtualisation = {
         writableStore = true;
-        memorySize = 768;
       };
 
       containers.webserver4 = webserverFor "10.231.136.1" "10.231.136.2";
diff --git a/nixos/tests/containers-macvlans.nix b/nixos/tests/containers-macvlans.nix
index d0f41be8c1251..a0cea8db4a1ab 100644
--- a/nixos/tests/containers-macvlans.nix
+++ b/nixos/tests/containers-macvlans.nix
@@ -15,7 +15,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     machine1 =
       { lib, ... }:
       {
-        virtualisation.memorySize = 256;
         virtualisation.vlans = [ 1 ];
 
         # To be able to ping containers from the host, it is necessary
@@ -55,7 +54,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     machine2 =
       { ... }:
       {
-        virtualisation.memorySize = 256;
         virtualisation.vlans = [ 1 ];
       };
 
diff --git a/nixos/tests/containers-physical_interfaces.nix b/nixos/tests/containers-physical_interfaces.nix
index 57bd0eedcc335..e203f88786a3e 100644
--- a/nixos/tests/containers-physical_interfaces.nix
+++ b/nixos/tests/containers-physical_interfaces.nix
@@ -7,7 +7,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   nodes = {
     server = { ... }:
       {
-        virtualisation.memorySize = 256;
         virtualisation.vlans = [ 1 ];
 
         containers.server = {
@@ -23,7 +22,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
         };
       };
     bridged = { ... }: {
-      virtualisation.memorySize = 128;
       virtualisation.vlans = [ 1 ];
 
       containers.bridged = {
@@ -41,7 +39,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     };
 
     bonded = { ... }: {
-      virtualisation.memorySize = 128;
       virtualisation.vlans = [ 1 ];
 
       containers.bonded = {
@@ -62,7 +59,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     };
 
     bridgedbond = { ... }: {
-      virtualisation.memorySize = 128;
       virtualisation.vlans = [ 1 ];
 
       containers.bridgedbond = {
diff --git a/nixos/tests/containers-portforward.nix b/nixos/tests/containers-portforward.nix
index e21f6cee76a9b..6cecd72f1bda3 100644
--- a/nixos/tests/containers-portforward.nix
+++ b/nixos/tests/containers-portforward.nix
@@ -15,7 +15,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     { pkgs, ... }:
     { imports = [ ../modules/installer/cd-dvd/channel.nix ];
       virtualisation.writableStore = true;
-      virtualisation.memorySize = 768;
 
       containers.webserver =
         { privateNetwork = true;
diff --git a/nixos/tests/containers-tmpfs.nix b/nixos/tests/containers-tmpfs.nix
index 0185c2d91f232..d95178d1ff588 100644
--- a/nixos/tests/containers-tmpfs.nix
+++ b/nixos/tests/containers-tmpfs.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
     { pkgs, ... }:
     { imports = [ ../modules/installer/cd-dvd/channel.nix ];
       virtualisation.writableStore = true;
-      virtualisation.memorySize = 768;
 
       containers.tmpfs =
         {
diff --git a/nixos/tests/custom-ca.nix b/nixos/tests/custom-ca.nix
index 4480519c7edcc..0ab49f3b34306 100644
--- a/nixos/tests/custom-ca.nix
+++ b/nixos/tests/custom-ca.nix
@@ -81,8 +81,6 @@ in
 
       # chromium-based browsers refuse to run as root
       test-support.displayManager.auto.user = "alice";
-      # browsers may hang with the default memory
-      virtualisation.memorySize = 500;
 
       networking.hosts."127.0.0.1" = [ "good.example.com" "bad.example.com" ];
       security.pki.certificateFiles = [ "${example-good-cert}/ca.crt" ];
diff --git a/nixos/tests/doas.nix b/nixos/tests/doas.nix
index 5e9ce4b2c799c..7f038b2bee296 100644
--- a/nixos/tests/doas.nix
+++ b/nixos/tests/doas.nix
@@ -85,6 +85,14 @@ import ./make-test-python.nix (
       # ../../pkgs/tools/security/doas/0001-add-NixOS-specific-dirs-to-safe-PATH.patch
       with subtest("recursive calls to doas from subprocesses should succeed"):
           machine.succeed('doas -u test0 sh -c "doas -u test0 true"')
+
+      with subtest("test0 should inherit TERMINFO_DIRS from the user environment"):
+          dirs = machine.succeed(
+               "su - test0 -c 'doas -u root $SHELL -c \"echo \$TERMINFO_DIRS\"'"
+          )
+
+          if not "test0" in dirs:
+             raise Exception(f"user profile TERMINFO_DIRS is not preserved: {dirs}")
     '';
   }
 )
diff --git a/nixos/tests/domination.nix b/nixos/tests/domination.nix
new file mode 100644
index 0000000000000..c76d4ed8c61b3
--- /dev/null
+++ b/nixos/tests/domination.nix
@@ -0,0 +1,26 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "domination";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ fgaz ];
+  };
+
+  machine = { config, pkgs, ... }: {
+    imports = [
+      ./common/x11.nix
+    ];
+
+    services.xserver.enable = true;
+    environment.systemPackages = [ pkgs.domination ];
+  };
+
+  enableOCR = true;
+
+  testScript =
+    ''
+      machine.wait_for_x()
+      machine.execute("domination >&2 &")
+      machine.wait_for_window("Menu")
+      machine.wait_for_text("New Game")
+      machine.screenshot("screen")
+    '';
+})
diff --git a/nixos/tests/enlightenment.nix b/nixos/tests/enlightenment.nix
index c5f0e208906b7..8506c348246de 100644
--- a/nixos/tests/enlightenment.nix
+++ b/nixos/tests/enlightenment.nix
@@ -19,7 +19,6 @@ import ./make-test-python.nix ({ pkgs, ...} :
       };
     };
     hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
-    virtualisation.memorySize = 1024;
     environment.systemPackages = [ pkgs.xdotool ];
     services.acpid.enable = true;
     services.connman.enable = true;
diff --git a/nixos/tests/fcitx/default.nix b/nixos/tests/fcitx/default.nix
index cbeb95d33b0cb..a243be8dc19b1 100644
--- a/nixos/tests/fcitx/default.nix
+++ b/nixos/tests/fcitx/default.nix
@@ -11,7 +11,6 @@ import ../make-test-python.nix (
           ...
         }:
           {
-            virtualisation.memorySize = 1024;
 
             imports = [
               ../common/user-account.nix
diff --git a/nixos/tests/fenics.nix b/nixos/tests/fenics.nix
index 56f09d6a27e40..f0a8c32c7cd8e 100644
--- a/nixos/tests/fenics.nix
+++ b/nixos/tests/fenics.nix
@@ -38,7 +38,6 @@ in
         gcc
         (python3.withPackages (ps: with ps; [ fenics ]))
       ];
-      virtualisation.memorySize = 512;
     };
   };
   testScript =
diff --git a/nixos/tests/firefox.nix b/nixos/tests/firefox.nix
index 7216ad43b8e9a..6101fc9735641 100644
--- a/nixos/tests/firefox.nix
+++ b/nixos/tests/firefox.nix
@@ -13,9 +13,6 @@ import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }: {
         pkgs.xdotool
       ];
 
-      # Need some more memory to record audio.
-      virtualisation.memorySize = 500;
-
       # Create a virtual sound device, with mixing
       # and all, for recording audio.
       boot.kernelModules = [ "snd-aloop" ];
diff --git a/nixos/tests/gerrit.nix b/nixos/tests/gerrit.nix
index b6b6486fae86c..8ae9e89cf6b0c 100644
--- a/nixos/tests/gerrit.nix
+++ b/nixos/tests/gerrit.nix
@@ -18,7 +18,6 @@ in {
       { config, pkgs, ... }: {
         networking.firewall.allowedTCPPorts = [ 80 2222 ];
 
-        virtualisation.memorySize = 1024;
 
         services.gerrit = {
           enable = true;
diff --git a/nixos/tests/gnome-xorg.nix b/nixos/tests/gnome-xorg.nix
index b9ff5e6828758..6264b87af4ec5 100644
--- a/nixos/tests/gnome-xorg.nix
+++ b/nixos/tests/gnome-xorg.nix
@@ -40,7 +40,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
         };
       };
 
-      virtualisation.memorySize = 1024;
     };
 
   testScript = { nodes, ... }: let
diff --git a/nixos/tests/gnome.nix b/nixos/tests/gnome.nix
index 1da97f733cfd8..06f387ecad67d 100644
--- a/nixos/tests/gnome.nix
+++ b/nixos/tests/gnome.nix
@@ -45,7 +45,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
         };
       };
 
-      virtualisation.memorySize = 1024;
     };
 
   testScript = { nodes, ... }: let
diff --git a/nixos/tests/graphite.nix b/nixos/tests/graphite.nix
index 137be2d89c8b3..496f16846ea6a 100644
--- a/nixos/tests/graphite.nix
+++ b/nixos/tests/graphite.nix
@@ -4,7 +4,6 @@ import ./make-test-python.nix ({ pkgs, ... } :
   nodes = {
     one =
       { ... }: {
-        virtualisation.memorySize = 1024;
         time.timeZone = "UTC";
         services.graphite = {
           web = {
diff --git a/nixos/tests/hadoop/hadoop.nix b/nixos/tests/hadoop/hadoop.nix
index b4ed0e17a8520..48737debab546 100644
--- a/nixos/tests/hadoop/hadoop.nix
+++ b/nixos/tests/hadoop/hadoop.nix
@@ -95,7 +95,6 @@ import ../make-test-python.nix ({pkgs, ...}: {
 
     # YARN cluster
     rm1 = {pkgs, options, ...}: {
-      virtualisation.memorySize = 1024;
       services.hadoop = {
         inherit package coreSite hdfsSite;
         yarnSite = options.services.hadoop.yarnSite.default // yarnSiteHA;
@@ -103,7 +102,6 @@ import ../make-test-python.nix ({pkgs, ...}: {
       };
     };
     rm2 = {pkgs, options, ...}: {
-      virtualisation.memorySize = 1024;
       services.hadoop = {
         inherit package coreSite hdfsSite;
         yarnSite = options.services.hadoop.yarnSite.default // yarnSiteHA;
diff --git a/nixos/tests/hadoop/hdfs.nix b/nixos/tests/hadoop/hdfs.nix
index 360dbd60ed274..b63cbf4803271 100644
--- a/nixos/tests/hadoop/hdfs.nix
+++ b/nixos/tests/hadoop/hdfs.nix
@@ -2,7 +2,6 @@
 import ../make-test-python.nix ({...}: {
   nodes = {
     namenode = {pkgs, ...}: {
-      virtualisation.memorySize = 1024;
       services.hadoop = {
         package = pkgs.hadoop;
         hdfs = {
diff --git a/nixos/tests/hibernate.nix b/nixos/tests/hibernate.nix
index 4f05b99a5a116..fc6857e2fd024 100644
--- a/nixos/tests/hibernate.nix
+++ b/nixos/tests/hibernate.nix
@@ -51,6 +51,7 @@ in makeTest {
         connect-timeout = 1
       '';
 
+      virtualisation.memorySize = 2048;
       virtualisation.diskSize = 8 * 1024;
       virtualisation.emptyDiskImages = [
         # Small root disk for installer
diff --git a/nixos/tests/installed-tests/fwupd.nix b/nixos/tests/installed-tests/fwupd.nix
index a8a683a1af7b0..65614e2689d8e 100644
--- a/nixos/tests/installed-tests/fwupd.nix
+++ b/nixos/tests/installed-tests/fwupd.nix
@@ -7,6 +7,5 @@ makeInstalledTest {
     services.fwupd.enable = true;
     services.fwupd.disabledPlugins = lib.mkForce []; # don't disable test plugin
     services.fwupd.enableTestRemote = true;
-    virtualisation.memorySize = 768;
   };
 }
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 48f0f59342557..0748a74ee1763 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -70,14 +70,14 @@ let
     let iface = if grubVersion == 1 then "ide" else "virtio";
         isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
         bios  = if pkgs.stdenv.isAarch64 then "QEMU_EFI.fd" else "OVMF.fd";
-    in if !isEfi && !(pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then
+    in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then
       throw "Non-EFI boot methods are only supported on i686 / x86_64"
     else ''
       def assemble_qemu_flags():
           flags = "-cpu max"
           ${if (system == "x86_64-linux" || system == "i686-linux")
-            then ''flags += " -m 1024"''
-            else ''flags += " -m 768 -enable-kvm -machine virt,gic-version=host"''
+            then ''flags += " -m 1500"''
+            else ''flags += " -m 1000 -enable-kvm -machine virt,gic-version=host"''
           }
           return flags
 
@@ -184,11 +184,12 @@ let
       with subtest("Check whether nixos-rebuild works"):
           machine.succeed("nixos-rebuild switch >&2")
 
-      with subtest("Test nixos-option"):
-          kernel_modules = machine.succeed("nixos-option boot.initrd.kernelModules")
-          assert "virtio_console" in kernel_modules
-          assert "List of modules" in kernel_modules
-          assert "qemu-guest.nix" in kernel_modules
+      # FIXME: Nix 2.4 broke nixos-option, someone has to fix it.
+      # with subtest("Test nixos-option"):
+      #     kernel_modules = machine.succeed("nixos-option boot.initrd.kernelModules")
+      #     assert "virtio_console" in kernel_modules
+      #     assert "List of modules" in kernel_modules
+      #     assert "qemu-guest.nix" in kernel_modules
 
       machine.shutdown()
 
@@ -287,7 +288,7 @@ let
           # builds stuff in the VM, needs more juice
           virtualisation.diskSize = 8 * 1024;
           virtualisation.cores = 8;
-          virtualisation.memorySize = 1536;
+          virtualisation.memorySize = 3096;
 
           # Use a small /dev/vdb as the root disk for the
           # installer. This ensures the target disk (/dev/vda) is
diff --git a/nixos/tests/jitsi-meet.nix b/nixos/tests/jitsi-meet.nix
index f9a0b121a2bfc..d95f7c2ea9eaa 100644
--- a/nixos/tests/jitsi-meet.nix
+++ b/nixos/tests/jitsi-meet.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     client = { nodes, pkgs, ... }: {
     };
     server = { config, pkgs, ... }: {
-      virtualisation.memorySize = 512;
       services.jitsi-meet = {
         enable = true;
         hostName = "server";
diff --git a/nixos/tests/kafka.nix b/nixos/tests/kafka.nix
index 95711808a2c35..5def759ca24d9 100644
--- a/nixos/tests/kafka.nix
+++ b/nixos/tests/kafka.nix
@@ -19,7 +19,6 @@ let
         };
 
         networking.firewall.allowedTCPPorts = [ 2181 ];
-        virtualisation.memorySize = 1024;
       };
       kafka = { ... }: {
         services.apache-kafka = {
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 3fdd3995b7aa3..45c5c1963a0db 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -29,6 +29,7 @@ let
       linux_4_19_hardened
       linux_5_4_hardened
       linux_5_10_hardened
+      linux_5_15_hardened
 
       linux_testing;
   };
diff --git a/nixos/tests/kexec.nix b/nixos/tests/kexec.nix
index b0d072167fada..010f3da49846a 100644
--- a/nixos/tests/kexec.nix
+++ b/nixos/tests/kexec.nix
@@ -4,12 +4,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
   name = "kexec";
   meta = with lib.maintainers; {
     maintainers = [ eelco ];
-    # Currently hangs forever; last output is:
-    #     machine # [   10.239914] dhcpcd[707]: eth0: adding default route via fe80::2
-    #     machine: waiting for the VM to finish booting
-    #     machine # Cannot find the ESP partition mount point.
-    #     machine # [   28.681197] nscd[692]: 692 checking for monitored file `/etc/netgroup': No such file or directory
-    broken = true;
   };
 
   machine = { ... }:
@@ -18,8 +12,11 @@ import ./make-test-python.nix ({ pkgs, lib, ...} : {
   testScript =
     ''
       machine.wait_for_unit("multi-user.target")
+      machine.succeed('kexec --load /run/current-system/kernel --initrd /run/current-system/initrd --command-line "$(</proc/cmdline)"')
       machine.execute("systemctl kexec >&2 &", check_return=False)
       machine.connected = False
+      machine.connect()
       machine.wait_for_unit("multi-user.target")
+      machine.shutdown()
     '';
 })
diff --git a/nixos/tests/keycloak.nix b/nixos/tests/keycloak.nix
index fc321b8902f1d..1be3fed6acc9d 100644
--- a/nixos/tests/keycloak.nix
+++ b/nixos/tests/keycloak.nix
@@ -17,7 +17,6 @@ let
 
       nodes = {
         keycloak = { ... }: {
-          virtualisation.memorySize = 1024;
 
           security.pki.certificateFiles = [
             certs.ca.cert
diff --git a/nixos/tests/libinput.nix b/nixos/tests/libinput.nix
new file mode 100644
index 0000000000000..2f84aaadcd0be
--- /dev/null
+++ b/nixos/tests/libinput.nix
@@ -0,0 +1,38 @@
+import ./make-test-python.nix ({ ... }:
+
+{
+  name = "libinput";
+
+  machine = { ... }:
+    {
+      imports = [
+        ./common/x11.nix
+        ./common/user-account.nix
+      ];
+
+      test-support.displayManager.auto.user = "alice";
+
+      services.xserver.libinput = {
+        enable = true;
+        mouse = {
+          naturalScrolling = true;
+          leftHanded = true;
+          middleEmulation = false;
+          horizontalScrolling = false;
+        };
+      };
+    };
+
+  testScript = ''
+    def expect_xserver_option(option, value):
+        machine.succeed(f"""cat /var/log/X.0.log | grep -F 'Option "{option}" "{value}"'""")
+
+    machine.start()
+    machine.wait_for_x()
+    machine.succeed("""cat /var/log/X.0.log | grep -F "Using input driver 'libinput'" """)
+    expect_xserver_option("NaturalScrolling", "on")
+    expect_xserver_option("LeftHanded", "on")
+    expect_xserver_option("MiddleEmulation", "off")
+    expect_xserver_option("HorizontalScrolling", "off")
+  '';
+})
diff --git a/nixos/tests/libresprite.nix b/nixos/tests/libresprite.nix
new file mode 100644
index 0000000000000..1a6210e3671ae
--- /dev/null
+++ b/nixos/tests/libresprite.nix
@@ -0,0 +1,30 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "libresprite";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ fgaz ];
+  };
+
+  machine = { config, pkgs, ... }: {
+    imports = [
+      ./common/x11.nix
+    ];
+
+    services.xserver.enable = true;
+    environment.systemPackages = [
+      pkgs.imagemagick
+      pkgs.libresprite
+    ];
+  };
+
+  enableOCR = true;
+
+  testScript =
+    ''
+      machine.wait_for_x()
+      machine.succeed("convert -font DejaVu-Sans +antialias label:'IT WORKS' image.png")
+      machine.execute("libresprite image.png >&2 &")
+      machine.wait_for_window("LibreSprite v${pkgs.libresprite.version}")
+      machine.wait_for_text("IT WORKS")
+      machine.screenshot("screen")
+    '';
+})
diff --git a/nixos/tests/lxd-image.nix b/nixos/tests/lxd-image.nix
index bc8274eebedd6..096b9d9aba906 100644
--- a/nixos/tests/lxd-image.nix
+++ b/nixos/tests/lxd-image.nix
@@ -46,8 +46,6 @@ in {
 
   machine = { lib, ... }: {
     virtualisation = {
-      # OOMs otherwise
-      memorySize = 1024;
       # disk full otherwise
       diskSize = 2048;
 
diff --git a/nixos/tests/matrix/mjolnir.nix b/nixos/tests/matrix/mjolnir.nix
new file mode 100644
index 0000000000000..bb55f6f5440b2
--- /dev/null
+++ b/nixos/tests/matrix/mjolnir.nix
@@ -0,0 +1,165 @@
+import ../make-test-python.nix (
+  { pkgs, ... }:
+  let
+    # Set up SSL certs for Synapse to be happy.
+    runWithOpenSSL = file: cmd: pkgs.runCommand file
+      {
+        buildInputs = [ pkgs.openssl ];
+      }
+      cmd;
+
+    ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
+    ca_pem = runWithOpenSSL "ca.pem" ''
+      openssl req \
+        -x509 -new -nodes -key ${ca_key} \
+        -days 10000 -out $out -subj "/CN=snakeoil-ca"
+    '';
+    key = runWithOpenSSL "matrix_key.pem" "openssl genrsa -out $out 2048";
+    csr = runWithOpenSSL "matrix.csr" ''
+      openssl req \
+         -new -key ${key} \
+         -out $out -subj "/CN=localhost" \
+    '';
+    cert = runWithOpenSSL "matrix_cert.pem" ''
+      openssl x509 \
+        -req -in ${csr} \
+        -CA ${ca_pem} -CAkey ${ca_key} \
+        -CAcreateserial -out $out \
+        -days 365
+    '';
+  in
+  {
+    name = "mjolnir";
+    meta = with pkgs.lib; {
+      maintainers = teams.matrix.members;
+    };
+
+    nodes = {
+      homeserver = { pkgs, ... }: {
+        services.matrix-synapse = {
+          enable = true;
+          database_type = "sqlite3";
+          tls_certificate_path = "${cert}";
+          tls_private_key_path = "${key}";
+          enable_registration = true;
+          registration_shared_secret = "supersecret-registration";
+
+          listeners = [
+            # The default but tls=false
+            {
+              "bind_address" = "";
+              "port" = 8448;
+              "resources" = [
+                { "compress" = true; "names" = [ "client" "webclient" ]; }
+                { "compress" = false; "names" = [ "federation" ]; }
+              ];
+              "tls" = false;
+              "type" = "http";
+              "x_forwarded" = false;
+            }
+          ];
+        };
+
+        networking.firewall.allowedTCPPorts = [ 8448 ];
+
+        environment.systemPackages = [
+          (pkgs.writeShellScriptBin "register_mjolnir_user" ''
+            exec ${pkgs.matrix-synapse}/bin/register_new_matrix_user \
+              -u mjolnir \
+              -p mjolnir-password \
+              --admin \
+              --shared-secret supersecret-registration \
+              http://localhost:8448
+          ''
+          )
+          (pkgs.writeShellScriptBin "register_moderator_user" ''
+            exec ${pkgs.matrix-synapse}/bin/register_new_matrix_user \
+              -u moderator \
+              -p moderator-password \
+              --no-admin \
+              --shared-secret supersecret-registration \
+              http://localhost:8448
+          ''
+          )
+        ];
+      };
+
+      mjolnir = { pkgs, ... }: {
+        services.mjolnir = {
+          enable = true;
+          homeserverUrl = "http://homeserver:8448";
+          pantalaimon = {
+            enable = true;
+            username = "mjolnir";
+            passwordFile = pkgs.writeText "password.txt" "mjolnir-password";
+          };
+          managementRoom = "#moderators:homeserver";
+        };
+      };
+
+      client = { pkgs, ... }: {
+        environment.systemPackages = [
+          (pkgs.writers.writePython3Bin "create_management_room_and_invite_mjolnir"
+            { libraries = [ pkgs.python3Packages.matrix-nio ]; } ''
+            import asyncio
+
+            from nio import (
+                AsyncClient,
+                EnableEncryptionBuilder
+            )
+
+
+            async def main() -> None:
+                client = AsyncClient("http://homeserver:8448", "moderator")
+
+                await client.login("moderator-password")
+
+                room = await client.room_create(
+                    name="Moderators",
+                    alias="moderators",
+                    initial_state=[EnableEncryptionBuilder().as_dict()],
+                )
+
+                await client.join(room.room_id)
+                await client.room_invite(room.room_id, "@mjolnir:homeserver")
+
+            asyncio.run(main())
+          ''
+          )
+        ];
+      };
+    };
+
+    testScript = ''
+      with subtest("start homeserver"):
+        homeserver.start()
+
+        homeserver.wait_for_unit("matrix-synapse.service")
+        homeserver.wait_until_succeeds("curl --fail -L http://localhost:8448/")
+
+      with subtest("register users"):
+        # register mjolnir user
+        homeserver.succeed("register_mjolnir_user")
+        # register moderator user
+        homeserver.succeed("register_moderator_user")
+
+      with subtest("start mjolnir"):
+        mjolnir.start()
+
+        # wait for pantalaimon to be ready
+        mjolnir.wait_for_unit("pantalaimon-mjolnir.service")
+        mjolnir.wait_for_unit("mjolnir.service")
+
+        mjolnir.wait_until_succeeds("curl --fail -L http://localhost:8009/")
+
+      with subtest("ensure mjolnir can be invited to the management room"):
+        client.start()
+
+        client.wait_until_succeeds("curl --fail -L http://homeserver:8448/")
+
+        client.succeed("create_management_room_and_invite_mjolnir")
+
+        mjolnir.wait_for_console_text("Startup complete. Now monitoring rooms")
+    '';
+  }
+)
diff --git a/nixos/tests/matrix/pantalaimon.nix b/nixos/tests/matrix/pantalaimon.nix
new file mode 100644
index 0000000000000..fcb9904b21388
--- /dev/null
+++ b/nixos/tests/matrix/pantalaimon.nix
@@ -0,0 +1,65 @@
+import ../make-test-python.nix (
+  { pkgs, ... }:
+  let
+    pantalaimonInstanceName = "testing";
+
+    # Set up SSL certs for Synapse to be happy.
+    runWithOpenSSL = file: cmd: pkgs.runCommand file
+      {
+        buildInputs = [ pkgs.openssl ];
+      }
+      cmd;
+
+    ca_key = runWithOpenSSL "ca-key.pem" "openssl genrsa -out $out 2048";
+    ca_pem = runWithOpenSSL "ca.pem" ''
+      openssl req \
+        -x509 -new -nodes -key ${ca_key} \
+        -days 10000 -out $out -subj "/CN=snakeoil-ca"
+    '';
+    key = runWithOpenSSL "matrix_key.pem" "openssl genrsa -out $out 2048";
+    csr = runWithOpenSSL "matrix.csr" ''
+      openssl req \
+         -new -key ${key} \
+         -out $out -subj "/CN=localhost" \
+    '';
+    cert = runWithOpenSSL "matrix_cert.pem" ''
+      openssl x509 \
+        -req -in ${csr} \
+        -CA ${ca_pem} -CAkey ${ca_key} \
+        -CAcreateserial -out $out \
+        -days 365
+    '';
+  in
+  {
+    name = "pantalaimon";
+    meta = with pkgs.lib; {
+      maintainers = teams.matrix.members;
+    };
+
+    machine = { pkgs, ... }: {
+      services.pantalaimon-headless.instances.${pantalaimonInstanceName} = {
+        homeserver = "https://localhost:8448";
+        listenAddress = "0.0.0.0";
+        listenPort = 8888;
+        logLevel = "debug";
+        ssl = false;
+      };
+
+      services.matrix-synapse = {
+        enable = true;
+        database_type = "sqlite3";
+        tls_certificate_path = "${cert}";
+        tls_private_key_path = "${key}";
+      };
+    };
+
+    testScript = ''
+      start_all()
+      machine.wait_for_unit("pantalaimon-${pantalaimonInstanceName}.service")
+      machine.wait_for_unit("matrix-synapse.service")
+      machine.wait_until_succeeds(
+          "curl --fail -L http://localhost:8888/"
+      )
+    '';
+  }
+)
diff --git a/nixos/tests/metabase.nix b/nixos/tests/metabase.nix
index 370114e922230..1b25071902e97 100644
--- a/nixos/tests/metabase.nix
+++ b/nixos/tests/metabase.nix
@@ -7,7 +7,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
   nodes = {
     machine = { ... }: {
       services.metabase.enable = true;
-      virtualisation.memorySize = 1024;
     };
   };
 
diff --git a/nixos/tests/misc.nix b/nixos/tests/misc.nix
index fb19b7060562f..0587912c9a226 100644
--- a/nixos/tests/misc.nix
+++ b/nixos/tests/misc.nix
@@ -50,17 +50,18 @@ import ./make-test-python.nix ({ pkgs, ...} : rec {
 
 
       def get_path_info(path):
-          result = machine.succeed(f"nix path-info --json {path}")
+          result = machine.succeed(f"nix --option experimental-features nix-command path-info --json {path}")
           parsed = json.loads(result)
           return parsed
 
 
       with subtest("nix-db"):
           info = get_path_info("${foo}")
+          print(info)
 
           if (
               info[0]["narHash"]
-              != "sha256:0afw0d9j1hvwiz066z93jiddc33nxg6i6qyp26vnqyglpyfivlq5"
+              != "sha256-BdMdnb/0eWy3EddjE83rdgzWWpQjfWPAj3zDIFMD3Ck="
           ):
               raise Exception("narHash not set")
 
diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix
index dce5fa26acf7f..2ac2b34a18e2b 100644
--- a/nixos/tests/mysql/mysql.nix
+++ b/nixos/tests/mysql/mysql.nix
@@ -64,10 +64,6 @@ in
       {
         imports = [ users ];
 
-        # prevent oom:
-        # Kernel panic - not syncing: Out of memory: compulsory panic_on_oom is enabled
-        virtualisation.memorySize = 1024;
-
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
           { name = "testdb3"; schema = ./testdb.sql; }
diff --git a/nixos/tests/networking-proxy.nix b/nixos/tests/networking-proxy.nix
index 62b5e690f6d1e..fcb2558cf3b08 100644
--- a/nixos/tests/networking-proxy.nix
+++ b/nixos/tests/networking-proxy.nix
@@ -8,7 +8,6 @@ let default-config = {
 
         services.xserver.enable = false;
 
-        virtualisation.memorySize = 128;
       };
 in import ./make-test-python.nix ({ pkgs, ...} : {
   name = "networking-proxy";
diff --git a/nixos/tests/nginx-etag.nix b/nixos/tests/nginx-etag.nix
index a7bfc0d26958b..b69511d081d4b 100644
--- a/nixos/tests/nginx-etag.nix
+++ b/nixos/tests/nginx-etag.nix
@@ -37,7 +37,6 @@ import ./make-test-python.nix {
     };
 
     client = { pkgs, lib, ... }: {
-      virtualisation.memorySize = 512;
       environment.systemPackages = let
         testRunner = pkgs.writers.writePython3Bin "test-runner" {
           libraries = [ pkgs.python3Packages.selenium ];
diff --git a/nixos/tests/nixops/default.nix b/nixos/tests/nixops/default.nix
index 3fb81906a5227..ec3d028aabae8 100644
--- a/nixos/tests/nixops/default.nix
+++ b/nixos/tests/nixops/default.nix
@@ -26,7 +26,6 @@ let
         nix.binaryCaches = lib.mkForce [ ];
         users.users.person.isNormalUser = true;
         virtualisation.writableStore = true;
-        virtualisation.memorySize = 1024 /*MiB*/;
         virtualisation.additionalPaths = [
           pkgs.hello
           pkgs.figlet
diff --git a/nixos/tests/opensmtpd-rspamd.nix b/nixos/tests/opensmtpd-rspamd.nix
index 9cb2624e6c4e9..19969a7b47ddd 100644
--- a/nixos/tests/opensmtpd-rspamd.nix
+++ b/nixos/tests/opensmtpd-rspamd.nix
@@ -39,7 +39,6 @@ import ./make-test-python.nix {
 
     smtp2 = { pkgs, ... }: {
       imports = [ common/user-account.nix ];
-      virtualisation.memorySize = 512;
       networking = {
         firewall.allowedTCPPorts = [ 25 143 ];
         useDHCP = false;
diff --git a/nixos/tests/pantheon.nix b/nixos/tests/pantheon.nix
index aebf6b534476e..989d29a966dfb 100644
--- a/nixos/tests/pantheon.nix
+++ b/nixos/tests/pantheon.nix
@@ -15,7 +15,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     services.xserver.enable = true;
     services.xserver.desktopManager.pantheon.enable = true;
 
-    virtualisation.memorySize = 1024;
   };
 
   enableOCR = true;
diff --git a/nixos/tests/paperless-ng.nix b/nixos/tests/paperless-ng.nix
index a4b2f348ec328..618eeec6b1259 100644
--- a/nixos/tests/paperless-ng.nix
+++ b/nixos/tests/paperless-ng.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ lib, ... }: {
       enable = true;
       passwordFile = builtins.toFile "password" "admin";
     };
-    virtualisation.memorySize = 1024;
   };
 
   testScript = ''
diff --git a/nixos/tests/plasma5-systemd-start.nix b/nixos/tests/plasma5-systemd-start.nix
index ac6fad7da6c6f..72de19af70cef 100644
--- a/nixos/tests/plasma5-systemd-start.nix
+++ b/nixos/tests/plasma5-systemd-start.nix
@@ -21,7 +21,6 @@ import ./make-test-python.nix ({ pkgs, ...} :
         user = "alice";
       };
     };
-    virtualisation.memorySize = 1024;
   };
 
   testScript = { nodes, ... }: let
diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix
index 281c9843e9015..5c7ea602f79e0 100644
--- a/nixos/tests/plasma5.nix
+++ b/nixos/tests/plasma5.nix
@@ -19,7 +19,6 @@ import ./make-test-python.nix ({ pkgs, ...} :
       user = "alice";
     };
     hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
-    virtualisation.memorySize = 1024;
   };
 
   testScript = { nodes, ... }: let
diff --git a/nixos/tests/pleroma.nix b/nixos/tests/pleroma.nix
index d0ae1488d1346..bf3623fce38b7 100644
--- a/nixos/tests/pleroma.nix
+++ b/nixos/tests/pleroma.nix
@@ -202,7 +202,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
       security.pki.certificateFiles = [ "${tls-cert}/cert.pem" ];
       networking.extraHosts = hosts nodes;
       networking.firewall.enable = false;
-      virtualisation.memorySize = 512;
       environment.systemPackages = with pkgs; [
         provision-db
         provision-secrets
diff --git a/nixos/tests/privacyidea.nix b/nixos/tests/privacyidea.nix
index 4a94f07279469..c1141465ec24e 100644
--- a/nixos/tests/privacyidea.nix
+++ b/nixos/tests/privacyidea.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ pkgs, ...} : rec {
 
   machine = { ... }: {
     virtualisation.cores = 2;
-    virtualisation.memorySize = 512;
 
     services.privacyidea = {
       enable = true;
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 38b93c4087c07..d069854328a2c 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -464,7 +464,6 @@ let
         extraFlags = [ "--lnd.network=regtest" ];
       };
       metricProvider = {
-        virtualisation.memorySize = 1024;
         systemd.services.prometheus-lnd-exporter.serviceConfig.RestartSec = 15;
         systemd.services.prometheus-lnd-exporter.after = [ "lnd.service" ];
         services.bitcoind.regtest = {
@@ -953,7 +952,6 @@ let
       };
       metricProvider = {
         services.rspamd.enable = true;
-        virtualisation.memorySize = 1024;
       };
       exporterTest = ''
         wait_for_unit("rspamd.service")
diff --git a/nixos/tests/prometheus.nix b/nixos/tests/prometheus.nix
index d102b4c075186..a075cfc1f1b72 100644
--- a/nixos/tests/prometheus.nix
+++ b/nixos/tests/prometheus.nix
@@ -130,14 +130,10 @@ in import ./make-test-python.nix {
 
             # This configuration just adds a new prometheus job
             # to scrape the node_exporter metrics of the s3 machine.
-            # We also use an environmentFile to test if that works correctly.
             services.prometheus = {
-              environmentFile = pkgs.writeText "prometheus-config-env-file" ''
-                JOB_NAME=s3-node_exporter
-              '';
               scrapeConfigs = [
                 {
-                  job_name = "$JOB_NAME";
+                  job_name = "s3-node_exporter";
                   static_configs = [
                     {
                       targets = [ "s3:9100" ];
@@ -192,7 +188,6 @@ in import ./make-test-python.nix {
       # Minio requires at least 1GiB of free disk space to run.
       virtualisation = {
         diskSize = 2 * 1024;
-        memorySize = 1024;
       };
       networking.firewall.allowedTCPPorts = [ minioPort ];
 
@@ -232,11 +227,6 @@ in import ./make-test-python.nix {
     # Check if prometheus responds to requests:
     prometheus.wait_for_unit("prometheus.service")
 
-    # Check if prometheus' config file is correctly locked down because it could contain secrets.
-    prometheus.succeed(
-        "stat -c '%a %U' /var/lib/prometheus2/prometheus-substituted.yaml | grep '600 prometheus'"
-    )
-
     prometheus.wait_for_open_port(${toString queryPort})
     prometheus.succeed("curl -sf http://127.0.0.1:${toString queryPort}/metrics")
 
diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix
index 3fd55444fd8a5..f0ccfe7ea0e6a 100644
--- a/nixos/tests/rspamd.nix
+++ b/nixos/tests/rspamd.nix
@@ -25,7 +25,6 @@ let
     machine = {
       services.rspamd.enable = true;
       networking.enableIPv6 = enableIPv6;
-      virtualisation.memorySize = 1024;
     };
     testScript = ''
       start_all()
@@ -69,7 +68,6 @@ in
           group = "rspamd";
         }];
       };
-      virtualisation.memorySize = 1024;
     };
 
     testScript = ''
@@ -118,7 +116,6 @@ in
           '';
         };
       };
-      virtualisation.memorySize = 1024;
     };
 
     testScript = ''
@@ -224,7 +221,6 @@ in
           rspamd_logger.infox(rspamd_config, 'Work dammit!!!')
         '';
       };
-      virtualisation.memorySize = 1024;
     };
     testScript = ''
       ${initMachine}
@@ -291,7 +287,6 @@ in
         postfix.enable = true;
         workers.rspamd_proxy.type = "rspamd_proxy";
       };
-      virtualisation.memorySize = 1024;
     };
     testScript = ''
       ${initMachine}
diff --git a/nixos/tests/seafile.nix b/nixos/tests/seafile.nix
index 70b9ba55457e1..6eec8b1fbe55c 100644
--- a/nixos/tests/seafile.nix
+++ b/nixos/tests/seafile.nix
@@ -1,7 +1,6 @@
 import ./make-test-python.nix ({ pkgs, ... }:
   let
     client = { config, pkgs, ... }: {
-      virtualisation.memorySize = 256;
       environment.systemPackages = [ pkgs.seafile-shared pkgs.curl ];
     };
   in {
@@ -12,7 +11,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
 
     nodes = {
       server = { config, pkgs, ... }: {
-        virtualisation.memorySize = 512;
         services.seafile = {
           enable = true;
           ccnetSettings.General.SERVICE_URL = "http://server";
diff --git a/nixos/tests/signal-desktop.nix b/nixos/tests/signal-desktop.nix
index 7bc7cfe18cf83..8c72306299230 100644
--- a/nixos/tests/signal-desktop.nix
+++ b/nixos/tests/signal-desktop.nix
@@ -29,7 +29,6 @@ in {
     environment.systemPackages = with pkgs; [
       signal-desktop file sqlite sqlcipher-signal
     ];
-    virtualisation.memorySize = 1024;
   };
 
   enableOCR = true;
diff --git a/nixos/tests/spark/default.nix b/nixos/tests/spark/default.nix
index 254cdec6e6b00..025c5a5222e71 100644
--- a/nixos/tests/spark/default.nix
+++ b/nixos/tests/spark/default.nix
@@ -3,7 +3,6 @@ import ../make-test-python.nix ({...}: {
 
   nodes = {
     worker = { nodes, pkgs, ... }: {
-      virtualisation.memorySize = 1024;
       services.spark.worker = {
         enable = true;
         master = "master:7077";
diff --git a/nixos/tests/sway.nix b/nixos/tests/sway.nix
index 01240ef572a68..3476ebab3e26c 100644
--- a/nixos/tests/sway.nix
+++ b/nixos/tests/sway.nix
@@ -44,7 +44,6 @@ import ./make-test-python.nix ({ pkgs, lib, ...} :
     # To test pinentry via gpg-agent:
     programs.gnupg.agent.enable = true;
 
-    virtualisation.memorySize = 1024;
     # Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch:
     virtualisation.qemu.options = [ "-vga none -device virtio-gpu-pci" ];
   };
diff --git a/nixos/tests/sympa.nix b/nixos/tests/sympa.nix
index eb38df180a789..aad7c95b6c99c 100644
--- a/nixos/tests/sympa.nix
+++ b/nixos/tests/sympa.nix
@@ -5,7 +5,6 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
   machine =
     { ... }:
     {
-      virtualisation.memorySize = 1024;
 
       services.sympa = {
         enable = true;
diff --git a/nixos/tests/systemd-cryptenroll.nix b/nixos/tests/systemd-cryptenroll.nix
index 2c436f2de890b..49634ef65672c 100644
--- a/nixos/tests/systemd-cryptenroll.nix
+++ b/nixos/tests/systemd-cryptenroll.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     environment.systemPackages = [ pkgs.cryptsetup ];
     virtualisation = {
       emptyDiskImages = [ 512 ];
-      memorySize = 1024;
       qemu.options = [
         "-chardev socket,id=chrtpm,path=/tmp/swtpm-sock"
         "-tpmdev emulator,id=tpm0,chardev=chrtpm"
diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix
index e0685f53a9454..6561f7efe1a5f 100644
--- a/nixos/tests/systemd.nix
+++ b/nixos/tests/systemd.nix
@@ -5,7 +5,6 @@ import ./make-test-python.nix ({ pkgs, ... }: {
     imports = [ common/user-account.nix common/x11.nix ];
 
     virtualisation.emptyDiskImages = [ 512 512 ];
-    virtualisation.memorySize = 1024;
 
     environment.systemPackages = [ pkgs.cryptsetup ];
 
diff --git a/nixos/tests/tinydns.nix b/nixos/tests/tinydns.nix
index b80e3451700a6..124508bc004ba 100644
--- a/nixos/tests/tinydns.nix
+++ b/nixos/tests/tinydns.nix
@@ -21,6 +21,20 @@ import ./make-test-python.nix ({ lib, ...} : {
   testScript = ''
     nameserver.start()
     nameserver.wait_for_unit("tinydns.service")
-    nameserver.succeed("host bla.foo.bar 192.168.1.1 | grep '1\.2\.3\.4'")
+
+    # We query tinydns a few times to trigger the bug:
+    #
+    #   nameserver # [    6.105872] mmap: tinydns (842): VmData 331776 exceed data ulimit 300000. Update limits or use boot option ignore_rlimit_data.
+    #
+    # which was reported in https://github.com/NixOS/nixpkgs/issues/119066.
+    # Without the patch <nixpkgs/pkgs/tools/networking/djbdns/softlimit.patch>
+    # it fails on the 10th iteration.
+    nameserver.succeed(
+        """
+          for i in {1..100}; do
+            host bla.foo.bar 192.168.1.1 | grep '1\.2\.3\.4'
+          done
+        """
+    )
   '';
 })
diff --git a/nixos/tests/vault-postgresql.nix b/nixos/tests/vault-postgresql.nix
index a563aead22a3b..071cfd106ffbc 100644
--- a/nixos/tests/vault-postgresql.nix
+++ b/nixos/tests/vault-postgresql.nix
@@ -12,7 +12,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
     maintainers = [ lnl7 roberth ];
   };
   machine = { lib, pkgs, ... }: {
-    virtualisation.memorySize = 512;
     environment.systemPackages = [ pkgs.vault ];
     environment.variables.VAULT_ADDR = "http://127.0.0.1:8200";
     services.vault.enable = true;
diff --git a/nixos/tests/vault.nix b/nixos/tests/vault.nix
index c3b28b62695ad..e86acd5b593fb 100644
--- a/nixos/tests/vault.nix
+++ b/nixos/tests/vault.nix
@@ -8,7 +8,6 @@ import ./make-test-python.nix ({ pkgs, ... }:
     environment.systemPackages = [ pkgs.vault ];
     environment.variables.VAULT_ADDR = "http://127.0.0.1:8200";
     services.vault.enable = true;
-    virtualisation.memorySize = 512;
   };
 
   testScript =
diff --git a/nixos/tests/vaultwarden.nix b/nixos/tests/vaultwarden.nix
index b5343f5cad2d7..56f1d245d5052 100644
--- a/nixos/tests/vaultwarden.nix
+++ b/nixos/tests/vaultwarden.nix
@@ -140,7 +140,6 @@ let
               in
               [ pkgs.firefox-unwrapped pkgs.geckodriver testRunner ];
 
-            virtualisation.memorySize = 768;
           }
         ];
 
diff --git a/nixos/tests/vscodium.nix b/nixos/tests/vscodium.nix
index 033090aa0e3d1..43a0d61c856f5 100644
--- a/nixos/tests/vscodium.nix
+++ b/nixos/tests/vscodium.nix
@@ -1,47 +1,69 @@
-import ./make-test-python.nix ({ pkgs, ...} :
+let
+  tests = {
+    wayland = { pkgs, ... }: {
+      imports = [ ./common/wayland-cage.nix ];
 
-{
-  name = "vscodium";
-  meta = with pkgs.lib.maintainers; {
-    maintainers = [ turion ];
+      services.cage.program = ''
+        ${pkgs.vscodium}/bin/codium \
+          --enable-features=UseOzonePlatform \
+          --ozone-platform=wayland
+      '';
+
+      fonts.fonts = with pkgs; [ dejavu_fonts ];
+    };
+    xorg = { pkgs, ... }: {
+      imports = [ ./common/user-account.nix ./common/x11.nix ];
+
+      virtualisation.memorySize = 2047;
+      services.xserver.enable = true;
+      services.xserver.displayManager.sessionCommands = ''
+        ${pkgs.vscodium}/bin/codium
+      '';
+      test-support.displayManager.auto.user = "alice";
+    };
   };
 
-  machine = { ... }:
+  mkTest = name: machine:
+    import ./make-test-python.nix ({ pkgs, ... }: {
+      inherit name;
 
-  {
-    imports = [
-      ./common/user-account.nix
-      ./common/x11.nix
-    ];
+      nodes = { "${name}" = machine; };
 
-    virtualisation.memorySize = 2047;
-    services.xserver.enable = true;
-    test-support.displayManager.auto.user = "alice";
-    environment.systemPackages = with pkgs; [
-      vscodium
-    ];
-  };
+      meta = with pkgs.lib.maintainers; {
+        maintainers = [ synthetica turion ];
+      };
+      enableOCR = true;
+      testScript = ''
+        start_all()
+
+        machine.wait_for_unit('graphical.target')
+        machine.wait_until_succeeds('pgrep -x codium')
 
-  enableOCR = true;
+        # Wait until vscodium is visible. "File" is in the menu bar.
+        machine.wait_for_text('File')
+        machine.screenshot('start_screen')
 
-  testScript = { nodes, ... }: ''
-    # Start up X
-    start_all()
-    machine.wait_for_x()
+        test_string = 'testfile'
 
-    # Start VSCodium with a file that doesn't exist yet
-    machine.fail("ls /home/alice/foo.txt")
-    machine.succeed("su - alice -c 'codium foo.txt' >&2 &")
+        # Create a new file
+        machine.send_key('ctrl-n')
+        machine.wait_for_text('Untitled')
+        machine.screenshot('empty_editor')
 
-    # Wait for the window to appear
-    machine.wait_for_text("VSCodium")
+        # Type a string
+        machine.send_chars(test_string)
+        machine.wait_for_text(test_string)
+        machine.screenshot('editor')
 
-    # Save file
-    machine.send_key("ctrl-s")
+        # Save the file
+        machine.send_key('ctrl-s')
+        machine.wait_for_text('Save')
+        machine.screenshot('save_window')
+        machine.send_key('ret')
 
-    # Wait until the file has been saved
-    machine.wait_for_file("/home/alice/foo.txt")
+        # (the default filename is the first line of the file)
+        machine.wait_for_file(f'/home/alice/{test_string}')
+      '';
+    });
 
-    machine.screenshot("VSCodium")
-  '';
-})
+in builtins.mapAttrs (k: v: mkTest k v { }) tests
diff --git a/nixos/tests/xfce.nix b/nixos/tests/xfce.nix
index 148eccdbe3084..9051deebae76e 100644
--- a/nixos/tests/xfce.nix
+++ b/nixos/tests/xfce.nix
@@ -23,7 +23,6 @@ import ./make-test-python.nix ({ pkgs, ...} : {
 
       hardware.pulseaudio.enable = true; # needed for the factl test, /dev/snd/* exists without them but udev doesn't care then
 
-      virtualisation.memorySize = 1024;
     };
 
   testScript = { nodes, ... }: let