about summary refs log tree commit diff
path: root/nixpkgs/nixos
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2024-03-01 11:40:12 +0100
committerAlyssa Ross <hi@alyssa.is>2024-03-01 11:40:12 +0100
commitbf6d657e5dbcb5e39fda280ef7e86b2a7794ca86 (patch)
tree8eb035cbab19794f6415cc460fac7226f7a58afc /nixpkgs/nixos
parent66f707d69f1e423db5a35c2fe43b32781125a9af (diff)
parent09c1497ce5d4ed4a0edfdd44450d3048074cb300 (diff)
downloadnixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.gz
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.bz2
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.lz
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.xz
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.tar.zst
nixlib-bf6d657e5dbcb5e39fda280ef7e86b2a7794ca86.zip
Merge branch 'nixos-unstable-small' of https://github.com/NixOS/nixpkgs
Diffstat (limited to 'nixpkgs/nixos')
-rw-r--r--nixpkgs/nixos/doc/manual/development/settings-options.section.md28
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md21
-rw-r--r--nixpkgs/nixos/lib/make-disk-image.nix55
-rw-r--r--nixpkgs/nixos/lib/systemd-lib.nix16
-rw-r--r--nixpkgs/nixos/lib/systemd-unit-options.nix10
-rw-r--r--nixpkgs/nixos/lib/test-driver/test_driver/driver.py55
-rw-r--r--nixpkgs/nixos/lib/test-driver/test_driver/machine.py95
-rw-r--r--nixpkgs/nixos/lib/test-script-prepend.py13
-rw-r--r--nixpkgs/nixos/lib/testing/nixos-test-base.nix6
-rw-r--r--nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix46
-rw-r--r--nixpkgs/nixos/modules/installer/netboot/netboot.nix19
-rw-r--r--nixpkgs/nixos/modules/module-list.nix3
-rw-r--r--nixpkgs/nixos/modules/programs/gnupg.nix1
-rw-r--r--nixpkgs/nixos/modules/programs/wayland/sway.nix4
-rw-r--r--nixpkgs/nixos/modules/security/pam.nix39
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix12
-rw-r--r--nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix16
-rw-r--r--nixpkgs/nixos/modules/services/games/armagetronad.nix268
-rw-r--r--nixpkgs/nixos/modules/services/hardware/monado.nix102
-rw-r--r--nixpkgs/nixos/modules/services/misc/ollama.nix37
-rw-r--r--nixpkgs/nixos/modules/services/misc/paperless.nix3
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix15
-rw-r--r--nixpkgs/nixos/modules/services/networking/dhcpcd.nix32
-rw-r--r--nixpkgs/nixos/modules/services/networking/mosquitto.nix34
-rw-r--r--nixpkgs/nixos/modules/services/networking/searx.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/unbound.nix25
-rw-r--r--nixpkgs/nixos/modules/services/security/kanidm.nix32
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mealie.nix79
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix2
-rw-r--r--nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix276
-rw-r--r--nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix26
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py69
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix65
-rw-r--r--nixpkgs/nixos/modules/system/boot/networkd.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/plymouth.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/coredump.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/repart.nix16
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/zfs.nix39
-rw-r--r--nixpkgs/nixos/modules/virtualisation/podman/default.nix2
-rw-r--r--nixpkgs/nixos/release-combined.nix1
-rw-r--r--nixpkgs/nixos/release-small.nix4
-rw-r--r--nixpkgs/nixos/release.nix6
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix5
-rw-r--r--nixpkgs/nixos/tests/armagetronad.nix272
-rw-r--r--nixpkgs/nixos/tests/boot.nix82
-rw-r--r--nixpkgs/nixos/tests/common/ec2.nix2
-rw-r--r--nixpkgs/nixos/tests/consul.nix4
-rw-r--r--nixpkgs/nixos/tests/docker-tools.nix22
-rw-r--r--nixpkgs/nixos/tests/incus/container.nix13
-rw-r--r--nixpkgs/nixos/tests/installer.nix46
-rw-r--r--nixpkgs/nixos/tests/k3s/default.nix5
-rw-r--r--nixpkgs/nixos/tests/k3s/etcd.nix100
-rw-r--r--nixpkgs/nixos/tests/kernel-generic.nix1
-rw-r--r--nixpkgs/nixos/tests/lomiri-system-settings.nix99
-rw-r--r--nixpkgs/nixos/tests/mealie.nix24
-rw-r--r--nixpkgs/nixos/tests/monado.nix39
-rw-r--r--nixpkgs/nixos/tests/plasma6.nix64
-rw-r--r--nixpkgs/nixos/tests/searx.nix6
-rw-r--r--nixpkgs/nixos/tests/systemd-boot.nix115
60 files changed, 2124 insertions, 361 deletions
diff --git a/nixpkgs/nixos/doc/manual/development/settings-options.section.md b/nixpkgs/nixos/doc/manual/development/settings-options.section.md
index 3a4800742b04..71ec9bbc8892 100644
--- a/nixpkgs/nixos/doc/manual/development/settings-options.section.md
+++ b/nixpkgs/nixos/doc/manual/development/settings-options.section.md
@@ -73,6 +73,34 @@ have a predefined type and string generator already declared under
 
     It returns a set with INI-specific attributes `type` and `generate`
     as specified [below](#pkgs-formats-result).
+    The type of the input is an *attrset* of sections; key-value pairs where
+    the key is the section name and the value is the corresponding content
+    which is also an *attrset* of key-value pairs for the actual key-value
+    mappings of the INI format.
+    The values of the INI atoms are subject to the above parameters (e.g. lists
+    may be transformed into multiple key-value pairs depending on
+    `listToValue`).
+
+`pkgs.formats.iniWithGlobalSection` { *`listsAsDuplicateKeys`* ? false, *`listToValue`* ? null, \.\.\. }
+
+:   A function taking an attribute set with values
+
+    `listsAsDuplicateKeys`
+
+    :   A boolean for controlling whether list values can be used to
+        represent duplicate INI keys
+
+    `listToValue`
+
+    :   A function for turning a list of values into a single value.
+
+    It returns a set with INI-specific attributes `type` and `generate`
+    as specified [below](#pkgs-formats-result).
+    The type of the input is an *attrset* of the structure
+    `{ sections = {}; globalSection = {}; }` where *sections* are several
+    sections as with *pkgs.formats.ini* and *globalSection* being just a single
+    attrset of key-value pairs for a single section, the global section which
+    preceedes the section definitions.
 
 `pkgs.formats.toml` { }
 
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md
index b5973c19a2c4..510019b58658 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -16,6 +16,8 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `linuxPackages_testing_bcachefs` is now fully deprecated by `linuxPackages_latest`, and is therefore no longer available.
 
+- The default kernel package has been updated from 6.1 to 6.6. All supported kernels remain available.
+
 - NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS.
    - This can be disabled through the `environment.stub-ld.enable` option.
    - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically.
@@ -50,6 +52,8 @@ In addition to numerous new and upgraded packages, this release has the followin
   }
   ```
 
+- Plasma 6 is now available and can be installed with `services.xserver.desktopManager.plasma6.enable = true;`. Plasma 5 will likely be deprecated in the next release (24.11). Note that Plasma 6 runs as Wayland by default, and the X11 session needs to be explicitly selected if necessary.
+
 ## New Services {#sec-release-24.05-new-services}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -85,8 +89,12 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [go-camo](https://github.com/cactus/go-camo), a secure image proxy server. Available as [services.go-camo](#opt-services.go-camo.enable).
 
+- [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable).
+
 - [Clevis](https://github.com/latchset/clevis), a pluggable framework for automated decryption, used to unlock encrypted devices in initrd. Available as [boot.initrd.clevis.enable](#opt-boot.initrd.clevis.enable).
 
+- [armagetronad](https://wiki.armagetronad.org), a mid-2000s 3D lightcycle game widely played at iD Tech Camps. You can define multiple servers using `services.armagetronad.<server>.enable`.
+
 - [TuxClocker](https://github.com/Lurkki14/tuxclocker), a hardware control and monitoring program. Available as [programs.tuxclocker](#opt-programs.tuxclocker.enable).
 
 - [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable)
@@ -97,11 +105,13 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [systemd-lock-handler](https://git.sr.ht/~whynothugo/systemd-lock-handler/), a bridge between logind D-Bus events and systemd targets. Available as [services.systemd-lock-handler.enable](#opt-services.systemd-lock-handler.enable).
 
+- [Mealie](https://nightly.mealie.io/), a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in NuxtJS for a pleasant user experience for the whole family. Available as [services.mealie](#opt-services.mealie.enable)
+
 ## Backward Incompatibilities {#sec-release-24.05-incompatibilities}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
 
-- `himalaya` was updated to v1.0.0-beta, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta) for details.
+- `himalaya` was updated to `v1.0.0-beta.3`, which introduces breaking changes. Check out the [release note](https://github.com/soywod/himalaya/releases/tag/v1.0.0-beta.3) for details.
 
 - The `power.ups` module now generates `upsd.conf`, `upsd.users` and `upsmon.conf` automatically from a set of new configuration options. This breaks compatibility with existing `power.ups` setups where these files were created manually. Back up these files before upgrading NixOS.
 
@@ -277,6 +287,10 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - Cinnamon has been updated to 6.0. Please beware that the [Wayland session](https://blog.linuxmint.com/?p=4591) is still experimental in this release.
 
+- New `boot.loader.systemd-boot.xbootldrMountPoint` allows setting up a separate [XBOOTLDR partition](https://uapi-group.org/specifications/specs/boot_loader_specification/) to store boot files. Useful on systems with a small EFI System partition that cannot be easily repartitioned.
+
+- `boot.loader.systemd-boot` will now verify that `efiSysMountPoint` (and `xbootldrMountPoint` if configured) are mounted partitions.
+
 - `services.postgresql.extraPlugins` changed its type from just a list of packages to also a function that returns such a list.
   For example a config line like ``services.postgresql.extraPlugins = with pkgs.postgresql_11.pkgs; [ postgis ];`` is recommended to be changed to ``services.postgresql.extraPlugins = ps: with ps; [ postgis ];``;
 
@@ -378,6 +392,11 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - The `mpich` package expression now requires `withPm` to be a list, e.g. `"hydra:gforker"` becomes `[ "hydra" "gforker" ]`.
 
+- When merging systemd unit options (of type `unitOption`),
+  if at least one definition is a list, all those which aren't are now lifted into a list,
+  making it possible to accumulate definitions without resorting to `mkForce`,
+  hence to retain the definitions not anticipating that need.
+
 - YouTrack is bumped to 2023.3. The update is not performed automatically, it requires manual interaction. See the YouTrack section in the manual for details.
 
 - QtMultimedia has changed its default backend to `QT_MEDIA_BACKEND=ffmpeg` (previously `gstreamer` on Linux or `darwin` on MacOS).
diff --git a/nixpkgs/nixos/lib/make-disk-image.nix b/nixpkgs/nixos/lib/make-disk-image.nix
index 047e72e2ac0d..da94ef16654c 100644
--- a/nixpkgs/nixos/lib/make-disk-image.nix
+++ b/nixpkgs/nixos/lib/make-disk-image.nix
@@ -56,6 +56,14 @@ This partition table type uses GPT and:
 - creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
 - creates an primary ext4 partition starting after the boot partition and extending to the full disk image
 
+#### `efixbootldr`
+
+This partition table type uses GPT and:
+
+- creates an FAT32 ESP partition from 8MiB to 100MiB, set it bootable ;
+- creates an FAT32 BOOT partition from 100MiB to specified `bootSize` parameter (256MiB by default), set `bls_boot` flag ;
+- creates an primary ext4 partition starting after the boot partition and extending to the full disk image
+
 #### `hybrid`
 
 This partition table type uses GPT and:
@@ -111,19 +119,7 @@ To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$imag
   # When setting one of `user' or `group', the other needs to be set too.
   contents ? []
 
-, # Type of partition table to use; either "legacy", "efi", or "none".
-  # For "efi" images, the GPT partition table is used and a mandatory ESP
-  #   partition of reasonable size is created in addition to the root partition.
-  # For "legacy", the msdos partition table is used and a single large root
-  #   partition is created.
-  # For "legacy+gpt", the GPT partition table is used, a 1MiB no-fs partition for
-  #   use by the bootloader is created, and a single large root partition is
-  #   created.
-  # For "hybrid", the GPT partition table is used and a mandatory ESP
-  #   partition of reasonable size is created in addition to the root partition.
-  #   Also a legacy MBR will be present.
-  # For "none", no partition table is created. Enabling `installBootLoader`
-  #   most likely fails as GRUB will probably refuse to install.
+, # Type of partition table to use; described in the `Image Partitioning` section above.
   partitionTableType ? "legacy"
 
 , # Whether to invoke `switch-to-configuration boot` during image creation
@@ -193,11 +189,11 @@ To solve this, you can run `fdisk -l $image` and generate `dd if=$image of=$imag
   additionalPaths ? []
 }:
 
-assert (lib.assertOneOf "partitionTableType" partitionTableType [ "legacy" "legacy+gpt" "efi" "hybrid" "none" ]);
+assert (lib.assertOneOf "partitionTableType" partitionTableType [ "legacy" "legacy+gpt" "efi" "efixbootldr" "hybrid" "none" ]);
 assert (lib.assertMsg (fsType == "ext4" && deterministic -> rootFSUID != null) "In deterministic mode with a ext4 partition, rootFSUID must be non-null, by default, it is equal to rootGPUID.");
   # We use -E offset=X below, which is only supported by e2fsprogs
 assert (lib.assertMsg (partitionTableType != "none" -> fsType == "ext4") "to produce a partition table, we need to use -E offset flag which is support only for fsType = ext4");
-assert (lib.assertMsg (touchEFIVars -> partitionTableType == "hybrid" || partitionTableType == "efi" || partitionTableType == "legacy+gpt") "EFI variables can be used only with a partition table of type: hybrid, efi or legacy+gpt.");
+assert (lib.assertMsg (touchEFIVars -> partitionTableType == "hybrid" || partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "legacy+gpt") "EFI variables can be used only with a partition table of type: hybrid, efi, efixbootldr, or legacy+gpt.");
   # If only Nix store image, then: contents must be empty, configFile must be unset, and we should no install bootloader.
 assert (lib.assertMsg (onlyNixStore -> contents == [] && configFile == null && !installBootLoader) "In a only Nix store image, the contents must be empty, no configuration must be provided and no bootloader should be installed.");
 # Either both or none of {user,group} need to be set
@@ -225,6 +221,7 @@ let format' = format; in let
     legacy = "1";
     "legacy+gpt" = "2";
     efi = "2";
+    efixbootldr = "3";
     hybrid = "3";
   }.${partitionTableType};
 
@@ -266,6 +263,23 @@ let format' = format; in let
           $diskImage
       ''}
     '';
+    efixbootldr = ''
+      parted --script $diskImage -- \
+        mklabel gpt \
+        mkpart ESP fat32 8MiB 100MiB \
+        set 1 boot on \
+        mkpart BOOT fat32 100MiB ${bootSize} \
+        set 2 bls_boot on \
+        mkpart ROOT ext4 ${bootSize} -1
+      ${optionalString deterministic ''
+          sgdisk \
+          --disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
+          --partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC  \
+          --partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F  \
+          --partition-guid=3:${rootGPUID} \
+          $diskImage
+      ''}
+    '';
     hybrid = ''
       parted --script $diskImage -- \
         mklabel gpt \
@@ -436,7 +450,7 @@ let format' = format; in let
     diskImage=nixos.raw
 
     ${if diskSize == "auto" then ''
-      ${if partitionTableType == "efi" || partitionTableType == "hybrid" then ''
+      ${if partitionTableType == "efi" || partitionTableType == "efixbootldr" || partitionTableType == "hybrid" then ''
         # Add the GPT at the end
         gptSpace=$(( 512 * 34 * 1 ))
         # Normally we'd need to account for alignment and things, if bootSize
@@ -570,6 +584,15 @@ let format' = format; in let
 
         ${optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
       ''}
+      ${optionalString (partitionTableType == "efixbootldr") ''
+        mkdir -p /mnt/{boot,efi}
+        mkfs.vfat -n ESP /dev/vda1
+        mkfs.vfat -n BOOT /dev/vda2
+        mount /dev/vda1 /mnt/efi
+        mount /dev/vda2 /mnt/boot
+
+        ${optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
+      ''}
 
       # Install a configuration.nix
       mkdir -p /mnt/etc/nixos
diff --git a/nixpkgs/nixos/lib/systemd-lib.nix b/nixpkgs/nixos/lib/systemd-lib.nix
index c9cca619ed70..ef218e674ebf 100644
--- a/nixpkgs/nixos/lib/systemd-lib.nix
+++ b/nixpkgs/nixos/lib/systemd-lib.nix
@@ -378,7 +378,7 @@ in rec {
     '';
 
   targetToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text =
         ''
           [Unit]
@@ -387,7 +387,7 @@ in rec {
     };
 
   serviceToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def (''
         [Service]
       '' + (let env = cfg.globalEnvironment // def.environment;
@@ -408,7 +408,7 @@ in rec {
     };
 
   socketToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def ''
         [Socket]
         ${attrsToSection def.socketConfig}
@@ -418,7 +418,7 @@ in rec {
     };
 
   timerToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def ''
         [Timer]
         ${attrsToSection def.timerConfig}
@@ -426,7 +426,7 @@ in rec {
     };
 
   pathToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def ''
         [Path]
         ${attrsToSection def.pathConfig}
@@ -434,7 +434,7 @@ in rec {
     };
 
   mountToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def ''
         [Mount]
         ${attrsToSection def.mountConfig}
@@ -442,7 +442,7 @@ in rec {
     };
 
   automountToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def ''
         [Automount]
         ${attrsToSection def.automountConfig}
@@ -450,7 +450,7 @@ in rec {
     };
 
   sliceToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable overrideStrategy;
+    { inherit (def) aliases wantedBy requiredBy upheldBy enable overrideStrategy;
       text = commonUnitText def ''
         [Slice]
         ${attrsToSection def.sliceConfig}
diff --git a/nixpkgs/nixos/lib/systemd-unit-options.nix b/nixpkgs/nixos/lib/systemd-unit-options.nix
index bc7880da9fe0..e4953ba72dd9 100644
--- a/nixpkgs/nixos/lib/systemd-unit-options.nix
+++ b/nixpkgs/nixos/lib/systemd-unit-options.nix
@@ -21,14 +21,8 @@ in rec {
       let
         defs' = filterOverrides defs;
       in
-        if isList (head defs').value
-        then concatMap (def:
-          if builtins.typeOf def.value == "list"
-          then def.value
-          else
-            throw "The definitions for systemd unit options should be either all lists, representing repeatable options, or all non-lists, but for the option ${showOption loc}, the definitions are a mix of list and non-list ${lib.options.showDefs defs'}"
-        ) defs'
-
+        if any (def: isList def.value) defs'
+        then concatMap (def: toList def.value) defs'
         else mergeEqualOption loc defs';
   };
 
diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py
index 786821b0cc0d..72a33e0b2d57 100644
--- a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py
+++ b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py
@@ -12,6 +12,8 @@ from test_driver.machine import Machine, NixStartScript, retry
 from test_driver.polling_condition import PollingCondition
 from test_driver.vlan import VLan
 
+SENTINEL = object()
+
 
 def get_tmp_dir() -> Path:
     """Returns a temporary directory that is defined by TMPDIR, TEMP, TMP or CWD
@@ -187,23 +189,58 @@ class Driver:
             # to swallow them and prevent itself from terminating.
             os.kill(os.getpid(), signal.SIGTERM)
 
-    def create_machine(self, args: Dict[str, Any]) -> Machine:
+    def create_machine(
+        self,
+        start_command: str | dict,
+        *,
+        name: Optional[str] = None,
+        keep_vm_state: bool = False,
+    ) -> Machine:
+        # Legacy args handling
+        # FIXME: remove after 24.05
+        if isinstance(start_command, dict):
+            if name is not None or keep_vm_state:
+                raise TypeError(
+                    "Dictionary passed to create_machine must be the only argument"
+                )
+
+            args = start_command
+            start_command = args.pop("startCommand", SENTINEL)
+
+            if start_command is SENTINEL:
+                raise TypeError(
+                    "Dictionary passed to create_machine must contain startCommand"
+                )
+
+            if not isinstance(start_command, str):
+                raise TypeError(
+                    f"startCommand must be a string, got: {repr(start_command)}"
+                )
+
+            name = args.pop("name", None)
+            keep_vm_state = args.pop("keep_vm_state", False)
+
+            if args:
+                raise TypeError(
+                    f"Unsupported arguments passed to create_machine: {args}"
+                )
+
+            rootlog.warning(
+                "Using create_machine with a single dictionary argument is deprecated, and will be removed in NixOS 24.11"
+            )
+        # End legacy args handling
+
         tmp_dir = get_tmp_dir()
 
-        if args.get("startCommand"):
-            start_command: str = args.get("startCommand", "")
-            cmd = NixStartScript(start_command)
-            name = args.get("name", cmd.machine_name)
-        else:
-            cmd = Machine.create_startcommand(args)  # type: ignore
-            name = args.get("name", "machine")
+        cmd = NixStartScript(start_command)
+        name = name or cmd.machine_name
 
         return Machine(
             tmp_dir=tmp_dir,
             out_dir=self.out_dir,
             start_command=cmd,
             name=name,
-            keep_vm_state=args.get("keep_vm_state", False),
+            keep_vm_state=keep_vm_state,
         )
 
     def serial_stdout_on(self) -> None:
diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py
index 93411a4a348e..df8628bce956 100644
--- a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py
@@ -208,7 +208,6 @@ class StartCommand:
             ),
             stdin=subprocess.PIPE,
             stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT,
             shell=True,
             cwd=state_dir,
             env=self.build_environment(state_dir, shared_dir),
@@ -235,77 +234,6 @@ class NixStartScript(StartCommand):
         return name
 
 
-class LegacyStartCommand(StartCommand):
-    """Used in some places to create an ad-hoc machine instead of
-    using nix test instrumentation + module system for that purpose.
-    Legacy.
-    """
-
-    def __init__(
-        self,
-        netBackendArgs: Optional[str] = None,  # noqa: N803
-        netFrontendArgs: Optional[str] = None,  # noqa: N803
-        hda: Optional[Tuple[Path, str]] = None,
-        cdrom: Optional[str] = None,
-        usb: Optional[str] = None,
-        bios: Optional[str] = None,
-        qemuBinary: Optional[str] = None,  # noqa: N803
-        qemuFlags: Optional[str] = None,  # noqa: N803
-    ):
-        if qemuBinary is not None:
-            self._cmd = qemuBinary
-        else:
-            self._cmd = "qemu-kvm"
-
-        self._cmd += " -m 384"
-
-        # networking
-        net_backend = "-netdev user,id=net0"
-        net_frontend = "-device virtio-net-pci,netdev=net0"
-        if netBackendArgs is not None:
-            net_backend += "," + netBackendArgs
-        if netFrontendArgs is not None:
-            net_frontend += "," + netFrontendArgs
-        self._cmd += f" {net_backend} {net_frontend}"
-
-        # hda
-        hda_cmd = ""
-        if hda is not None:
-            hda_path = hda[0].resolve()
-            hda_interface = hda[1]
-            if hda_interface == "scsi":
-                hda_cmd += (
-                    f" -drive id=hda,file={hda_path},werror=report,if=none"
-                    " -device scsi-hd,drive=hda"
-                )
-            else:
-                hda_cmd += f" -drive file={hda_path},if={hda_interface},werror=report"
-        self._cmd += hda_cmd
-
-        # cdrom
-        if cdrom is not None:
-            self._cmd += f" -cdrom {cdrom}"
-
-        # usb
-        usb_cmd = ""
-        if usb is not None:
-            # https://github.com/qemu/qemu/blob/master/docs/usb2.txt
-            usb_cmd += (
-                " -device usb-ehci"
-                f" -drive id=usbdisk,file={usb},if=none,readonly"
-                " -device usb-storage,drive=usbdisk "
-            )
-        self._cmd += usb_cmd
-
-        # bios
-        if bios is not None:
-            self._cmd += f" -bios {bios}"
-
-        # qemu flags
-        if qemuFlags is not None:
-            self._cmd += f" {qemuFlags}"
-
-
 class Machine:
     """A handle to the machine with this name, that also knows how to manage
     the machine lifecycle with the help of a start script / command."""
@@ -377,29 +305,6 @@ class Machine:
         self.booted = False
         self.connected = False
 
-    @staticmethod
-    def create_startcommand(args: Dict[str, str]) -> StartCommand:
-        rootlog.warning(
-            "Using legacy create_startcommand(), "
-            "please use proper nix test vm instrumentation, instead "
-            "to generate the appropriate nixos test vm qemu startup script"
-        )
-        hda = None
-        if args.get("hda"):
-            hda_arg: str = args.get("hda", "")
-            hda_arg_path: Path = Path(hda_arg)
-            hda = (hda_arg_path, args.get("hdaInterface", ""))
-        return LegacyStartCommand(
-            netBackendArgs=args.get("netBackendArgs"),
-            netFrontendArgs=args.get("netFrontendArgs"),
-            hda=hda,
-            cdrom=args.get("cdrom"),
-            usb=args.get("usb"),
-            bios=args.get("bios"),
-            qemuBinary=args.get("qemuBinary"),
-            qemuFlags=args.get("qemuFlags"),
-        )
-
     def is_up(self) -> bool:
         return self.booted and self.connected
 
diff --git a/nixpkgs/nixos/lib/test-script-prepend.py b/nixpkgs/nixos/lib/test-script-prepend.py
index 15e59ce01047..976992ea0015 100644
--- a/nixpkgs/nixos/lib/test-script-prepend.py
+++ b/nixpkgs/nixos/lib/test-script-prepend.py
@@ -26,6 +26,17 @@ class PollingConditionProtocol(Protocol):
         raise Exception("This is just type information for the Nix test driver")
 
 
+class CreateMachineProtocol(Protocol):
+    def __call__(
+        self,
+        start_command: str | dict,
+        *,
+        name: Optional[str] = None,
+        keep_vm_state: bool = False,
+    ) -> Machine:
+        raise Exception("This is just type information for the Nix test driver")
+
+
 start_all: Callable[[], None]
 subtest: Callable[[str], ContextManager[None]]
 retry: RetryProtocol
@@ -34,7 +45,7 @@ machines: List[Machine]
 vlans: List[VLan]
 driver: Driver
 log: Logger
-create_machine: Callable[[Dict[str, Any]], Machine]
+create_machine: CreateMachineProtocol
 run_tests: Callable[[], None]
 join_all: Callable[[], None]
 serial_stdout_off: Callable[[], None]
diff --git a/nixpkgs/nixos/lib/testing/nixos-test-base.nix b/nixpkgs/nixos/lib/testing/nixos-test-base.nix
index 59e6e3843367..d76a25361f8c 100644
--- a/nixpkgs/nixos/lib/testing/nixos-test-base.nix
+++ b/nixpkgs/nixos/lib/testing/nixos-test-base.nix
@@ -16,7 +16,11 @@ in
       # The human version (e.g. 21.05-pre) is left as is, because it is useful
       # for external modules that test with e.g. testers.nixosTest and rely on that
       # version number.
-      config.system.nixos.revision = mkForce "constant-nixos-revision";
+      config.system.nixos = {
+        revision = mkForce "constant-nixos-revision";
+        versionSuffix = mkForce "test";
+        label = mkForce "test";
+      };
     }
 
   ];
diff --git a/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix
new file mode 100644
index 000000000000..11118db3aae2
--- /dev/null
+++ b/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix
@@ -0,0 +1,46 @@
+# This module defines a NixOS installation CD that contains Plasma 6.
+
+{ pkgs, ... }:
+
+{
+  imports = [ ./installation-cd-graphical-calamares.nix ];
+
+  isoImage.edition = "plasma6";
+
+  services.xserver = {
+    desktopManager.plasma6.enable = true;
+
+    # Automatically login as nixos.
+    displayManager = {
+      sddm.enable = true;
+      autoLogin = {
+        enable = true;
+        user = "nixos";
+      };
+    };
+  };
+
+  environment.systemPackages = [
+    # FIXME: using Qt5 builds of Maliit as upstream has not ported to Qt6 yet
+    pkgs.maliit-framework
+    pkgs.maliit-keyboard
+  ];
+
+  system.activationScripts.installerDesktop = let
+
+    # Comes from documentation.nix when xserver and nixos.enable are true.
+    manualDesktopFile = "/run/current-system/sw/share/applications/nixos-manual.desktop";
+
+    homeDir = "/home/nixos/";
+    desktopDir = homeDir + "Desktop/";
+
+  in ''
+    mkdir -p ${desktopDir}
+    chown nixos ${homeDir} ${desktopDir}
+
+    ln -sfT ${manualDesktopFile} ${desktopDir + "nixos-manual.desktop"}
+    ln -sfT ${pkgs.gparted}/share/applications/gparted.desktop ${desktopDir + "gparted.desktop"}
+    ln -sfT ${pkgs.calamares-nixos}/share/applications/io.calamares.calamares.desktop ${desktopDir + "io.calamares.calamares.desktop"}
+  '';
+
+}
diff --git a/nixpkgs/nixos/modules/installer/netboot/netboot.nix b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
index a50f22cbe471..028a2d74041e 100644
--- a/nixpkgs/nixos/modules/installer/netboot/netboot.nix
+++ b/nixpkgs/nixos/modules/installer/netboot/netboot.nix
@@ -62,19 +62,12 @@ with lib;
       };
 
     fileSystems."/nix/store" = mkImageMediaOverride
-      { fsType = "overlay";
-        device = "overlay";
-        options = [
-          "lowerdir=/nix/.ro-store"
-          "upperdir=/nix/.rw-store/store"
-          "workdir=/nix/.rw-store/work"
-        ];
-
-        depends = [
-          "/nix/.ro-store"
-          "/nix/.rw-store/store"
-          "/nix/.rw-store/work"
-        ];
+      { overlay = {
+          lowerdir = [ "/nix/.ro-store" ];
+          upperdir = "/nix/.rw-store/store";
+          workdir = "/nix/.rw-store/work";
+        };
+        neededForBoot = true;
       };
 
     boot.initrd.availableKernelModules = [ "squashfs" "overlay" ];
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index 5d82c6de77e2..08144140b830 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -512,6 +512,7 @@
   ./services/editors/infinoted.nix
   ./services/finance/odoo.nix
   ./services/games/archisteamfarm.nix
+  ./services/games/armagetronad.nix
   ./services/games/crossfire-server.nix
   ./services/games/deliantra-server.nix
   ./services/games/factorio.nix
@@ -549,6 +550,7 @@
   ./services/hardware/lcd.nix
   ./services/hardware/lirc.nix
   ./services/hardware/nvidia-container-toolkit-cdi-generator
+  ./services/hardware/monado.nix
   ./services/hardware/nvidia-optimus.nix
   ./services/hardware/openrgb.nix
   ./services/hardware/pcscd.nix
@@ -1324,6 +1326,7 @@
   ./services/web-apps/mastodon.nix
   ./services/web-apps/matomo.nix
   ./services/web-apps/mattermost.nix
+  ./services/web-apps/mealie.nix
   ./services/web-apps/mediawiki.nix
   ./services/web-apps/meme-bingo-web.nix
   ./services/web-apps/microbin.nix
diff --git a/nixpkgs/nixos/modules/programs/gnupg.nix b/nixpkgs/nixos/modules/programs/gnupg.nix
index 8f82de033666..179d2de87cc5 100644
--- a/nixpkgs/nixos/modules/programs/gnupg.nix
+++ b/nixpkgs/nixos/modules/programs/gnupg.nix
@@ -15,6 +15,7 @@ let
   defaultPinentryFlavor =
     if xserverCfg.desktopManager.lxqt.enable
     || xserverCfg.desktopManager.plasma5.enable
+    || xserverCfg.desktopManager.plasma6.enable
     || xserverCfg.desktopManager.deepin.enable then
       "qt"
     else if xserverCfg.desktopManager.xfce.enable then
diff --git a/nixpkgs/nixos/modules/programs/wayland/sway.nix b/nixpkgs/nixos/modules/programs/wayland/sway.nix
index 57ee629b2881..ca2503ae5da7 100644
--- a/nixpkgs/nixos/modules/programs/wayland/sway.nix
+++ b/nixpkgs/nixos/modules/programs/wayland/sway.nix
@@ -119,10 +119,10 @@ in {
     extraPackages = mkOption {
       type = with types; listOf package;
       default = with pkgs; [
-        swaylock swayidle foot dmenu
+        swaylock swayidle foot dmenu wmenu
       ];
       defaultText = literalExpression ''
-        with pkgs; [ swaylock swayidle foot dmenu ];
+        with pkgs; [ swaylock swayidle foot dmenu wmenu ];
       '';
       example = literalExpression ''
         with pkgs; [
diff --git a/nixpkgs/nixos/modules/security/pam.nix b/nixpkgs/nixos/modules/security/pam.nix
index ed03254cb5ee..b87e22b23980 100644
--- a/nixpkgs/nixos/modules/security/pam.nix
+++ b/nixpkgs/nixos/modules/security/pam.nix
@@ -96,6 +96,10 @@ let
 
   pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in {
 
+    imports = [
+      (lib.mkRenamedOptionModule [ "enableKwallet" ] [ "kwallet" "enable" ])
+    ];
+
     options = {
 
       name = mkOption {
@@ -462,16 +466,23 @@ let
         '';
       };
 
-      enableKwallet = mkOption {
-        default = false;
-        type = types.bool;
-        description = lib.mdDoc ''
-          If enabled, pam_wallet will attempt to automatically unlock the
-          user's default KDE wallet upon login. If the user has no wallet named
-          "kdewallet", or the login password does not match their wallet
-          password, KDE will prompt separately after login.
-        '';
+      kwallet = {
+        enable = mkOption {
+          default = false;
+          type = types.bool;
+          description = lib.mdDoc ''
+            If enabled, pam_wallet will attempt to automatically unlock the
+            user's default KDE wallet upon login. If the user has no wallet named
+            "kdewallet", or the login password does not match their wallet
+            password, KDE will prompt separately after login.
+          '';
+        };
+
+        package = mkPackageOption pkgs.plasma5Packages "kwallet-pam" {
+          pkgsText = "pkgs.plasma5Packages";
+        };
       };
+
       sssdStrictAccess = mkOption {
         default = false;
         type = types.bool;
@@ -686,7 +697,7 @@ let
             (config.security.pam.enableEcryptfs
               || config.security.pam.enableFscrypt
               || cfg.pamMount
-              || cfg.enableKwallet
+              || cfg.kwallet.enable
               || cfg.enableGnomeKeyring
               || config.services.intune.enable
               || cfg.googleAuthenticator.enable
@@ -711,9 +722,7 @@ let
               { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = {
                 disable_interactive = true;
               }; }
-              { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = {
-                kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5";
-              }; }
+              { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; }
               { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; }
               { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
               { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
@@ -848,9 +857,7 @@ let
             order = "user,group,default";
             debug = true;
           }; }
-          { name = "kwallet5"; enable = cfg.enableKwallet; control = "optional"; modulePath = "${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so"; settings = {
-            kwalletd = "${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5";
-          }; }
+          { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; }
           { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = {
             auto_start = true;
           }; }
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
index aa24c0842bab..8f3ad78d50ce 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/pipewire.nix
@@ -293,6 +293,18 @@ in {
         assertion = (cfg.alsa.enable || cfg.pulse.enable) -> cfg.audio.enable;
         message = "Using PipeWire's ALSA/PulseAudio compatibility layers requires running PipeWire as the sound server. Set `services.pipewire.audio.enable` to true.";
       }
+      {
+        assertion = builtins.length
+          (builtins.attrNames
+            (
+              lib.filterAttrs
+                (name: value:
+                  lib.hasPrefix "pipewire/" name || name == "pipewire"
+                )
+                config.environment.etc
+            )) == 1;
+        message = "Using `environment.etc.\"pipewire<...>\"` directly is no longer supported in 24.05. Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` instead.";
+      }
     ];
 
     environment.systemPackages = [ cfg.package ]
diff --git a/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix b/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix
index dc4d726d7632..99aea8facb16 100644
--- a/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix
+++ b/nixpkgs/nixos/modules/services/desktops/pipewire/wireplumber.nix
@@ -56,13 +56,13 @@ in
         -- PipeWire is not used for audio, so prevent it from grabbing audio devices
         alsa_monitor.enable = function() end
       '';
-      systemwideConfigPkg = pkgs.writeTextDir "wireplumber/main.lua.d/80-systemwide.lua" ''
+      systemwideConfigPkg = pkgs.writeTextDir "share/wireplumber/main.lua.d/80-systemwide.lua" ''
         -- When running system-wide, these settings need to be disabled (they
         -- use functions that aren't available on the system dbus).
         alsa_monitor.properties["alsa.reserve"] = false
         default_access.properties["enable-flatpak-portal"] = false
       '';
-      systemwideBluetoothConfigPkg = pkgs.writeTextDir "wireplumber/bluetooth.lua.d/80-systemwide.lua" ''
+      systemwideBluetoothConfigPkg = pkgs.writeTextDir "share/wireplumber/bluetooth.lua.d/80-systemwide.lua" ''
         -- When running system-wide, logind-integration needs to be disabled.
         bluez_monitor.properties["with-logind"] = false
       '';
@@ -98,6 +98,18 @@ in
           assertion = !config.hardware.bluetooth.hsphfpd.enable;
           message = "Using WirePlumber conflicts with hsphfpd, as it provides the same functionality. `hardware.bluetooth.hsphfpd.enable` needs be set to false";
         }
+        {
+          assertion = builtins.length
+            (builtins.attrNames
+              (
+                lib.filterAttrs
+                  (name: value:
+                    lib.hasPrefix "wireplumber/" name || name == "wireplumber"
+                  )
+                  config.environment.etc
+              )) == 1;
+          message = "Using `environment.etc.\"wireplumber<...>\"` directly is no longer supported in 24.05. Use `services.wireplumber.configPackages` instead.";
+        }
       ];
 
       environment.systemPackages = [ cfg.package ];
diff --git a/nixpkgs/nixos/modules/services/games/armagetronad.nix b/nixpkgs/nixos/modules/services/games/armagetronad.nix
new file mode 100644
index 000000000000..f79818e0e53b
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/games/armagetronad.nix
@@ -0,0 +1,268 @@
+{ config, lib, pkgs, ... }:
+let
+  inherit (lib) mkEnableOption mkIf mkOption mkMerge literalExpression;
+  inherit (lib) mapAttrsToList filterAttrs unique recursiveUpdate types;
+
+  mkValueStringArmagetron = with lib; v:
+    if isInt v then toString v
+    else if isFloat v then toString v
+    else if isString v then v
+    else if true == v then "1"
+    else if false == v then "0"
+    else if null == v then ""
+    else throw "unsupported type: ${builtins.typeOf v}: ${(lib.generators.toPretty {} v)}";
+
+  settingsFormat = pkgs.formats.keyValue {
+    mkKeyValue = lib.generators.mkKeyValueDefault
+      {
+        mkValueString = mkValueStringArmagetron;
+      } " ";
+    listsAsDuplicateKeys = true;
+  };
+
+  cfg = config.services.armagetronad;
+  enabledServers = lib.filterAttrs (n: v: v.enable) cfg.servers;
+  nameToId = serverName: "armagetronad-${serverName}";
+  getStateDirectory = serverName: "armagetronad/${serverName}";
+  getServerRoot = serverName: "/var/lib/${getStateDirectory serverName}";
+in
+{
+  options = {
+    services.armagetronad = {
+      servers = mkOption {
+        description = lib.mdDoc "Armagetron server definitions.";
+        default = { };
+        type = types.attrsOf (types.submodule {
+          options = {
+            enable = mkEnableOption (lib.mdDoc "armagetronad");
+
+            package = lib.mkPackageOptionMD pkgs "armagetronad-dedicated" {
+              example = ''
+                pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated
+              '';
+              extraDescription = ''
+                Ensure that you use a derivation which contains the path `bin/armagetronad-dedicated`.
+              '';
+            };
+
+            host = mkOption {
+              type = types.str;
+              default = "0.0.0.0";
+              description = lib.mdDoc "Host to listen on. Used for SERVER_IP.";
+            };
+
+            port = mkOption {
+              type = types.port;
+              default = 4534;
+              description = lib.mdDoc "Port to listen on. Used for SERVER_PORT.";
+            };
+
+            dns = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              description = lib.mdDoc "DNS address to use for this server. Optional.";
+            };
+
+            openFirewall = mkOption {
+              type = types.bool;
+              default = true;
+              description = lib.mdDoc "Set to true to open the configured UDP port for Armagetron Advanced.";
+            };
+
+            name = mkOption {
+              type = types.str;
+              description = "The name of this server.";
+            };
+
+            settings = mkOption {
+              type = settingsFormat.type;
+              default = { };
+              description = lib.mdDoc ''
+                Armagetron Advanced server rules configuration. Refer to:
+                <https://wiki.armagetronad.org/index.php?title=Console_Commands>
+                or `armagetronad-dedicated --doc` for a list.
+
+                This attrset is used to populate `settings_custom.cfg`; see:
+                <https://wiki.armagetronad.org/index.php/Configuration_Files>
+              '';
+              example = literalExpression ''
+                {
+                  CYCLE_RUBBER = 40;
+                }
+              '';
+            };
+
+            roundSettings = mkOption {
+              type = settingsFormat.type;
+              default = { };
+              description = lib.mdDoc ''
+                Armagetron Advanced server per-round configuration. Refer to:
+                <https://wiki.armagetronad.org/index.php?title=Console_Commands>
+                or `armagetronad-dedicated --doc` for a list.
+
+                This attrset is used to populate `everytime.cfg`; see:
+                <https://wiki.armagetronad.org/index.php/Configuration_Files>
+              '';
+              example = literalExpression ''
+                {
+                  SAY = [
+                    "Hosted on NixOS"
+                    "https://nixos.org"
+                    "iD Tech High Rubber rul3z!! Happy New Year 2008!!1"
+                  ];
+                }
+              '';
+            };
+          };
+        });
+      };
+    };
+  };
+
+  config = mkIf (enabledServers != { }) {
+    systemd.tmpfiles.settings = mkMerge (mapAttrsToList
+      (serverName: serverCfg:
+        let
+          serverId = nameToId serverName;
+          serverRoot = getServerRoot serverName;
+          serverInfo = (
+            {
+              SERVER_IP = serverCfg.host;
+              SERVER_PORT = serverCfg.port;
+              SERVER_NAME = serverCfg.name;
+            } // (lib.optionalAttrs (serverCfg.dns != null) { SERVER_DNS = serverCfg.dns; })
+          );
+          customSettings = serverCfg.settings;
+          everytimeSettings = serverCfg.roundSettings;
+
+          serverInfoCfg = settingsFormat.generate "server_info.${serverName}.cfg" serverInfo;
+          customSettingsCfg = settingsFormat.generate "settings_custom.${serverName}.cfg" customSettings;
+          everytimeSettingsCfg = settingsFormat.generate "everytime.${serverName}.cfg" everytimeSettings;
+        in
+        {
+          "10-armagetronad-${serverId}" = {
+            "${serverRoot}/data" = {
+              d = {
+                group = serverId;
+                user = serverId;
+                mode = "0750";
+              };
+            };
+            "${serverRoot}/settings" = {
+              d = {
+                group = serverId;
+                user = serverId;
+                mode = "0750";
+              };
+            };
+            "${serverRoot}/var" = {
+              d = {
+                group = serverId;
+                user = serverId;
+                mode = "0750";
+              };
+            };
+            "${serverRoot}/resource" = {
+              d = {
+                group = serverId;
+                user = serverId;
+                mode = "0750";
+              };
+            };
+            "${serverRoot}/input" = {
+              "f+" = {
+                group = serverId;
+                user = serverId;
+                mode = "0640";
+              };
+            };
+            "${serverRoot}/settings/server_info.cfg" = {
+              "L+" = {
+                argument = "${serverInfoCfg}";
+              };
+            };
+            "${serverRoot}/settings/settings_custom.cfg" = {
+              "L+" = {
+                argument = "${customSettingsCfg}";
+              };
+            };
+            "${serverRoot}/settings/everytime.cfg" = {
+              "L+" = {
+                argument = "${everytimeSettingsCfg}";
+              };
+            };
+          };
+        }
+      )
+      enabledServers
+    );
+
+    systemd.services = mkMerge (mapAttrsToList
+      (serverName: serverCfg:
+        let
+          serverId = nameToId serverName;
+        in
+        {
+          "armagetronad-${serverName}" = {
+            description = "Armagetron Advanced Dedicated Server for ${serverName}";
+            wants = [ "basic.target" ];
+            after = [ "basic.target" "network.target" "multi-user.target" ];
+            wantedBy = [ "multi-user.target" ];
+            serviceConfig =
+              let
+                serverRoot = getServerRoot serverName;
+              in
+              {
+                Type = "simple";
+                StateDirectory = getStateDirectory serverName;
+                ExecStart = "${lib.getExe serverCfg.package} --daemon --input ${serverRoot}/input --userdatadir ${serverRoot}/data --userconfigdir ${serverRoot}/settings --vardir ${serverRoot}/var --autoresourcedir ${serverRoot}/resource";
+                Restart = "on-failure";
+                CapabilityBoundingSet = "";
+                LockPersonality = true;
+                NoNewPrivileges = true;
+                PrivateDevices = true;
+                PrivateTmp = true;
+                PrivateUsers = true;
+                ProtectClock = true;
+                ProtectControlGroups = true;
+                ProtectHome = true;
+                ProtectHostname = true;
+                ProtectKernelLogs = true;
+                ProtectKernelModules = true;
+                ProtectKernelTunables = true;
+                ProtectProc = "invisible";
+                ProtectSystem = "strict";
+                RestrictNamespaces = true;
+                RestrictSUIDSGID = true;
+                User = serverId;
+                Group = serverId;
+              };
+          };
+        })
+      enabledServers
+    );
+
+    networking.firewall.allowedUDPPorts =
+      unique (mapAttrsToList (serverName: serverCfg: serverCfg.port) (filterAttrs (serverName: serverCfg: serverCfg.openFirewall) enabledServers));
+
+    users.users = mkMerge (mapAttrsToList
+      (serverName: serverCfg:
+        {
+          ${nameToId serverName} = {
+            group = nameToId serverName;
+            description = "Armagetron Advanced dedicated user for server ${serverName}";
+            isSystemUser = true;
+          };
+        })
+      enabledServers
+    );
+
+    users.groups = mkMerge (mapAttrsToList
+      (serverName: serverCfg:
+        {
+          ${nameToId serverName} = { };
+        })
+      enabledServers
+    );
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/hardware/monado.nix b/nixpkgs/nixos/modules/services/hardware/monado.nix
new file mode 100644
index 000000000000..9f9c6c39a0b4
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/hardware/monado.nix
@@ -0,0 +1,102 @@
+{ config
+, lib
+, pkgs
+, ...
+}:
+let
+  inherit (lib) mkDefault mkEnableOption mkIf mkOption mkPackageOption types;
+
+  cfg = config.services.monado;
+
+in
+{
+  options.services.monado = {
+    enable = mkEnableOption "Monado user service";
+
+    package = mkPackageOption pkgs "monado" { };
+
+    defaultRuntime = mkOption {
+      type = types.bool;
+      description = ''
+        Whether to enable Monado as the default OpenXR runtime on the system.
+
+        Note that applications can bypass this option by setting an active
+        runtime in a writable XDG_CONFIG_DIRS location like `~/.config`.
+      '';
+      default = false;
+      example = true;
+    };
+
+    highPriority = mkEnableOption "high priority capability for monado-service"
+      // mkOption { default = true; };
+  };
+
+  config = mkIf cfg.enable {
+    security.wrappers."monado-service" = mkIf cfg.highPriority {
+      setuid = false;
+      owner = "root";
+      group = "root";
+      # cap_sys_nice needed for asynchronous reprojection
+      capabilities = "cap_sys_nice+eip";
+      source = lib.getExe' cfg.package "monado-service";
+    };
+
+    services.udev.packages = with pkgs; [ xr-hardware ];
+
+    systemd.user = {
+      services.monado = {
+        description = "Monado XR runtime service module";
+        requires = [ "monado.socket" ];
+        conflicts = [ "monado-dev.service" ];
+
+        unitConfig.ConditionUser = "!root";
+
+        environment = {
+          # Default options
+          # https://gitlab.freedesktop.org/monado/monado/-/blob/4548e1738591d0904f8db4df8ede652ece889a76/src/xrt/targets/service/monado.in.service#L12
+          XRT_COMPOSITOR_LOG = mkDefault "debug";
+          XRT_PRINT_OPTIONS = mkDefault "on";
+          IPC_EXIT_ON_DISCONNECT = mkDefault "off";
+        };
+
+        serviceConfig = {
+          ExecStart =
+            if cfg.highPriority
+            then "${config.security.wrapperDir}/monado-service"
+            else lib.getExe' cfg.package "monado-service";
+          Restart = "no";
+        };
+
+        restartTriggers = [ cfg.package ];
+      };
+
+      sockets.monado = {
+        description = "Monado XR service module connection socket";
+        conflicts = [ "monado-dev.service" ];
+
+        unitConfig.ConditionUser = "!root";
+
+        socketConfig = {
+          ListenStream = "%t/monado_comp_ipc";
+          RemoveOnStop = true;
+
+          # If Monado crashes while starting up, we want to close incoming OpenXR connections
+          FlushPending = true;
+        };
+
+        restartTriggers = [ cfg.package ];
+
+        wantedBy = [ "sockets.target" ];
+      };
+    };
+
+    environment.systemPackages = [ cfg.package ];
+    environment.pathsToLink = [ "/share/openxr" ];
+
+    environment.etc."xdg/openxr/1/active_runtime.json" = mkIf cfg.defaultRuntime {
+      source = "${cfg.package}/share/openxr/1/openxr_monado.json";
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ Scrumplex ];
+}
diff --git a/nixpkgs/nixos/modules/services/misc/ollama.nix b/nixpkgs/nixos/modules/services/misc/ollama.nix
index d9359d2b5cd4..3ac3beb4de07 100644
--- a/nixpkgs/nixos/modules/services/misc/ollama.nix
+++ b/nixpkgs/nixos/modules/services/misc/ollama.nix
@@ -1,27 +1,44 @@
-{ config, lib, pkgs, ... }: let
+{ config, lib, pkgs, ... }:
+let
+  inherit (lib) types;
 
   cfg = config.services.ollama;
-
-in {
-
+  ollamaPackage = cfg.package.override {
+    inherit (cfg) acceleration;
+    linuxPackages = config.boot.kernelPackages // {
+      nvidia_x11 = config.hardware.nvidia.package;
+    };
+  };
+in
+{
   options = {
     services.ollama = {
       enable = lib.mkEnableOption (
         lib.mdDoc "Server for local large language models"
       );
       listenAddress = lib.mkOption {
-        type = lib.types.str;
+        type = types.str;
         default = "127.0.0.1:11434";
         description = lib.mdDoc ''
           Specifies the bind address on which the ollama server HTTP interface listens.
         '';
       };
+      acceleration = lib.mkOption {
+        type = types.nullOr (types.enum [ "rocm" "cuda" ]);
+        default = null;
+        example = "rocm";
+        description = lib.mdDoc ''
+          Specifies the interface to use for hardware acceleration.
+
+          - `rocm`: supported by modern AMD GPUs
+          - `cuda`: supported by modern NVIDIA GPUs
+        '';
+      };
       package = lib.mkPackageOption pkgs "ollama" { };
     };
   };
 
   config = lib.mkIf cfg.enable {
-
     systemd = {
       services.ollama = {
         wantedBy = [ "multi-user.target" ];
@@ -33,7 +50,7 @@ in {
           OLLAMA_HOST = cfg.listenAddress;
         };
         serviceConfig = {
-          ExecStart = "${lib.getExe cfg.package} serve";
+          ExecStart = "${lib.getExe ollamaPackage} serve";
           WorkingDirectory = "/var/lib/ollama";
           StateDirectory = [ "ollama" ];
           DynamicUser = true;
@@ -41,10 +58,8 @@ in {
       };
     };
 
-    environment.systemPackages = [ cfg.package ];
-
+    environment.systemPackages = [ ollamaPackage ];
   };
 
-  meta.maintainers = with lib.maintainers; [ onny ];
-
+  meta.maintainers = with lib.maintainers; [ abysssol onny ];
 }
diff --git a/nixpkgs/nixos/modules/services/misc/paperless.nix b/nixpkgs/nixos/modules/services/misc/paperless.nix
index 1256d8315c8b..ab042e4b6ee2 100644
--- a/nixpkgs/nixos/modules/services/misc/paperless.nix
+++ b/nixpkgs/nixos/modules/services/misc/paperless.nix
@@ -307,6 +307,9 @@ in
         Restart = "on-failure";
       };
       environment = env;
+      # Allow the consumer to access the private /tmp directory of the server.
+      # This is required to support consuming files via a local folder.
+      unitConfig.JoinsNamespaceOf = "paperless-task-queue.service";
     };
 
     systemd.services.paperless-web = {
diff --git a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
index 1c86b48b4509..e58a394456a3 100644
--- a/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/prometheus/exporters/nut.nix
@@ -36,6 +36,17 @@ in
         provisioned outside of Nix store.
       '';
     };
+    nutVariables = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      description = ''
+        List of NUT variable names to monitor.
+
+        If no variables are set, all numeric variables will be exported automatically.
+        See the [upstream docs](https://github.com/DRuggeri/nut_exporter?tab=readme-ov-file#variables-and-information)
+        for more information.
+      '';
+    };
   };
   serviceOpts = {
     script = ''
@@ -44,7 +55,9 @@ in
       ${pkgs.prometheus-nut-exporter}/bin/nut_exporter \
         --nut.server=${cfg.nutServer} \
         --web.listen-address="${cfg.listenAddress}:${toString cfg.port}" \
-        ${optionalString (cfg.nutUser != "") "--nut.username=${cfg.nutUser}"}
+        ${optionalString (cfg.nutUser != "") "--nut.username=${cfg.nutUser}"} \
+        ${optionalString (cfg.nutVariables != []) "--nut.vars_enable=${concatStringsSep "," cfg.nutVariables}"} \
+        ${concatStringsSep " " cfg.extraFlags}
     '';
   };
 }
diff --git a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
index 266a7ea1435e..8d5ac02ba88b 100644
--- a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix
@@ -13,6 +13,8 @@ let
   enableDHCP = config.networking.dhcpcd.enable &&
         (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces);
 
+  enableNTPService = (config.services.ntp.enable || config.services.ntpd-rs.enable || config.services.openntpd.enable || config.services.chrony.enable);
+
   # Don't start dhcpcd on explicitly configured interfaces or on
   # interfaces that are part of a bridge, bond or sit device.
   ignoredInterfaces =
@@ -89,20 +91,22 @@ let
       ${cfg.extraConfig}
     '';
 
-  exitHook = pkgs.writeText "dhcpcd.exit-hook"
-    ''
+  exitHook = pkgs.writeText "dhcpcd.exit-hook" ''
+    ${optionalString enableNTPService ''
       if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then
-          # Restart ntpd.  We need to restart it to make sure that it
-          # will actually do something: if ntpd cannot resolve the
-          # server hostnames in its config file, then it will never do
-          # anything ever again ("couldn't resolve ..., giving up on
-          # it"), so we silently lose time synchronisation. This also
-          # applies to openntpd.
-          /run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service ntpd-rs.service || true
+        # Restart ntpd. We need to restart it to make sure that it will actually do something:
+        # if ntpd cannot resolve the server hostnames in its config file, then it will never do
+        # anything ever again ("couldn't resolve ..., giving up on it"), so we silently lose
+        # time synchronisation. This also applies to openntpd.
+        ${optionalString config.services.ntp.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service || true"}
+        ${optionalString config.services.ntpd-rs.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd-rs.service || true"}
+        ${optionalString config.services.openntpd.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart openntpd.service || true"}
+        ${optionalString config.services.chrony.enable "/run/current-system/systemd/bin/systemctl try-reload-or-restart chronyd.service || true"}
       fi
+    ''}
 
-      ${cfg.runHook}
-    '';
+    ${cfg.runHook}
+  '';
 
 in
 
@@ -232,7 +236,7 @@ in
         wants = [ "network.target" ];
         before = [ "network-online.target" ];
 
-        restartTriggers = [ exitHook ];
+        restartTriggers = optional (enableNTPService || cfg.runHook != "") [ exitHook ];
 
         # Stopping dhcpcd during a reconfiguration is undesirable
         # because it brings down the network interfaces configured by
@@ -261,7 +265,9 @@ in
 
     environment.systemPackages = [ dhcpcd ];
 
-    environment.etc."dhcpcd.exit-hook".source = exitHook;
+    environment.etc."dhcpcd.exit-hook" = mkIf (enableNTPService || cfg.runHook != "") {
+      source = exitHook;
+    };
 
     powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable
       ''
diff --git a/nixpkgs/nixos/modules/services/networking/mosquitto.nix b/nixpkgs/nixos/modules/services/networking/mosquitto.nix
index ad9eefb42252..4a08f5ed2370 100644
--- a/nixpkgs/nixos/modules/services/networking/mosquitto.nix
+++ b/nixpkgs/nixos/modules/services/networking/mosquitto.nix
@@ -177,17 +177,6 @@ let
            ''
            ++ hashedLines));
 
-  makeACLFile = idx: users: supplement:
-    pkgs.writeText "mosquitto-acl-${toString idx}.conf"
-      (concatStringsSep
-        "\n"
-        (flatten [
-          supplement
-          (mapAttrsToList
-            (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl)
-            users)
-        ]));
-
   authPluginOptions = with types; submodule {
     options = {
       plugin = mkOption {
@@ -342,7 +331,7 @@ let
   formatListener = idx: listener:
     [
       "listener ${toString listener.port} ${toString listener.address}"
-      "acl_file ${makeACLFile idx listener.users listener.acl}"
+      "acl_file /etc/mosquitto/acl-${toString idx}.conf"
     ]
     ++ optional (! listener.omitPasswordAuth) "password_file ${cfg.dataDir}/passwd-${toString idx}"
     ++ formatFreeform {} listener.settings
@@ -698,6 +687,27 @@ in
             cfg.listeners);
     };
 
+    environment.etc = listToAttrs (
+      imap0
+        (idx: listener: {
+          name = "mosquitto/acl-${toString idx}.conf";
+          value = {
+            user = config.users.users.mosquitto.name;
+            group = config.users.users.mosquitto.group;
+            mode = "0400";
+            text = (concatStringsSep
+              "\n"
+              (flatten [
+                listener.acl
+                (mapAttrsToList
+                  (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl)
+                  listener.users)
+              ]));
+          };
+        })
+        cfg.listeners
+    );
+
     users.users.mosquitto = {
       description = "Mosquitto MQTT Broker Daemon owner";
       group = "mosquitto";
diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix
index 938d585e3179..5bbf875f0d57 100644
--- a/nixpkgs/nixos/modules/services/networking/searx.nix
+++ b/nixpkgs/nixos/modules/services/networking/searx.nix
@@ -213,7 +213,7 @@ in
       serviceConfig = {
         User  = "searx";
         Group = "searx";
-        ExecStart = "${cfg.package}/bin/searx-run";
+        ExecStart = lib.getExe cfg.package;
       } // optionalAttrs (cfg.environmentFile != null)
         { EnvironmentFile = builtins.toPath cfg.environmentFile; };
       environment = {
diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix
index 616b32f11797..8438e472e11e 100644
--- a/nixpkgs/nixos/modules/services/networking/unbound.nix
+++ b/nixpkgs/nixos/modules/services/networking/unbound.nix
@@ -24,12 +24,24 @@ let
   confNoServer = concatStringsSep "\n" ((mapAttrsToList (toConf "") (builtins.removeAttrs cfg.settings [ "server" ])) ++ [""]);
   confServer = concatStringsSep "\n" (mapAttrsToList (toConf "  ") (builtins.removeAttrs cfg.settings.server [ "define-tag" ]));
 
-  confFile = pkgs.writeText "unbound.conf" ''
+  confFileUnchecked = pkgs.writeText "unbound.conf" ''
     server:
     ${optionalString (cfg.settings.server.define-tag != "") (toOption "  " "define-tag" cfg.settings.server.define-tag)}
     ${confServer}
     ${confNoServer}
   '';
+  confFile = if cfg.checkconf then pkgs.runCommandLocal "unbound-checkconf" { } ''
+    cp ${confFileUnchecked} unbound.conf
+
+    # fake stateDir which is not accesible in the sandbox
+    mkdir -p $PWD/state
+    sed -i unbound.conf \
+      -e '/auto-trust-anchor-file/d' \
+      -e "s|${cfg.stateDir}|$PWD/state|"
+    ${cfg.package}/bin/unbound-checkconf unbound.conf
+
+    cp ${confFileUnchecked} $out
+  '' else confFileUnchecked;
 
   rootTrustAnchorFile = "${cfg.stateDir}/root.key";
 
@@ -62,6 +74,17 @@ in {
         description = lib.mdDoc "Directory holding all state for unbound to run.";
       };
 
+      checkconf = mkOption {
+        type = types.bool;
+        default = !cfg.settings ? include;
+        defaultText = "!config.services.unbound.settings ? include";
+        description = lib.mdDoc ''
+          Wether to check the resulting config file with unbound checkconf for syntax errors.
+
+          If settings.include is used, then this options is disabled, as the import can likely not be resolved at build time.
+        '';
+      };
+
       resolveLocalQueries = mkOption {
         type = types.bool;
         default = true;
diff --git a/nixpkgs/nixos/modules/services/security/kanidm.nix b/nixpkgs/nixos/modules/services/security/kanidm.nix
index c659d93b4087..9d074c3027d0 100644
--- a/nixpkgs/nixos/modules/services/security/kanidm.nix
+++ b/nixpkgs/nixos/modules/services/security/kanidm.nix
@@ -132,6 +132,28 @@ in
             default = "WriteReplica";
             type = lib.types.enum [ "WriteReplica" "WriteReplicaNoUI" "ReadOnlyReplica" ];
           };
+          online_backup = {
+            path = lib.mkOption {
+              description = lib.mdDoc "Path to the output directory for backups.";
+              type = lib.types.path;
+              default = "/var/lib/kanidm/backups";
+            };
+            schedule = lib.mkOption {
+              description = lib.mdDoc "The schedule for backups in cron format.";
+              type = lib.types.str;
+              default = "00 22 * * *";
+            };
+            versions = lib.mkOption {
+              description = lib.mdDoc ''
+                Number of backups to keep.
+
+                The default is set to `0`, in order to disable backups by default.
+              '';
+              type = lib.types.ints.unsigned;
+              default = 0;
+              example = 7;
+            };
+          };
         };
       };
       default = { };
@@ -233,6 +255,14 @@ in
 
     environment.systemPackages = lib.mkIf cfg.enableClient [ cfg.package ];
 
+    systemd.tmpfiles.settings."10-kanidm" = {
+      ${cfg.serverSettings.online_backup.path}.d = {
+        mode = "0700";
+        user = "kanidm";
+        group = "kanidm";
+      };
+    };
+
     systemd.services.kanidm = lib.mkIf cfg.enableServer {
       description = "kanidm identity management daemon";
       wantedBy = [ "multi-user.target" ];
@@ -253,6 +283,8 @@ in
           BindPaths = [
             # To create the socket
             "/run/kanidmd:/run/kanidmd"
+            # To store backups
+            cfg.serverSettings.online_backup.path
           ];
 
           AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
diff --git a/nixpkgs/nixos/modules/services/web-apps/mealie.nix b/nixpkgs/nixos/modules/services/web-apps/mealie.nix
new file mode 100644
index 000000000000..8bb7542c6b56
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/mealie.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ...}:
+let
+  cfg = config.services.mealie;
+  pkg = cfg.package;
+in
+{
+  options.services.mealie = {
+    enable = lib.mkEnableOption "Mealie, a recipe manager and meal planner";
+
+    package = lib.mkPackageOption pkgs "mealie" { };
+
+    listenAddress = lib.mkOption {
+      type = lib.types.str;
+      default = "0.0.0.0";
+      description = "Address on which the service should listen.";
+    };
+
+    port = lib.mkOption {
+      type = lib.types.port;
+      default = 9000;
+      description = "Port on which to serve the Mealie service.";
+    };
+
+    settings = lib.mkOption {
+      type = with lib.types; attrsOf anything;
+      default = {};
+      description = lib.mdDoc ''
+        Configuration of the Mealie service.
+
+        See [the mealie documentation](https://nightly.mealie.io/documentation/getting-started/installation/backend-config/) for available options and default values.
+
+        In addition to the official documentation, you can set {env}`MEALIE_LOG_FILE`.
+      '';
+      example = {
+        ALLOW_SIGNUP = "false";
+      };
+    };
+
+    credentialsFile = lib.mkOption {
+      type = with lib.types; nullOr path;
+      default = null;
+      example = "/run/secrets/mealie-credentials.env";
+      description = ''
+        File containing credentials used in mealie such as {env}`POSTGRES_PASSWORD`
+        or sensitive LDAP options.
+
+        Expects the format of an `EnvironmentFile=`, as described by {manpage}`systemd.exec(5)`.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.mealie = {
+      description = "Mealie, a self hosted recipe manager and meal planner";
+
+      after = [ "network-online.target" ];
+      wants = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        PRODUCTION = "true";
+        ALEMBIC_CONFIG_FILE="${pkg}/config/alembic.ini";
+        API_PORT = toString cfg.port;
+        DATA_DIR = "/var/lib/mealie";
+        CRF_MODEL_PATH = "/var/lib/mealie/model.crfmodel";
+      } // (builtins.mapAttrs (_: val: toString val) cfg.settings);
+
+      serviceConfig = {
+        DynamicUser = true;
+        User = "mealie";
+        ExecStartPre = "${pkg}/libexec/init_db";
+        ExecStart = "${lib.getExe pkg} -b ${cfg.listenAddress}:${builtins.toString cfg.port}";
+        EnvironmentFile = lib.mkIf (cfg.credentialsFile != null) cfg.credentialsFile;
+        StateDirectory = "mealie";
+        StandardOutput="journal";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix
index 66cb4ee29c0a..ecb8d1e91bde 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/default.nix
@@ -18,7 +18,7 @@ in
   # determines the default: later modules (if enabled) are preferred.
   # E.g., if Plasma 5 is enabled, it supersedes xterm.
   imports = [
-    ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ./lumina.nix
+    ./none.nix ./xterm.nix ./phosh.nix ./xfce.nix ./plasma5.nix ./plasma6.nix ./lumina.nix
     ./lxqt.nix ./enlightenment.nix ./gnome.nix ./retroarch.nix ./kodi.nix
     ./mate.nix ./pantheon.nix ./surf-display.nix ./cde.nix
     ./cinnamon.nix ./budgie.nix ./deepin.nix
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
index 0eb492ce4684..7645b3070369 100644
--- a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma5.nix
@@ -362,7 +362,7 @@ in
 
       security.pam.services.kde = { allowNullPassword = true; };
 
-      security.pam.services.login.enableKwallet = true;
+      security.pam.services.login.kwallet.enable = true;
 
       systemd.user.services = {
         plasma-early-setup = mkIf cfg.runUsingSystemd {
diff --git a/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix
new file mode 100644
index 000000000000..bc246b1af278
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/x11/desktop-managers/plasma6.nix
@@ -0,0 +1,276 @@
+{
+  config,
+  lib,
+  pkgs,
+  utils,
+  ...
+}: let
+  xcfg = config.services.xserver;
+  cfg = xcfg.desktopManager.plasma6;
+
+  inherit (pkgs) kdePackages;
+  inherit (lib) literalExpression mkDefault mkIf mkOption mkPackageOptionMD types;
+in {
+  options = {
+    services.xserver.desktopManager.plasma6 = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = lib.mdDoc "Enable the Plasma 6 (KDE 6) desktop environment.";
+      };
+
+      enableQt5Integration = mkOption {
+        type = types.bool;
+        default = true;
+        description = lib.mdDoc "Enable Qt 5 integration (theming, etc). Disable for a pure Qt 6 system.";
+      };
+
+      notoPackage = mkPackageOptionMD pkgs "Noto fonts - used for UI by default" {
+        default = ["noto-fonts"];
+        example = "noto-fonts-lgc-plus";
+      };
+    };
+
+    environment.plasma6.excludePackages = mkOption {
+      description = lib.mdDoc "List of default packages to exclude from the configuration";
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression "[ pkgs.kdePackages.elisa ]";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = cfg.enable -> !config.services.xserver.desktopManager.plasma5.enable;
+        message = "Cannot enable plasma5 and plasma6 at the same time!";
+      }
+    ];
+
+    qt.enable = true;
+    environment.systemPackages = with kdePackages; let
+      requiredPackages = [
+        # Hack? To make everything run on Wayland
+        qtwayland
+        # Needed to render SVG icons
+        qtsvg
+
+        # Frameworks with globally loadable bits
+        frameworkintegration # provides Qt plugin
+        kauth # provides helper service
+        kcoreaddons # provides extra mime type info
+        kded # provides helper service
+        kfilemetadata # provides Qt plugins
+        kguiaddons # provides geo URL handlers
+        kiconthemes # provides Qt plugins
+        kimageformats # provides Qt plugins
+        kio # provides helper service + a bunch of other stuff
+        kpackage # provides kpackagetool tool
+        kservice # provides kbuildsycoca6 tool
+        kwallet # provides helper service
+        kwallet-pam # provides helper service
+        kwalletmanager # provides KCMs and stuff
+        plasma-activities # provides plasma-activities-cli tool
+        solid # provides solid-hardware6 tool
+        phonon-vlc # provides Phonon plugin
+
+        # Core Plasma parts
+        kwin
+        pkgs.xwayland
+
+        kscreen
+        libkscreen
+
+        kscreenlocker
+
+        kactivitymanagerd
+        kde-cli-tools
+        kglobalacceld
+        kwrited # wall message proxy, not to be confused with kwrite
+
+        milou
+        polkit-kde-agent-1
+
+        plasma-desktop
+        plasma-workspace
+
+        # Crash handler
+        drkonqi
+
+        # Application integration
+        libplasma # provides Kirigami platform theme
+        plasma-integration # provides Qt platform theme
+        kde-gtk-config
+
+        # Artwork + themes
+        breeze
+        breeze-icons
+        breeze-gtk
+        ocean-sound-theme
+        plasma-workspace-wallpapers
+        pkgs.hicolor-icon-theme # fallback icons
+        qqc2-breeze-style
+        qqc2-desktop-style
+
+        # misc Plasma extras
+        kdeplasma-addons
+
+        pkgs.xdg-user-dirs # recommended upstream
+
+        # Plasma utilities
+        kmenuedit
+
+        kinfocenter
+        plasma-systemmonitor
+        ksystemstats
+        libksysguard
+
+        spectacle
+        systemsettings
+
+        # Gear
+        baloo
+        dolphin
+        dolphin-plugins
+        ffmpegthumbs
+        kdegraphics-thumbnailers
+        kde-inotify-survey
+        kio-admin
+        kio-extras
+        kio-fuse
+      ];
+      optionalPackages = [
+        plasma-browser-integration
+        konsole
+        (lib.getBin qttools) # Expose qdbus in PATH
+
+        ark
+        elisa
+        gwenview
+        okular
+        kate
+        khelpcenter
+        print-manager
+      ];
+    in
+      requiredPackages
+      ++ utils.removePackagesByName optionalPackages config.environment.plasma6.excludePackages
+      ++ lib.optionals config.services.xserver.desktopManager.plasma6.enableQt5Integration [
+        breeze.qt5
+        plasma-integration.qt5
+        pkgs.plasma5Packages.kwayland-integration
+        kio-extras-kf5
+      ]
+      # Optional hardware support features
+      ++ lib.optionals config.hardware.bluetooth.enable [bluedevil bluez-qt pkgs.openobex pkgs.obexftp]
+      ++ lib.optional config.networking.networkmanager.enable plasma-nm
+      ++ lib.optional config.hardware.pulseaudio.enable plasma-pa
+      ++ lib.optional config.services.pipewire.pulse.enable plasma-pa
+      ++ lib.optional config.powerManagement.enable powerdevil
+      ++ lib.optional config.services.colord.enable colord-kde
+      ++ lib.optional config.services.hardware.bolt.enable plasma-thunderbolt
+      ++ lib.optionals config.services.samba.enable [kdenetwork-filesharing pkgs.samba]
+      ++ lib.optional config.services.xserver.wacom.enable wacomtablet
+      ++ lib.optional config.services.flatpak.enable flatpak-kcm;
+
+    environment.pathsToLink = [
+      # FIXME: modules should link subdirs of `/share` rather than relying on this
+      "/share"
+      "/libexec" # for drkonqi
+    ];
+
+    environment.etc."X11/xkb".source = xcfg.xkb.dir;
+
+    # Add ~/.config/kdedefaults to XDG_CONFIG_DIRS for shells, since Plasma sets that.
+    # FIXME: maybe we should append to XDG_CONFIG_DIRS in /etc/set-environment instead?
+    environment.sessionVariables.XDG_CONFIG_DIRS = ["$HOME/.config/kdedefaults"];
+
+    # Needed for things that depend on other store.kde.org packages to install correctly,
+    # notably Plasma look-and-feel packages (a.k.a. Global Themes)
+    #
+    # FIXME: this is annoyingly impure and should really be fixed at source level somehow,
+    # but kpackage is a library so we can't just wrap the one thing invoking it and be done.
+    # This also means things won't work for people not on Plasma, but at least this way it
+    # works for SOME people.
+    environment.sessionVariables.KPACKAGE_DEP_RESOLVERS_PATH = "${kdePackages.frameworkintegration.out}/libexec/kf6/kpackagehandlers";
+
+    # Enable GTK applications to load SVG icons
+    services.xserver.gdk-pixbuf.modulePackages = [pkgs.librsvg];
+
+    fonts.packages = [cfg.notoPackage pkgs.hack-font];
+    fonts.fontconfig.defaultFonts = {
+      monospace = ["Hack" "Noto Sans Mono"];
+      sansSerif = ["Noto Sans"];
+      serif = ["Noto Serif"];
+    };
+
+    programs.ssh.askPassword = mkDefault "${kdePackages.ksshaskpass.out}/bin/ksshaskpass";
+
+    # Enable helpful DBus services.
+    services.accounts-daemon.enable = true;
+    # when changing an account picture the accounts-daemon reads a temporary file containing the image which systemsettings5 may place under /tmp
+    systemd.services.accounts-daemon.serviceConfig.PrivateTmp = false;
+
+    services.power-profiles-daemon.enable = mkDefault true;
+    services.system-config-printer.enable = mkIf config.services.printing.enable (mkDefault true);
+    services.udisks2.enable = true;
+    services.upower.enable = config.powerManagement.enable;
+    services.xserver.libinput.enable = mkDefault true;
+
+    # Extra UDEV rules used by Solid
+    services.udev.packages = [
+      # libmtp has "bin", "dev", "out" outputs. UDEV rules file is in "out".
+      pkgs.libmtp.out
+      pkgs.media-player-info
+    ];
+
+    # Set up Dr. Konqi as crash handler
+    systemd.packages = [kdePackages.drkonqi];
+    systemd.services."drkonqi-coredump-processor@".wantedBy = ["systemd-coredump@.service"];
+
+    xdg.portal.enable = true;
+    xdg.portal.extraPortals = [kdePackages.xdg-desktop-portal-kde];
+    xdg.portal.configPackages = mkDefault [kdePackages.xdg-desktop-portal-kde];
+    services.pipewire.enable = mkDefault true;
+
+    services.xserver.displayManager = {
+      sessionPackages = [kdePackages.plasma-workspace];
+      defaultSession = mkDefault "plasma";
+    };
+    services.xserver.displayManager.sddm = {
+      package = kdePackages.sddm;
+      theme = mkDefault "breeze";
+      extraPackages = with kdePackages; [
+        breeze-icons
+        kirigami
+        plasma5support
+        qtsvg
+        qtvirtualkeyboard
+      ];
+    };
+
+    security.pam.services = {
+      login.kwallet = {
+        enable = true;
+        package = kdePackages.kwallet-pam;
+      };
+      kde.kwallet = {
+        enable = true;
+        package = kdePackages.kwallet-pam;
+      };
+      kde-fingerprint = lib.mkIf config.services.fprintd.enable { fprintAuth = true; };
+      kde-smartcard = lib.mkIf config.security.pam.p11.enable { p11Auth = true; };
+    };
+
+    programs.dconf.enable = true;
+
+    programs.firefox.nativeMessagingHosts.packages = [kdePackages.plasma-browser-integration];
+
+    programs.chromium = {
+      enablePlasmaBrowserIntegration = true;
+      plasmaBrowserIntegrationPackage = pkgs.kdePackages.plasma-browser-integration;
+    };
+
+    programs.kdeconnect.package = kdePackages.kdeconnect-kde;
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
index 0576619cc8d2..5b7f4bc58d80 100644
--- a/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
+++ b/nixpkgs/nixos/modules/services/x11/display-managers/sddm.nix
@@ -7,7 +7,10 @@ let
   cfg = dmcfg.sddm;
   xEnv = config.systemd.services.display-manager.environment;
 
-  sddm = cfg.package;
+  sddm = cfg.package.override(old: {
+    withWayland = cfg.wayland.enable;
+    extraPackages = old.extraPackages or [] ++ cfg.extraPackages;
+  });
 
   iniFmt = pkgs.formats.ini { };
 
@@ -140,6 +143,15 @@ in
         '';
       };
 
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        defaultText = "[]";
+        description = lib.mdDoc ''
+          Extra Qt plugins / QML libraries to add to the environment.
+        '';
+      };
+
       autoNumlock = mkOption {
         type = types.bool;
         default = false;
@@ -211,7 +223,7 @@ in
                 keymap_variant = xcfg.xkb.variant;
                 keymap_options = xcfg.xkb.options;
               };
-            }; in "${pkgs.weston}/bin/weston --shell=fullscreen-shell.so -c ${westonIni}";
+            }; in "${pkgs.weston}/bin/weston --shell=kiosk -c ${westonIni}";
           description = lib.mdDoc "Command used to start the selected compositor";
         };
       };
@@ -235,15 +247,7 @@ in
       }
     ];
 
-    services.xserver.displayManager.job = {
-      environment = {
-        # Load themes from system environment
-        QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix;
-        QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix;
-      };
-
-      execCmd = "exec /run/current-system/sw/bin/sddm";
-    };
+    services.xserver.displayManager.job.execCmd = "exec /run/current-system/sw/bin/sddm";
 
     security.pam.services = {
       sddm.text = ''
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index a9978d7adf80..258cf622a894 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -17,6 +17,9 @@ from dataclasses import dataclass
 
 # These values will be replaced with actual values during the package build
 EFI_SYS_MOUNT_POINT = "@efiSysMountPoint@"
+BOOT_MOUNT_POINT = "@bootMountPoint@"
+LOADER_CONF = f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"  # Always stored on the ESP
+NIXOS_DIR = "@nixosDir@"
 TIMEOUT = "@timeout@"
 EDITOR = "@editor@" == "1"
 CONSOLE_MODE = "@consoleMode@"
@@ -28,6 +31,7 @@ CONFIGURATION_LIMIT = int("@configurationLimit@")
 CAN_TOUCH_EFI_VARIABLES = "@canTouchEfiVariables@"
 GRACEFUL = "@graceful@"
 COPY_EXTRA_FILES = "@copyExtraFiles@"
+CHECK_MOUNTPOINTS = "@checkMountpoints@"
 
 @dataclass
 class BootSpec:
@@ -87,7 +91,7 @@ def generation_conf_filename(profile: str | None, generation: int, specialisatio
 
 
 def write_loader_conf(profile: str | None, generation: int, specialisation: str | None) -> None:
-    with open(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", 'w') as f:
+    with open(f"{LOADER_CONF}.tmp", 'w') as f:
         if TIMEOUT != "":
             f.write(f"timeout {TIMEOUT}\n")
         f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation))
@@ -96,7 +100,7 @@ def write_loader_conf(profile: str | None, generation: int, specialisation: str
         f.write(f"console-mode {CONSOLE_MODE}\n")
         f.flush()
         os.fsync(f.fileno())
-    os.rename(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf")
+    os.rename(f"{LOADER_CONF}.tmp", LOADER_CONF)
 
 
 def get_bootspec(profile: str | None, generation: int) -> BootSpec:
@@ -126,9 +130,9 @@ def copy_from_file(file: str, dry_run: bool = False) -> str:
     store_file_path = os.path.realpath(file)
     suffix = os.path.basename(store_file_path)
     store_dir = os.path.basename(os.path.dirname(store_file_path))
-    efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
+    efi_file_path = f"{NIXOS_DIR}/{store_dir}-{suffix}.efi"
     if not dry_run:
-        copy_if_not_exists(store_file_path, f"{EFI_SYS_MOUNT_POINT}%s" % (efi_file_path))
+        copy_if_not_exists(store_file_path, f"{BOOT_MOUNT_POINT}{efi_file_path}")
     return efi_file_path
 
 def write_entry(profile: str | None, generation: int, specialisation: str | None,
@@ -145,7 +149,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
 
     try:
         if bootspec.initrdSecrets is not None:
-            subprocess.check_call([bootspec.initrdSecrets, f"{EFI_SYS_MOUNT_POINT}%s" % (initrd)])
+            subprocess.check_call([bootspec.initrdSecrets, f"{BOOT_MOUNT_POINT}%s" % (initrd)])
     except subprocess.CalledProcessError:
         if current:
             print("failed to create initrd secrets!", file=sys.stderr)
@@ -155,7 +159,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
                   f'for "{title} - Configuration {generation}", an older generation', file=sys.stderr)
             print("note: this is normal after having removed "
                   "or renamed a file in `boot.initrd.secrets`", file=sys.stderr)
-    entry_file = f"{EFI_SYS_MOUNT_POINT}/loader/entries/%s" % (
+    entry_file = f"{BOOT_MOUNT_POINT}/loader/entries/%s" % (
         generation_conf_filename(profile, generation, specialisation))
     tmp_path = "%s.tmp" % (entry_file)
     kernel_params = "init=%s " % bootspec.init
@@ -202,14 +206,14 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
 
 
 def remove_old_entries(gens: list[SystemIdentifier]) -> None:
-    rex_profile = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$")
-    rex_generation = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$")
+    rex_profile = re.compile(r"^" + re.escape(BOOT_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$")
+    rex_generation = re.compile(r"^" + re.escape(BOOT_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$")
     known_paths = []
     for gen in gens:
         bootspec = get_bootspec(gen.profile, gen.generation)
         known_paths.append(copy_from_file(bootspec.kernel, True))
         known_paths.append(copy_from_file(bootspec.initrd, True))
-    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"):
+    for path in glob.iglob(f"{BOOT_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"):
         if rex_profile.match(path):
             prof = rex_profile.sub(r"\1", path)
         else:
@@ -220,11 +224,18 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None:
             continue
         if not (prof, gen_number, None) in gens:
             os.unlink(path)
-    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/*"):
+    for path in glob.iglob(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/*"):
         if not path in known_paths and not os.path.isdir(path):
             os.unlink(path)
 
 
+def cleanup_esp() -> None:
+    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*"):
+        os.unlink(path)
+    if os.path.isdir(f"{EFI_SYS_MOUNT_POINT}/{NIXOS_DIR}"):
+        shutil.rmtree(f"{EFI_SYS_MOUNT_POINT}/{NIXOS_DIR}")
+
+
 def get_profiles() -> list[str]:
     if os.path.isdir("/nix/var/nix/profiles/system-profiles/"):
         return [x
@@ -255,6 +266,9 @@ def install_bootloader(args: argparse.Namespace) -> None:
     # flags to pass to bootctl install/update
     bootctl_flags = []
 
+    if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT:
+        bootctl_flags.append(f"--boot-path={BOOT_MOUNT_POINT}")
+
     if CAN_TOUCH_EFI_VARIABLES != "1":
         bootctl_flags.append("--no-variables")
 
@@ -263,8 +277,8 @@ def install_bootloader(args: argparse.Namespace) -> None:
 
     if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
         # bootctl uses fopen() with modes "wxe" and fails if the file exists.
-        if os.path.exists(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"):
-            os.unlink(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf")
+        if os.path.exists(LOADER_CONF):
+            os.unlink(LOADER_CONF)
 
         subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"])
     else:
@@ -291,13 +305,15 @@ def install_bootloader(args: argparse.Namespace) -> None:
             print("updating systemd-boot from %s to %s" % (installed_version, available_version))
             subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["update"])
 
-    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos", exist_ok=True)
-    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/loader/entries", exist_ok=True)
+    os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}", exist_ok=True)
+    os.makedirs(f"{BOOT_MOUNT_POINT}/loader/entries", exist_ok=True)
 
     gens = get_generations()
     for profile in get_profiles():
         gens += get_generations(profile)
+
     remove_old_entries(gens)
+
     for gen in gens:
         try:
             bootspec = get_bootspec(gen.profile, gen.generation)
@@ -315,9 +331,15 @@ def install_bootloader(args: argparse.Namespace) -> None:
             else:
                 raise e
 
-    for root, _, files in os.walk(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", topdown=False):
-        relative_root = root.removeprefix(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files").removeprefix("/")
-        actual_root = os.path.join(f"{EFI_SYS_MOUNT_POINT}", relative_root)
+    if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT:
+        # Cleanup any entries in ESP if xbootldrMountPoint is set.
+        # If the user later unsets xbootldrMountPoint, entries in XBOOTLDR will not be cleaned up
+        # automatically, as we don't have information about the mount point anymore.
+        cleanup_esp()
+
+    for root, _, files in os.walk(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", topdown=False):
+        relative_root = root.removeprefix(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files").removeprefix("/")
+        actual_root = os.path.join(f"{BOOT_MOUNT_POINT}", relative_root)
 
         for file in files:
             actual_file = os.path.join(actual_root, file)
@@ -330,7 +352,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
             os.rmdir(actual_root)
         os.rmdir(root)
 
-    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", exist_ok=True)
+    os.makedirs(f"{BOOT_MOUNT_POINT}/{NIXOS_DIR}/.extra-files", exist_ok=True)
 
     subprocess.check_call(COPY_EXTRA_FILES)
 
@@ -340,6 +362,8 @@ def main() -> None:
     parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot")
     args = parser.parse_args()
 
+    subprocess.check_call(CHECK_MOUNTPOINTS)
+
     try:
         install_bootloader(args)
     finally:
@@ -347,9 +371,14 @@ def main() -> None:
         # it can leave the system in an unbootable state, when a crash/outage
         # happens shortly after an update. To decrease the likelihood of this
         # event sync the efi filesystem after each update.
-        rc = libc.syncfs(os.open(f"{EFI_SYS_MOUNT_POINT}", os.O_RDONLY))
+        rc = libc.syncfs(os.open(f"{BOOT_MOUNT_POINT}", os.O_RDONLY))
         if rc != 0:
-            print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
+            print(f"could not sync {BOOT_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
+
+        if BOOT_MOUNT_POINT != EFI_SYS_MOUNT_POINT:
+            rc = libc.syncfs(os.open(EFI_SYS_MOUNT_POINT, os.O_RDONLY))
+            if rc != 0:
+                print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
 
 
 if __name__ == '__main__':
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index ea4553b8208f..645b764760da 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -7,7 +7,7 @@ let
 
   efi = config.boot.loader.efi;
 
-  systemdBootBuilder = pkgs.substituteAll {
+  systemdBootBuilder = pkgs.substituteAll rec {
     src = ./systemd-boot-builder.py;
 
     isExecutable = true;
@@ -28,33 +28,48 @@ let
 
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
 
+    bootMountPoint = if cfg.xbootldrMountPoint != null
+      then cfg.xbootldrMountPoint
+      else efi.efiSysMountPoint;
+
+    nixosDir = "/EFI/nixos";
+
     inherit (config.system.nixos) distroName;
 
     memtest86 = optionalString cfg.memtest86.enable pkgs.memtest86plus;
 
     netbootxyz = optionalString cfg.netbootxyz.enable pkgs.netbootxyz-efi;
 
+    checkMountpoints = pkgs.writeShellScript "check-mountpoints" ''
+      fail() {
+        echo "$1 = '$2' is not a mounted partition. Is the path configured correctly?" >&2
+        exit 1
+      }
+      ${pkgs.util-linuxMinimal}/bin/findmnt ${efiSysMountPoint} > /dev/null || fail efiSysMountPoint ${efiSysMountPoint}
+      ${lib.optionalString
+        (cfg.xbootldrMountPoint != null)
+        "${pkgs.util-linuxMinimal}/bin/findmnt ${cfg.xbootldrMountPoint} > /dev/null || fail xbootldrMountPoint ${cfg.xbootldrMountPoint}"}
+    '';
+
     copyExtraFiles = pkgs.writeShellScript "copy-extra-files" ''
       empty_file=$(${pkgs.coreutils}/bin/mktemp)
 
       ${concatStrings (mapAttrsToList (n: v: ''
-        ${pkgs.coreutils}/bin/install -Dp "${v}" "${efi.efiSysMountPoint}/"${escapeShellArg n}
-        ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -Dp "${v}" "${bootMountPoint}/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/"${escapeShellArg n}
       '') cfg.extraFiles)}
 
       ${concatStrings (mapAttrsToList (n: v: ''
-        ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${efi.efiSysMountPoint}/loader/entries/"${escapeShellArg n}
-        ${pkgs.coreutils}/bin/install -D $empty_file "${efi.efiSysMountPoint}/efi/nixos/.extra-files/loader/entries/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -Dp "${pkgs.writeText n v}" "${bootMountPoint}/loader/entries/"${escapeShellArg n}
+        ${pkgs.coreutils}/bin/install -D $empty_file "${bootMountPoint}/${nixosDir}/.extra-files/loader/entries/"${escapeShellArg n}
       '') cfg.extraEntries)}
     '';
   };
 
-  checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" {
-    nativeBuildInputs = [ pkgs.mypy ];
-  } ''
+  checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { } ''
     mkdir -p $out/bin
     install -m755 ${systemdBootBuilder} $out/bin/systemd-boot-builder
-    mypy \
+    ${lib.getExe pkgs.buildPackages.mypy} \
       --no-implicit-optional \
       --disallow-untyped-calls \
       --disallow-untyped-defs \
@@ -101,6 +116,18 @@ in {
       '';
     };
 
+    xbootldrMountPoint = mkOption {
+      default = null;
+      type = types.nullOr types.str;
+      description = lib.mdDoc ''
+        Where the XBOOTLDR partition is mounted.
+
+        If set, this partition will be used as $BOOT to store boot loader entries and extra files
+        instead of the EFI partition. As per the bootloader specification, it is recommended that
+        the EFI and XBOOTLDR partitions be mounted at `/efi` and `/boot`, respectively.
+      '';
+    };
+
     configurationLimit = mkOption {
       default = null;
       example = 120;
@@ -110,7 +137,7 @@ in {
         Useful to prevent boot partition running out of disk space.
 
         `null` means no limit i.e. all generations
-        that were not garbage collected yet.
+        that have not been garbage collected yet.
       '';
     };
 
@@ -202,7 +229,7 @@ in {
       '';
       description = lib.mdDoc ''
         Any additional entries you want added to the `systemd-boot` menu.
-        These entries will be copied to {file}`/boot/loader/entries`.
+        These entries will be copied to {file}`$BOOT/loader/entries`.
         Each attribute name denotes the destination file name,
         and the corresponding attribute value is the contents of the entry.
 
@@ -219,9 +246,9 @@ in {
         { "efi/memtest86/memtest.efi" = "''${pkgs.memtest86plus}/memtest.efi"; }
       '';
       description = lib.mdDoc ''
-        A set of files to be copied to {file}`/boot`.
+        A set of files to be copied to {file}`$BOOT`.
         Each attribute name denotes the destination file name in
-        {file}`/boot`, while the corresponding
+        {file}`$BOOT`, while the corresponding
         attribute value specifies the source file.
       '';
     };
@@ -246,6 +273,18 @@ in {
   config = mkIf cfg.enable {
     assertions = [
       {
+        assertion = (hasPrefix "/" efi.efiSysMountPoint);
+        message = "The ESP mount point '${efi.efiSysMountPoint}' must be an absolute path";
+      }
+      {
+        assertion = cfg.xbootldrMountPoint == null || (hasPrefix "/" cfg.xbootldrMountPoint);
+        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' must be an absolute path";
+      }
+      {
+        assertion = cfg.xbootldrMountPoint != efi.efiSysMountPoint;
+        message = "The XBOOTLDR mount point '${cfg.xbootldrMountPoint}' cannot be the same as the ESP mount point '${efi.efiSysMountPoint}'";
+      }
+      {
         assertion = (config.boot.kernelPackages.kernel.features or { efiBootStub = true; }) ? efiBootStub;
         message = "This kernel does not support the EFI boot stub";
       }
diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix
index a7399bd55e77..88d6a2ded873 100644
--- a/nixpkgs/nixos/modules/system/boot/networkd.nix
+++ b/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -647,9 +647,9 @@ let
           "BatmanAdvanced"
         ])
         # Note: For DHCP the values both, none, v4, v6 are deprecated
-        (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"])
+        (assertValueOneOf "DHCP" (boolValues ++ ["ipv4" "ipv6"]))
         (assertValueOneOf "DHCPServer" boolValues)
-        (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "fallback" "ipv4-fallback"])
+        (assertValueOneOf "LinkLocalAddressing" (boolValues ++ ["ipv4" "ipv6" "fallback" "ipv4-fallback"]))
         (assertValueOneOf "IPv6LinkLocalAddressGenerationMode" ["eui64" "none" "stable-privacy" "random"])
         (assertValueOneOf "IPv4LLRoute" boolValues)
         (assertValueOneOf "DefaultRouteOnDevice" boolValues)
diff --git a/nixpkgs/nixos/modules/system/boot/plymouth.nix b/nixpkgs/nixos/modules/system/boot/plymouth.nix
index b041b8951fa3..16bca40993ae 100644
--- a/nixpkgs/nixos/modules/system/boot/plymouth.nix
+++ b/nixpkgs/nixos/modules/system/boot/plymouth.nix
@@ -186,6 +186,8 @@ in
           # module might come from a theme
           cp ${themesEnv}/lib/plymouth/*.so $out
           cp ${plymouth}/lib/plymouth/renderers/*.so $out/renderers
+          # useless in the initrd, and adds several megabytes to the closure
+          rm $out/renderers/x11.so
         '';
         "/etc/plymouth/themes".source = pkgs.runCommand "plymouth-initrd-themes" {} ''
           # Check if the actual requested theme is here
@@ -271,6 +273,8 @@ in
       # module might come from a theme
       cp ${themesEnv}/lib/plymouth/*.so $out/lib/plymouth
       cp ${plymouth}/lib/plymouth/renderers/*.so $out/lib/plymouth/renderers
+      # useless in the initrd, and adds several megabytes to the closure
+      rm $out/lib/plymouth/renderers/x11.so
 
       mkdir -p $out/share/plymouth/themes
       cp ${plymouth}/share/plymouth/plymouthd.defaults $out/share/plymouth
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
index 03ef00e5683c..271d8f86d0e6 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
@@ -52,7 +52,7 @@ in {
           # See: https://github.com/NixOS/nixpkgs/issues/213408
           pkgs.substitute {
             src = "${systemd}/example/sysctl.d/50-coredump.conf";
-            replacements = [
+            substitutions = [
               "--replace"
               "${systemd}"
               "${pkgs.symlinkJoin { name = "systemd"; paths = [ systemd ]; }}"
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
index 3be744acd0b3..6cc387cb6f43 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/repart.nix
@@ -10,6 +10,20 @@ let
     "repart.d"
     format
     (lib.mapAttrs (_n: v: { Partition = v; }) cfg.partitions);
+
+  partitionAssertions = lib.mapAttrsToList (fileName: definition:
+    let
+      maxLabelLength = 36; # GPT_LABEL_MAX defined in systemd's gpt.h
+      labelLength = builtins.stringLength definition.Label;
+    in
+    {
+      assertion = definition ? Label -> maxLabelLength >= labelLength;
+      message = ''
+        The partition label '${definition.Label}' defined for '${fileName}' is ${toString labelLength}
+        characters long, but the maximum label length supported by systemd is ${toString maxLabelLength}.
+      '';
+    }
+  ) cfg.partitions;
 in
 {
   options = {
@@ -81,7 +95,7 @@ in
           'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled.
         '';
       }
-    ];
+    ] ++ partitionAssertions;
 
     # systemd-repart uses loopback devices for partition creation
     boot.initrd.availableKernelModules = lib.optional initrdCfg.enable "loop";
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
index 98df6a40e8a1..58aca3fdbd4f 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/zfs.nix
@@ -347,24 +347,12 @@ in
       removeLinuxDRM = lib.mkOption {
         type = types.bool;
         default = false;
-        description = lib.mdDoc ''
-          Linux 6.2 dropped some kernel symbols required on aarch64 required by zfs.
-          Enabling this option will bring them back to allow this kernel version.
-          Note that in some jurisdictions this may be illegal as it might be considered
-          removing copyright protection from the code.
-          See https://www.ifross.org/?q=en/artikel/ongoing-dispute-over-value-exportsymbolgpl-function for further information.
-
-          If configure your kernel package with `zfs.latestCompatibleLinuxPackages`, you will need to also pass removeLinuxDRM to that package like this:
+        description = ''
+          Patch the kernel to change symbols needed by ZFS from
+          EXPORT_SYMBOL_GPL to EXPORT_SYMBOL.
 
-          ```
-          { pkgs, ... }: {
-            boot.kernelPackages = (pkgs.zfs.override {
-              removeLinuxDRM = pkgs.hostPlatform.isAarch64;
-            }).latestCompatibleLinuxPackages;
-
-            boot.zfs.removeLinuxDRM = true;
-          }
-          ```
+          Currently has no effect, but may again in future if a kernel
+          update breaks ZFS due to symbols being newly changed to GPL.
         '';
       };
     };
@@ -588,7 +576,7 @@ in
         kernelParams = lib.optionals (!config.boot.zfs.allowHibernation) [ "nohibernate" ];
 
         extraModulePackages = [
-          (cfgZfs.modulePackage.override { inherit (cfgZfs) removeLinuxDRM; })
+          cfgZfs.modulePackage
         ];
       };
 
@@ -725,21 +713,6 @@ in
       services.udev.packages = [ cfgZfs.package ]; # to hook zvol naming, etc.
       systemd.packages = [ cfgZfs.package ];
 
-      # Export kernel_neon_* symbols again.
-      # This change is necessary until ZFS figures out a solution
-      # with upstream or in their build system to fill the gap for
-      # this symbol.
-      # In the meantime, we restore what was once a working piece of code
-      # in the kernel.
-      boot.kernelPatches = lib.optional (cfgZfs.removeLinuxDRM && pkgs.stdenv.hostPlatform.system == "aarch64-linux") {
-        name = "export-neon-symbols-as-gpl";
-        patch = pkgs.fetchpatch {
-          url = "https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3.patch";
-          hash = "sha256-L2g4G1tlWPIi/QRckMuHDcdWBcKpObSWSRTvbHRIwIk=";
-          revert = true;
-        };
-      };
-
       systemd.services = let
         createImportService' = pool: createImportService {
           inherit pool;
diff --git a/nixpkgs/nixos/modules/virtualisation/podman/default.nix b/nixpkgs/nixos/modules/virtualisation/podman/default.nix
index 5a99dc8a1bb9..a97739054216 100644
--- a/nixpkgs/nixos/modules/virtualisation/podman/default.nix
+++ b/nixpkgs/nixos/modules/virtualisation/podman/default.nix
@@ -216,9 +216,11 @@ in
         requires = [ "podman.service" ];
       };
 
+      systemd.services.podman.environment = config.networking.proxy.envVars;
       systemd.sockets.podman.wantedBy = [ "sockets.target" ];
       systemd.sockets.podman.socketConfig.SocketGroup = "podman";
 
+      systemd.user.services.podman.environment = config.networking.proxy.envVars;
       systemd.user.sockets.podman.wantedBy = [ "sockets.target" ];
 
       systemd.timers.podman-prune.timerConfig = lib.mkIf cfg.autoPrune.enable {
diff --git a/nixpkgs/nixos/release-combined.nix b/nixpkgs/nixos/release-combined.nix
index 7700441b1d6b..459700514ffc 100644
--- a/nixpkgs/nixos/release-combined.nix
+++ b/nixpkgs/nixos/release-combined.nix
@@ -142,7 +142,6 @@ in rec {
         (onFullSupported "nixos.tests.networking.networkd.virtual")
         (onFullSupported "nixos.tests.networking.networkd.vlan")
         (onFullSupported "nixos.tests.systemd-networkd-ipv6-prefix-delegation")
-        (onFullSupported "nixos.tests.nfs3.simple")
         (onFullSupported "nixos.tests.nfs4.simple")
         (onSystems ["x86_64-linux"] "nixos.tests.oci-containers.podman")
         (onFullSupported "nixos.tests.openssh")
diff --git a/nixpkgs/nixos/release-small.nix b/nixpkgs/nixos/release-small.nix
index 6204dc731ad9..cac20b63925f 100644
--- a/nixpkgs/nixos/release-small.nix
+++ b/nixpkgs/nixos/release-small.nix
@@ -43,7 +43,7 @@ in rec {
         login
         misc
         nat
-        nfs3
+        nfs4
         openssh
         php
         predictable-interface-names
@@ -125,7 +125,7 @@ in rec {
         "nixos.tests.misc"
         "nixos.tests.nat.firewall"
         "nixos.tests.nat.standalone"
-        "nixos.tests.nfs3.simple"
+        "nixos.tests.nfs4.simple"
         "nixos.tests.openssh"
         "nixos.tests.php.fpm"
         "nixos.tests.php.pcre"
diff --git a/nixpkgs/nixos/release.nix b/nixpkgs/nixos/release.nix
index 2acc5ade7848..ff60b0b79f6d 100644
--- a/nixpkgs/nixos/release.nix
+++ b/nixpkgs/nixos/release.nix
@@ -177,6 +177,12 @@ in rec {
     inherit system;
   });
 
+  iso_plasma6 = forMatchingSystems supportedSystems (system: makeIso {
+    module = ./modules/installer/cd-dvd/installation-cd-graphical-calamares-plasma6.nix;
+    type = "plasma6";
+    inherit system;
+  });
+
   iso_gnome = forMatchingSystems supportedSystems (system: makeIso {
     module = ./modules/installer/cd-dvd/installation-cd-graphical-calamares-gnome.nix;
     type = "gnome";
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index 1144a5611dcf..231767ca2b97 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -128,6 +128,7 @@ in {
   appliance-repart-image = runTest ./appliance-repart-image.nix;
   apparmor = handleTest ./apparmor.nix {};
   archi = handleTest ./archi.nix {};
+  armagetronad = handleTest ./armagetronad.nix {};
   atd = handleTest ./atd.nix {};
   atop = handleTest ./atop.nix {};
   atuin = handleTest ./atuin.nix {};
@@ -497,6 +498,7 @@ in {
   lxd = pkgs.recurseIntoAttrs (handleTest ./lxd { inherit handleTestOn; });
   lxd-image-server = handleTest ./lxd-image-server.nix {};
   #logstash = handleTest ./logstash.nix {};
+  lomiri-system-settings = handleTest ./lomiri-system-settings.nix {};
   lorri = handleTest ./lorri/default.nix {};
   maddy = discoverTests (import ./maddy { inherit handleTest; });
   maestral = handleTest ./maestral.nix {};
@@ -516,6 +518,7 @@ in {
   matrix-synapse = handleTest ./matrix/synapse.nix {};
   matrix-synapse-workers = handleTest ./matrix/synapse-workers.nix {};
   mattermost = handleTest ./mattermost.nix {};
+  mealie = handleTest ./mealie.nix {};
   mediamtx = handleTest ./mediamtx.nix {};
   mediatomb = handleTest ./mediatomb.nix {};
   mediawiki = handleTest ./mediawiki.nix {};
@@ -535,6 +538,7 @@ in {
   mobilizon = handleTest ./mobilizon.nix {};
   mod_perl = handleTest ./mod_perl.nix {};
   molly-brown = handleTest ./molly-brown.nix {};
+  monado = handleTest ./monado.nix {};
   monica = handleTest ./web-apps/monica.nix {};
   mongodb = handleTest ./mongodb.nix {};
   moodle = handleTest ./moodle.nix {};
@@ -692,6 +696,7 @@ in {
   plantuml-server = handleTest ./plantuml-server.nix {};
   plasma-bigscreen = handleTest ./plasma-bigscreen.nix {};
   plasma5 = handleTest ./plasma5.nix {};
+  plasma6 = handleTest ./plasma6.nix {};
   plasma5-systemd-start = handleTest ./plasma5-systemd-start.nix {};
   plausible = handleTest ./plausible.nix {};
   please = handleTest ./please.nix {};
diff --git a/nixpkgs/nixos/tests/armagetronad.nix b/nixpkgs/nixos/tests/armagetronad.nix
new file mode 100644
index 000000000000..ff2841dedd21
--- /dev/null
+++ b/nixpkgs/nixos/tests/armagetronad.nix
@@ -0,0 +1,272 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+
+let
+  user = "alice";
+
+  client =
+    { pkgs, ... }:
+
+    { imports = [ ./common/user-account.nix ./common/x11.nix ];
+      hardware.opengl.driSupport = true;
+      virtualisation.memorySize = 256;
+      environment = {
+        systemPackages = [ pkgs.armagetronad ];
+        variables.XAUTHORITY = "/home/${user}/.Xauthority";
+      };
+      test-support.displayManager.auto.user = user;
+    };
+
+in {
+  name = "armagetronad";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ numinit ];
+  };
+
+  enableOCR = true;
+
+  nodes =
+    {
+      server = {
+        services.armagetronad.servers = {
+          high-rubber = {
+            enable = true;
+            name = "Smoke Test High Rubber Server";
+            port = 4534;
+            settings = {
+              SERVER_OPTIONS = "High Rubber server made to run smoke tests.";
+              CYCLE_RUBBER = 40;
+              SIZE_FACTOR = 0.5;
+            };
+            roundSettings = {
+              SAY = [
+                "NixOS Smoke Test Server"
+                "https://nixos.org"
+              ];
+            };
+          };
+          sty = {
+            enable = true;
+            name = "Smoke Test sty+ct+ap Server";
+            package = pkgs.armagetronad."0.2.9-sty+ct+ap".dedicated;
+            port = 4535;
+            settings = {
+              SERVER_OPTIONS = "sty+ct+ap server made to run smoke tests.";
+              CYCLE_RUBBER = 20;
+              SIZE_FACTOR = 0.5;
+            };
+            roundSettings = {
+              SAY = [
+                "NixOS Smoke Test sty+ct+ap Server"
+                "https://nixos.org"
+              ];
+            };
+          };
+          trunk = {
+            enable = true;
+            name = "Smoke Test trunk Server";
+            package = pkgs.armagetronad."0.4".dedicated;
+            port = 4536;
+            settings = {
+              SERVER_OPTIONS = "0.4 server made to run smoke tests.";
+              CYCLE_RUBBER = 20;
+              SIZE_FACTOR = 0.5;
+            };
+            roundSettings = {
+              SAY = [
+                "NixOS Smoke Test 0.4 Server"
+                "https://nixos.org"
+              ];
+            };
+          };
+        };
+      };
+
+      client1 = client;
+      client2 = client;
+    };
+
+  testScript = let
+    xdo = name: text: let
+      xdoScript = pkgs.writeText "${name}.xdo" text;
+    in "${pkgs.xdotool}/bin/xdotool ${xdoScript}";
+  in
+    ''
+      import shlex
+      import threading
+      from collections import namedtuple
+
+      class Client(namedtuple('Client', ('node', 'name'))):
+        def send(self, *keys):
+          for key in keys:
+            self.node.send_key(key)
+
+        def send_on(self, text, *keys):
+          self.node.wait_for_text(text)
+          self.send(*keys)
+
+      Server = namedtuple('Server', ('node', 'name', 'address', 'port', 'welcome', 'attacker', 'victim', 'coredump_delay'))
+
+      # Clients and their in-game names
+      clients = (
+        Client(client1, 'Arduino'),
+        Client(client2, 'SmOoThIcE')
+      )
+
+      # Server configs.
+      servers = (
+        Server(server, 'high-rubber', 'server', 4534, 'NixOS Smoke Test Server', 'SmOoThIcE', 'Arduino', 8),
+        Server(server, 'sty', 'server', 4535, 'NixOS Smoke Test sty+ct+ap Server', 'Arduino', 'SmOoThIcE', 8),
+        Server(server, 'trunk', 'server', 4536, 'NixOS Smoke Test 0.4 Server', 'Arduino', 'SmOoThIcE', 8)
+      )
+
+      """
+      Runs a command as the client user.
+      """
+      def run(cmd):
+        return "su - ${user} -c " + shlex.quote(cmd)
+
+      screenshot_idx = 1
+
+      """
+      Takes screenshots on all clients.
+      """
+      def take_screenshots(screenshot_idx):
+        for client in clients:
+          client.node.screenshot(f"screen_{client.name}_{screenshot_idx}")
+        return screenshot_idx + 1
+
+      # Wait for the servers to come up.
+      start_all()
+      for srv in servers:
+        srv.node.wait_for_unit(f"armagetronad-{srv.name}")
+        srv.node.wait_until_succeeds(f"ss --numeric --udp --listening | grep -q {srv.port}")
+
+      # Make sure console commands work through the named pipe we created.
+      for srv in servers:
+        srv.node.succeed(
+          f"echo 'say Testing!' >> /var/lib/armagetronad/{srv.name}/input"
+        )
+        srv.node.succeed(
+          f"echo 'say Testing again!' >> /var/lib/armagetronad/{srv.name}/input"
+        )
+        srv.node.wait_until_succeeds(
+          f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing!'"
+        )
+        srv.node.wait_until_succeeds(
+          f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: Testing again!'"
+        )
+
+      """
+      Sets up a client, waiting for the given barrier on completion.
+      """
+      def client_setup(client, servers, barrier):
+        client.node.wait_for_x()
+
+        # Configure Armagetron.
+        client.node.succeed(
+          run("mkdir -p ~/.armagetronad/var"),
+          run(f"echo 'PLAYER_1 {client.name}' >> ~/.armagetronad/var/autoexec.cfg")
+        )
+        for idx, srv in enumerate(servers):
+          client.node.succeed(
+            run(f"echo 'BOOKMARK_{idx+1}_ADDRESS {srv.address}' >> ~/.armagetronad/var/autoexec.cfg"),
+            run(f"echo 'BOOKMARK_{idx+1}_NAME {srv.name}' >> ~/.armagetronad/var/autoexec.cfg"),
+            run(f"echo 'BOOKMARK_{idx+1}_PORT {srv.port}' >> ~/.armagetronad/var/autoexec.cfg")
+          )
+
+        # Start Armagetron.
+        client.node.succeed(run("ulimit -c unlimited; armagetronad >&2 & disown"))
+        client.node.wait_until_succeeds(
+          run(
+            "${xdo "create_new_win-select_main_window" ''
+              search --onlyvisible --name "Armagetron Advanced"
+              windowfocus --sync
+              windowactivate --sync
+            ''}"
+          )
+        )
+
+        # Get through the tutorial.
+        client.send_on('Language Settings', 'ret')
+        client.send_on('First Setup', 'ret')
+        client.send_on('Welcome to Armagetron Advanced', 'ret')
+        client.send_on('round 1', 'esc')
+        client.send_on('Menu', 'up', 'up', 'ret')
+        client.send_on('We hope you', 'ret')
+        client.send_on('Armagetron Advanced', 'ret')
+        client.send_on('Play Game', 'ret')
+
+        # Online > LAN > Network Setup > Mates > Server Bookmarks
+        client.send_on('Multiplayer', 'down', 'down', 'down', 'down', 'ret')
+
+        barrier.wait()
+
+      # Get to the Server Bookmarks screen on both clients. This takes a while so do it asynchronously.
+      barrier = threading.Barrier(3, timeout=120)
+      for client in clients:
+        threading.Thread(target=client_setup, args=(client, servers, barrier)).start()
+      barrier.wait()
+
+      # Main testing loop. Iterates through each server bookmark and connects to them in sequence.
+      # Assumes that the game is currently on the Server Bookmarks screen.
+      for srv in servers:
+        screenshot_idx = take_screenshots(screenshot_idx)
+
+        # Connect both clients at once, one second apart.
+        for client in clients:
+          client.send('ret')
+          client.node.sleep(1)
+
+        # Wait for clients to connect
+        for client in clients:
+          srv.node.wait_until_succeeds(
+            f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*entered the game'"
+          )
+
+        # Wait for the match to start
+        srv.node.wait_until_succeeds(
+          f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: {srv.welcome}'"
+        )
+        srv.node.wait_until_succeeds(
+          f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Admin: https://nixos.org'"
+        )
+        srv.node.wait_until_succeeds(
+          f"journalctl -u armagetronad-{srv.name} -e | grep -q 'Go (round 1 of 10)'"
+        )
+
+        # Wait a bit
+        srv.node.sleep(srv.coredump_delay)
+
+        # Turn the attacker player's lightcycle left
+        attacker = next(client for client in clients if client.name == srv.attacker)
+        victim = next(client for client in clients if client.name == srv.victim)
+        attacker.send('left')
+        screenshot_idx = take_screenshots(screenshot_idx)
+
+        # Wait for coredump.
+        srv.node.wait_until_succeeds(
+          f"journalctl -u armagetronad-{srv.name} -e | grep -q '{attacker.name} core dumped {victim.name}'"
+        )
+        screenshot_idx = take_screenshots(screenshot_idx)
+
+        # Disconnect both clients from the server
+        for client in clients:
+          client.send('esc')
+          client.send_on('Menu', 'up', 'up', 'ret')
+          srv.node.wait_until_succeeds(
+            f"journalctl -u armagetronad-{srv.name} -e | grep -q '{client.name}.*left the game'"
+          )
+
+        # Next server.
+        for client in clients:
+          client.send_on('Server Bookmarks', 'down')
+
+      # Stop the servers
+      for srv in servers:
+        srv.node.succeed(
+          f"systemctl stop armagetronad-{srv.name}"
+        )
+        srv.node.wait_until_fails(f"ss --numeric --udp --listening | grep -q {srv.port}")
+    '';
+
+})
diff --git a/nixpkgs/nixos/tests/boot.nix b/nixpkgs/nixos/tests/boot.nix
index ec2a9f6527c9..56f72dddf526 100644
--- a/nixpkgs/nixos/tests/boot.nix
+++ b/nixpkgs/nixos/tests/boot.nix
@@ -4,10 +4,41 @@
 }:
 
 with import ../lib/testing-python.nix { inherit system pkgs; };
-with pkgs.lib;
 
 let
-  qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
+  lib = pkgs.lib;
+  qemu-common = import ../lib/qemu-common.nix { inherit lib pkgs; };
+
+  mkStartCommand = {
+    memory ? 2048,
+    cdrom ? null,
+    usb ? null,
+    pxe ? null,
+    uboot ? false,
+    uefi ? false,
+    extraFlags ? [],
+  }: let
+    qemu = qemu-common.qemuBinary pkgs.qemu_test;
+
+    flags = [
+      "-m" (toString memory)
+      "-netdev" ("user,id=net0" + (lib.optionalString (pxe != null) ",tftp=${pxe},bootfile=netboot.ipxe"))
+      "-device" ("virtio-net-pci,netdev=net0" + (lib.optionalString (pxe != null && uefi) ",romfile=${pkgs.ipxe}/ipxe.efirom"))
+    ] ++ lib.optionals (cdrom != null) [
+      "-cdrom" cdrom
+    ] ++ lib.optionals (usb != null) [
+      "-device" "usb-ehci"
+      "-drive" "id=usbdisk,file=${usb},if=none,readonly"
+      "-device" "usb-storage,drive=usbdisk"
+    ] ++ lib.optionals (pxe != null) [
+      "-boot" "order=n"
+    ] ++ lib.optionals uefi [
+      "-drive" "if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}"
+      "-drive" "if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}"
+    ] ++ extraFlags;
+
+    flagsStr = lib.concatStringsSep " " flags;
+  in "${qemu} ${flagsStr}";
 
   iso =
     (import ../lib/eval-config.nix {
@@ -28,21 +59,16 @@ let
       ];
     }).config.system.build.sdImage;
 
-  pythonDict = params: "\n    {\n        ${concatStringsSep ",\n        " (mapAttrsToList (name: param: "\"${name}\": \"${param}\"") params)},\n    }\n";
-
-  makeBootTest = name: extraConfig:
+  makeBootTest = name: config:
     let
-      machineConfig = pythonDict ({
-        qemuBinary = qemu-common.qemuBinary pkgs.qemu_test;
-        qemuFlags = "-m 768";
-      } // extraConfig);
+      startCommand = mkStartCommand config;
     in
       makeTest {
         name = "boot-" + name;
         nodes = { };
         testScript =
           ''
-            machine = create_machine(${machineConfig})
+            machine = create_machine("${startCommand}")
             machine.start()
             machine.wait_for_unit("multi-user.target")
             machine.succeed("nix store verify --no-trust -r --option experimental-features nix-command /run/current-system")
@@ -73,43 +99,35 @@ let
           config.system.build.netbootIpxeScript
         ];
       };
-      machineConfig = pythonDict ({
-        qemuBinary = qemu-common.qemuBinary pkgs.qemu_test;
-        qemuFlags = "-boot order=n -m 2000";
-        netBackendArgs = "tftp=${ipxeBootDir},bootfile=netboot.ipxe";
+      startCommand = mkStartCommand ({
+        pxe = ipxeBootDir;
       } // extraConfig);
     in
       makeTest {
         name = "boot-netboot-" + name;
         nodes = { };
         testScript = ''
-            machine = create_machine(${machineConfig})
+            machine = create_machine("${startCommand}")
             machine.start()
             machine.wait_for_unit("multi-user.target")
             machine.shutdown()
           '';
       };
-  uefiBinary = {
-    x86_64-linux = "${pkgs.OVMF.fd}/FV/OVMF.fd";
-    aarch64-linux = "${pkgs.OVMF.fd}/FV/QEMU_EFI.fd";
-  }.${pkgs.stdenv.hostPlatform.system};
 in {
     uefiCdrom = makeBootTest "uefi-cdrom" {
+      uefi = true;
       cdrom = "${iso}/iso/${iso.isoName}";
-      bios = uefiBinary;
     };
 
     uefiUsb = makeBootTest "uefi-usb" {
+      uefi = true;
       usb = "${iso}/iso/${iso.isoName}";
-      bios = uefiBinary;
     };
 
     uefiNetboot = makeNetbootTest "uefi" {
-      bios = uefiBinary;
-      # Custom ROM is needed for EFI PXE boot. I failed to understand exactly why, because QEMU should still use iPXE for EFI.
-      netFrontendArgs = "romfile=${pkgs.ipxe}/ipxe.efirom";
+      uefi = true;
     };
-} // optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
+} // lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "x86_64-linux") {
     biosCdrom = makeBootTest "bios-cdrom" {
       cdrom = "${iso}/iso/${iso.isoName}";
     };
@@ -124,9 +142,12 @@ in {
       sdImage = "${sd}/sd-image/${sd.imageName}";
       mutableImage = "/tmp/linked-image.qcow2";
 
-      machineConfig = pythonDict {
-        bios = "${pkgs.ubootQemuX86}/u-boot.rom";
-        qemuFlags = "-m 768 -machine type=pc,accel=tcg -drive file=${mutableImage},if=ide,format=qcow2";
+      startCommand = mkStartCommand {
+        extraFlags = [
+          "-bios" "${pkgs.ubootQemuX86}/u-boot.rom"
+          "-machine" "type=pc,accel=tcg"
+          "-drive" "file=${mutableImage},if=virtio"
+        ];
       };
     in makeTest {
       name = "boot-uboot-extlinux";
@@ -138,11 +159,14 @@ in {
         if os.system("qemu-img create -f qcow2 -F raw -b ${sdImage} ${mutableImage}") != 0:
             raise RuntimeError("Could not create mutable linked image")
 
-        machine = create_machine(${machineConfig})
+        machine = create_machine("${startCommand}")
         machine.start()
         machine.wait_for_unit("multi-user.target")
         machine.succeed("nix store verify -r --no-trust --option experimental-features nix-command /run/current-system")
         machine.shutdown()
       '';
+
+      # kernel can't find rootfs after boot - investigate?
+      meta.broken = true;
     };
 }
diff --git a/nixpkgs/nixos/tests/common/ec2.nix b/nixpkgs/nixos/tests/common/ec2.nix
index 1a64c464039b..82922102f07b 100644
--- a/nixpkgs/nixos/tests/common/ec2.nix
+++ b/nixpkgs/nixos/tests/common/ec2.nix
@@ -61,7 +61,7 @@ with pkgs.lib;
             + " $QEMU_OPTS"
         )
 
-        machine = create_machine({"startCommand": start_command})
+        machine = create_machine(start_command)
         try:
       '' + indentLines script + ''
         finally:
diff --git a/nixpkgs/nixos/tests/consul.nix b/nixpkgs/nixos/tests/consul.nix
index 6233234ff083..c819312068dc 100644
--- a/nixpkgs/nixos/tests/consul.nix
+++ b/nixpkgs/nixos/tests/consul.nix
@@ -42,6 +42,8 @@ let
         ];
         networking.firewall = firewallSettings;
 
+        nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ];
+
         services.consul = {
           enable = true;
           inherit webUi;
@@ -65,6 +67,8 @@ let
         ];
         networking.firewall = firewallSettings;
 
+        nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "consul" ];
+
         services.consul =
           assert builtins.elem thisConsensusServerHost allConsensusServerHosts;
           {
diff --git a/nixpkgs/nixos/tests/docker-tools.nix b/nixpkgs/nixos/tests/docker-tools.nix
index f252eb9ff61e..7d91076600f9 100644
--- a/nixpkgs/nixos/tests/docker-tools.nix
+++ b/nixpkgs/nixos/tests/docker-tools.nix
@@ -58,6 +58,20 @@ let
       '';
       config.Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "/testfile" ];
     };
+
+  nonRootTestImage =
+    pkgs.dockerTools.streamLayeredImage rec {
+      name = "non-root-test";
+      tag = "latest";
+      uid = 1000;
+      gid = 1000;
+      uname = "user";
+      gname = "user";
+      config = {
+        User = "user";
+        Cmd = [ "${pkgs.coreutils}/bin/stat" "-c" "%u:%g" "${pkgs.coreutils}/bin/stat" ];
+      };
+    };
 in {
   name = "docker-tools";
   meta = with pkgs.lib.maintainers; {
@@ -181,7 +195,7 @@ in {
     ):
         docker.succeed(
             "docker load --input='${examples.bashLayeredWithUser}'",
-            "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 555 == $(stat --format=%a /nix) && test 555 == $(stat --format=%a /nix/store)'",
+            "docker run -u somebody --rm ${examples.bashLayeredWithUser.imageName} ${pkgs.bash}/bin/bash -c 'test 755 == $(stat --format=%a /nix) && test 755 == $(stat --format=%a /nix/store)'",
             "docker rmi ${examples.bashLayeredWithUser.imageName}",
         )
 
@@ -604,5 +618,11 @@ in {
             "${chownTestImage} | docker load",
             "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)"
         )
+
+    with subtest("streamLayeredImage: with non-root user"):
+        docker.succeed(
+            "${nonRootTestImage} | docker load",
+            "docker run --rm ${chownTestImage.imageName} | diff /dev/stdin <(echo 12345:12345)"
+        )
   '';
 })
diff --git a/nixpkgs/nixos/tests/incus/container.nix b/nixpkgs/nixos/tests/incus/container.nix
index 0f42d16f133d..eb00429e53fe 100644
--- a/nixpkgs/nixos/tests/incus/container.nix
+++ b/nixpkgs/nixos/tests/incus/container.nix
@@ -5,6 +5,8 @@ let
     configuration = {
       # Building documentation makes the test unnecessarily take a longer time:
       documentation.enable = lib.mkForce false;
+
+      boot.kernel.sysctl."net.ipv4.ip_forward" = "1";
     } // extra;
   };
 
@@ -40,6 +42,12 @@ in
         with machine.nested("Waiting for instance to start and be usable"):
           retry(instance_is_up)
 
+    def check_sysctl(instance):
+        with subtest("systemd sysctl settings are applied"):
+            machine.succeed(f"incus exec {instance} -- systemctl status systemd-sysctl")
+            sysctl = machine.succeed(f"incus exec {instance} -- sysctl net.ipv4.ip_forward").strip().split(" ")[-1]
+            assert "1" == sysctl, f"systemd-sysctl configuration not correctly applied, {sysctl} != 1"
+
     machine.wait_for_unit("incus.service")
 
     # no preseed should mean no service
@@ -83,6 +91,7 @@ in
     with subtest("lxc-container generator configures plain container"):
         # reuse the existing container to save some time
         machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+        check_sysctl("container")
 
     with subtest("lxc-container generator configures nested container"):
         machine.execute("incus delete --force container")
@@ -94,6 +103,8 @@ in
         target = machine.succeed("incus exec container readlink -- -f /run/systemd/system/systemd-binfmt.service").strip()
         assert target == "/dev/null", "lxc generator did not correctly mask /run/systemd/system/systemd-binfmt.service"
 
+        check_sysctl("container")
+
     with subtest("lxc-container generator configures privileged container"):
         machine.execute("incus delete --force container")
         machine.succeed("incus launch nixos container --config security.privileged=true")
@@ -101,5 +112,7 @@ in
           retry(instance_is_up)
 
         machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+
+        check_sysctl("container")
   '';
 })
diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix
index b6cb6a0c6d45..97bb7f8def59 100644
--- a/nixpkgs/nixos/tests/installer.nix
+++ b/nixpkgs/nixos/tests/installer.nix
@@ -83,46 +83,34 @@ let
                   , postInstallCommands, preBootCommands, postBootCommands, extraConfig
                   , testSpecialisationConfig, testFlakeSwitch, clevisTest, clevisFallbackTest
                   }:
-    let iface = "virtio";
-        isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
-        bios  = if pkgs.stdenv.isAarch64 then "QEMU_EFI.fd" else "OVMF.fd";
+    let
+      qemu-common = import ../lib/qemu-common.nix { inherit (pkgs) lib pkgs; };
+      isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
+      qemu = qemu-common.qemuBinary pkgs.qemu_test;
     in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then ''
       machine.succeed("true")
     '' else ''
+      import os
       import subprocess
-      tpm_folder = os.environ['NIX_BUILD_TOP']
-      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"''
-          }
-          ${optionalString clevisTest ''flags += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"''}
-          ${optionalString clevisTest ''flags += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""''}
-          return flags
 
+      tpm_folder = os.environ['NIX_BUILD_TOP']
 
-      qemu_flags = {"qemuFlags": assemble_qemu_flags()}
+      startcommand = "${qemu} -m 2048"
 
-      import os
+      ${optionalString clevisTest ''
+        startcommand += f" -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"
+        startcommand += " -device virtio-net-pci,netdev=vlan1,mac=52:54:00:12:11:02 -netdev vde,id=vlan1,sock=\"$QEMU_VDE_SOCKET_1\""
+      ''}
+      ${optionalString isEfi ''
+        startcommand +=" -drive if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware} -drive if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}"
+      ''}
 
       image_dir = machine.state_dir
       disk_image = os.path.join(image_dir, "machine.qcow2")
-
-      hd_flags = {
-          "hdaInterface": "${iface}",
-          "hda": disk_image,
-      }
-      ${optionalString isEfi ''
-        hd_flags.update(
-            bios="${pkgs.OVMF.fd}/FV/${bios}"
-        )''
-      }
-      default_flags = {**hd_flags, **qemu_flags}
-
+      startcommand += f" -drive file={disk_image},if=virtio,werror=report"
 
       def create_machine_named(name):
-          return create_machine({**default_flags, "name": name})
+          return create_machine(startcommand, name=name)
 
       class Tpm:
             def __init__(self):
@@ -471,7 +459,7 @@ let
           # builds stuff in the VM, needs more juice
           virtualisation.diskSize = 8 * 1024;
           virtualisation.cores = 8;
-          virtualisation.memorySize = 1536;
+          virtualisation.memorySize = 2048;
 
           boot.initrd.systemd.enable = systemdStage1;
 
diff --git a/nixpkgs/nixos/tests/k3s/default.nix b/nixpkgs/nixos/tests/k3s/default.nix
index e168f8233c76..512dc06ee77e 100644
--- a/nixpkgs/nixos/tests/k3s/default.nix
+++ b/nixpkgs/nixos/tests/k3s/default.nix
@@ -6,6 +6,11 @@ let
   allK3s = lib.filterAttrs (n: _: lib.strings.hasPrefix "k3s_" n) pkgs;
 in
 {
+  # Testing K3s with Etcd backend
+  etcd = lib.mapAttrs (_: k3s: import ./etcd.nix {
+    inherit system pkgs k3s;
+    inherit (pkgs) etcd;
+  }) allK3s;
   # Run a single node k3s cluster and verify a pod can run
   single-node = lib.mapAttrs (_: k3s: import ./single-node.nix { inherit system pkgs k3s; }) allK3s;
   # Run a multi-node k3s cluster and verify pod networking works across nodes
diff --git a/nixpkgs/nixos/tests/k3s/etcd.nix b/nixpkgs/nixos/tests/k3s/etcd.nix
new file mode 100644
index 000000000000..d6e9a294adb1
--- /dev/null
+++ b/nixpkgs/nixos/tests/k3s/etcd.nix
@@ -0,0 +1,100 @@
+import ../make-test-python.nix ({ pkgs, lib, k3s, etcd, ... }:
+
+{
+  name = "${k3s.name}-etcd";
+
+  nodes = {
+
+    etcd = { ... }: {
+      services.etcd = {
+        enable = true;
+        openFirewall = true;
+        listenClientUrls = [ "http://192.168.1.1:2379" "http://127.0.0.1:2379" ];
+        listenPeerUrls = [ "http://192.168.1.1:2380" ];
+        initialAdvertisePeerUrls = [ "http://192.168.1.1:2380" ];
+        initialCluster = [ "etcd=http://192.168.1.1:2380" ];
+      };
+      networking = {
+        useDHCP = false;
+        defaultGateway = "192.168.1.1";
+        interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+          { address = "192.168.1.1"; prefixLength = 24; }
+        ];
+      };
+    };
+
+    k3s = { pkgs, ... }: {
+      environment.systemPackages = with pkgs; [ jq ];
+      # k3s uses enough resources the default vm fails.
+      virtualisation.memorySize = 1536;
+      virtualisation.diskSize = 4096;
+
+      services.k3s = {
+        enable = true;
+        role = "server";
+        extraFlags = builtins.toString [
+          "--datastore-endpoint=\"http://192.168.1.1:2379\""
+          "--disable" "coredns"
+          "--disable" "local-storage"
+          "--disable" "metrics-server"
+          "--disable" "servicelb"
+          "--disable" "traefik"
+          "--node-ip" "192.168.1.2"
+        ];
+      };
+
+      networking = {
+        firewall = {
+          allowedTCPPorts = [ 2379 2380 6443 ];
+          allowedUDPPorts = [ 8472 ];
+        };
+        useDHCP = false;
+        defaultGateway = "192.168.1.2";
+        interfaces.eth1.ipv4.addresses = pkgs.lib.mkForce [
+          { address = "192.168.1.2"; prefixLength = 24; }
+        ];
+      };
+    };
+
+  };
+
+  testScript = ''
+    with subtest("should start etcd"):
+        etcd.start()
+        etcd.wait_for_unit("etcd.service")
+
+    with subtest("should wait for etcdctl endpoint status to succeed"):
+        etcd.wait_until_succeeds("etcdctl endpoint status")
+
+    with subtest("should start k3s"):
+        k3s.start()
+        k3s.wait_for_unit("k3s")
+
+    with subtest("should test if kubectl works"):
+        k3s.wait_until_succeeds("k3s kubectl get node")
+
+    with subtest("should wait for service account to show up; takes a sec"):
+        k3s.wait_until_succeeds("k3s kubectl get serviceaccount default")
+
+    with subtest("should create a sample secret object"):
+        k3s.succeed("k3s kubectl create secret generic nixossecret --from-literal thesecret=abacadabra")
+
+    with subtest("should check if secret is correct"):
+        k3s.wait_until_succeeds("[[ $(kubectl get secrets nixossecret -o json | jq -r .data.thesecret | base64 -d) == abacadabra ]]")
+
+    with subtest("should have a secret in database"):
+        etcd.wait_until_succeeds("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]")
+
+    with subtest("should delete the secret"):
+        k3s.succeed("k3s kubectl delete secret nixossecret")
+
+    with subtest("should not have a secret in database"):
+        etcd.wait_until_fails("[[ $(etcdctl get /registry/secrets/default/nixossecret | head -c1 | wc -c) -ne 0 ]]")
+
+    with subtest("should shutdown k3s and etcd"):
+        k3s.shutdown()
+        etcd.shutdown()
+  '';
+
+  meta.maintainers = etcd.meta.maintainers ++ k3s.meta.maintainers;
+})
diff --git a/nixpkgs/nixos/tests/kernel-generic.nix b/nixpkgs/nixos/tests/kernel-generic.nix
index 0dcab39f3fad..9714a94382ee 100644
--- a/nixpkgs/nixos/tests/kernel-generic.nix
+++ b/nixpkgs/nixos/tests/kernel-generic.nix
@@ -30,7 +30,6 @@ let
       linux_5_10_hardened
       linux_5_15_hardened
       linux_6_1_hardened
-      linux_6_5_hardened
       linux_6_6_hardened
       linux_6_7_hardened
       linux_rt_5_4
diff --git a/nixpkgs/nixos/tests/lomiri-system-settings.nix b/nixpkgs/nixos/tests/lomiri-system-settings.nix
new file mode 100644
index 000000000000..867fc14797e7
--- /dev/null
+++ b/nixpkgs/nixos/tests/lomiri-system-settings.nix
@@ -0,0 +1,99 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "lomiri-system-settings-standalone";
+  meta.maintainers = lib.teams.lomiri.members;
+
+  nodes.machine = { config, pkgs, ... }: {
+    imports = [
+      ./common/x11.nix
+    ];
+
+    services.xserver.enable = true;
+
+    environment = {
+      systemPackages = with pkgs.lomiri; [
+        suru-icon-theme
+        lomiri-system-settings
+      ];
+      variables = {
+        UITK_ICON_THEME = "suru";
+      };
+    };
+
+    i18n.supportedLocales = [ "all" ];
+
+    fonts.packages = with pkgs; [
+      # Intended font & helps with OCR
+      ubuntu_font_family
+    ];
+
+    services.upower.enable = true;
+  };
+
+  enableOCR = true;
+
+  testScript = let
+    settingsPages = [
+      # Base pages
+      { name = "wifi"; type = "internal"; element = "networks"; }
+      { name = "bluetooth"; type = "internal"; element = "discoverable|None detected"; }
+      # only text we can really look for with VPN is on a button, OCR on CI struggles with it
+      { name = "vpn"; type = "internal"; element = "Add|Manual|Configuration"; skipOCR = true; }
+      { name = "appearance"; type = "internal"; element = "Background image|blur effects"; }
+      { name = "desktop"; type = "internal"; element = "workspaces|Icon size"; }
+      { name = "sound"; type = "internal"; element = "Silent Mode|Message sound"; }
+      { name = "language"; type = "internal"; element = "Display language|External keyboard"; }
+      { name = "notification"; type = "internal"; element = "Apps that notify"; }
+      { name = "gestures"; type = "internal"; element = "Edge drag"; }
+      { name = "mouse"; type = "internal"; element = "Cursor speed|Wheel scrolling speed"; }
+      { name = "timedate"; type = "internal"; element = "Time zone|Set the time and date"; }
+
+      # External plugins
+      { name = "security-privacy"; type = "external"; element = "Locking|unlocking|permissions"; elementLocalised = "Sperren|Entsperren|Berechtigungen"; }
+    ];
+  in
+  ''
+    machine.wait_for_x()
+
+    with subtest("lomiri system settings launches"):
+        machine.execute("lomiri-system-settings >&2 &")
+        machine.wait_for_text("System Settings")
+        machine.screenshot("lss_open")
+
+    # Move focus to start of plugins list for following list of tests
+    machine.send_key("tab")
+    machine.send_key("tab")
+    machine.screenshot("lss_focus")
+
+    # tab through & open all sub-menus, to make sure none of them fail
+  '' + (lib.strings.concatMapStringsSep "\n" (page: ''
+    machine.send_key("tab")
+    machine.send_key("kp_enter")
+  ''
+  + lib.optionalString (!(page.skipOCR or false)) ''
+    with subtest("lomiri system settings ${page.name} works"):
+        machine.wait_for_text(r"(${page.element})")
+        machine.screenshot("lss_page_${page.name}")
+  '') settingsPages) + ''
+
+    machine.execute("pkill -f lomiri-system-settings")
+
+    with subtest("lomiri system settings localisation works"):
+        machine.execute("env LANG=de_DE.UTF-8 lomiri-system-settings >&2 &")
+        machine.wait_for_text("Systemeinstellungen")
+        machine.screenshot("lss_localised_open")
+
+    # Move focus to start of plugins list for following list of tests
+    machine.send_key("tab")
+    machine.send_key("tab")
+    machine.screenshot("lss_focus_localised")
+
+  '' + (lib.strings.concatMapStringsSep "\n" (page: ''
+    machine.send_key("tab")
+    machine.send_key("kp_enter")
+  '' + lib.optionalString (page.type == "external") ''
+    with subtest("lomiri system settings ${page.name} localisation works"):
+        machine.wait_for_text(r"(${page.elementLocalised})")
+        machine.screenshot("lss_localised_page_${page.name}")
+  '') settingsPages) + ''
+  '';
+})
diff --git a/nixpkgs/nixos/tests/mealie.nix b/nixpkgs/nixos/tests/mealie.nix
new file mode 100644
index 000000000000..88f749c71294
--- /dev/null
+++ b/nixpkgs/nixos/tests/mealie.nix
@@ -0,0 +1,24 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+
+{
+  name = "mealie";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ litchipi ];
+  };
+
+  nodes = {
+    server = {
+      services.mealie = {
+        enable = true;
+        port = 9001;
+      };
+    };
+  };
+
+  testScript = ''
+    start_all()
+    server.wait_for_unit("mealie.service")
+    server.wait_for_open_port(9001)
+    server.succeed("curl --fail http://localhost:9001")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/monado.nix b/nixpkgs/nixos/tests/monado.nix
new file mode 100644
index 000000000000..8368950951e7
--- /dev/null
+++ b/nixpkgs/nixos/tests/monado.nix
@@ -0,0 +1,39 @@
+import ./make-test-python.nix ({ pkgs, ... }: {
+  name = "monado";
+
+  nodes.machine =
+    { pkgs, ... }:
+
+    {
+      hardware.opengl.enable = true;
+      users.users.alice = {
+        isNormalUser = true;
+        uid = 1000;
+      };
+
+      services.monado = {
+        enable = true;
+        defaultRuntime = true;
+      };
+      # Stop Monado from probing for any hardware
+      systemd.user.services.monado.environment.SIMULATED_ENABLE = "1";
+
+      environment.systemPackages = with pkgs; [ openxr-loader ];
+    };
+
+  testScript = { nodes, ... }:
+    let
+      userId = toString nodes.machine.users.users.alice.uid;
+      runtimePath = "/run/user/${userId}";
+    in
+    ''
+      machine.succeed("loginctl enable-linger alice")
+      machine.wait_for_unit("user@${userId}.service")
+
+      machine.wait_for_unit("monado.socket", "alice")
+      machine.systemctl("start monado.service", "alice")
+      machine.wait_for_unit("monado.service", "alice")
+
+      machine.succeed("su -- alice -c env XDG_RUNTIME_DIR=${runtimePath} openxr_runtime_list")
+    '';
+})
diff --git a/nixpkgs/nixos/tests/plasma6.nix b/nixpkgs/nixos/tests/plasma6.nix
new file mode 100644
index 000000000000..ec5b3f24ef74
--- /dev/null
+++ b/nixpkgs/nixos/tests/plasma6.nix
@@ -0,0 +1,64 @@
+import ./make-test-python.nix ({ pkgs, ...} :
+
+{
+  name = "plasma6";
+  meta = with pkgs.lib.maintainers; {
+    maintainers = [ k900 ];
+  };
+
+  nodes.machine = { ... }:
+
+  {
+    imports = [ ./common/user-account.nix ];
+    services.xserver.enable = true;
+    services.xserver.displayManager.sddm.enable = true;
+    # FIXME: this should be testing Wayland
+    services.xserver.displayManager.defaultSession = "plasmax11";
+    services.xserver.desktopManager.plasma6.enable = true;
+    environment.plasma6.excludePackages = [ pkgs.kdePackages.elisa ];
+    services.xserver.displayManager.autoLogin = {
+      enable = true;
+      user = "alice";
+    };
+  };
+
+  testScript = { nodes, ... }: let
+    user = nodes.machine.users.users.alice;
+    xdo = "${pkgs.xdotool}/bin/xdotool";
+  in ''
+    with subtest("Wait for login"):
+        start_all()
+        machine.wait_for_file("/tmp/xauth_*")
+        machine.succeed("xauth merge /tmp/xauth_*")
+
+    with subtest("Check plasmashell started"):
+        machine.wait_until_succeeds("pgrep plasmashell")
+        machine.wait_for_window("^Desktop ")
+
+    with subtest("Check that KDED is running"):
+        machine.succeed("pgrep kded6")
+
+    with subtest("Ensure Elisa is not installed"):
+        machine.fail("which elisa")
+
+    machine.succeed("su - ${user.name} -c 'xauth merge /tmp/xauth_*'")
+
+    with subtest("Run Dolphin"):
+        machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 dolphin >&2 &'")
+        machine.wait_for_window(" Dolphin")
+
+    with subtest("Run Konsole"):
+        machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 konsole >&2 &'")
+        machine.wait_for_window("Konsole")
+
+    with subtest("Run systemsettings"):
+        machine.execute("su - ${user.name} -c 'DISPLAY=:0.0 systemsettings >&2 &'")
+        machine.wait_for_window("Settings")
+
+    with subtest("Wait to get a screenshot"):
+        machine.execute(
+            "${xdo} key Alt+F1 sleep 10"
+        )
+        machine.screenshot("screen")
+  '';
+})
diff --git a/nixpkgs/nixos/tests/searx.nix b/nixpkgs/nixos/tests/searx.nix
index 2f808cb65266..02a88f690db7 100644
--- a/nixpkgs/nixos/tests/searx.nix
+++ b/nixpkgs/nixos/tests/searx.nix
@@ -36,7 +36,7 @@ import ./make-test-python.nix ({ pkgs, ...} :
   };
 
   # fancy setup: run in uWSGI and use nginx as proxy
-  nodes.fancy = { ... }: {
+  nodes.fancy = { config, ... }: {
     imports = [ ../modules/profiles/minimal.nix ];
 
     services.searx = {
@@ -65,7 +65,7 @@ import ./make-test-python.nix ({ pkgs, ...} :
           include ${pkgs.nginx}/conf/uwsgi_params;
           uwsgi_pass unix:/run/searx/uwsgi.sock;
         '';
-      locations."/searx/static/".alias = "${pkgs.searx}/share/static/";
+      locations."/searx/static/".alias = "${config.services.searx.package}/share/static/";
     };
 
     # allow nginx access to the searx socket
@@ -108,7 +108,7 @@ import ./make-test-python.nix ({ pkgs, ...} :
               "${pkgs.curl}/bin/curl --fail http://localhost/searx >&2"
           )
           fancy.succeed(
-              "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/oscar/js/bootstrap.min.js >&2"
+              "${pkgs.curl}/bin/curl --fail http://localhost/searx/static/themes/simple/js/leaflet.js >&2"
           )
     '';
 })
diff --git a/nixpkgs/nixos/tests/systemd-boot.nix b/nixpkgs/nixos/tests/systemd-boot.nix
index ce3245f3d862..1b7e83253e59 100644
--- a/nixpkgs/nixos/tests/systemd-boot.nix
+++ b/nixpkgs/nixos/tests/systemd-boot.nix
@@ -14,6 +14,72 @@ let
     boot.loader.efi.canTouchEfiVariables = true;
     environment.systemPackages = [ pkgs.efibootmgr ];
   };
+
+  commonXbootldr = { config, lib, pkgs, ... }:
+    let
+      diskImage = import ../lib/make-disk-image.nix {
+        inherit config lib pkgs;
+        label = "nixos";
+        format = "qcow2";
+        partitionTableType = "efixbootldr";
+        touchEFIVars = true;
+        installBootLoader = true;
+      };
+    in
+    {
+      imports = [ common ];
+      virtualisation.useBootLoader = lib.mkForce false; # Only way to tell qemu-vm not to create the default system image
+      virtualisation.directBoot.enable = false; # But don't direct boot either because we're testing systemd-boot
+
+      system.build.diskImage = diskImage; # Use custom disk image with an XBOOTLDR partition
+      virtualisation.efi.variables = "${diskImage}/efi-vars.fd";
+
+      virtualisation.useDefaultFilesystems = false; # Needs custom setup for `diskImage`
+      virtualisation.bootPartition = null;
+      virtualisation.fileSystems = {
+        "/" = {
+          device = "/dev/vda3";
+          fsType = "ext4";
+        };
+        "/boot" = {
+          device = "/dev/vda2";
+          fsType = "vfat";
+          noCheck = true;
+        };
+        "/efi" = {
+          device = "/dev/vda1";
+          fsType = "vfat";
+          noCheck = true;
+        };
+      };
+
+      boot.loader.systemd-boot.enable = true;
+      boot.loader.efi.efiSysMountPoint = "/efi";
+      boot.loader.systemd-boot.xbootldrMountPoint = "/boot";
+    };
+
+  customDiskImage = nodes: ''
+    import os
+    import subprocess
+    import tempfile
+
+    tmp_disk_image = tempfile.NamedTemporaryFile()
+
+    subprocess.run([
+      "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
+      "create",
+      "-f",
+      "qcow2",
+      "-b",
+      "${nodes.machine.system.build.diskImage}/nixos.qcow2",
+      "-F",
+      "qcow2",
+      tmp_disk_image.name,
+    ])
+
+    # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
+    os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
+  '';
 in
 {
   basic = makeTest {
@@ -65,6 +131,32 @@ in
     '';
   };
 
+  basicXbootldr = makeTest {
+    name = "systemd-boot-xbootldr";
+    meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ];
+
+    nodes.machine = commonXbootldr;
+
+    testScript = { nodes, ... }: ''
+      ${customDiskImage nodes}
+
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi")
+      machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+
+      # Ensure we actually booted using systemd-boot
+      # Magic number is the vendor UUID used by systemd-boot.
+      machine.succeed(
+          "test -e /sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"
+      )
+
+      # "bootctl install" should have created an EFI entry
+      machine.succeed('efibootmgr | grep "Linux Boot Manager"')
+    '';
+  };
+
   # Check that specialisations create corresponding boot entries.
   specialisation = makeTest {
     name = "systemd-boot-specialisation";
@@ -184,6 +276,29 @@ in
     '';
   };
 
+  entryFilenameXbootldr = makeTest {
+    name = "systemd-boot-entry-filename-xbootldr";
+    meta.maintainers = with pkgs.lib.maintainers; [ sdht0 ];
+
+    nodes.machine = { pkgs, lib, ... }: {
+      imports = [ commonXbootldr ];
+      boot.loader.systemd-boot.memtest86.enable = true;
+      boot.loader.systemd-boot.memtest86.entryFilename = "apple.conf";
+    };
+
+    testScript = { nodes, ... }: ''
+      ${customDiskImage nodes}
+
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      machine.succeed("test -e /efi/EFI/systemd/systemd-bootx64.efi")
+      machine.fail("test -e /boot/loader/entries/memtest86.conf")
+      machine.succeed("test -e /boot/loader/entries/apple.conf")
+      machine.succeed("test -e /boot/EFI/memtest86/memtest.efi")
+    '';
+  };
+
   extraEntries = makeTest {
     name = "systemd-boot-extra-entries";
     meta.maintainers = with pkgs.lib.maintainers; [ Enzime julienmalka ];