about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/abstractions.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md48
-rw-r--r--nixos/lib/systemd-lib.nix3
-rw-r--r--nixos/modules/config/terminfo.nix2
-rw-r--r--nixos/modules/config/xdg/terminal-exec.nix54
-rw-r--r--nixos/modules/hardware/openrazer.nix21
-rw-r--r--nixos/modules/hardware/video/intel-gpu-tools.nix25
-rw-r--r--nixos/modules/module-list.nix3
-rw-r--r--nixos/modules/profiles/perlless.nix2
-rw-r--r--nixos/modules/programs/fish.nix10
-rw-r--r--nixos/modules/programs/wayland/hyprland.nix18
-rw-r--r--nixos/modules/services/backup/restic.nix19
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/desktop-managers/plasma6.nix9
-rw-r--r--nixos/modules/services/desktops/gnome/gnome-remote-desktop.nix14
-rw-r--r--nixos/modules/services/display-managers/default.nix2
-rw-r--r--nixos/modules/services/hardware/kanata.nix53
-rw-r--r--nixos/modules/services/hardware/udev.nix14
-rw-r--r--nixos/modules/services/matrix/mautrix-signal.nix249
-rw-r--r--nixos/modules/services/misc/portunus.nix14
-rw-r--r--nixos/modules/services/misc/wastebin.nix2
-rw-r--r--nixos/modules/services/misc/zoneminder.nix2
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix4
-rw-r--r--nixos/modules/services/networking/clatd.nix1
-rw-r--r--nixos/modules/services/networking/jotta-cli.md4
-rw-r--r--nixos/modules/services/networking/jotta-cli.nix4
-rw-r--r--nixos/modules/services/networking/networkmanager.nix2
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix13
-rw-r--r--nixos/modules/services/networking/sunshine.nix6
-rw-r--r--nixos/modules/services/security/oauth2-proxy.nix2
-rw-r--r--nixos/modules/services/security/step-ca.nix2
-rw-r--r--nixos/modules/services/system/dbus.nix12
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.md5
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix21
-rw-r--r--nixos/modules/services/web-apps/plausible.nix9
-rw-r--r--nixos/modules/services/web-servers/caddy/default.nix3
-rw-r--r--nixos/modules/services/web-servers/traefik.nix2
-rw-r--r--nixos/modules/services/x11/desktop-managers/cinnamon.nix1
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome.nix4
-rw-r--r--nixos/modules/services/x11/window-managers/clfswm.nix2
-rwxr-xr-xnixos/modules/system/activation/switch-to-configuration.pl3
-rw-r--r--nixos/modules/system/activation/test.nix4
-rw-r--r--nixos/modules/system/activation/top-level.nix23
-rw-r--r--nixos/modules/system/boot/networkd.nix2
-rw-r--r--nixos/modules/tasks/filesystems/zfs.nix4
-rw-r--r--nixos/modules/testing/test-instrumentation.nix2
-rw-r--r--nixos/modules/virtualisation/containers.nix13
-rw-r--r--nixos/modules/virtualisation/incus.nix74
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/archi.nix4
-rw-r--r--nixos/tests/fish.nix2
-rw-r--r--nixos/tests/incus/container.nix101
-rw-r--r--nixos/tests/incus/default.nix2
-rw-r--r--nixos/tests/incus/incusd-options.nix104
-rw-r--r--nixos/tests/incus/preseed.nix63
-rw-r--r--nixos/tests/incus/virtual-machine.nix6
-rw-r--r--nixos/tests/installed-tests/gnome-photos.nix2
-rw-r--r--nixos/tests/jotta-cli.nix2
-rw-r--r--nixos/tests/k3s/single-node.nix12
-rw-r--r--nixos/tests/logrotate.nix90
-rw-r--r--nixos/tests/patroni.nix2
-rw-r--r--nixos/tests/postgresql-jit.nix15
-rw-r--r--nixos/tests/postgresql-wal-receiver.nix202
64 files changed, 976 insertions, 424 deletions
diff --git a/nixos/doc/manual/configuration/abstractions.section.md b/nixos/doc/manual/configuration/abstractions.section.md
index 06356c472ba94..3ff8ac885b653 100644
--- a/nixos/doc/manual/configuration/abstractions.section.md
+++ b/nixos/doc/manual/configuration/abstractions.section.md
@@ -10,14 +10,12 @@ If you find yourself repeating yourself over and over, it’s time to abstract.
         adminAddr = "alice@example.org";
         forceSSL = true;
         enableACME = true;
-        enablePHP = true;
       };
       "wiki.example.org" = {
         documentRoot = "/webroot/wiki.example.org";
         adminAddr = "alice@example.org";
         forceSSL = true;
         enableACME = true;
-        enablePHP = true;
       };
     };
 }
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index af797385b1386..4058f4e504418 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -53,6 +53,10 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi
 - `system.etc.overlay.enable` option was added. If enabled, `/etc` is
   mounted via an overlayfs instead of being created by a custom perl script.
 
+- For each supporting version of the Linux kernel firmware blobs
+  are compressed with zstd. For firmware blobs this means an increase of 4.4% in size, however
+  a significantly higher decompression speed.
+
 - NixOS AMIs are now uploaded regularly to a new AWS Account.
   Instructions on how to use them can be found on <https://nixos.github.io/amis>.
   We are working on integration the data into the NixOS homepage.
@@ -92,7 +96,7 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi
 
 - [PhotonVision](https://photonvision.org/), a free, fast, and easy-to-use computer vision solution for the FIRST® Robotics Competition.
 
-- [clatd](https://github.com/toreanderson/clatd), a a CLAT / SIIT-DC Edge Relay implementation for Linux.
+- [clatd](https://github.com/toreanderson/clatd), a CLAT / SIIT-DC Edge Relay implementation for Linux.
 
 - [pyLoad](https://pyload.net/), a FOSS download manager written in Python. Available as [services.pyload](#opt-services.pyload.enable)
 
@@ -128,12 +132,14 @@ Use `services.pipewire.extraConfig` or `services.pipewire.configPackages` for Pi
 
 - [db-rest](https://github.com/derhuerst/db-rest), a wrapper around Deutsche Bahn's internal API for public transport data. Available as [services.db-rest](#opt-services.db-rest.enable).
 
+- [mautrix-signal](https://github.com/mautrix/signal), a Matrix-Signal puppeting bridge. Available as [services.mautrix-signal](#opt-services.mautrix-signal.enable).
+
 - [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
 The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been marked deprecated and will be dropped after 24.05 due to lack of maintenance of the anki-sync-server software.
 
 - [mautrix-meta](https://github.com/mautrix/meta), a Matrix <-> Facebook and Matrix <-> Instagram hybrid puppeting/relaybot bridge. Available as services.mautrix-meta
 
-- [Jottacloud Command-line Tool](https://docs.jottacloud.com/en/articles/1436834-jottacloud-command-line-tool), a CLI for the [Jottacloud](https://jottacloud.com/) cloud storage provider. Available as [user.services.jotta-cli](#opt-user.services.jotta-cli.enable).
+- [Jottacloud Command-line Tool](https://docs.jottacloud.com/en/articles/1436834-jottacloud-command-line-tool), a CLI for the [Jottacloud](https://jottacloud.com/) cloud storage provider. Available as [services.jotta-cli](#opt-services.jotta-cli.enable).
 
 - [transfer-sh](https://github.com/dutchcoders/transfer.sh), a tool that supports easy and fast file sharing from the command-line. Available as [services.transfer-sh](#opt-services.transfer-sh.enable).
 
@@ -155,6 +161,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [Monado](https://monado.freedesktop.org/), an open source XR runtime. Available as [services.monado](#opt-services.monado.enable).
 
+- [intel-gpu-tools](https://drm.pages.freedesktop.org/igt-gpu-tools), tools for development and testing of the Intel DRM driver. Available as [hardware.intel-gpu-tools](#opt-hardware.intel-gpu-tools.enable)
+
 - [Pretix](https://pretix.eu/about/en/), an open source ticketing software for events. Available as [services.pretix](#opt-services.pretix.enable).
 
 - [microsocks](https://github.com/rofl0r/microsocks), a tiny, portable SOCKS5 server with very moderate resource usage. Available as [services.microsocks](#opt-services.microsocks.enable).
@@ -177,6 +185,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - [ALVR](https://github.com/alvr-org/alvr), a VR desktop streamer. Available as [programs.alvr](#opt-programs.alvr.enable)
 
+- [xdg-terminal-exec](https://github.com/Vladimir-csp/xdg-terminal-exec), the proposed Default Terminal Execution Specification.
+
 - [RustDesk](https://rustdesk.com), a full-featured open source remote control alternative for self-hosting and security with minimal configuration. Alternative to TeamViewer. Available as [services.rustdesk-server](#opt-services.rustdesk-server.enable).
 
 - [Scrutiny](https://github.com/AnalogJ/scrutiny), a S.M.A.R.T monitoring tool for hard disks with a web frontend. Available as [services.scrutiny](#opt-services.scrutiny.enable).
@@ -267,6 +277,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `mongodb-4_4` has been removed as it has reached end of life. Consequently, `unifi7` and `unifi8` now use MongoDB 5.0 by default.
 
+- `mongodb-5_0` and newer requires a cpu with the avx instruction set to run.
+
 - `nitter` requires a `guest_accounts.jsonl` to be provided as a path or loaded into the default location at `/var/lib/nitter/guest_accounts.jsonl`. See [Guest Account Branch Deployment](https://github.com/zedeus/nitter/wiki/Guest-Account-Branch-Deployment) for details.
 
 - `boot.supportedFilesystems` and `boot.initrd.supportedFilesystems` are now attribute sets instead of lists. Assignment from lists as done previously is still supported, but checking whether a filesystem is enabled must now by done using `supportedFilesystems.fs or false` instead of using `lib.elem "fs" supportedFilesystems` as was done previously.
@@ -278,6 +290,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   "mysecret"` becomes `services.aria2.rpcSecretFile = "/path/to/secret_file"`
   where the file `secret_file` contains the string `mysecret`.
 
+- The `system.forbiddenDependenciesRegex` option has been renamed to `system.forbiddenDependenciesRegexes` and now has the type of `listOf string` instead of `string` to accept multiple regexes.
+
 - `openssh`, `openssh_hpn` and `openssh_gssapi` are now compiled without support for the DSA signature algorithm as it is being deprecated upstream. Users still relying on DSA keys should consider upgrading
   to another signature algorithm. However, for the time being it is possible to restore DSA key support using `override` to set `dsaKeysSupport = true`.
 
@@ -317,9 +331,11 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   - Proxies are now defined with a new option `settings.proxies` which takes a list of proxies.
   - Consult the [upstream documentation](https://github.com/fatedier/frp#example-usage) for more details on the changes.
 
-- `mkosi` was updated to v20. Parts of the user interface have changed. Consult the
-  release notes of [v19](https://github.com/systemd/mkosi/releases/tag/v19) and
-  [v20](https://github.com/systemd/mkosi/releases/tag/v20) for a list of changes.
+- `mkosi` was updated to v22. Parts of the user interface have changed. Consult the
+  release notes of [v19](https://github.com/systemd/mkosi/releases/tag/v19),
+  [v20](https://github.com/systemd/mkosi/releases/tag/v20),
+  [v21](https://github.com/systemd/mkosi/releases/tag/v21) and
+  [v22](https://github.com/systemd/mkosi/releases/tag/v22) for a list of changes.
 
 - `gonic` has been updated to v0.16.4. Config now requires `playlists-path` to be set. See the rest of the [v0.16.0 release notes](https://github.com/sentriz/gonic/releases/tag/v0.16.0) for more details.
 
@@ -349,7 +365,7 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `optparse-bash` is now dropped due to upstream inactivity. Alternatives available in Nixpkgs include [`argc`](https://github.com/sigoden/argc), [`argbash`](https://github.com/matejak/argbash), [`bashly`](https://github.com/DannyBen/bashly) and [`gum`](https://github.com/charmbracelet/gum), to name a few.
 
-- `kanata` package has been updated to v1.6.0, which includes breaking changes.  Check out the changelog of [v1.5.0](https://github.com/jtroo/kanata/releases/tag/v1.5.0) and [v1.6.0](https://github.com/jtroo/kanata/releases/tag/v1.6.0) for details.
+- `kanata` package has been updated to v1.6.1, which includes breaking changes.  Check out the changelog of [v1.5.0](https://github.com/jtroo/kanata/releases/tag/v1.5.0) and [v1.6.0](https://github.com/jtroo/kanata/releases/tag/v1.6.0) for details.
 
 - `craftos-pc` package has been updated to v2.8, which includes [breaking changes](https://github.com/MCJack123/craftos2/releases/tag/v2.8).
   - Files are now handled in binary mode; this could break programs with embedded UTF-8 characters.
@@ -366,7 +382,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   - If [`system.stateVersion`](#opt-system.stateVersion) is >=24.05, `pkgs.nextcloud29` will be installed by default.
   - If [`system.stateVersion`](#opt-system.stateVersion) is >=23.11, `pkgs.nextcloud27` will be installed by default.
   - Please note that an upgrade from v27 (or older) to v29 directly is not possible. Please upgrade to `nextcloud28` (or earlier) first. Nextcloud prohibits skipping major versions while upgrading. You can upgrade by declaring [`services.nextcloud.package = pkgs.nextcloud28;`](options.html#opt-services.nextcloud.package).
-  - Known warnings after the upgrade are documented in [](#module-services-nextcloud-known-warnings).
+  - Known warnings after the upgrade are documented in [](#module-services-nextcloud-known-warnings) from now on.
+  - The "Photos" app only displays Media from inside the `Photos` directory. This can be changed manually in the "Photos" tab below "Photos settings".
 
 - The vendored third party libraries have been mostly removed from `cudaPackages.nsight_systems`, which we now only ship for `cudaPackages_11_8` and later due to outdated dependencies. Users comfortable with the vendored dependencies may use `overrideAttrs` to amend the `postPatch` phase and the `meta.broken` correspondingly. Alternatively, one could package the deprecated `boost170` locally, as required for `cudaPackages_11_4.nsight_systems`.
 
@@ -505,6 +522,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 
 - `erlang-ls` package no longer ships the `els_dap` binary as of v0.51.0.
 
+- `icu` no longer includes `install-sh` and `mkinstalldirs` in the shared folder.
+
 ## Other Notable Changes {#sec-release-24.05-notable-changes}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -527,6 +546,17 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
 - `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 ];``;
 
+- `services.openssh` now has an option `authorizedKeysInHomedir`, controlling whether `~/.ssh/authorizedKeys` is
+  added to `authorizedKeysFiles`.
+  ::: {.note}
+  This option currently defaults to `true` for NixOS 24.05, preserving the previous behaviour.
+  This is expected to change in NixOS 24.11.
+  :::
+  ::: {.warning}
+  Users should check that their SSH keys are in `users.users.*.openssh`, or that they have another way to access
+  and administer the system, before setting this option to `false`.
+  :::
+
 - [`matrix-synapse`](https://element-hq.github.io/synapse/) homeserver module now supports configuring UNIX domain socket [`listeners`](#opt-services.matrix-synapse.settings.listeners) through the `path` option.
   The default replication worker on the main instance has been migrated away from TCP sockets to UNIX domain sockets.
 
@@ -659,6 +689,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   making it possible to accumulate definitions without resorting to `mkForce`,
   hence to retain the definitions not anticipating that need.
 
+- Lisp modules: previously deprecated interface based on `common-lisp.sh` has now been removed.
+
 - `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).
@@ -672,3 +704,5 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   This enables mandoc to find manual pages in Nix profiles. To set the manual search paths via the `mandoc.conf` configuration file like before, use `documentation.man.mandoc.settings.manpath` instead.
 
 - `grafana-loki` package was updated to 3.0.0 which includes [breaking changes](https://github.com/grafana/loki/releases/tag/v3.0.0)
+
+- `programs.fish.package` now allows you to override the package used in the `fish` module
diff --git a/nixos/lib/systemd-lib.nix b/nixos/lib/systemd-lib.nix
index eef49f8c4ef38..0641da8e77517 100644
--- a/nixos/lib/systemd-lib.nix
+++ b/nixos/lib/systemd-lib.nix
@@ -18,6 +18,7 @@ let
     flip
     head
     isInt
+    isFloat
     isList
     isPath
     length
@@ -152,7 +153,7 @@ in rec {
       "Systemd ${group} field `${name}' is outside the range [${toString min},${toString max}]";
 
   assertRangeOrOneOf = name: min: max: values: group: attr:
-    optional (attr ? ${name} && !((min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values))
+    optional (attr ? ${name} && !(((isInt attr.${name} || isFloat attr.${name}) && min <= attr.${name} && max >= attr.${name}) || elem attr.${name} values))
       "Systemd ${group} field `${name}' is not a value in range [${toString min},${toString max}], or one of ${toString values}";
 
   assertMinimum = name: min: group: attr:
diff --git a/nixos/modules/config/terminfo.nix b/nixos/modules/config/terminfo.nix
index 4b58605aa7f1a..b538d749ffcbf 100644
--- a/nixos/modules/config/terminfo.nix
+++ b/nixos/modules/config/terminfo.nix
@@ -31,7 +31,7 @@ with lib;
     # attrNames (filterAttrs
     #  (_: drv: (builtins.tryEval (isDerivation drv && drv ? terminfo)).value)
     #  pkgs)
-    environment.systemPackages = mkIf config.environment.enableAllTerminfo (map (x: x.terminfo) (with pkgs; [
+    environment.systemPackages = mkIf config.environment.enableAllTerminfo (map (x: x.terminfo) (with pkgs.pkgsBuildBuild; [
       alacritty
       contour
       foot
diff --git a/nixos/modules/config/xdg/terminal-exec.nix b/nixos/modules/config/xdg/terminal-exec.nix
new file mode 100644
index 0000000000000..daf2055d2e906
--- /dev/null
+++ b/nixos/modules/config/xdg/terminal-exec.nix
@@ -0,0 +1,54 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.xdg.terminal-exec;
+  inherit (lib) mkIf mkEnableOption mkOption mkPackageOption types;
+in
+{
+  meta.maintainers = with lib.maintainers; [ Cryolitia ];
+
+  ###### interface
+
+  options = {
+    xdg.terminal-exec = {
+      enable = mkEnableOption "xdg-terminal-exec, the [proposed](https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/46) Default Terminal Execution Specification";
+      package = mkPackageOption pkgs "xdg-terminal-exec" { };
+      settings = mkOption {
+        type = with types; attrsOf (listOf str);
+        default = { };
+        description = ''
+          Configuration options for the Default Terminal Execution Specification.
+
+          The keys are the desktop environments that are matched (case-insensitively) against `$XDG_CURRENT_DESKTOP`,
+          or `default` which is used when the current desktop environment is not found in the configuration.
+          The values are a list of terminals' [desktop file IDs](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s02.html#desktop-file-id) to try in order of decreasing priority.
+        '';
+        example = {
+          default = [ "kitty.desktop" ];
+          GNOME = [ "com.raggesilver.BlackBox.desktop" "org.gnome.Terminal.desktop" ];
+        };
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment = {
+      systemPackages = [ cfg.package ];
+
+      etc = lib.mapAttrs' (
+        desktop: terminals:
+        # map desktop name such as GNOME to `xdg/gnome-xdg-terminals.list`, default to `xdg/xdg-terminals.list`
+        lib.nameValuePair (
+          "xdg/${if desktop == "default" then "" else "${lib.toLower desktop}-"}xdg-terminals.list"
+        ) { text = lib.concatLines terminals; }
+      ) cfg.settings;
+    };
+  };
+}
diff --git a/nixos/modules/hardware/openrazer.nix b/nixos/modules/hardware/openrazer.nix
index 5ba6abfdb3d7e..6f61254a60c1f 100644
--- a/nixos/modules/hardware/openrazer.nix
+++ b/nixos/modules/hardware/openrazer.nix
@@ -19,7 +19,7 @@ let
       [Startup]
       sync_effects_enabled = ${toPyBoolStr cfg.syncEffectsEnabled}
       devices_off_on_screensaver = ${toPyBoolStr cfg.devicesOffOnScreensaver}
-      battery_notifier = ${toPyBoolStr (cfg.mouseBatteryNotifier || cfg.batteryNotifier.enable)}
+      battery_notifier = ${toPyBoolStr cfg.batteryNotifier.enable}
       battery_notifier_freq = ${builtins.toString cfg.batteryNotifier.frequency}
       battery_notifier_percent = ${builtins.toString cfg.batteryNotifier.percentage}
 
@@ -80,14 +80,6 @@ in
         '';
       };
 
-      mouseBatteryNotifier = mkOption {
-        type = types.bool;
-        default = true;
-        description = ''
-          Mouse battery notifier.
-        '';
-      };
-
       batteryNotifier = mkOption {
         description = ''
           Settings for device battery notifications.
@@ -143,14 +135,11 @@ in
     };
   };
 
-  config = mkIf cfg.enable {
-    warnings = flatten [
-      (optional cfg.mouseBatteryNotifier ''
-        The option openrazer.mouseBatteryNotifier is deprecated.
-        Please use openrazer.batteryNotifier instead to enable and configure battery notifications.
-      '')
-    ];
+  imports = [
+    (mkRenamedOptionModule [ "hardware" "openrazer" "mouseBatteryNotifier" ] [ "hardware" "openrazer" "batteryNotifier" "enable" ])
+  ];
 
+  config = mkIf cfg.enable {
     boot.extraModulePackages = [ kernelPackages.openrazer ];
     boot.kernelModules = drivers;
 
diff --git a/nixos/modules/hardware/video/intel-gpu-tools.nix b/nixos/modules/hardware/video/intel-gpu-tools.nix
new file mode 100644
index 0000000000000..b69fefcae1184
--- /dev/null
+++ b/nixos/modules/hardware/video/intel-gpu-tools.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.intel-gpu-tools;
+in
+{
+  options = {
+    hardware.intel-gpu-tools = {
+      enable = lib.mkEnableOption "a setcap wrapper for intel-gpu-tools";
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    security.wrappers.intel_gpu_top = {
+      owner = "root";
+      group = "root";
+      source = "${pkgs.intel-gpu-tools}/bin/intel_gpu_top";
+      capabilities = "cap_perfmon+ep";
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ kira-bruneau ];
+  };
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 111b5c129cb3c..76ccf5a341094 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -45,6 +45,7 @@
   ./config/xdg/portals/lxqt.nix
   ./config/xdg/portals/wlr.nix
   ./config/xdg/sounds.nix
+  ./config/xdg/terminal-exec.nix
   ./config/zram.nix
   ./hardware/acpilight.nix
   ./hardware/all-firmware.nix
@@ -106,6 +107,7 @@
   ./hardware/video/bumblebee.nix
   ./hardware/video/capture/mwprocapture.nix
   ./hardware/video/displaylink.nix
+  ./hardware/video/intel-gpu-tools.nix
   ./hardware/video/nvidia.nix
   ./hardware/video/switcheroo-control.nix
   ./hardware/video/uvcvideo/default.nix
@@ -664,6 +666,7 @@
   ./services/matrix/maubot.nix
   ./services/matrix/mautrix-facebook.nix
   ./services/matrix/mautrix-meta.nix
+  ./services/matrix/mautrix-signal.nix
   ./services/matrix/mautrix-telegram.nix
   ./services/matrix/mautrix-whatsapp.nix
   ./services/matrix/mjolnir.nix
diff --git a/nixos/modules/profiles/perlless.nix b/nixos/modules/profiles/perlless.nix
index 90abd14f077e4..010e4f8f2a28e 100644
--- a/nixos/modules/profiles/perlless.nix
+++ b/nixos/modules/profiles/perlless.nix
@@ -26,6 +26,6 @@
 
   # Check that the system does not contain a Nix store path that contains the
   # string "perl".
-  system.forbiddenDependenciesRegex = "perl";
+  system.forbiddenDependenciesRegexes = ["perl"];
 
 }
diff --git a/nixos/modules/programs/fish.nix b/nixos/modules/programs/fish.nix
index 2102a07cd0bca..ebc16f6ff0129 100644
--- a/nixos/modules/programs/fish.nix
+++ b/nixos/modules/programs/fish.nix
@@ -55,6 +55,8 @@ in
         type = types.bool;
       };
 
+      package = mkPackageOption pkgs "fish" { };
+
       useBabelfish = mkOption {
         type = types.bool;
         default = false;
@@ -244,8 +246,8 @@ in
           patchedGenerator = pkgs.stdenv.mkDerivation {
             name = "fish_patched-completion-generator";
             srcs = [
-              "${pkgs.fish}/share/fish/tools/create_manpage_completions.py"
-              "${pkgs.fish}/share/fish/tools/deroff.py"
+              "${cfg.package}/share/fish/tools/create_manpage_completions.py"
+              "${cfg.package}/share/fish/tools/deroff.py"
             ];
             unpackCmd = "cp $curSrc $(basename $curSrc)";
             sourceRoot = ".";
@@ -287,12 +289,12 @@ in
         ++ optional cfg.vendor.functions.enable "/share/fish/vendor_functions.d";
       }
 
-      { systemPackages = [ pkgs.fish ]; }
+      { systemPackages = [ cfg.package ]; }
 
       {
         shells = [
           "/run/current-system/sw/bin/fish"
-          "${pkgs.fish}/bin/fish"
+          (lib.getExe cfg.package)
         ];
       }
     ];
diff --git a/nixos/modules/programs/wayland/hyprland.nix b/nixos/modules/programs/wayland/hyprland.nix
index e648eaa1b68e4..5891ff25e4375 100644
--- a/nixos/modules/programs/wayland/hyprland.nix
+++ b/nixos/modules/programs/wayland/hyprland.nix
@@ -42,15 +42,6 @@ in
 
     xwayland.enable = mkEnableOption ("XWayland") // { default = true; };
 
-    envVars.enable = mkEnableOption null // {
-      default = true;
-      example = false;
-      description = ''
-        Set environment variables for Hyprland to work properly.
-        Enabled by default.
-      '';
-    };
-
     systemd.setPath.enable = mkEnableOption null // {
       default = true;
       example = false;
@@ -83,15 +74,6 @@ in
       configPackages = mkDefault [ cfg.finalPackage ];
     };
 
-    environment.sessionVariables = mkIf cfg.envVars.enable {
-      XDG_CURRENT_DESKTOP = "Hyprland";
-      XDG_SESSION_DESKTOP = "Hyprland";
-      XDG_SESSION_TYPE = "wayland";
-      GDK_BACKEND = "wayland,x11";
-      QT_QPA_PLATFORM = "wayland;xcb";
-      _JAVA_AWT_WM_NONREPARENTING = "1"; # Fix for Java applications on tiling window managers
-    };
-
     systemd = mkIf cfg.systemd.setPath.enable {
       user.extraConfig = ''
         DefaultEnvironment="PATH=$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/%u/bin:/run/wrappers/bin"
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index 8b56636c79695..8be2649189b95 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -11,7 +11,7 @@ in
     description = ''
       Periodic backups to create with Restic.
     '';
-    type = types.attrsOf (types.submodule ({ config, name, ... }: {
+    type = types.attrsOf (types.submodule ({ name, ... }: {
       options = {
         passwordFile = mkOption {
           type = types.str;
@@ -206,12 +206,19 @@ in
           ];
         };
 
+        runCheck = mkOption {
+          type = types.bool;
+          default = (builtins.length config.services.restic.backups.${name}.checkOpts > 0);
+          defaultText = literalExpression ''builtins.length config.services.backups.${name}.checkOpts > 0'';
+          description = "Whether to run the `check` command with the provided `checkOpts` options.";
+          example = true;
+        };
+
         checkOpts = mkOption {
           type = types.listOf types.str;
           default = [ ];
           description = ''
-            A list of options for 'restic check', which is run after
-            pruning.
+            A list of options for 'restic check'.
           '';
           example = [
             "--with-cache"
@@ -298,7 +305,9 @@ in
             doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != []);
             pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [
               (resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts))
-              (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
+            ];
+            checkCmd = optionals backup.runCheck [
+                (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
             ];
             # Helper functions for rclone remotes
             rcloneRemoteName = builtins.elemAt (splitString ":" backup.repository) 1;
@@ -331,7 +340,7 @@ in
             serviceConfig = {
               Type = "oneshot";
               ExecStart = (optionals doBackup [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ])
-                ++ pruneCmd;
+                ++ pruneCmd ++ checkCmd;
               User = backup.user;
               RuntimeDirectory = "restic-backups-${name}";
               CacheDirectory = "restic-backups-${name}";
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 35d3ba0aa2094..8a9d8c210b34d 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -37,7 +37,7 @@ let
       #     package = pkgs.postgresql_<major>;
       #   };
       # works.
-      base = if cfg.enableJIT then cfg.package.withJIT else cfg.package;
+      base = if cfg.enableJIT then cfg.package.withJIT else cfg.package.withoutJIT;
     in
     if cfg.extraPlugins == []
       then base
diff --git a/nixos/modules/services/desktop-managers/plasma6.nix b/nixos/modules/services/desktop-managers/plasma6.nix
index 08507b4d370a7..842b0716b928d 100644
--- a/nixos/modules/services/desktop-managers/plasma6.nix
+++ b/nixos/modules/services/desktop-managers/plasma6.nix
@@ -286,6 +286,15 @@ in {
       kde-smartcard = lib.mkIf config.security.pam.p11.enable { p11Auth = true; };
     };
 
+    security.wrappers = {
+      kwin_wayland = {
+        owner = "root";
+        group = "root";
+        capabilities = "cap_sys_nice+ep";
+        source = "${lib.getBin pkgs.kdePackages.kwin}/bin/kwin_wayland";
+      };
+    };
+
     programs.dconf.enable = true;
 
     programs.firefox.nativeMessagingHosts.packages = [kdePackages.plasma-browser-integration];
diff --git a/nixos/modules/services/desktops/gnome/gnome-remote-desktop.nix b/nixos/modules/services/desktops/gnome/gnome-remote-desktop.nix
index b56027b6eb4be..d81a9edfa1266 100644
--- a/nixos/modules/services/desktops/gnome/gnome-remote-desktop.nix
+++ b/nixos/modules/services/desktops/gnome/gnome-remote-desktop.nix
@@ -16,7 +16,21 @@
   ###### implementation
   config = lib.mkIf config.services.gnome.gnome-remote-desktop.enable {
     services.pipewire.enable = true;
+    services.dbus.packages = [ pkgs.gnome.gnome-remote-desktop ];
+
+    environment.systemPackages = [ pkgs.gnome.gnome-remote-desktop ];
 
     systemd.packages = [ pkgs.gnome.gnome-remote-desktop ];
+    systemd.tmpfiles.packages = [ pkgs.gnome.gnome-remote-desktop ];
+
+    # TODO: if possible, switch to using provided g-r-d sysusers.d
+    users = {
+      users.gnome-remote-desktop = {
+        isSystemUser = true;
+        group = "gnome-remote-desktop";
+        home = "/var/lib/gnome-remote-desktop";
+      };
+      groups.gnome-remote-desktop = { };
+    };
   };
 }
diff --git a/nixos/modules/services/display-managers/default.nix b/nixos/modules/services/display-managers/default.nix
index 005ae8f1c8a58..6fa8556e39bee 100644
--- a/nixos/modules/services/display-managers/default.nix
+++ b/nixos/modules/services/display-managers/default.nix
@@ -116,7 +116,7 @@ in
             lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d config.services.displayManager.sessionData.sessionNames)) ''
                 Default graphical session, '${d}', not found.
                 Valid names for 'services.displayManager.defaultSession' are:
-                  ${lib.concatStringsSep "\n  " cfg.displayManager.sessionData.sessionNames}
+                  ${lib.concatStringsSep "\n  " cfg.sessionData.sessionNames}
               '';
         };
         default = null;
diff --git a/nixos/modules/services/hardware/kanata.nix b/nixos/modules/services/hardware/kanata.nix
index 333b2d2a88a55..46af3e36b9859 100644
--- a/nixos/modules/services/hardware/kanata.nix
+++ b/nixos/modules/services/hardware/kanata.nix
@@ -5,6 +5,8 @@ with lib;
 let
   cfg = config.services.kanata;
 
+  upstreamDoc = "See [the upstream documentation](https://github.com/jtroo/kanata/blob/main/docs/config.adoc) and [example config files](https://github.com/jtroo/kanata/tree/main/cfg_samples) for more information.";
+
   keyboard = {
     options = {
       devices = mkOption {
@@ -22,28 +24,16 @@ let
         type = types.lines;
         example = ''
           (defsrc
-            grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
-            tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
-            caps a    s    d    f    g    h    j    k    l    ;    '    ret
-            lsft z    x    c    v    b    n    m    ,    .    /    rsft
-            lctl lmet lalt           spc            ralt rmet rctl)
-
-          (deflayer qwerty
-            grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
-            tab  q    w    e    r    t    y    u    i    o    p    [    ]    \
-            @cap a    s    d    f    g    h    j    k    l    ;    '    ret
-            lsft z    x    c    v    b    n    m    ,    .    /    rsft
-            lctl lmet lalt           spc            ralt rmet rctl)
-
-          (defalias
-            ;; tap within 100ms for capslk, hold more than 100ms for lctl
-            cap (tap-hold 100 100 caps lctl))
+            caps)
+
+          (deflayermap (default-layer)
+            ;; tap caps lock as caps lock, hold caps lock as left control
+            caps (tap-hold 100 100 caps lctl))
         '';
         description = ''
           Configuration other than `defcfg`.
 
-          See [example config files](https://github.com/jtroo/kanata)
-          for more information.
+          ${upstreamDoc}
         '';
       };
       extraDefCfg = mkOption {
@@ -55,8 +45,7 @@ let
           from the devices option) and
           `linux-continue-if-no-devs-found` (hardcoded to be yes).
 
-          See [example config files](https://github.com/jtroo/kanata)
-          for more information.
+          ${upstreamDoc}
         '';
       };
       extraArgs = mkOption {
@@ -86,14 +75,20 @@ let
     in
     optionalString ((length devices) > 0) "linux-dev (${devicesString})";
 
-  mkConfig = name: keyboard: pkgs.writeText "${mkName name}-config.kdb" ''
-    (defcfg
-      ${keyboard.extraDefCfg}
-      ${mkDevices keyboard.devices}
-      linux-continue-if-no-devs-found yes)
-
-    ${keyboard.config}
-  '';
+  mkConfig = name: keyboard: pkgs.writeTextFile {
+    name = "${mkName name}-config.kdb";
+    text = ''
+      (defcfg
+        ${keyboard.extraDefCfg}
+        ${mkDevices keyboard.devices}
+        linux-continue-if-no-devs-found yes)
+
+      ${keyboard.config}
+    '';
+    checkPhase = ''
+      ${getExe cfg.package} --cfg "$target" --check --debug
+    '';
+  };
 
   mkService = name: keyboard: nameValuePair (mkName name) {
     wantedBy = [ "multi-user.target" ];
@@ -153,7 +148,7 @@ in
   options.services.kanata = {
     enable = mkEnableOption "kanata, a tool to improve keyboard comfort and usability with advanced customization";
     package = mkPackageOption pkgs "kanata" {
-      example = "kanata-with-cmd";
+      example = [ "kanata-with-cmd" ];
       extraDescription = ''
         ::: {.note}
         If {option}`danger-enable-cmd` is enabled in any of the keyboards, the
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 3db6616442816..62603d20e2d30 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -167,10 +167,16 @@ let
       mv etc/udev/hwdb.bin $out
     '';
 
-  compressFirmware = firmware: if (config.boot.kernelPackages.kernelAtLeast "5.3" && (firmware.compressFirmware or true)) then
-    pkgs.compressFirmwareXz firmware
-  else
-    id firmware;
+  compressFirmware = firmware:
+    let
+      inherit (config.boot.kernelPackages) kernelAtLeast;
+    in
+      if ! (firmware.compressFirmware or true) then
+        firmware
+      else
+        if kernelAtLeast "5.19" then pkgs.compressFirmwareZstd firmware
+        else if kernelAtLeast "5.3" then pkgs.compressFirmwareXz firmware
+        else firmware;
 
   # Udev has a 512-character limit for ENV{PATH}, so create a symlink
   # tree to work around this.
diff --git a/nixos/modules/services/matrix/mautrix-signal.nix b/nixos/modules/services/matrix/mautrix-signal.nix
new file mode 100644
index 0000000000000..faca10551abb6
--- /dev/null
+++ b/nixos/modules/services/matrix/mautrix-signal.nix
@@ -0,0 +1,249 @@
+{ lib
+, config
+, pkgs
+, ...
+}:
+let
+  cfg = config.services.mautrix-signal;
+  dataDir = "/var/lib/mautrix-signal";
+  registrationFile = "${dataDir}/signal-registration.yaml";
+  settingsFile = "${dataDir}/config.yaml";
+  settingsFileUnsubstituted = settingsFormat.generate "mautrix-signal-config-unsubstituted.json" cfg.settings;
+  settingsFormat = pkgs.formats.json { };
+  appservicePort = 29328;
+
+  # to be used with a list of lib.mkIf values
+  optOneOf = lib.lists.findFirst (value: value.condition) (lib.mkIf false null);
+  mkDefaults = lib.mapAttrsRecursive (n: v: lib.mkDefault v);
+  defaultConfig = {
+    homeserver.address = "http://localhost:8448";
+    appservice = {
+      hostname = "[::]";
+      port = appservicePort;
+      database.type = "sqlite3";
+      database.uri = "file:${dataDir}/mautrix-signal.db?_txlock=immediate";
+      id = "signal";
+      bot = {
+        username = "signalbot";
+        displayname = "Signal Bridge Bot";
+      };
+      as_token = "";
+      hs_token = "";
+    };
+    bridge = {
+      username_template = "signal_{{.}}";
+      displayname_template = "{{or .ProfileName .PhoneNumber \"Unknown user\"}}";
+      double_puppet_server_map = { };
+      login_shared_secret_map = { };
+      command_prefix = "!signal";
+      permissions."*" = "relay";
+      relay.enabled = true;
+    };
+    logging = {
+      min_level = "info";
+      writers = lib.singleton {
+        type = "stdout";
+        format = "pretty-colored";
+        time_format = " ";
+      };
+    };
+  };
+
+in
+{
+  options.services.mautrix-signal = {
+    enable = lib.mkEnableOption "mautrix-signal, a Matrix-Signal puppeting bridge.";
+
+    settings = lib.mkOption {
+      apply = lib.recursiveUpdate defaultConfig;
+      type = settingsFormat.type;
+      default = defaultConfig;
+      description = ''
+        {file}`config.yaml` configuration as a Nix attribute set.
+        Configuration options should match those described in
+        [example-config.yaml](https://github.com/mautrix/signal/blob/master/example-config.yaml).
+        Secret tokens should be specified using {option}`environmentFile`
+        instead of this world-readable attribute set.
+      '';
+      example = {
+        appservice = {
+          database = {
+            type = "postgres";
+            uri = "postgresql:///mautrix_signal?host=/run/postgresql";
+          };
+          id = "signal";
+          ephemeral_events = false;
+        };
+        bridge = {
+          history_sync = {
+            request_full_sync = true;
+          };
+          private_chat_portal_meta = true;
+          mute_bridging = true;
+          encryption = {
+            allow = true;
+            default = true;
+            require = true;
+          };
+          provisioning = {
+            shared_secret = "disable";
+          };
+          permissions = {
+            "example.com" = "user";
+          };
+        };
+      };
+    };
+
+    environmentFile = lib.mkOption {
+      type = lib.types.nullOr lib.types.path;
+      default = null;
+      description = ''
+        File containing environment variables to be passed to the mautrix-signal service.
+        If an environment variable `MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET` is set,
+        then its value will be used in the configuration file for the option
+        `login_shared_secret_map` without leaking it to the store, using the configured
+        `homeserver.domain` as key.
+        See [here](https://github.com/mautrix/signal/blob/main/example-config.yaml)
+        for the documentation of `login_shared_secret_map`.
+      '';
+    };
+
+    serviceDependencies = lib.mkOption {
+      type = with lib.types; listOf str;
+      default = (lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit)
+        ++ (lib.optional config.services.matrix-conduit.enable "conduit.service");
+      defaultText = lib.literalExpression ''
+        (optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit)
+        ++ (optional config.services.matrix-conduit.enable "conduit.service")
+      '';
+      description = ''
+        List of systemd units to require and wait for when starting the application service.
+      '';
+    };
+
+    registerToSynapse = lib.mkOption {
+      type = lib.types.bool;
+      default = config.services.matrix-synapse.enable;
+      defaultText = lib.literalExpression ''
+        config.services.matrix-synapse.enable
+      '';
+      description = ''
+        Whether to add the bridge's app service registration file to
+        `services.matrix-synapse.settings.app_service_config_files`.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    users.users.mautrix-signal = {
+      isSystemUser = true;
+      group = "mautrix-signal";
+      home = dataDir;
+      description = "Mautrix-Signal bridge user";
+    };
+
+    users.groups.mautrix-signal = { };
+
+    services.matrix-synapse = lib.mkIf cfg.registerToSynapse {
+      settings.app_service_config_files = [ registrationFile ];
+    };
+    systemd.services.matrix-synapse = lib.mkIf cfg.registerToSynapse {
+      serviceConfig.SupplementaryGroups = [ "mautrix-signal" ];
+    };
+
+    # Note: this is defined here to avoid the docs depending on `config`
+    services.mautrix-signal.settings.homeserver = optOneOf (with config.services; [
+      (lib.mkIf matrix-synapse.enable (mkDefaults {
+        domain = matrix-synapse.settings.server_name;
+      }))
+      (lib.mkIf matrix-conduit.enable (mkDefaults {
+        domain = matrix-conduit.settings.global.server_name;
+        address = "http://localhost:${toString matrix-conduit.settings.global.port}";
+      }))
+    ]);
+
+    systemd.services.mautrix-signal = {
+      description = "mautrix-signal, a Matrix-Signal puppeting bridge.";
+
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      after = [ "network-online.target" ] ++ cfg.serviceDependencies;
+      # ffmpeg is required for conversion of voice messages
+      path = [ pkgs.ffmpeg-headless ];
+
+      preStart = ''
+        # substitute the settings file by environment variables
+        # in this case read from EnvironmentFile
+        test -f '${settingsFile}' && rm -f '${settingsFile}'
+        old_umask=$(umask)
+        umask 0177
+        ${pkgs.envsubst}/bin/envsubst \
+          -o '${settingsFile}' \
+          -i '${settingsFileUnsubstituted}'
+        umask $old_umask
+
+        # generate the appservice's registration file if absent
+        if [ ! -f '${registrationFile}' ]; then
+          ${pkgs.mautrix-signal}/bin/mautrix-signal \
+            --generate-registration \
+            --config='${settingsFile}' \
+            --registration='${registrationFile}'
+        fi
+        chmod 640 ${registrationFile}
+
+        umask 0177
+        # 1. Overwrite registration tokens in config
+        # 2. If environment variable MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET
+        #    is set, set it as the login shared secret value for the configured
+        #    homeserver domain.
+        ${pkgs.yq}/bin/yq -s '.[0].appservice.as_token = .[1].as_token
+          | .[0].appservice.hs_token = .[1].hs_token
+          | .[0]
+          | if env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET then .bridge.login_shared_secret_map.[.homeserver.domain] = env.MAUTRIX_SIGNAL_BRIDGE_LOGIN_SHARED_SECRET else . end' \
+          '${settingsFile}' '${registrationFile}' > '${settingsFile}.tmp'
+        mv '${settingsFile}.tmp' '${settingsFile}'
+        umask $old_umask
+      '';
+
+      serviceConfig = {
+        User = "mautrix-signal";
+        Group = "mautrix-signal";
+        EnvironmentFile = cfg.environmentFile;
+        StateDirectory = baseNameOf dataDir;
+        WorkingDirectory = dataDir;
+        ExecStart = ''
+          ${pkgs.mautrix-signal}/bin/mautrix-signal \
+          --config='${settingsFile}' \
+          --registration='${registrationFile}'
+        '';
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectSystem = "strict";
+        Restart = "on-failure";
+        RestartSec = "30s";
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SystemCallArchitectures = "native";
+        SystemCallErrorNumber = "EPERM";
+        SystemCallFilter = [ "@system-service" ];
+        Type = "simple";
+        UMask = 0027;
+      };
+      restartTriggers = [ settingsFileUnsubstituted ];
+    };
+  };
+  meta.maintainers = with lib.maintainers; [ niklaskorz ];
+}
diff --git a/nixos/modules/services/misc/portunus.nix b/nixos/modules/services/misc/portunus.nix
index ab78479c96cd3..bdb35da788e3a 100644
--- a/nixos/modules/services/misc/portunus.nix
+++ b/nixos/modules/services/misc/portunus.nix
@@ -231,12 +231,14 @@ in
     };
 
     systemd.services = {
-      dex.serviceConfig = mkIf cfg.dex.enable {
-        # `dex.service` is super locked down out of the box, but we need some
-        # place to write the SQLite database. This creates $STATE_DIRECTORY below
-        # /var/lib/private because DynamicUser=true, but it gets symlinked into
-        # /var/lib/dex inside the unit
-        StateDirectory = "dex";
+      dex = mkIf cfg.dex.enable {
+        serviceConfig = {
+          # `dex.service` is super locked down out of the box, but we need some
+          # place to write the SQLite database. This creates $STATE_DIRECTORY below
+          # /var/lib/private because DynamicUser=true, but it gets symlinked into
+          # /var/lib/dex inside the unit
+          StateDirectory = "dex";
+        };
       };
 
       portunus = {
diff --git a/nixos/modules/services/misc/wastebin.nix b/nixos/modules/services/misc/wastebin.nix
index 3d0af2862683d..f24bf94fa52bb 100644
--- a/nixos/modules/services/misc/wastebin.nix
+++ b/nixos/modules/services/misc/wastebin.nix
@@ -10,7 +10,7 @@ in
 
   options.services.wastebin = {
 
-    enable = mkEnableOption "Wastenbin pastebin service";
+    enable = mkEnableOption "Wastebin, a pastebin service";
 
     package = mkPackageOption pkgs "wastebin" { };
 
diff --git a/nixos/modules/services/misc/zoneminder.nix b/nixos/modules/services/misc/zoneminder.nix
index 84c3a6710c0db..d09cd87febfff 100644
--- a/nixos/modules/services/misc/zoneminder.nix
+++ b/nixos/modules/services/misc/zoneminder.nix
@@ -350,7 +350,7 @@ in {
           RestartSec = "10s";
           CacheDirectory = dirs cacheDirs;
           RuntimeDirectory = dirName;
-          ReadWriteDirectories = lib.mkIf useCustomDir [ cfg.storageDir ];
+          ReadWritePaths = lib.mkIf useCustomDir [ cfg.storageDir ];
           StateDirectory = dirs (lib.optionals (!useCustomDir) libDirs);
           LogsDirectory = dirName;
           PrivateTmp = true;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
index e44140b1f51a0..e1b7dc91a0d77 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/bitcoin.nix
@@ -7,6 +7,8 @@ in
 {
   port = 9332;
   extraOpts = {
+    package = lib.mkPackageOption pkgs "prometheus-bitcoin-exporter" { };
+
     rpcUser = mkOption {
       type = types.str;
       default = "bitcoinrpc";
@@ -65,7 +67,7 @@ in
   serviceOpts = {
     script = ''
       export BITCOIN_RPC_PASSWORD=$(cat ${cfg.rpcPasswordFile})
-      exec ${pkgs.prometheus-bitcoin-exporter}/bin/bitcoind-monitor.py
+      exec ${cfg.package}/bin/bitcoind-monitor.py
     '';
 
     environment = {
diff --git a/nixos/modules/services/networking/clatd.nix b/nixos/modules/services/networking/clatd.nix
index 76e0c130ca466..de6cde4e979c0 100644
--- a/nixos/modules/services/networking/clatd.nix
+++ b/nixos/modules/services/networking/clatd.nix
@@ -43,7 +43,6 @@ in
 
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/clatd -c ${configFile}";
-        startLimitIntervalSec = 0;
 
         # Hardening
         CapabilityBoundingSet = [
diff --git a/nixos/modules/services/networking/jotta-cli.md b/nixos/modules/services/networking/jotta-cli.md
index fee002a4e6046..335e5c8e38563 100644
--- a/nixos/modules/services/networking/jotta-cli.md
+++ b/nixos/modules/services/networking/jotta-cli.md
@@ -6,7 +6,7 @@ The [Jottacloud Command-line Tool](https://docs.jottacloud.com/en/articles/14368
 
 ```nix
 {
-  user.services.jotta-cli.enable = true;
+  services.jotta-cli.enable = true;
 }
 ```
 
@@ -15,7 +15,7 @@ This adds `jotta-cli` to `environment.systemPackages` and starts a user service
 ## Example Configuration {#module-services-jotta-cli-example-configuration}
 
 ```nix
-user.services.jotta-cli = {
+services.jotta-cli = {
   enable = true;
   options = [ "slow" ];
   package = pkgs.jotta-cli;
diff --git a/nixos/modules/services/networking/jotta-cli.nix b/nixos/modules/services/networking/jotta-cli.nix
index c7e6dad5453ca..e0fa1ef332fe6 100644
--- a/nixos/modules/services/networking/jotta-cli.nix
+++ b/nixos/modules/services/networking/jotta-cli.nix
@@ -2,10 +2,10 @@
 
 with lib;
 
-let cfg = config.user.services.jotta-cli;
+let cfg = config.services.jotta-cli;
 in {
   options = {
-    user.services.jotta-cli = {
+    services.jotta-cli = {
 
       enable = mkEnableOption "Jottacloud Command-line Tool";
 
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index e33bbb2af178f..b7143cf520f96 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -470,7 +470,7 @@ in
       -      [main]
       -      no-auto-default=*
       -    '''
-      +    extraConfig.main.no-auto-default = "*";
+      +    settings.main.no-auto-default = "*";
          };
       ```
     ''
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index c62bccd462d33..0fdb708bf052f 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -296,6 +296,17 @@ in
         '';
       };
 
+      authorizedKeysInHomedir = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Enables the use of the `~/.ssh/authorized_keys` file.
+
+          Otherwise, the only files trusted by default are those in `/etc/ssh/authorized_keys.d`,
+          *i.e.* SSH keys from [](#opt-users.users._name_.openssh.authorizedKeys.keys).
+        '';
+      };
+
       authorizedKeysCommand = mkOption {
         type = types.str;
         default = "none";
@@ -635,7 +646,7 @@ in
     # https://github.com/NixOS/nixpkgs/pull/10155
     # https://github.com/NixOS/nixpkgs/pull/41745
     services.openssh.authorizedKeysFiles =
-      [ "%h/.ssh/authorized_keys" "/etc/ssh/authorized_keys.d/%u" ];
+      lib.optional cfg.authorizedKeysInHomedir "%h/.ssh/authorized_keys" ++ [ "/etc/ssh/authorized_keys.d/%u" ];
 
     services.openssh.settings.AuthorizedPrincipalsFile = mkIf (authPrincipalsFiles != {}) "/etc/ssh/authorized_principals.d/%u";
 
diff --git a/nixos/modules/services/networking/sunshine.nix b/nixos/modules/services/networking/sunshine.nix
index 0749eaee95d8a..ec78db1f3f8e9 100644
--- a/nixos/modules/services/networking/sunshine.nix
+++ b/nixos/modules/services/networking/sunshine.nix
@@ -1,6 +1,6 @@
 { config, lib, pkgs, utils, ... }:
 let
-  inherit (lib) mkEnableOption mkPackageOption mkOption mkIf mkDefault types optionals getExe;
+  inherit (lib) mkEnableOption mkPackageOption mkOption literalExpression mkIf mkDefault types optionals getExe;
   inherit (utils) escapeSystemdExecArgs;
   cfg = config.services.sunshine;
 
@@ -46,7 +46,7 @@ in
 
         See https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/advanced_usage.html#configuration for syntax.
       '';
-      example = ''
+      example = literalExpression ''
         {
           sunshine_name = "nixos";
         }
@@ -67,7 +67,7 @@ in
       description = ''
         Configuration for applications to be exposed to Moonlight. If this is set, no configuration is possible from the web UI, and must be by the `settings` option.
       '';
-      example = ''
+      example = literalExpression ''
         {
           env = {
             PATH = "$(PATH):$(HOME)/.local/bin";
diff --git a/nixos/modules/services/security/oauth2-proxy.nix b/nixos/modules/services/security/oauth2-proxy.nix
index 075e64b743b1e..78a772845a352 100644
--- a/nixos/modules/services/security/oauth2-proxy.nix
+++ b/nixos/modules/services/security/oauth2-proxy.nix
@@ -17,7 +17,7 @@ let
       inherit (cfg.github) org team;
     }; };
 
-    google = cfg: { google = with cfg.google; optionalAttrs (groups != []) {
+    google = cfg: { google = with cfg.google; lib.optionalAttrs (groups != []) {
       admin-email = adminEmail;
       service-account = serviceAccountJSON;
       group = groups;
diff --git a/nixos/modules/services/security/step-ca.nix b/nixos/modules/services/security/step-ca.nix
index c708cb2b8910d..e9195fbd51608 100644
--- a/nixos/modules/services/security/step-ca.nix
+++ b/nixos/modules/services/security/step-ca.nix
@@ -107,7 +107,7 @@ in
           UMask = "0077";
           Environment = "HOME=%S/step-ca";
           WorkingDirectory = ""; # override upstream
-          ReadWriteDirectories = ""; # override upstream
+          ReadWritePaths = ""; # override upstream
 
           # LocalCredential handles file permission problems arising from the use of DynamicUser.
           LoadCredential = "intermediate_password:${cfg.intermediatePasswordFile}";
diff --git a/nixos/modules/services/system/dbus.nix b/nixos/modules/services/system/dbus.nix
index 8dba0aca64337..26f4eba707f92 100644
--- a/nixos/modules/services/system/dbus.nix
+++ b/nixos/modules/services/system/dbus.nix
@@ -147,6 +147,10 @@ in
       };
 
       systemd.services.dbus = {
+        aliases = [
+          # hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
+          "dbus-broker.service"
+        ];
         # Don't restart dbus-daemon. Bad things tend to happen if we do.
         reloadIfChanged = true;
         restartTriggers = [
@@ -158,6 +162,10 @@ in
       };
 
       systemd.user.services.dbus = {
+        aliases = [
+          # hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
+          "dbus-broker.service"
+        ];
         # Don't restart dbus-daemon. Bad things tend to happen if we do.
         reloadIfChanged = true;
         restartTriggers = [
@@ -184,6 +192,8 @@ in
       # https://github.com/NixOS/nixpkgs/issues/108643
       systemd.services.dbus-broker = {
         aliases = [
+          # allow other services to just depend on dbus,
+          # but also a hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
           "dbus.service"
         ];
         unitConfig = {
@@ -203,6 +213,8 @@ in
 
       systemd.user.services.dbus-broker = {
         aliases = [
+          # allow other services to just depend on dbus,
+          # but also a hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
           "dbus.service"
         ];
         # Don't restart dbus. Bad things tend to happen if we do.
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index cf1282b3d4cf4..201085daa74a8 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -663,7 +663,7 @@ in
             '' + ''
               export KEYCLOAK_ADMIN=admin
               export KEYCLOAK_ADMIN_PASSWORD=${escapeShellArg cfg.initialAdminPassword}
-              kc.sh start --optimized
+              kc.sh --verbose start --optimized
             '';
           };
 
diff --git a/nixos/modules/services/web-apps/nextcloud.md b/nixos/modules/services/web-apps/nextcloud.md
index ec860d307b381..0b615deae44be 100644
--- a/nixos/modules/services/web-apps/nextcloud.md
+++ b/nixos/modules/services/web-apps/nextcloud.md
@@ -205,11 +205,6 @@ it complains loudly now. So nothing actionable here by default. Alternatively yo
 * set [](#opt-services.nextcloud.settings.log_type) to "file" to be able to view logs
   from the admin panel.
 
-### Your web server is not properly set up to resolve `.well-known` URLs, failed on: `/.well-known/caldav` {#module-services-nextcloud-warning-wellknown-caldav}
-
-This warning appearing seems to be an upstream issue and is being sorted out
-in [nextcloud/server#45033](https://github.com/nextcloud/server/issues/45033).
-
 ## Maintainer information {#module-services-nextcloud-maintainer-info}
 
 As stated in the previous paragraph, we must provide a clean upgrade-path for Nextcloud
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 21f76938f20c5..36c8d2ed6dbd4 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -939,6 +939,7 @@ in {
 
         in {
           wantedBy = [ "multi-user.target" ];
+          wants = [ "nextcloud-update-db.service" ];
           before = [ "phpfpm-nextcloud.service" ];
           after = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
           requires = optional mysqlLocal "mysql.service" ++ optional pgsqlLocal "postgresql.service";
@@ -997,7 +998,7 @@ in {
           after = [ "nextcloud-setup.service" ];
           environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config";
           serviceConfig = {
-            Type = "oneshot";
+            Type = "exec";
             User = "nextcloud";
             ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e";
             ExecStart = "${lib.getExe phpPackage} -f ${webroot}/cron.php";
@@ -1013,6 +1014,20 @@ in {
           };
           startAt = cfg.autoUpdateApps.startAt;
         };
+        nextcloud-update-db = {
+          after = [ "nextcloud-setup.service" ];
+          environment.NEXTCLOUD_CONFIG_DIR = "${datadir}/config";
+          script = ''
+            ${occ}/bin/nextcloud-occ db:add-missing-columns
+            ${occ}/bin/nextcloud-occ db:add-missing-indices
+            ${occ}/bin/nextcloud-occ db:add-missing-primary-keys
+          '';
+          serviceConfig = {
+            Type = "exec";
+            User = "nextcloud";
+            ExecCondition = "${lib.getExe phpPackage} -f ${webroot}/occ status -e";
+          };
+        };
       };
 
       services.phpfpm = {
@@ -1105,10 +1120,10 @@ in {
             extraConfig = ''
               absolute_redirect off;
               location = /.well-known/carddav {
-                return 301 /remote.php/dav;
+                return 301 /remote.php/dav/;
               }
               location = /.well-known/caldav {
-                return 301 /remote.php/dav;
+                return 301 /remote.php/dav/;
               }
               location ~ ^/\.well-known/(?!acme-challenge|pki-validation) {
                 return 301 /index.php$request_uri;
diff --git a/nixos/modules/services/web-apps/plausible.nix b/nixos/modules/services/web-apps/plausible.nix
index 8e49e591f75cd..1f909bbd67a36 100644
--- a/nixos/modules/services/web-apps/plausible.nix
+++ b/nixos/modules/services/web-apps/plausible.nix
@@ -276,8 +276,11 @@ in {
             ${lib.optionalString (cfg.mail.smtp.passwordFile != null)
               ''export SMTP_USER_PWD="$(< $CREDENTIALS_DIRECTORY/SMTP_USER_PWD )"''}
 
-            # setup
-            ${cfg.package}/createdb.sh
+            ${lib.optionalString cfg.database.postgres.setup ''
+              # setup
+              ${cfg.package}/createdb.sh
+            ''}
+
             ${cfg.package}/migrate.sh
             export IP_GEOLOCATION_DB=${pkgs.dbip-country-lite}/share/dbip/dbip-country-lite.mmdb
             ${cfg.package}/bin/plausible eval "(Plausible.Release.prepare() ; Plausible.Auth.create_user(\"$ADMIN_USER_NAME\", \"$ADMIN_USER_EMAIL\", \"$ADMIN_USER_PWD\"))"
@@ -326,6 +329,6 @@ in {
     ];
   };
 
-  meta.maintainers = with maintainers; [ ];
+  meta.maintainers = with maintainers; [ xanderio ];
   meta.doc = ./plausible.md;
 }
diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix
index 08ce50bff62c0..1cd1448c7d567 100644
--- a/nixos/modules/services/web-servers/caddy/default.nix
+++ b/nixos/modules/services/web-servers/caddy/default.nix
@@ -360,6 +360,7 @@ in
       serviceConfig = let
         runOptions = ''--config ${configPath} ${optionalString (cfg.adapter != null) "--adapter ${cfg.adapter}"}'';
       in {
+        # Override the `ExecStart` line from upstream's systemd unit file by our own:
         # https://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
         # If the empty string is assigned to this option, the list of commands to start is reset, prior assignments of this option will have no effect.
         ExecStart = [ "" ''${cfg.package}/bin/caddy run ${runOptions} ${optionalString cfg.resume "--resume"}'' ];
@@ -367,7 +368,7 @@ in
         ExecReload = [ "" ''${cfg.package}/bin/caddy reload ${runOptions} --force'' ];
         User = cfg.user;
         Group = cfg.group;
-        ReadWriteDirectories = cfg.dataDir;
+        ReadWritePaths = [ cfg.dataDir ];
         StateDirectory = mkIf (cfg.dataDir == "/var/lib/caddy") [ "caddy" ];
         LogsDirectory = mkIf (cfg.logDir == "/var/log/caddy") [ "caddy" ];
         Restart = "on-failure";
diff --git a/nixos/modules/services/web-servers/traefik.nix b/nixos/modules/services/web-servers/traefik.nix
index 9c53455bcf3d7..1a65ce21112ee 100644
--- a/nixos/modules/services/web-servers/traefik.nix
+++ b/nixos/modules/services/web-servers/traefik.nix
@@ -170,7 +170,7 @@ in {
         PrivateDevices = true;
         ProtectHome = true;
         ProtectSystem = "full";
-        ReadWriteDirectories = cfg.dataDir;
+        ReadWritePaths = [ cfg.dataDir ];
         RuntimeDirectory = "traefik";
       };
     };
diff --git a/nixos/modules/services/x11/desktop-managers/cinnamon.nix b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
index 482527d1e8ad5..2e0eef67c0b3e 100644
--- a/nixos/modules/services/x11/desktop-managers/cinnamon.nix
+++ b/nixos/modules/services/x11/desktop-managers/cinnamon.nix
@@ -157,6 +157,7 @@ in
 
         # packages
         nemo-with-extensions
+        gnome-online-accounts-gtk
         cinnamon-control-center
         cinnamon-settings-daemon
         libgnomekbd
diff --git a/nixos/modules/services/x11/desktop-managers/gnome.nix b/nixos/modules/services/x11/desktop-managers/gnome.nix
index ce300431d47c9..fe50d930b5af0 100644
--- a/nixos/modules/services/x11/desktop-managers/gnome.nix
+++ b/nixos/modules/services/x11/desktop-managers/gnome.nix
@@ -343,10 +343,6 @@ in
 
       services.avahi.enable = mkDefault true;
 
-      xdg.portal.extraPortals = [
-        pkgs.gnome.gnome-shell
-      ];
-
       services.geoclue2.enable = mkDefault true;
       services.geoclue2.enableDemoAgent = false; # GNOME has its own geoclue agent
 
diff --git a/nixos/modules/services/x11/window-managers/clfswm.nix b/nixos/modules/services/x11/window-managers/clfswm.nix
index 5500c77a038be..09b49962f2ad7 100644
--- a/nixos/modules/services/x11/window-managers/clfswm.nix
+++ b/nixos/modules/services/x11/window-managers/clfswm.nix
@@ -10,7 +10,7 @@ in
   options = {
     services.xserver.windowManager.clfswm = {
       enable = mkEnableOption "clfswm";
-      package = mkPackageOption pkgs [ "lispPackages" "clfswm" ] { };
+      package = mkPackageOption pkgs [ "sbclPackages" "clfswm" ] { };
     };
   };
 
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index ba45231465fb4..cabc1dcc2d65a 100755
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -472,6 +472,9 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
             $units_to_reload->{$unit} = 1;
             record_unit($reload_list_file, $unit);
         }
+        elsif ($unit eq "dbus.service" || $unit eq "dbus-broker.service") {
+            # dbus service should only ever be reloaded, not started/stoped/restarted as that would break the system.
+        }
         elsif (!parse_systemd_bool(\%new_unit_info, "Service", "X-RestartIfChanged", 1) || parse_systemd_bool(\%new_unit_info, "Unit", "RefuseManualStop", 0) || parse_systemd_bool(\%new_unit_info, "Unit", "X-OnlyManualStart", 0)) {
             $units_to_skip->{$unit} = 1;
         } else {
diff --git a/nixos/modules/system/activation/test.nix b/nixos/modules/system/activation/test.nix
index 8cf000451c6e3..fd251d5289579 100644
--- a/nixos/modules/system/activation/test.nix
+++ b/nixos/modules/system/activation/test.nix
@@ -5,7 +5,7 @@
 }:
 let
   node-forbiddenDependencies-fail = nixos ({ ... }: {
-    system.forbiddenDependenciesRegex = "-dev$";
+    system.forbiddenDependenciesRegexes = ["-dev$"];
     environment.etc."dev-dependency" = {
       text = "${expect.dev}";
     };
@@ -14,7 +14,7 @@ let
     boot.loader.grub.enable = false;
   });
   node-forbiddenDependencies-succeed = nixos ({ ... }: {
-    system.forbiddenDependenciesRegex = "-dev$";
+    system.forbiddenDependenciesRegexes = ["-dev$"];
     system.extraDependencies = [ expect.dev ];
     documentation.enable = false;
     fileSystems."/".device = "ignore-root-device";
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 4cf3012646fa3..ed0ece19f2fa2 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -86,6 +86,7 @@ in
     ../build.nix
     (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
     (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
+    (mkRenamedOptionModule [ "system" "forbiddenDependenciesRegex" ] [ "system" "forbiddenDependenciesRegexes" ])
   ];
 
   options = {
@@ -160,12 +161,12 @@ in
       '';
     };
 
-    system.forbiddenDependenciesRegex = mkOption {
-      default = "";
-      example = "-dev$";
-      type = types.str;
+    system.forbiddenDependenciesRegexes = mkOption {
+      default = [];
+      example = ["-dev$"];
+      type = types.listOf types.str;
       description = ''
-        A POSIX Extended Regular Expression that matches store paths that
+        POSIX Extended Regular Expressions that match store paths that
         should not appear in the system closure, with the exception of {option}`system.extraDependencies`, which is not checked.
       '';
     };
@@ -289,15 +290,14 @@ in
             "$out/configuration.nix"
         '' +
       optionalString
-        (config.system.forbiddenDependenciesRegex != "")
-        ''
-          if [[ $forbiddenDependenciesRegex != "" && -n $closureInfo ]]; then
-            if forbiddenPaths="$(grep -E -- "$forbiddenDependenciesRegex" $closureInfo/store-paths)"; then
+        (config.system.forbiddenDependenciesRegexes != []) (lib.concatStringsSep "\n" (map (regex: ''
+          if [[ ${regex} != "" && -n $closureInfo ]]; then
+            if forbiddenPaths="$(grep -E -- "${regex}" $closureInfo/store-paths)"; then
               echo -e "System closure $out contains the following disallowed paths:\n$forbiddenPaths"
               exit 1
             fi
           fi
-        '';
+        '') config.system.forbiddenDependenciesRegexes));
 
     system.systemBuilderArgs = {
 
@@ -319,8 +319,7 @@ in
       # option, as opposed to `system.extraDependencies`.
       passedChecks = concatStringsSep " " config.system.checks;
     }
-    // lib.optionalAttrs (config.system.forbiddenDependenciesRegex != "") {
-      inherit (config.system) forbiddenDependenciesRegex;
+    // lib.optionalAttrs (config.system.forbiddenDependenciesRegexes != []) {
       closureInfo = pkgs.closureInfo { rootPaths = [
         # override to avoid  infinite recursion (and to allow using extraDependencies to add forbidden dependencies)
         (config.system.build.toplevel.overrideAttrs (_: { extraDependencies = []; closureInfo = null; }))
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index bb899c8d89994..7f53efbf83f5d 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -17,11 +17,13 @@ let
           "ManageForeignRoutingPolicyRules"
           "ManageForeignRoutes"
           "RouteTable"
+          "IPv6PrivacyExtensions"
         ])
         (assertValueOneOf "SpeedMeter" boolValues)
         (assertInt "SpeedMeterIntervalSec")
         (assertValueOneOf "ManageForeignRoutingPolicyRules" boolValues)
         (assertValueOneOf "ManageForeignRoutes" boolValues)
+        (assertValueOneOf "IPv6PrivacyExtensions" (boolValues ++ ["prefer-public" "kernel"]))
       ];
 
       sectionDHCPv4 = checkUnitConfig "DHCPv4" [
diff --git a/nixos/modules/tasks/filesystems/zfs.nix b/nixos/modules/tasks/filesystems/zfs.nix
index cf8ad94eea135..b75817a011cbd 100644
--- a/nixos/modules/tasks/filesystems/zfs.nix
+++ b/nixos/modules/tasks/filesystems/zfs.nix
@@ -459,9 +459,9 @@ in
       enable = mkEnableOption "periodic scrubbing of ZFS pools";
 
       interval = mkOption {
-        default = "Sun, 02:00";
+        default = "monthly";
         type = types.str;
-        example = "daily";
+        example = "quarterly";
         description = ''
           Systemd calendar expression when to scrub ZFS pools. See
           {manpage}`systemd.time(7)`.
diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix
index 28abbe66adafb..dd8816063c70c 100644
--- a/nixos/modules/testing/test-instrumentation.nix
+++ b/nixos/modules/testing/test-instrumentation.nix
@@ -218,6 +218,8 @@ in
 
     services.displayManager.logToJournal = true;
 
+    services.logrotate.enable = lib.mkDefault false;
+
     # Make sure we use the Guest Agent from the QEMU package for testing
     # to reduce the closure size required for the tests.
     services.qemuGuest.package = pkgs.qemu_test.ga;
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index 65620dd3935b8..c3639f660dfe3 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -53,13 +53,6 @@ in
 
     storage.settings = mkOption {
       type = toml.type;
-      default = {
-        storage = {
-          driver = "overlay";
-          graphroot = "/var/lib/containers/storage";
-          runroot = "/run/containers/storage";
-        };
-      };
       description = "storage.conf configuration";
     };
 
@@ -124,6 +117,12 @@ in
       };
     };
 
+    virtualisation.containers.storage.settings.storage = {
+      driver = lib.mkDefault "overlay";
+      graphroot = lib.mkDefault "/var/lib/containers/storage";
+      runroot = lib.mkDefault "/run/containers/storage";
+    };
+
     environment.etc = {
       "containers/containers.conf".source =
         toml.generate "containers.conf" cfg.containersConf.settings;
diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix
index 4d04853d20a56..87568390bd3b8 100644
--- a/nixos/modules/virtualisation/incus.nix
+++ b/nixos/modules/virtualisation/incus.nix
@@ -105,6 +105,37 @@ let
       path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd";
     }
   ];
+
+  environment = lib.mkMerge [
+    {
+      INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
+      INCUS_OVMF_PATH = ovmf;
+      INCUS_USBIDS_PATH = "${pkgs.hwdata}/share/hwdata/usb.ids";
+      PATH = lib.mkForce serverBinPath;
+    }
+    (lib.mkIf (cfg.ui.enable) { "INCUS_UI" = cfg.ui.package; })
+  ];
+
+  incus-startup = pkgs.writeShellScript "incus-startup" ''
+    case "$1" in
+        start)
+          systemctl is-active incus.service -q && exit 0
+          exec incusd activateifneeded
+        ;;
+
+        stop)
+          systemctl is-active incus.service -q || exit 0
+          exec incusd shutdown
+        ;;
+
+        *)
+          echo "unknown argument \`$1'" >&2
+          exit 1
+        ;;
+    esac
+
+    exit 0
+  '';
 in
 {
   meta = {
@@ -137,6 +168,14 @@ in
         description = "The incus client package to use. This package is added to PATH.";
       };
 
+      softDaemonRestart = lib.mkOption {
+        type = lib.types.bool;
+        default = true;
+        description = ''
+          Allow for incus.service to be stopped without affecting running instances.
+        '';
+      };
+
       preseed = lib.mkOption {
         type = lib.types.nullOr (lib.types.submodule { freeformType = preseedFormat.type; });
 
@@ -282,6 +321,8 @@ in
     systemd.services.incus = {
       description = "Incus Container and Virtual Machine Management Daemon";
 
+      inherit environment;
+
       wantedBy = lib.mkIf (!cfg.socketActivation) [ "multi-user.target" ];
       after = [
         "network-online.target"
@@ -296,20 +337,10 @@ in
 
       wants = [ "network-online.target" ];
 
-      environment = lib.mkMerge [
-        {
-          INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config";
-          INCUS_OVMF_PATH = ovmf;
-          INCUS_USBIDS_PATH = "${pkgs.hwdata}/share/hwdata/usb.ids";
-          PATH = lib.mkForce serverBinPath;
-        }
-        (lib.mkIf (cfg.ui.enable) { "INCUS_UI" = cfg.ui.package; })
-      ];
-
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/incusd --group incus-admin";
         ExecStartPost = "${cfg.package}/bin/incusd waitready --timeout=${cfg.startTimeout}";
-        ExecStop = "${cfg.package}/bin/incus admin shutdown";
+        ExecStop = lib.optionalString (!cfg.softDaemonRestart) "${cfg.package}/bin/incus admin shutdown";
 
         KillMode = "process"; # when stopping, leave the containers alone
         Delegate = "yes";
@@ -324,6 +355,27 @@ in
       };
     };
 
+    systemd.services.incus-startup = lib.mkIf cfg.softDaemonRestart {
+      description = "Incus Instances Startup/Shutdown";
+
+      inherit environment;
+
+      after = [
+        "incus.service"
+        "incus.socket"
+      ];
+      requires = [ "incus.socket" ];
+
+      serviceConfig = {
+        ExecStart = "${incus-startup} start";
+        ExecStop = "${incus-startup} stop";
+        RemainAfterExit = true;
+        TimeoutStartSec = "600s";
+        TimeoutStopSec = "600s";
+        Type = "oneshot";
+      };
+    };
+
     systemd.sockets.incus = {
       description = "Incus UNIX socket";
       wantedBy = [ "sockets.target" ];
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index d4da32c44990f..ba876fe31fc04 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -994,6 +994,7 @@ in {
   v2ray = handleTest ./v2ray.nix {};
   varnish60 = handleTest ./varnish.nix { package = pkgs.varnish60; };
   varnish74 = handleTest ./varnish.nix { package = pkgs.varnish74; };
+  varnish75 = handleTest ./varnish.nix { package = pkgs.varnish75; };
   vault = handleTest ./vault.nix {};
   vault-agent = handleTest ./vault-agent.nix {};
   vault-dev = handleTest ./vault-dev.nix {};
diff --git a/nixos/tests/archi.nix b/nixos/tests/archi.nix
index 59f2e940c0050..a8cb1c503d4f7 100644
--- a/nixos/tests/archi.nix
+++ b/nixos/tests/archi.nix
@@ -24,7 +24,9 @@ import ./make-test-python.nix ({ lib, ... }: {
          machine.wait_for_window("Archi")
 
          # wait till main UI is open
-         machine.wait_for_text("Welcome to Archi")
+         # since OCR seems to be buggy wait_for_text was replaced by sleep, issue: #302965
+         # machine.wait_for_text("Welcome to Archi")
+         machine.sleep(20)
 
          machine.screenshot("welcome-screen")
   '';
diff --git a/nixos/tests/fish.nix b/nixos/tests/fish.nix
index 3d9b13c6af70a..c9a1bef51478e 100644
--- a/nixos/tests/fish.nix
+++ b/nixos/tests/fish.nix
@@ -10,6 +10,8 @@ import ./make-test-python.nix ({ pkgs, ... }: {
         coreutils
         procps # kill collides with coreutils' to test https://github.com/NixOS/nixpkgs/issues/56432
       ];
+      # TODO: remove if/when #267880 is merged and this is a default
+      services.logrotate.enable = false;
     };
 
   testScript =
diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix
index a71c5355046a5..f6bc295448e28 100644
--- a/nixos/tests/incus/container.nix
+++ b/nixos/tests/incus/container.nix
@@ -70,51 +70,60 @@ in
         machine.succeed("incus exec container mount | grep 'lxcfs on /proc/cpuinfo type fuse.lxcfs'")
         machine.succeed("incus exec container mount | grep 'lxcfs on /proc/meminfo type fuse.lxcfs'")
 
-    with subtest("Container CPU limits can be managed"):
-        set_container("limits.cpu 1")
-        cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
-        assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}"
-
-        set_container("limits.cpu 2")
-        cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
-        assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}"
-
-    with subtest("Container memory limits can be managed"):
-        set_container("limits.memory 64MB")
-        meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
-        meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
-        assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'"
-
-        set_container("limits.memory 128MB")
-        meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
-        meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
-        assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'"
-
-    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")
-        machine.succeed("incus launch nixos container --config security.nesting=true")
-        with machine.nested("Waiting for instance to start and be usable"):
-          retry(instance_is_up)
-
-        machine.fail("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
-        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")
-        with machine.nested("Waiting for instance to start and be usable"):
-          retry(instance_is_up)
-
-        machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
-
-        check_sysctl("container")
+    with subtest("resource limits"):
+        with subtest("Container CPU limits can be managed"):
+            set_container("limits.cpu 1")
+            cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
+            assert cpuinfo == "1", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 1, got: {cpuinfo}"
+
+            set_container("limits.cpu 2")
+            cpuinfo = machine.succeed("incus exec container grep -- -c ^processor /proc/cpuinfo").strip()
+            assert cpuinfo == "2", f"Wrong number of CPUs reported from /proc/cpuinfo, want: 2, got: {cpuinfo}"
+
+        with subtest("Container memory limits can be managed"):
+            set_container("limits.memory 64MB")
+            meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
+            meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
+            assert meminfo_bytes == "62500 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '62500 kB', got: '{meminfo_bytes}'"
+
+            set_container("limits.memory 128MB")
+            meminfo = machine.succeed("incus exec container grep -- MemTotal /proc/meminfo").strip()
+            meminfo_bytes = " ".join(meminfo.split(' ')[-2:])
+            assert meminfo_bytes == "125000 kB", f"Wrong amount of memory reported from /proc/meminfo, want: '125000 kB', got: '{meminfo_bytes}'"
+
+    with subtest("lxc-generator"):
+        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")
+            machine.succeed("incus launch nixos container --config security.nesting=true")
+            with machine.nested("Waiting for instance to start and be usable"):
+              retry(instance_is_up)
+
+            machine.fail("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+            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")
+            with machine.nested("Waiting for instance to start and be usable"):
+              retry(instance_is_up)
+
+            machine.succeed("incus exec container test -- -e /run/systemd/system/service.d/zzz-lxc-service.conf")
+
+            check_sysctl("container")
+
+    with subtest("softDaemonRestart"):
+        with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
+            pid = machine.succeed("incus info container | grep 'PID'").split(":")[1].strip()
+            machine.succeed(f"ps {pid}")
+            machine.succeed("systemctl stop incus")
+            machine.succeed(f"ps {pid}")
   '';
 })
diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix
index b850c4fba018d..d778928a3b9f5 100644
--- a/nixos/tests/incus/default.nix
+++ b/nixos/tests/incus/default.nix
@@ -16,9 +16,9 @@
       boot.initrd.systemd.enable = true;
     };
   };
+  incusd-options = import ./incusd-options.nix { inherit system pkgs; };
   lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; };
   openvswitch = import ./openvswitch.nix { inherit system pkgs; };
-  preseed = import ./preseed.nix { inherit system pkgs; };
   socket-activated = import ./socket-activated.nix { inherit system pkgs; };
   storage = import ./storage.nix { inherit system pkgs; };
   ui = import ./ui.nix { inherit system pkgs; };
diff --git a/nixos/tests/incus/incusd-options.nix b/nixos/tests/incus/incusd-options.nix
new file mode 100644
index 0000000000000..b1e7f497fe3e7
--- /dev/null
+++ b/nixos/tests/incus/incusd-options.nix
@@ -0,0 +1,104 @@
+# this is a set of tests for non-default options. typically the default options
+# will be handled by the other tests
+import ../make-test-python.nix (
+  { pkgs, lib, ... }:
+
+  let
+    releases = import ../../release.nix {
+      configuration = {
+        # Building documentation makes the test unnecessarily take a longer time:
+        documentation.enable = lib.mkForce false;
+      };
+    };
+
+    container-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system};
+    container-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system};
+  in
+  {
+    name = "incusd-options";
+
+    meta = {
+      maintainers = lib.teams.lxc.members;
+    };
+
+    nodes.machine = {
+      virtualisation = {
+        cores = 2;
+        memorySize = 1024;
+        diskSize = 4096;
+
+        incus = {
+          enable = true;
+          softDaemonRestart = false;
+
+          preseed = {
+            networks = [
+              {
+                name = "nixostestbr0";
+                type = "bridge";
+                config = {
+                  "ipv4.address" = "10.0.100.1/24";
+                  "ipv4.nat" = "true";
+                };
+              }
+            ];
+            profiles = [
+              {
+                name = "default";
+                devices = {
+                  eth0 = {
+                    name = "eth0";
+                    network = "nixostestbr0";
+                    type = "nic";
+                  };
+                  root = {
+                    path = "/";
+                    pool = "nixostest_pool";
+                    size = "35GiB";
+                    type = "disk";
+                  };
+                };
+              }
+            ];
+            storage_pools = [
+              {
+                name = "nixostest_pool";
+                driver = "dir";
+              }
+            ];
+          };
+        };
+      };
+      networking.nftables.enable = true;
+    };
+
+    testScript = ''
+      def instance_is_up(_) -> bool:
+          status, _ = machine.execute("incus exec container --disable-stdin --force-interactive /run/current-system/sw/bin/systemctl -- is-system-running")
+          return status == 0
+
+      machine.wait_for_unit("incus.service")
+      machine.wait_for_unit("incus-preseed.service")
+
+      with subtest("Container image can be imported"):
+          machine.succeed("incus image import ${container-image-metadata}/*/*.tar.xz ${container-image-rootfs}/*/*.tar.xz --alias nixos")
+
+      with subtest("Container can be launched and managed"):
+          machine.succeed("incus launch nixos container")
+          with machine.nested("Waiting for instance to start and be usable"):
+            retry(instance_is_up)
+          machine.succeed("echo true | incus exec container /run/current-system/sw/bin/bash -")
+
+      with subtest("Verify preseed resources created"):
+          machine.succeed("incus profile show default")
+          machine.succeed("incus network info nixostestbr0")
+          machine.succeed("incus storage show nixostest_pool")
+
+      with subtest("Instance is stopped when softDaemonRestart is disabled and services is stopped"):
+          pid = machine.succeed("incus info container | grep 'PID'").split(":")[1].strip()
+          machine.succeed(f"ps {pid}")
+          machine.succeed("systemctl stop incus")
+          machine.fail(f"ps {pid}")
+    '';
+  }
+)
diff --git a/nixos/tests/incus/preseed.nix b/nixos/tests/incus/preseed.nix
deleted file mode 100644
index f2d928115f3ec..0000000000000
--- a/nixos/tests/incus/preseed.nix
+++ /dev/null
@@ -1,63 +0,0 @@
-import ../make-test-python.nix ({ pkgs, lib, ... } :
-
-{
-  name = "incus-preseed";
-
-  meta = {
-    maintainers = lib.teams.lxc.members;
-  };
-
-  nodes.machine = { lib, ... }: {
-    virtualisation = {
-      incus.enable = true;
-
-      incus.preseed = {
-        networks = [
-          {
-            name = "nixostestbr0";
-            type = "bridge";
-            config = {
-              "ipv4.address" = "10.0.100.1/24";
-              "ipv4.nat" = "true";
-            };
-          }
-        ];
-        profiles = [
-          {
-            name = "nixostest_default";
-            devices = {
-              eth0 = {
-                name = "eth0";
-                network = "nixostestbr0";
-                type = "nic";
-              };
-              root = {
-                path = "/";
-                pool = "default";
-                size = "35GiB";
-                type = "disk";
-              };
-            };
-          }
-        ];
-        storage_pools = [
-          {
-            name = "nixostest_pool";
-            driver = "dir";
-          }
-        ];
-      };
-    };
-    networking.nftables.enable = true;
-  };
-
-  testScript = ''
-    machine.wait_for_unit("incus.service")
-    machine.wait_for_unit("incus-preseed.service")
-
-    with subtest("Verify preseed resources created"):
-      machine.succeed("incus profile show nixostest_default")
-      machine.succeed("incus network info nixostestbr0")
-      machine.succeed("incus storage show nixostest_pool")
-  '';
-})
diff --git a/nixos/tests/incus/virtual-machine.nix b/nixos/tests/incus/virtual-machine.nix
index eebbbd113ed16..c460447f7832d 100644
--- a/nixos/tests/incus/virtual-machine.nix
+++ b/nixos/tests/incus/virtual-machine.nix
@@ -75,5 +75,11 @@ in
         machine.succeed("incus config set ${instance-name} limits.cpu=2")
         count = int(machine.succeed("incus exec ${instance-name} -- nproc").strip())
         assert count == 2, f"Wrong number of CPUs reported, want: 2, got: {count}"
+
+    with subtest("Instance remains running when softDaemonRestart is enabled and services is stopped"):
+        pid = machine.succeed("incus info ${instance-name} | grep 'PID'").split(":")[1].strip()
+        machine.succeed(f"ps {pid}")
+        machine.succeed("systemctl stop incus")
+        machine.succeed(f"ps {pid}")
   '';
 })
diff --git a/nixos/tests/installed-tests/gnome-photos.nix b/nixos/tests/installed-tests/gnome-photos.nix
index bcb6479ee89c6..010ad97024026 100644
--- a/nixos/tests/installed-tests/gnome-photos.nix
+++ b/nixos/tests/installed-tests/gnome-photos.nix
@@ -13,7 +13,7 @@ makeInstalledTest {
       (stdenv.mkDerivation {
         name = "desktop-gsettings";
         dontUnpack = true;
-        nativeBuildInputs = [ glib wrapGAppsHook ];
+        nativeBuildInputs = [ glib wrapGAppsHook3 ];
         buildInputs = [ gsettings-desktop-schemas ];
         installPhase = ''
           runHook preInstall
diff --git a/nixos/tests/jotta-cli.nix b/nixos/tests/jotta-cli.nix
index 5eefe65c1d385..0df23ee2cba5c 100644
--- a/nixos/tests/jotta-cli.nix
+++ b/nixos/tests/jotta-cli.nix
@@ -4,7 +4,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
   meta.maintainers = with pkgs.lib.maintainers; [ evenbrenden ];
 
   nodes.machine = { pkgs, ... }: {
-    user.services.jotta-cli.enable = true;
+    services.jotta-cli.enable = true;
     imports = [ ./common/user-account.nix ];
   };
 
diff --git a/nixos/tests/k3s/single-node.nix b/nixos/tests/k3s/single-node.nix
index fd64a050e61ef..b7ac5d9eeeac7 100644
--- a/nixos/tests/k3s/single-node.nix
+++ b/nixos/tests/k3s/single-node.nix
@@ -78,6 +78,18 @@ import ../make-test-python.nix ({ pkgs, lib, k3s, ... }:
       # regression test for #176445
       machine.fail("journalctl -o cat -u k3s.service | grep 'ipset utility not found'")
 
+      with subtest("Run k3s-killall"):
+          # Call the killall script with a clean path to assert that
+          # all required commands are wrapped
+          output = machine.succeed("PATH= ${k3s}/bin/k3s-killall.sh 2>&1 | tee /dev/stderr")
+          assert "command not found" not in output, "killall script contains unknown command"
+
+          # Check that killall cleaned up properly
+          machine.fail("systemctl is-active k3s.service")
+          machine.fail("systemctl list-units | grep containerd")
+          machine.fail("ip link show | awk -F': ' '{print $2}' | grep -e flannel -e cni0")
+          machine.fail("ip netns show | grep cni-")
+
       machine.shutdown()
     '';
   })
diff --git a/nixos/tests/logrotate.nix b/nixos/tests/logrotate.nix
index bcbe89c259ae5..f9c5e90609709 100644
--- a/nixos/tests/logrotate.nix
+++ b/nixos/tests/logrotate.nix
@@ -16,52 +16,60 @@ import ./make-test-python.nix ({ pkgs, ... }: rec {
   };
 
   nodes = {
-    defaultMachine = { ... }: { };
+    defaultMachine = { ... }: {
+      services.logrotate.enable = true;
+    };
     failingMachine = { ... }: {
-      services.logrotate.configFile = pkgs.writeText "logrotate.conf" ''
-        # self-written config file
-        su notarealuser notagroupeither
-      '';
+      services.logrotate = {
+        enable = true;
+        configFile = pkgs.writeText "logrotate.conf" ''
+          # self-written config file
+          su notarealuser notagroupeither
+        '';
+      };
     };
     machine = { config, ... }: {
       imports = [ importTest ];
 
-      services.logrotate.settings = {
-        # remove default frequency header and add another
-        header = {
-          frequency = null;
-          delaycompress = true;
-        };
-        # extra global setting... affecting nothing
-        last_line = {
-          global = true;
-          priority = 2000;
-          shred = true;
-        };
-        # using mail somewhere should add --mail to logrotate invocation
-        sendmail = {
-          mail = "user@domain.tld";
-        };
-        # postrotate should be suffixed by 'endscript'
-        postrotate = {
-          postrotate = "touch /dev/null";
-        };
-        # check checkConfig works as expected: there is nothing to check here
-        # except that the file build passes
-        checkConf = {
-          su = "root utmp";
-          createolddir = "0750 root utmp";
-          create = "root utmp";
-          "create " = "0750 root utmp";
-        };
-        # multiple paths should be aggregated
-        multipath = {
-          files = [ "file1" "file2" ];
-        };
-        # overriding imported path should keep existing attributes
-        # (e.g. olddir is still set)
-        import = {
-          notifempty = true;
+      services.logrotate = {
+        enable = true;
+        settings = {
+          # remove default frequency header and add another
+          header = {
+            frequency = null;
+            delaycompress = true;
+          };
+          # extra global setting... affecting nothing
+          last_line = {
+            global = true;
+            priority = 2000;
+            shred = true;
+          };
+          # using mail somewhere should add --mail to logrotate invocation
+          sendmail = {
+            mail = "user@domain.tld";
+          };
+          # postrotate should be suffixed by 'endscript'
+          postrotate = {
+            postrotate = "touch /dev/null";
+          };
+          # check checkConfig works as expected: there is nothing to check here
+          # except that the file build passes
+          checkConf = {
+            su = "root utmp";
+            createolddir = "0750 root utmp";
+            create = "root utmp";
+            "create " = "0750 root utmp";
+          };
+          # multiple paths should be aggregated
+          multipath = {
+            files = [ "file1" "file2" ];
+          };
+          # overriding imported path should keep existing attributes
+          # (e.g. olddir is still set)
+          import = {
+            notifempty = true;
+          };
         };
       };
     };
diff --git a/nixos/tests/patroni.nix b/nixos/tests/patroni.nix
index 1f15cd59677ad..68fce4051553e 100644
--- a/nixos/tests/patroni.nix
+++ b/nixos/tests/patroni.nix
@@ -155,7 +155,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
               print(node.succeed("patronictl list cluster1"))
               node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'length') == {expected_replicas + 1} ]")
               node.wait_until_succeeds("[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Leader$\"))) | map(select(.State | test(\"^running$\"))) | length') == 1 ]")
-              node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Replica$\"))) | map(select(.State | test(\"^running$\"))) | length') == {expected_replicas} ]")
+              node.wait_until_succeeds(f"[ $(patronictl list -f json cluster1 | jq 'map(select(.Role | test(\"^Replica$\"))) | map(select(.State | test(\"^streaming$\"))) | length') == {expected_replicas} ]")
               print(node.succeed("patronictl list cluster1"))
           client.wait_until_succeeds("psql -h 127.0.0.1 -U postgres --command='select 1;'")
 
diff --git a/nixos/tests/postgresql-jit.nix b/nixos/tests/postgresql-jit.nix
index baf26b8da2b39..f4b1d07a7faf8 100644
--- a/nixos/tests/postgresql-jit.nix
+++ b/nixos/tests/postgresql-jit.nix
@@ -1,6 +1,7 @@
 { system ? builtins.currentSystem
 , config ? {}
 , pkgs ? import ../.. { inherit system config; }
+, package ? null
 }:
 
 with import ../lib/testing-python.nix { inherit system pkgs; };
@@ -9,14 +10,17 @@ let
   inherit (pkgs) lib;
   packages = builtins.attrNames (import ../../pkgs/servers/sql/postgresql pkgs);
 
-  mkJitTest = packageName: makeTest {
-    name = "${packageName}";
+  mkJitTestFromName = name:
+    mkJitTest pkgs.${name};
+
+  mkJitTest = package: makeTest {
+    name = package.name;
     meta.maintainers = with lib.maintainers; [ ma27 ];
     nodes.machine = { pkgs, lib, ... }: {
       services.postgresql = {
+        inherit package;
         enable = true;
         enableJIT = true;
-        package = pkgs.${packageName};
         initialScript = pkgs.writeText "init.sql" ''
           create table demo (id int);
           insert into demo (id) select generate_series(1, 5);
@@ -45,4 +49,7 @@ let
     '';
   };
 in
-lib.genAttrs packages mkJitTest
+if package == null then
+  lib.genAttrs packages mkJitTestFromName
+else
+  mkJitTest package
diff --git a/nixos/tests/postgresql-wal-receiver.nix b/nixos/tests/postgresql-wal-receiver.nix
index b0bd7711dbcd9..ab2ab4ad0d4fa 100644
--- a/nixos/tests/postgresql-wal-receiver.nix
+++ b/nixos/tests/postgresql-wal-receiver.nix
@@ -1,6 +1,7 @@
 { system ? builtins.currentSystem,
   config ? {},
-  pkgs ? import ../.. { inherit system config; }
+  pkgs ? import ../.. { inherit system config; },
+  package ? null
 }:
 
 with import ../lib/testing-python.nix { inherit system pkgs; };
@@ -9,111 +10,110 @@ let
   lib = pkgs.lib;
 
   # Makes a test for a PostgreSQL package, given by name and looked up from `pkgs`.
-  makePostgresqlWalReceiverTest = postgresqlPackage:
+  makeTestAttribute = name:
   {
-    name = postgresqlPackage;
-    value =
-      let
-        pkg = pkgs."${postgresqlPackage}";
-        postgresqlDataDir = "/var/lib/postgresql/${pkg.psqlSchema}";
-        replicationUser = "wal_receiver_user";
-        replicationSlot = "wal_receiver_slot";
-        replicationConn = "postgresql://${replicationUser}@localhost";
-        baseBackupDir = "/tmp/pg_basebackup";
-        walBackupDir = "/tmp/pg_wal";
-        atLeast12 = lib.versionAtLeast pkg.version "12.0";
-
-        recoveryFile = if atLeast12
-            then pkgs.writeTextDir "recovery.signal" ""
-            else pkgs.writeTextDir "recovery.conf" "restore_command = 'cp ${walBackupDir}/%f %p'";
-
-      in makeTest {
-        name = "postgresql-wal-receiver-${postgresqlPackage}";
-        meta.maintainers = with lib.maintainers; [ pacien ];
-
-        nodes.machine = { ... }: {
-          services.postgresql = {
-            package = pkg;
-            enable = true;
-            settings = lib.mkMerge [
-              {
-                wal_level = "archive"; # alias for replica on pg >= 9.6
-                max_wal_senders = 10;
-                max_replication_slots = 10;
-              }
-              (lib.mkIf atLeast12 {
-                restore_command = "cp ${walBackupDir}/%f %p";
-                recovery_end_command = "touch recovery.done";
-              })
-            ];
-            authentication = ''
-              host replication ${replicationUser} all trust
-            '';
-            initialScript = pkgs.writeText "init.sql" ''
-              create user ${replicationUser} replication;
-              select * from pg_create_physical_replication_slot('${replicationSlot}');
-            '';
-          };
+    inherit name;
+    value = makePostgresqlWalReceiverTest pkgs."${name}";
+  };
+
+  makePostgresqlWalReceiverTest = pkg:
+    let
+      postgresqlDataDir = "/var/lib/postgresql/${pkg.psqlSchema}";
+      replicationUser = "wal_receiver_user";
+      replicationSlot = "wal_receiver_slot";
+      replicationConn = "postgresql://${replicationUser}@localhost";
+      baseBackupDir = "/tmp/pg_basebackup";
+      walBackupDir = "/tmp/pg_wal";
+
+      recoveryFile = pkgs.writeTextDir "recovery.signal" "";
 
-          services.postgresqlWalReceiver.receivers.main = {
-            postgresqlPackage = pkg;
-            connection = replicationConn;
-            slot = replicationSlot;
-            directory = walBackupDir;
+    in makeTest {
+      name = "postgresql-wal-receiver-${pkg.name}";
+      meta.maintainers = with lib.maintainers; [ pacien ];
+
+      nodes.machine = { ... }: {
+        services.postgresql = {
+          package = pkg;
+          enable = true;
+          settings = {
+            max_replication_slots = 10;
+            max_wal_senders = 10;
+            recovery_end_command = "touch recovery.done";
+            restore_command = "cp ${walBackupDir}/%f %p";
+            wal_level = "archive"; # alias for replica on pg >= 9.6
           };
-          # This is only to speedup test, it isn't time racing. Service is set to autorestart always,
-          # default 60sec is fine for real system, but is too much for a test
-          systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
+          authentication = ''
+            host replication ${replicationUser} all trust
+          '';
+          initialScript = pkgs.writeText "init.sql" ''
+            create user ${replicationUser} replication;
+            select * from pg_create_physical_replication_slot('${replicationSlot}');
+          '';
         };
 
-        testScript = ''
-          # make an initial base backup
-          machine.wait_for_unit("postgresql")
-          machine.wait_for_unit("postgresql-wal-receiver-main")
-          # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
-          # required only for 9.4
-          machine.sleep(5)
-          machine.succeed(
-              "${pkg}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
-          )
-
-          # create a dummy table with 100 records
-          machine.succeed(
-              "sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
-          )
-
-          # stop postgres and destroy data
-          machine.systemctl("stop postgresql")
-          machine.systemctl("stop postgresql-wal-receiver-main")
-          machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
-
-          # restore the base backup
-          machine.succeed(
-              "cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
-          )
-
-          # prepare WAL and recovery
-          machine.succeed("chmod a+rX -R ${walBackupDir}")
-          machine.execute(
-              "for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
-          )  # make use of partial segments too
-          machine.succeed(
-              "cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
-          )
-
-          # replay WAL
-          machine.systemctl("start postgresql")
-          machine.wait_for_file("${postgresqlDataDir}/recovery.done")
-          machine.systemctl("restart postgresql")
-          machine.wait_for_unit("postgresql")
-
-          # check that our records have been restored
-          machine.succeed(
-              "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
-          )
-        '';
+        services.postgresqlWalReceiver.receivers.main = {
+          postgresqlPackage = pkg;
+          connection = replicationConn;
+          slot = replicationSlot;
+          directory = walBackupDir;
+        };
+        # This is only to speedup test, it isn't time racing. Service is set to autorestart always,
+        # default 60sec is fine for real system, but is too much for a test
+        systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
       };
+
+      testScript = ''
+        # make an initial base backup
+        machine.wait_for_unit("postgresql")
+        machine.wait_for_unit("postgresql-wal-receiver-main")
+        # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
+        # required only for 9.4
+        machine.sleep(5)
+        machine.succeed(
+            "${pkg}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
+        )
+
+        # create a dummy table with 100 records
+        machine.succeed(
+            "sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
+        )
+
+        # stop postgres and destroy data
+        machine.systemctl("stop postgresql")
+        machine.systemctl("stop postgresql-wal-receiver-main")
+        machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
+
+        # restore the base backup
+        machine.succeed(
+            "cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
+        )
+
+        # prepare WAL and recovery
+        machine.succeed("chmod a+rX -R ${walBackupDir}")
+        machine.execute(
+            "for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
+        )  # make use of partial segments too
+        machine.succeed(
+            "cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
+        )
+
+        # replay WAL
+        machine.systemctl("start postgresql")
+        machine.wait_for_file("${postgresqlDataDir}/recovery.done")
+        machine.systemctl("restart postgresql")
+        machine.wait_for_unit("postgresql")
+
+        # check that our records have been restored
+        machine.succeed(
+            "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
+        )
+      '';
     };
 
-# Maps the generic function over all attributes of PostgreSQL packages
-in builtins.listToAttrs (map makePostgresqlWalReceiverTest (builtins.attrNames (import ../../pkgs/servers/sql/postgresql pkgs)))
+in
+if package == null then
+  # all-tests.nix: Maps the generic function over all attributes of PostgreSQL packages
+  builtins.listToAttrs (map makeTestAttribute (builtins.attrNames (import ../../pkgs/servers/sql/postgresql pkgs)))
+else
+  # Called directly from <package>.tests
+  makePostgresqlWalReceiverTest package