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/administration/nixos-state.section.md28
-rw-r--r--nixos/doc/manual/administration/running.md1
-rw-r--r--nixos/doc/manual/administration/system-state.chapter.md17
-rw-r--r--nixos/doc/manual/administration/systemd-state.section.md52
-rw-r--r--nixos/doc/manual/administration/zfs-state.section.md16
-rw-r--r--nixos/doc/manual/release-notes/release-notes.md1
-rw-r--r--nixos/doc/manual/release-notes/rl-2111.section.md2
-rw-r--r--nixos/doc/manual/release-notes/rl-2311.section.md8
-rw-r--r--nixos/doc/manual/release-notes/rl-2405.section.md6
-rw-r--r--nixos/lib/test-driver/test_driver/machine.py32
-rw-r--r--nixos/modules/config/ldso.nix58
-rw-r--r--nixos/modules/config/stub-ld.nix56
-rw-r--r--nixos/modules/config/sysctl.nix24
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/hardware/keyboard/qmk.nix1
-rw-r--r--nixos/modules/module-list.nix4
-rw-r--r--nixos/modules/profiles/minimal.nix2
-rw-r--r--nixos/modules/programs/hyprland.nix11
-rw-r--r--nixos/modules/programs/nix-ld.nix2
-rw-r--r--nixos/modules/programs/wayland/river.nix3
-rw-r--r--nixos/modules/services/backup/restic.nix3
-rw-r--r--nixos/modules/services/backup/snapraid.nix (renamed from nixos/modules/tasks/snapraid.nix)9
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix1
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/hardware/udev.nix3
-rw-r--r--nixos/modules/services/matrix/synapse.md8
-rw-r--r--nixos/modules/services/matrix/synapse.nix14
-rw-r--r--nixos/modules/services/misc/guix/default.nix1
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix8
-rw-r--r--nixos/modules/services/networking/jigasi.nix2
-rw-r--r--nixos/modules/services/networking/nebula.nix2
-rw-r--r--nixos/modules/services/networking/syncthing.nix11
-rw-r--r--nixos/modules/services/networking/tinyproxy.nix2
-rw-r--r--nixos/modules/services/networking/vdirsyncer.nix8
-rw-r--r--nixos/modules/services/networking/wpa_supplicant.nix4
-rw-r--r--nixos/modules/services/torrent/transmission.nix2
-rw-r--r--nixos/modules/services/web-apps/node-red.nix2
-rw-r--r--nixos/modules/services/web-servers/caddy/default.nix3
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/ceph-single-node.nix21
-rw-r--r--nixos/tests/guix/basic.nix6
-rw-r--r--nixos/tests/nebula.nix5
-rw-r--r--nixos/tests/stub-ld.nix73
-rw-r--r--nixos/tests/telegraf.nix1
44 files changed, 457 insertions, 61 deletions
diff --git a/nixos/doc/manual/administration/nixos-state.section.md b/nixos/doc/manual/administration/nixos-state.section.md
new file mode 100644
index 000000000000..9819d613198c
--- /dev/null
+++ b/nixos/doc/manual/administration/nixos-state.section.md
@@ -0,0 +1,28 @@
+# NixOS {#sec-nixos-state}
+
+## `/nix` {#sec-state-nix}
+
+NixOS needs the entirety of `/nix` to be persistent, as it includes:
+- `/nix/store`, which contains all the system's executables, libraries, and supporting data;
+- `/nix/var/nix`, which contains:
+  - the Nix daemon's database;
+  - roots whose transitive closure is preserved when garbage-collecting the Nix store;
+  - system-wide and per-user profiles.
+
+## `/boot` {#sec-state-boot}
+
+`/boot` should also be persistent, as it contains:
+- the kernel and initrd which the bootloader loads,
+- the bootloader's configuration, including the kernel's command-line which
+  determines the store path to use as system environment.
+
+
+## Users and groups {#sec-state-users}
+
+- `/var/lib/nixos` should persist: it holds state needed to generate stable
+  uids and gids for declaratively-managed users and groups, etc.
+- `users.mutableUsers` should be false, *or* the following files under `/etc`
+  should all persist:
+  - {manpage}`passwd(5)` and {manpage}`group(5)`,
+  - {manpage}`shadow(5)` and {manpage}`gshadow(5)`,
+  - {manpage}`subuid(5)` and {manpage}`subgid(5)`.
diff --git a/nixos/doc/manual/administration/running.md b/nixos/doc/manual/administration/running.md
index 48e8c7c6668b..83412d9b7af5 100644
--- a/nixos/doc/manual/administration/running.md
+++ b/nixos/doc/manual/administration/running.md
@@ -8,6 +8,7 @@ rebooting.chapter.md
 user-sessions.chapter.md
 control-groups.chapter.md
 logging.chapter.md
+system-state.chapter.md
 cleaning-store.chapter.md
 containers.chapter.md
 troubleshooting.chapter.md
diff --git a/nixos/doc/manual/administration/system-state.chapter.md b/nixos/doc/manual/administration/system-state.chapter.md
new file mode 100644
index 000000000000..6840cc390257
--- /dev/null
+++ b/nixos/doc/manual/administration/system-state.chapter.md
@@ -0,0 +1,17 @@
+# Necessary system state {#ch-system-state}
+
+Normally — on systems with a persistent `rootfs` — system services can persist state to
+the filesystem without administrator intervention.
+
+However, it is possible and not-uncommon to create [impermanent systems], whose
+`rootfs` is either a `tmpfs` or reset during boot. While NixOS itself supports
+this kind of configuration, special care needs to be taken.
+
+[impermanent systems]: https://nixos.wiki/wiki/Impermanence
+
+
+```{=include=} sections
+nixos-state.section.md
+systemd-state.section.md
+zfs-state.section.md
+```
diff --git a/nixos/doc/manual/administration/systemd-state.section.md b/nixos/doc/manual/administration/systemd-state.section.md
new file mode 100644
index 000000000000..84f074871a65
--- /dev/null
+++ b/nixos/doc/manual/administration/systemd-state.section.md
@@ -0,0 +1,52 @@
+# systemd {#sec-systemd-state}
+
+## `machine-id(5)` {#sec-machine-id}
+
+`systemd` uses per-machine identifier — {manpage}`machine-id(5)` — which must be
+unique and persistent; otherwise, the system journal may fail to list earlier
+boots, etc.
+
+`systemd` generates a random `machine-id(5)` during boot if it does not already exist,
+and persists it in `/etc/machine-id`.  As such, it suffices to make that file persistent.
+
+Alternatively, it is possible to generate a random `machine-id(5)`; while the
+specification allows for *any* hex-encoded 128b value, systemd itself uses
+[UUIDv4], *i.e.* random UUIDs, and it is thus preferable to do so as well, in
+case some software assumes `machine-id(5)` to be a UUIDv4. Those can be
+generated with `uuidgen -r | tr -d -` (`tr` being used to remove the dashes).
+
+Such a `machine-id(5)` can be set by writing it to `/etc/machine-id` or through
+the kernel's command-line, though NixOS' systemd maintainers [discourage] the
+latter approach.
+
+[UUIDv4]: https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
+[discourage]: https://github.com/NixOS/nixpkgs/pull/268995
+
+
+## `/var/lib/systemd` {#sec-var-systemd}
+
+Moreover, `systemd` expects its state directory — `/var/lib/systemd` — to persist, for:
+- {manpage}`systemd-random-seed(8)`, which loads a 256b “seed” into the kernel's RNG
+  at boot time, and saves a fresh one during shutdown;
+- {manpage}`systemd.timer(5)` with `Persistent=yes`, which are then run after boot if
+  the timer would have triggered during the time the system was shut down;
+- {manpage}`systemd-coredump(8)` to store core dumps there by default;
+  (see {manpage}`coredump.conf(5)`)
+- {manpage}`systemd-timesyncd(8)`;
+- {manpage}`systemd-backlight(8)` and {manpage}`systemd-rfkill(8)` persist hardware-related
+  state;
+- possibly other things, this list is not meant to be exhaustive.
+
+In any case, making `/var/lib/systemd` persistent is recommended.
+
+
+## `/var/log/journal/{machine-id}` {#sec-var-journal}
+
+Lastly, {manpage}`systemd-journald(8)` writes the system's journal in binary
+form to `/var/log/journal/{machine-id}`; if (locally) persisting the entire log
+is desired, it is recommended to make all of `/var/log/journal` persistent.
+
+If not, one can set `Storage=volatile` in {manpage}`journald.conf(5)`
+([`services.journald.storage = "volatile";`](#opt-services.journald.storage)),
+which disables journal persistence and causes it to be written to
+`/run/log/journal`.
diff --git a/nixos/doc/manual/administration/zfs-state.section.md b/nixos/doc/manual/administration/zfs-state.section.md
new file mode 100644
index 000000000000..11ad5badea7e
--- /dev/null
+++ b/nixos/doc/manual/administration/zfs-state.section.md
@@ -0,0 +1,16 @@
+# ZFS {#sec-zfs-state}
+
+When using ZFS, `/etc/zfs/zpool.cache` should be persistent (or a symlink to a persistent
+location) as it is the default value for the `cachefile` [property](man:zpoolprops(7)).
+
+This cachefile is used on system startup to discover ZFS pools, so ZFS pools
+holding the `rootfs` and/or early-boot datasets such as `/nix` can be set to
+`cachefile=none`.
+
+In principle, if there are no other pools attached to the system, `zpool.cache`
+does not need to be persisted; it is however *strongly recommended* to persist
+it, in case additional pools are added later on, temporarily or permanently:
+
+While mishandling the cachefile does not lead to data loss by itself, it may
+cause zpools not to be imported during boot, and services may then write to a
+location where a dataset was expected to be mounted.
diff --git a/nixos/doc/manual/release-notes/release-notes.md b/nixos/doc/manual/release-notes/release-notes.md
index 3f926fb21a5c..0514a1b0044a 100644
--- a/nixos/doc/manual/release-notes/release-notes.md
+++ b/nixos/doc/manual/release-notes/release-notes.md
@@ -3,6 +3,7 @@
 This section lists the release notes for each stable version of NixOS and current unstable revision.
 
 ```{=include=} sections
+rl-2405.section.md
 rl-2311.section.md
 rl-2305.section.md
 rl-2211.section.md
diff --git a/nixos/doc/manual/release-notes/rl-2111.section.md b/nixos/doc/manual/release-notes/rl-2111.section.md
index 400eb1062d9a..8edf4fd35e4f 100644
--- a/nixos/doc/manual/release-notes/rl-2111.section.md
+++ b/nixos/doc/manual/release-notes/rl-2111.section.md
@@ -100,7 +100,7 @@ In addition to numerous new and upgraded packages, this release has the followin
 - [opensnitch](https://github.com/evilsocket/opensnitch), an application firewall. Available as [services.opensnitch](#opt-services.opensnitch.enable).
 
 - [snapraid](https://www.snapraid.it/), a backup program for disk arrays.
-  Available as [snapraid](#opt-snapraid.enable).
+  Available as [snapraid](#opt-services.snapraid.enable).
 
 - [Hockeypuck](https://github.com/hockeypuck/hockeypuck), a OpenPGP Key Server. Available as [services.hockeypuck](#opt-services.hockeypuck.enable).
 
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md
index 5c6bdf97d120..1aef1828908f 100644
--- a/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -1313,6 +1313,14 @@ Make sure to also check the many updates in the [Nixpkgs library](#sec-release-2
 - When using [split parity files](https://www.snapraid.it/manual#7.1) in `snapraid`,
   the snapraid-sync systemd service will no longer fail to run.
 
+- `wpa_supplicant`'s configuration file cannot be read by non-root users, and
+  secrets (such as Pre-Shared Keys) can safely be passed via
+  `networking.wireless.environmentFile`.
+
+  The configuration file could previously be read, when `userControlled.enable` (non-default),
+  by users who are in both `wheel` and `userControlled.group` (defaults to `wheel`)
+
+
 ## Nixpkgs Library {#sec-release-23.11-nixpkgs-lib}
 
 ### Breaking Changes {#sec-release-23.11-lib-breaking}
diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md
index 3ea887bfee40..f792194da224 100644
--- a/nixos/doc/manual/release-notes/rl-2405.section.md
+++ b/nixos/doc/manual/release-notes/rl-2405.section.md
@@ -10,6 +10,10 @@ In addition to numerous new and upgraded packages, this release has the followin
 
 - `screen`'s module has been cleaned, and will now require you to set `programs.screen.enable` in order to populate `screenrc` and add the program to the environment.
 
+- NixOS now installs a stub ELF loader that prints an informative error message when users attempt to run binaries not made for NixOS.
+   - This can be disabled through the `environment.stub-ld.enable` option.
+   - If you use `programs.nix-ld.enable`, no changes are needed. The stub will be disabled automatically.
+
 ## New Services {#sec-release-24.05-new-services}
 
 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
@@ -73,6 +77,8 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m
   `globalRedirect` can now have redirect codes other than 301 through
   `redirectCode`.
 
+- [](#opt-boot.kernel.sysctl._net.core.wmem_max_) changed from a string to an integer because of the addition of a custom merge option (taking the highest value defined to avoid conflicts between 2 services trying to set that value), just as [](#opt-boot.kernel.sysctl._net.core.rmem_max_) since 22.11.
+
 - Gitea 1.21 upgrade has several breaking changes, including:
   - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*`
   - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command.
diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py
index f430321bb607..da60b669fa27 100644
--- a/nixos/lib/test-driver/test_driver/machine.py
+++ b/nixos/lib/test-driver/test_driver/machine.py
@@ -447,8 +447,7 @@ class Machine:
         """
 
         def check_active(_: Any) -> bool:
-            info = self.get_unit_info(unit, user)
-            state = info["ActiveState"]
+            state = self.get_unit_property(unit, "ActiveState", user)
             if state == "failed":
                 raise Exception(f'unit "{unit}" reached state "{state}"')
 
@@ -491,6 +490,35 @@ class Machine:
             if line_pattern.match(line)
         )
 
+    def get_unit_property(
+        self,
+        unit: str,
+        property: str,
+        user: Optional[str] = None,
+    ) -> str:
+        status, lines = self.systemctl(
+            f'--no-pager show "{unit}" --property="{property}"',
+            user,
+        )
+        if status != 0:
+            raise Exception(
+                f'retrieving systemctl property "{property}" for unit "{unit}"'
+                + ("" if user is None else f' under user "{user}"')
+                + f" failed with exit code {status}"
+            )
+
+        invalid_output_message = (
+            f'systemctl show --property "{property}" "{unit}"'
+            f"produced invalid output: {lines}"
+        )
+
+        line_pattern = re.compile(r"^([^=]+)=(.*)$")
+        match = line_pattern.match(lines)
+        assert match is not None, invalid_output_message
+
+        assert match[1] == property, invalid_output_message
+        return match[2]
+
     def systemctl(self, q: str, user: Optional[str] = None) -> Tuple[int, str]:
         """
         Runs `systemctl` commands with optional support for
diff --git a/nixos/modules/config/ldso.nix b/nixos/modules/config/ldso.nix
new file mode 100644
index 000000000000..e5ae13a21145
--- /dev/null
+++ b/nixos/modules/config/ldso.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) last splitString mkOption types mdDoc optionals;
+
+  libDir = pkgs.stdenv.hostPlatform.libDir;
+  ldsoBasename = last (splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker);
+
+  pkgs32 = pkgs.pkgsi686Linux;
+  libDir32 = pkgs32.stdenv.hostPlatform.libDir;
+  ldsoBasename32 = last (splitString "/" pkgs32.stdenv.cc.bintools.dynamicLinker);
+in {
+  options = {
+    environment.ldso = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = mdDoc ''
+        The executable to link into the normal FHS location of the ELF loader.
+      '';
+    };
+
+    environment.ldso32 = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = mdDoc ''
+        The executable to link into the normal FHS location of the 32-bit ELF loader.
+
+        This currently only works on x86_64 architectures.
+      '';
+    };
+  };
+
+  config = {
+    assertions = [
+      { assertion = isNull config.environment.ldso32 || pkgs.stdenv.isx86_64;
+        message = "Option environment.ldso32 currently only works on x86_64.";
+      }
+    ];
+
+    systemd.tmpfiles.rules = (
+      if isNull config.environment.ldso then [
+        "r /${libDir}/${ldsoBasename} - - - - -"
+      ] else [
+        "d /${libDir} 0755 root root - -"
+        "L+ /${libDir}/${ldsoBasename} - - - - ${config.environment.ldso}"
+      ]
+    ) ++ optionals pkgs.stdenv.isx86_64 (
+      if isNull config.environment.ldso32 then [
+        "r /${libDir32}/${ldsoBasename32} - - - - -"
+      ] else [
+        "d /${libDir32} 0755 root root - -"
+        "L+ /${libDir32}/${ldsoBasename32} - - - - ${config.environment.ldso32}"
+      ]
+    );
+  };
+
+  meta.maintainers = with lib.maintainers; [ tejing ];
+}
diff --git a/nixos/modules/config/stub-ld.nix b/nixos/modules/config/stub-ld.nix
new file mode 100644
index 000000000000..14c07466d061
--- /dev/null
+++ b/nixos/modules/config/stub-ld.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) optionalString mkOption types mdDoc mkIf mkDefault;
+
+  cfg = config.environment.stub-ld;
+
+  message = ''
+    NixOS cannot run dynamically linked executables intended for generic
+    linux environments out of the box. For more information, see:
+    https://nix.dev/permalink/stub-ld
+  '';
+
+  stub-ld-for = pkgsArg: messageArg: pkgsArg.pkgsStatic.runCommandCC "stub-ld" {
+    nativeBuildInputs = [ pkgsArg.unixtools.xxd ];
+    inherit messageArg;
+  } ''
+    printf "%s" "$messageArg" | xxd -i -n message >main.c
+    cat <<EOF >>main.c
+    #include <stdio.h>
+    int main(int argc, char * argv[]) {
+      fprintf(stderr, "Could not start dynamically linked executable: %s\n", argv[0]);
+      fwrite(message, sizeof(unsigned char), message_len, stderr);
+      return 127; // matches behavior of bash and zsh without a loader. fish uses 139
+    }
+    EOF
+    $CC -Os main.c -o $out
+  '';
+
+  pkgs32 = pkgs.pkgsi686Linux;
+
+  stub-ld = stub-ld-for pkgs message;
+  stub-ld32 = stub-ld-for pkgs32 message;
+in {
+  options = {
+    environment.stub-ld = {
+      enable = mkOption {
+        type = types.bool;
+        default = true;
+        example = false;
+        description = mdDoc ''
+          Install a stub ELF loader to print an informative error message
+          in the event that a user attempts to run an ELF binary not
+          compiled for NixOS.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.ldso = mkDefault stub-ld;
+    environment.ldso32 = mkIf pkgs.stdenv.isx86_64 (mkDefault stub-ld32);
+  };
+
+  meta.maintainers = with lib.maintainers; [ tejing ];
+}
diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix
index 452c050b6dda..b779f12aca30 100644
--- a/nixos/modules/config/sysctl.nix
+++ b/nixos/modules/config/sysctl.nix
@@ -21,19 +21,27 @@ in
   options = {
 
     boot.kernel.sysctl = mkOption {
-      type = types.submodule {
+      type = let
+        highestValueType = types.ints.unsigned // {
+          merge = loc: defs:
+            foldl
+              (a: b: if b.value == null then null else lib.max a b.value)
+              0
+              (filterOverrides defs);
+        };
+      in types.submodule {
         freeformType = types.attrsOf sysctlOption;
         options."net.core.rmem_max" = mkOption {
-          type = types.nullOr types.ints.unsigned // {
-            merge = loc: defs:
-              foldl
-                (a: b: if b.value == null then null else lib.max a b.value)
-                0
-                (filterOverrides defs);
-          };
+          type = types.nullOr highestValueType;
           default = null;
           description = lib.mdDoc "The maximum socket receive buffer size. In case of conflicting values, the highest will be used.";
         };
+
+        options."net.core.wmem_max" = mkOption {
+          type = types.nullOr highestValueType;
+          default = null;
+          description = lib.mdDoc "The maximum socket send buffer size. In case of conflicting values, the highest will be used.";
+        };
       };
       default = {};
       example = literalExpression ''
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 39aac9fb821b..2aed620eb154 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -475,7 +475,7 @@ let
   sdInitrdUidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) config.boot.initrd.systemd.users) "uid";
   sdInitrdGidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) config.boot.initrd.systemd.groups) "gid";
   groupNames = lib.mapAttrsToList (n: g: g.name) cfg.groups;
-  usersWithoutExistingGroup = lib.filterAttrs (n: u: !lib.elem u.group groupNames) cfg.users;
+  usersWithoutExistingGroup = lib.filterAttrs (n: u: u.group != "" && !lib.elem u.group groupNames) cfg.users;
 
   spec = pkgs.writeText "users-groups.json" (builtins.toJSON {
     inherit (cfg) mutableUsers;
diff --git a/nixos/modules/hardware/keyboard/qmk.nix b/nixos/modules/hardware/keyboard/qmk.nix
index df3bcaeccd2e..d95d36dedb44 100644
--- a/nixos/modules/hardware/keyboard/qmk.nix
+++ b/nixos/modules/hardware/keyboard/qmk.nix
@@ -12,5 +12,6 @@ in
 
   config = mkIf cfg.enable {
     services.udev.packages = [ pkgs.qmk-udev-rules ];
+    users.groups.plugdev = {};
   };
 }
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 820ce54ddeb6..c0bd0b1600b9 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -12,6 +12,7 @@
   ./config/iproute2.nix
   ./config/krb5/default.nix
   ./config/ldap.nix
+  ./config/ldso.nix
   ./config/locale.nix
   ./config/malloc.nix
   ./config/mysql.nix
@@ -28,6 +29,7 @@
   ./config/resolvconf.nix
   ./config/shells-environment.nix
   ./config/stevenblack.nix
+  ./config/stub-ld.nix
   ./config/swap.nix
   ./config/sysctl.nix
   ./config/system-environment.nix
@@ -364,6 +366,7 @@
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
   ./services/backup/postgresql-wal-receiver.nix
+  ./services/backup/snapraid.nix
   ./services/backup/restic-rest-server.nix
   ./services/backup/restic.nix
   ./services/backup/rsnapshot.nix
@@ -1507,7 +1510,6 @@
   ./tasks/network-interfaces.nix
   ./tasks/powertop.nix
   ./tasks/scsi-link-power-management.nix
-  ./tasks/snapraid.nix
   ./tasks/stratis.nix
   ./tasks/swraid.nix
   ./tasks/trackpoint.nix
diff --git a/nixos/modules/profiles/minimal.nix b/nixos/modules/profiles/minimal.nix
index 75f355b4a002..b76740f7cc58 100644
--- a/nixos/modules/profiles/minimal.nix
+++ b/nixos/modules/profiles/minimal.nix
@@ -21,6 +21,8 @@ with lib;
   # Perl is a default package.
   environment.defaultPackages = mkDefault [ ];
 
+  environment.stub-ld.enable = false;
+
   # The lessopen package pulls in Perl.
   programs.less.lessopen = mkDefault null;
 
diff --git a/nixos/modules/programs/hyprland.nix b/nixos/modules/programs/hyprland.nix
index 166c6cbc5c18..9061ce5da83a 100644
--- a/nixos/modules/programs/hyprland.nix
+++ b/nixos/modules/programs/hyprland.nix
@@ -30,7 +30,6 @@ in
       readOnly = true;
       default = cfg.package.override {
         enableXWayland = cfg.xwayland.enable;
-        enableNvidiaPatches = cfg.enableNvidiaPatches;
       };
       defaultText = literalExpression
         "`programs.hyprland.package` with applied configuration";
@@ -42,8 +41,6 @@ in
     portalPackage = mkPackageOption pkgs "xdg-desktop-portal-hyprland" { };
 
     xwayland.enable = mkEnableOption (mdDoc "XWayland") // { default = true; };
-
-    enableNvidiaPatches = mkEnableOption (mdDoc "patching wlroots for better Nvidia support");
   };
 
   config = mkIf cfg.enable {
@@ -73,9 +70,13 @@ in
       [ "programs" "hyprland" "xwayland" "hidpi" ]
       "XWayland patches are deprecated. Refer to https://wiki.hyprland.org/Configuring/XWayland"
     )
-    (mkRenamedOptionModule
-      [ "programs" "hyprland" "nvidiaPatches" ]
+    (mkRemovedOptionModule
       [ "programs" "hyprland" "enableNvidiaPatches" ]
+      "Nvidia patches are no longer needed"
+    )
+    (mkRemovedOptionModule
+      [ "programs" "hyprland" "nvidiaPatches" ]
+      "Nvidia patches are no longer needed"
     )
   ];
 }
diff --git a/nixos/modules/programs/nix-ld.nix b/nixos/modules/programs/nix-ld.nix
index e3a9bb16410c..6f36ce33640c 100644
--- a/nixos/modules/programs/nix-ld.nix
+++ b/nixos/modules/programs/nix-ld.nix
@@ -47,7 +47,7 @@ in
   };
 
   config = lib.mkIf config.programs.nix-ld.enable {
-    systemd.tmpfiles.packages = [ cfg.package ];
+    environment.ldso = "${cfg.package}/libexec/nix-ld";
 
     environment.systemPackages = [ nix-ld-libraries ];
 
diff --git a/nixos/modules/programs/wayland/river.nix b/nixos/modules/programs/wayland/river.nix
index ec59bd50a015..995129b9710a 100644
--- a/nixos/modules/programs/wayland/river.nix
+++ b/nixos/modules/programs/wayland/river.nix
@@ -48,6 +48,9 @@ in {
 
         # To make a river session available if a display manager like SDDM is enabled:
         services.xserver.displayManager.sessionPackages = optionals (cfg.package != null) [ cfg.package ];
+
+        # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050913
+        xdg.portal.config.river.default = mkDefault [ "wlr" "gtk" ];
       }
       (import ./wayland-session.nix { inherit lib pkgs; })
     ]);
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index e3eb504e0adf..b222dd952d15 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -384,10 +384,11 @@ in
       ${lib.optionalString (backup.environmentFile != null) "source ${backup.environmentFile}"}
       # set same environment variables as the systemd service
       ${lib.pipe config.systemd.services."restic-backups-${name}".environment [
-        (lib.filterAttrs (_: v: v != null))
+        (lib.filterAttrs (n: v: v != null && n != "PATH"))
         (lib.mapAttrsToList (n: v: "${n}=${v}"))
         (lib.concatStringsSep "\n")
       ]}
+      PATH=${config.systemd.services."restic-backups-${name}".environment.PATH}:$PATH
 
       exec ${resticCmd} $@
     '') (lib.filterAttrs (_: v: v.createWrapper) config.services.restic.backups);
diff --git a/nixos/modules/tasks/snapraid.nix b/nixos/modules/services/backup/snapraid.nix
index 9570c6b76123..c9b2550e80e8 100644
--- a/nixos/modules/tasks/snapraid.nix
+++ b/nixos/modules/services/backup/snapraid.nix
@@ -2,10 +2,15 @@
 
 with lib;
 
-let cfg = config.snapraid;
+let cfg = config.services.snapraid;
 in
 {
-  options.snapraid = with types; {
+  imports = [
+    # Should have never been on the top-level.
+    (mkRenamedOptionModule [ "snapraid" ] [ "services" "snapraid" ])
+  ];
+
+  options.services.snapraid = with types; {
     enable = mkEnableOption (lib.mdDoc "SnapRAID");
     dataDisks = mkOption {
       default = { };
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index e96743784e04..d69cf4587aab 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -236,6 +236,7 @@ in {
 
       serviceConfig = {
         User = cfg.user;
+        StateDirectory = mkIf (hasPrefix "/var/lib/jenkins" cfg.home) "jenkins";
       };
     };
   };
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index d0058fd1948b..ed5915735730 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -404,7 +404,7 @@ in
         default = {};
         description = lib.mdDoc ''
           PostgreSQL configuration. Refer to
-          <https://www.postgresql.org/docs/15/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
+          <https://www.postgresql.org/docs/current/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
           for an overview of `postgresql.conf`.
 
           ::: {.note}
diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix
index 08ca7a0d247d..311f60795bae 100644
--- a/nixos/modules/services/hardware/udev.nix
+++ b/nixos/modules/services/hardware/udev.nix
@@ -222,6 +222,9 @@ in
         description = lib.mdDoc ''
           Packages added to the {env}`PATH` environment variable when
           executing programs from Udev rules.
+
+          coreutils, gnu{sed,grep}, util-linux and config.systemd.package are
+          automatically included.
         '';
       };
 
diff --git a/nixos/modules/services/matrix/synapse.md b/nixos/modules/services/matrix/synapse.md
index 58be24204fcf..f270be8c8d78 100644
--- a/nixos/modules/services/matrix/synapse.md
+++ b/nixos/modules/services/matrix/synapse.md
@@ -16,13 +16,13 @@ around Matrix.
 
 ## Synapse Homeserver {#module-services-matrix-synapse}
 
-[Synapse](https://github.com/matrix-org/synapse) is
+[Synapse](https://github.com/element-hq/synapse) is
 the reference homeserver implementation of Matrix from the core development
 team at matrix.org. The following configuration example will set up a
 synapse server for the `example.org` domain, served from
 the host `myhostname.example.org`. For more information,
 please refer to the
-[installation instructions of Synapse](https://matrix-org.github.io/synapse/latest/setup/installation.html) .
+[installation instructions of Synapse](https://element-hq.github.io/synapse/latest/setup/installation.html) .
 ```
 { pkgs, lib, config, ... }:
 let
@@ -70,7 +70,7 @@ in {
         # the domain (i.e. example.org from @foo:example.org) and the federation port
         # is 8448.
         # Further reference can be found in the docs about delegation under
-        # https://matrix-org.github.io/synapse/latest/delegate.html
+        # https://element-hq.github.io/synapse/latest/delegate.html
         locations."= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
         # This is usually needed for homeserver discovery (from e.g. other Matrix clients).
         # Further reference can be found in the upstream docs at
@@ -169,7 +169,7 @@ in an additional file like this:
 ::: {.note}
 It's also possible to user alternative authentication mechanism such as
 [LDAP (via `matrix-synapse-ldap3`)](https://github.com/matrix-org/matrix-synapse-ldap3)
-or [OpenID](https://matrix-org.github.io/synapse/latest/openid.html).
+or [OpenID](https://element-hq.github.io/synapse/latest/openid.html).
 :::
 
 ## Element (formerly known as Riot) Web Client {#module-services-matrix-element-web}
diff --git a/nixos/modules/services/matrix/synapse.nix b/nixos/modules/services/matrix/synapse.nix
index 9cc769c2d0db..50019d2a25cb 100644
--- a/nixos/modules/services/matrix/synapse.nix
+++ b/nixos/modules/services/matrix/synapse.nix
@@ -446,7 +446,7 @@ in {
         default = { };
         description = mdDoc ''
           The primary synapse configuration. See the
-          [sample configuration](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_config.yaml)
+          [sample configuration](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_config.yaml)
           for possible values.
 
           Secrets should be passed in by using the `extraConfigFiles` option.
@@ -749,7 +749,7 @@ in {
                     by the module, but in practice it broke on runtime and as a result, no URL
                     preview worked anywhere if this was set.
 
-                    See https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html#url_preview_url_blacklist
+                    See https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#url_preview_url_blacklist
                     on how to configure it properly.
                   ''))
                   (types.attrsOf types.str));
@@ -873,7 +873,7 @@ in {
                 Redis configuration for synapse.
 
                 See the
-                [upstream documentation](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/usage/configuration/config_documentation.md#redis)
+                [upstream documentation](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/usage/configuration/config_documentation.md#redis)
                 for available options.
               '';
             };
@@ -886,7 +886,7 @@ in {
         description = lib.mdDoc ''
           Options for configuring workers. Worker support will be enabled if at least one worker is configured here.
 
-          See the [worker documention](https://matrix-org.github.io/synapse/latest/workers.html#worker-configuration)
+          See the [worker documention](https://element-hq.github.io/synapse/latest/workers.html#worker-configuration)
           for possible options for each worker. Worker-specific options overriding the shared homeserver configuration can be
           specified here for each worker.
 
@@ -900,9 +900,9 @@ in {
             using [`services.matrix-synapse.configureRedisLocally`](#opt-services.matrix-synapse.configureRedisLocally).
 
             Workers also require a proper reverse proxy setup to direct incoming requests to the appropriate process. See
-            the [reverse proxy documentation](https://matrix-org.github.io/synapse/latest/reverse_proxy.html) for a
+            the [reverse proxy documentation](https://element-hq.github.io/synapse/latest/reverse_proxy.html) for a
             general reverse proxying setup and
-            the [worker documentation](https://matrix-org.github.io/synapse/latest/workers.html#available-worker-applications)
+            the [worker documentation](https://element-hq.github.io/synapse/latest/workers.html#available-worker-applications)
             for the available endpoints per worker application.
           :::
         '';
@@ -932,7 +932,7 @@ in {
                 The file for log configuration.
 
                 See the [python documentation](https://docs.python.org/3/library/logging.config.html#configuration-dictionary-schema)
-                for the schema and the [upstream repository](https://github.com/matrix-org/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_log_config.yaml)
+                for the schema and the [upstream repository](https://github.com/element-hq/synapse/blob/v${pkgs.matrix-synapse-unwrapped.version}/docs/sample_log_config.yaml)
                 for an example.
               '';
             };
diff --git a/nixos/modules/services/misc/guix/default.nix b/nixos/modules/services/misc/guix/default.nix
index 7b468e7067cc..3e1a212693b9 100644
--- a/nixos/modules/services/misc/guix/default.nix
+++ b/nixos/modules/services/misc/guix/default.nix
@@ -373,7 +373,6 @@ in
         serviceConfig = {
           Type = "oneshot";
 
-          MemoryDenyWriteExecute = true;
           PrivateDevices = true;
           PrivateNetworks = true;
           ProtectControlGroups = true;
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index e78cb4d01dc5..b4ac8e21451a 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -1435,6 +1435,10 @@ let
       remote_timeout = mkOpt types.str ''
         Timeout for requests to the remote write endpoint.
       '';
+      headers = mkOpt (types.attrsOf types.str) ''
+        Custom HTTP headers to be sent along with each remote write request.
+        Be aware that headers that are set by Prometheus itself can't be overwritten.
+      '';
       write_relabel_configs = mkOpt (types.listOf promTypes.relabel_config) ''
         List of remote write relabel configurations.
       '';
@@ -1530,6 +1534,10 @@ let
       remote_timeout = mkOpt types.str ''
         Timeout for requests to the remote read endpoint.
       '';
+      headers = mkOpt (types.attrsOf types.str) ''
+        Custom HTTP headers to be sent along with each remote read request.
+        Be aware that headers that are set by Prometheus itself can't be overwritten.
+      '';
       read_recent = mkOpt types.bool ''
         Whether reads should be made for queries for time ranges that
         the local storage should have complete data for.
diff --git a/nixos/modules/services/networking/jigasi.nix b/nixos/modules/services/networking/jigasi.nix
index 8d2d25c6edfc..e701689031b1 100644
--- a/nixos/modules/services/networking/jigasi.nix
+++ b/nixos/modules/services/networking/jigasi.nix
@@ -96,7 +96,7 @@ in
     config = mkOption {
       type = attrsOf str;
       default = { };
-      example = literalExample ''
+      example = literalExpression ''
         {
           "org.jitsi.jigasi.auth.URL" = "XMPP:jitsi-meet.example.com";
         }
diff --git a/nixos/modules/services/networking/nebula.nix b/nixos/modules/services/networking/nebula.nix
index b9ebbfbd9a29..e13876172dac 100644
--- a/nixos/modules/services/networking/nebula.nix
+++ b/nixos/modules/services/networking/nebula.nix
@@ -196,7 +196,7 @@ in
             before = [ "sshd.service" ];
             wantedBy = [ "multi-user.target" ];
             serviceConfig = {
-              Type = "simple";
+              Type = "notify";
               Restart = "always";
               ExecStart = "${netCfg.package}/bin/nebula -config ${configFile}";
               UMask = "0027";
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index 99d4d9eeffcc..e0425792431e 100644
--- a/nixos/modules/services/networking/syncthing.nix
+++ b/nixos/modules/services/networking/syncthing.nix
@@ -559,6 +559,15 @@ in {
         '';
       };
 
+      databaseDir = mkOption {
+        type = types.path;
+        description = lib.mdDoc ''
+          The directory containing the database and logs.
+        '';
+        default = cfg.configDir;
+        defaultText = literalExpression "config.${opt.configDir}";
+      };
+
       extraFlags = mkOption {
         type = types.listOf types.str;
         default = [];
@@ -660,7 +669,7 @@ in {
               -no-browser \
               -gui-address=${if isUnixGui then "unix://" else ""}${cfg.guiAddress} \
               -config=${cfg.configDir} \
-              -data=${cfg.dataDir} \
+              -data=${cfg.databaseDir} \
               ${escapeShellArgs cfg.extraFlags}
           '';
           MemoryDenyWriteExecute = true;
diff --git a/nixos/modules/services/networking/tinyproxy.nix b/nixos/modules/services/networking/tinyproxy.nix
index 42d45c460c2e..8ff12b52f10c 100644
--- a/nixos/modules/services/networking/tinyproxy.nix
+++ b/nixos/modules/services/networking/tinyproxy.nix
@@ -85,7 +85,7 @@ in
         User = "tinyproxy";
         Group = "tinyproxy";
         Type = "simple";
-        ExecStart = "${getExe pkgs.tinyproxy} -d -c ${configFile}";
+        ExecStart = "${getExe cfg.package} -d -c ${configFile}";
         ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
         KillSignal = "SIGINT";
         TimeoutStopSec = "30s";
diff --git a/nixos/modules/services/networking/vdirsyncer.nix b/nixos/modules/services/networking/vdirsyncer.nix
index 6a069943434d..165dc70f0876 100644
--- a/nixos/modules/services/networking/vdirsyncer.nix
+++ b/nixos/modules/services/networking/vdirsyncer.nix
@@ -20,9 +20,11 @@ let
     else
       pkgs.writeText "vdirsyncer-${name}.conf" (toIniJson (
         {
-          general = cfg'.config.general // (lib.optionalAttrs (cfg'.config.statusPath == null) {
-            status_path = "/var/lib/vdirsyncer/${name}";
-          });
+          general = cfg'.config.general // {
+            status_path = if cfg'.config.statusPath == null
+                          then "/var/lib/vdirsyncer/${name}"
+                          else cfg'.config.statusPath;
+          };
         } // (
           mapAttrs' (name: nameValuePair "pair ${name}") cfg'.config.pairs
         ) // (
diff --git a/nixos/modules/services/networking/wpa_supplicant.nix b/nixos/modules/services/networking/wpa_supplicant.nix
index 90d9c68433cf..4586550ed75e 100644
--- a/nixos/modules/services/networking/wpa_supplicant.nix
+++ b/nixos/modules/services/networking/wpa_supplicant.nix
@@ -107,6 +107,10 @@ let
       stopIfChanged = false;
 
       path = [ package ];
+      # if `userControl.enable`, the supplicant automatically changes the permissions
+      #  and owning group of the runtime dir; setting `umask` ensures the generated
+      #  config file isn't readable (except to root);  see nixpkgs#267693
+      serviceConfig.UMask = "066";
       serviceConfig.RuntimeDirectory = "wpa_supplicant";
       serviceConfig.RuntimeDirectoryMode = "700";
       serviceConfig.EnvironmentFile = mkIf (cfg.environmentFile != null)
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 88537f8c4f7b..7fb7847ce935 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -434,7 +434,7 @@ in
       # at least up to the values hardcoded here:
       (mkIf cfg.settings.utp-enabled {
         "net.core.rmem_max" = mkDefault 4194304; # 4MB
-        "net.core.wmem_max" = mkDefault "1048576"; # 1MB
+        "net.core.wmem_max" = mkDefault 1048576; # 1MB
       })
       (mkIf cfg.performanceNetParameters {
         # Increase the number of available source (local) TCP and UDP ports to 49151.
diff --git a/nixos/modules/services/web-apps/node-red.nix b/nixos/modules/services/web-apps/node-red.nix
index de78f05a98ca..7f265d289bdb 100644
--- a/nixos/modules/services/web-apps/node-red.nix
+++ b/nixos/modules/services/web-apps/node-red.nix
@@ -19,7 +19,7 @@ in
   options.services.node-red = {
     enable = mkEnableOption (lib.mdDoc "the Node-RED service");
 
-    package = mkPackageOption pkgs "nodePackages.node-red" { };
+    package = mkPackageOption pkgs.nodePackages "node-red" { };
 
     openFirewall = mkOption {
       type = types.bool;
diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix
index 9a544e98cfc4..95dc219d108c 100644
--- a/nixos/modules/services/web-servers/caddy/default.nix
+++ b/nixos/modules/services/web-servers/caddy/default.nix
@@ -342,8 +342,9 @@ in
       }
     '';
 
-    # https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size
+    # https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes
     boot.kernel.sysctl."net.core.rmem_max" = mkDefault 2500000;
+    boot.kernel.sysctl."net.core.wmem_max" = mkDefault 2500000;
 
     systemd.packages = [ cfg.package ];
     systemd.services.caddy = {
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 523035ae2a0a..ab07428cf055 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -788,6 +788,7 @@ in {
   step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {};
   stratis = handleTest ./stratis {};
   strongswan-swanctl = handleTest ./strongswan-swanctl.nix {};
+  stub-ld = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./stub-ld.nix {};
   stunnel = handleTest ./stunnel.nix {};
   sudo = handleTest ./sudo.nix {};
   sudo-rs = handleTest ./sudo-rs.nix {};
diff --git a/nixos/tests/ceph-single-node.nix b/nixos/tests/ceph-single-node.nix
index 4a5636fac156..a3a4072365af 100644
--- a/nixos/tests/ceph-single-node.nix
+++ b/nixos/tests/ceph-single-node.nix
@@ -182,16 +182,19 @@ let
     monA.wait_until_succeeds("ceph -s | grep 'mgr: ${cfg.monA.name}(active,'")
     monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
 
+    # This test has been commented out due to the upstream issue with pyo3
+    # that has broken this dashboard
+    # Reference: https://www.spinics.net/lists/ceph-users/msg77812.html
     # Enable the dashboard and recheck health
-    monA.succeed(
-        "ceph mgr module enable dashboard",
-        "ceph config set mgr mgr/dashboard/ssl false",
-        # default is 8080 but it's better to be explicit
-        "ceph config set mgr mgr/dashboard/server_port 8080",
-    )
-    monA.wait_for_open_port(8080)
-    monA.wait_until_succeeds("curl -q --fail http://localhost:8080")
-    monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
+    # monA.succeed(
+    #     "ceph mgr module enable dashboard",
+    #     "ceph config set mgr mgr/dashboard/ssl false",
+    #     # default is 8080 but it's better to be explicit
+    #     "ceph config set mgr mgr/dashboard/server_port 8080",
+    # )
+    # monA.wait_for_open_port(8080)
+    # monA.wait_until_succeeds("curl -q --fail http://localhost:8080")
+    # monA.wait_until_succeeds("ceph -s | grep 'HEALTH_OK'")
   '';
 in {
   name = "basic-single-node-ceph-cluster";
diff --git a/nixos/tests/guix/basic.nix b/nixos/tests/guix/basic.nix
index 7f90bdeeb1e0..9b943b8965e6 100644
--- a/nixos/tests/guix/basic.nix
+++ b/nixos/tests/guix/basic.nix
@@ -11,7 +11,10 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
 
   nodes.machine = { config, ... }: {
     environment.etc."guix/scripts".source = ./scripts;
-    services.guix.enable = true;
+    services.guix = {
+      enable = true;
+      gc.enable = true;
+    };
   };
 
   testScript = ''
@@ -19,6 +22,7 @@ import ../make-test-python.nix ({ lib, pkgs, ... }: {
 
     machine.wait_for_unit("multi-user.target")
     machine.wait_for_unit("guix-daemon.service")
+    machine.succeed("systemctl start guix-gc.service")
 
     # Can't do much here since the environment has restricted network access.
     with subtest("Guix basic package management"):
diff --git a/nixos/tests/nebula.nix b/nixos/tests/nebula.nix
index 1bece2200f23..89b91d89fcb3 100644
--- a/nixos/tests/nebula.nix
+++ b/nixos/tests/nebula.nix
@@ -144,7 +144,6 @@ in
 
     restartAndCheckNebula = name: ip: ''
       ${name}.systemctl("restart nebula@smoke.service")
-      ${name}.wait_for_unit("nebula@smoke.service")
       ${name}.succeed("ping -c5 ${ip}")
     '';
 
@@ -180,8 +179,6 @@ in
       ${nodeB}.succeed("iptables -I INPUT -s " + node_a + " -j DROP")
       ${nodeA}.systemctl("restart nebula@smoke.service")
       ${nodeB}.systemctl("restart nebula@smoke.service")
-      ${nodeA}.wait_for_unit("nebula@smoke.service")
-      ${nodeB}.wait_for_unit("nebula@smoke.service")
     '';
     allowTrafficBetween = nodeA: nodeB: ''
       node_a = ${getPublicIp nodeA}
@@ -190,8 +187,6 @@ in
       ${nodeB}.succeed("iptables -D INPUT -s " + node_a + " -j DROP")
       ${nodeA}.systemctl("restart nebula@smoke.service")
       ${nodeB}.systemctl("restart nebula@smoke.service")
-      ${nodeA}.wait_for_unit("nebula@smoke.service")
-      ${nodeB}.wait_for_unit("nebula@smoke.service")
     '';
   in ''
     # Create the certificate and sign the lighthouse's keys.
diff --git a/nixos/tests/stub-ld.nix b/nixos/tests/stub-ld.nix
new file mode 100644
index 000000000000..25161301741b
--- /dev/null
+++ b/nixos/tests/stub-ld.nix
@@ -0,0 +1,73 @@
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "stub-ld";
+
+  nodes.machine = { lib, ... }:
+    {
+      environment.stub-ld.enable = true;
+
+      specialisation.nostub = {
+        inheritParentConfig = true;
+
+        configuration = { ... }: {
+          environment.stub-ld.enable = lib.mkForce false;
+        };
+      };
+    };
+
+  testScript = let
+    libDir = pkgs.stdenv.hostPlatform.libDir;
+    ldsoBasename = lib.last (lib.splitString "/" pkgs.stdenv.cc.bintools.dynamicLinker);
+
+    check32 = pkgs.stdenv.isx86_64;
+    pkgs32 = pkgs.pkgsi686Linux;
+
+    libDir32 = pkgs32.stdenv.hostPlatform.libDir;
+    ldsoBasename32 = lib.last (lib.splitString "/" pkgs32.stdenv.cc.bintools.dynamicLinker);
+
+    test-exec = builtins.mapAttrs (n: v: pkgs.runCommand "test-exec-${n}" { src = pkgs.fetchurl v; } "mkdir -p $out;cd $out;tar -xzf $src") {
+      x86_64-linux.url = "https://github.com/rustic-rs/rustic/releases/download/v0.6.1/rustic-v0.6.1-x86_64-unknown-linux-gnu.tar.gz";
+      x86_64-linux.hash = "sha256-3zySzx8MKFprMOi++yr2ZGASE0aRfXHQuG3SN+kWUCI=";
+      i686-linux.url = "https://github.com/rustic-rs/rustic/releases/download/v0.6.1/rustic-v0.6.1-i686-unknown-linux-gnu.tar.gz";
+      i686-linux.hash = "sha256-fWNiATFeg0B2pfB5zndlnzGn7Ztl8diVS1rFLEDnSLU=";
+      aarch64-linux.url = "https://github.com/rustic-rs/rustic/releases/download/v0.6.1/rustic-v0.6.1-aarch64-unknown-linux-gnu.tar.gz";
+      aarch64-linux.hash = "sha256-hnldbd2cctQIAhIKoEZLIWY8H3jiFBClkNy2UlyyvAs=";
+    };
+    exec-name = "rustic";
+
+    if32 = pythonStatement: if check32 then pythonStatement else "pass";
+  in
+    ''
+      machine.start()
+      machine.wait_for_unit("multi-user.target")
+
+      with subtest("Check for stub (enabled, initial)"):
+          machine.succeed('test -L /${libDir}/${ldsoBasename}')
+          ${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"}
+
+      with subtest("Try FHS executable"):
+          machine.copy_from_host('${test-exec.${pkgs.system}}','test-exec')
+          machine.succeed('if test-exec/${exec-name} 2>outfile; then false; else [ $? -eq 127 ];fi')
+          machine.succeed('grep -qi nixos outfile')
+          ${if32 "machine.copy_from_host('${test-exec.${pkgs32.system}}','test-exec32')"}
+          ${if32 "machine.succeed('if test-exec32/${exec-name} 2>outfile32; then false; else [ $? -eq 127 ];fi')"}
+          ${if32 "machine.succeed('grep -qi nixos outfile32')"}
+
+      with subtest("Disable stub"):
+          machine.succeed("/run/booted-system/specialisation/nostub/bin/switch-to-configuration test")
+
+      with subtest("Check for stub (disabled)"):
+          machine.fail('test -e /${libDir}/${ldsoBasename}')
+          ${if32 "machine.fail('test -e /${libDir32}/${ldsoBasename32}')"}
+
+      with subtest("Create file in stub location (to be overwritten)"):
+          machine.succeed('mkdir -p /${libDir};touch /${libDir}/${ldsoBasename}')
+          ${if32 "machine.succeed('mkdir -p /${libDir32};touch /${libDir32}/${ldsoBasename32}')"}
+
+      with subtest("Re-enable stub"):
+          machine.succeed("/run/booted-system/bin/switch-to-configuration test")
+
+      with subtest("Check for stub (enabled, final)"):
+          machine.succeed('test -L /${libDir}/${ldsoBasename}')
+          ${if32 "machine.succeed('test -L /${libDir32}/${ldsoBasename32}')"}
+    '';
+})
diff --git a/nixos/tests/telegraf.nix b/nixos/tests/telegraf.nix
index af9c5c387a5d..c3cdb1645213 100644
--- a/nixos/tests/telegraf.nix
+++ b/nixos/tests/telegraf.nix
@@ -12,7 +12,6 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     services.telegraf.extraConfig = {
       agent.interval = "1s";
       agent.flush_interval = "1s";
-      inputs.procstat = {};
       inputs.exec = {
         commands = [
           "${pkgs.runtimeShell} -c 'echo $SECRET,tag=a i=42i'"