about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authormaralorn <mail@maralorn.de>2023-06-26 22:57:50 +0200
committermaralorn <mail@maralorn.de>2023-06-26 22:57:50 +0200
commit0e87c159c8dce30d15a1621b500e397758bd956d (patch)
tree544f8344381d0c2eb1b2a97b4460a0494020f6d7 /nixos
parent6cd94a89355137e382f97aa22cad2a146f0ba577 (diff)
parent6e5936969052d3caf4a624ad62ec8186ce9ad63b (diff)
Merge branch 'master' into haskell-updates
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2311.section.md4
-rw-r--r--nixos/lib/qemu-common.nix2
-rw-r--r--nixos/modules/misc/version.nix2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/programs/gnupg.nix82
-rw-r--r--nixos/modules/programs/starship.nix6
-rw-r--r--nixos/modules/security/lock-kernel-modules.nix11
-rw-r--r--nixos/modules/security/sudo.nix4
-rw-r--r--nixos/modules/services/audio/roon-bridge.nix5
-rw-r--r--nixos/modules/services/audio/roon-server.nix5
-rw-r--r--nixos/modules/services/backup/restic.nix2
-rw-r--r--nixos/modules/services/games/factorio.nix2
-rw-r--r--nixos/modules/services/games/freeciv.nix2
-rw-r--r--nixos/modules/services/hardware/fwupd.nix2
-rw-r--r--nixos/modules/services/mail/davmail.nix27
-rw-r--r--nixos/modules/services/mail/spamassassin.nix2
-rw-r--r--nixos/modules/services/misc/disnix.nix4
-rw-r--r--nixos/modules/services/misc/docker-registry.nix10
-rw-r--r--nixos/modules/services/monitoring/below.nix106
-rw-r--r--nixos/modules/services/network-filesystems/glusterfs.nix4
-rw-r--r--nixos/modules/services/networking/avahi-daemon.nix17
-rw-r--r--nixos/modules/services/networking/ddclient.nix2
-rw-r--r--nixos/modules/services/networking/i2pd.nix53
-rw-r--r--nixos/modules/services/networking/prosody.nix5
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix2
-rw-r--r--nixos/modules/services/networking/vsftpd.nix2
-rw-r--r--nixos/modules/services/networking/zerobin.nix5
-rw-r--r--nixos/modules/services/security/tor.nix5
-rw-r--r--nixos/modules/services/web-apps/dex.nix2
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix4
-rw-r--r--nixos/modules/services/web-apps/nifi.nix4
-rw-r--r--nixos/modules/services/web-apps/peertube.nix4
-rw-r--r--nixos/modules/services/web-apps/pixelfed.nix2
-rw-r--r--nixos/modules/system/boot/luksroot.nix2
-rw-r--r--nixos/modules/virtualisation/nixos-containers.nix8
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix22
-rw-r--r--nixos/tests/all-tests.nix2
-rw-r--r--nixos/tests/scaphandre.nix18
-rw-r--r--nixos/tests/virtualbox.nix2
39 files changed, 329 insertions, 115 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md
index 1cff442f57c3b..172919b3192f5 100644
--- a/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -30,6 +30,8 @@
 
 - `mariadb` now defaults to `mariadb_1011` instead of `mariadb_106`, meaning the default version was upgraded from 10.6.x to 10.11.x. See the [upgrade notes](https://mariadb.com/kb/en/upgrading-from-mariadb-10-6-to-mariadb-10-11/) for potential issues.
 
+- `getent` has been moved from `glibc`'s `bin` output to its own dedicated output, reducing closure size for many dependents. Dependents using the `getent` alias should not be affected; others should move from using `glibc.bin` or `getBin glibc` to `getent` (which also improves compatibility with non-glibc platforms).
+
 - `etcd` has been updated to 3.5, you will want to read the [3.3 to 3.4](https://etcd.io/docs/v3.5/upgrades/upgrade_3_4/) and [3.4 to 3.5](https://etcd.io/docs/v3.5/upgrades/upgrade_3_5/) upgrade guides
 
 - `himalaya` has been updated to `0.8.0`, which drops the native TLS support (in favor of Rustls) and add OAuth 2.0 support. See the [release note](https://github.com/soywod/himalaya/releases/tag/v0.8.0) for more details.
@@ -52,6 +54,8 @@
 
 - The following packages in `haskellPackages` have now a separate bin output: `cabal-fmt`, `calligraphy`, `eventlog2html`, `ghc-debug-brick`, `hindent`, `nixfmt`, `releaser`. This means you need to replace e.g. `"${pkgs.haskellPackages.nixfmt}/bin/nixfmt"` with `"${lib.getBin pkgs.haskellPackages.nixfmt}/bin/nixfmt"` or `"${lib.getExe pkgs.haskellPackages.nixfmt}"`. The binaries also won’t be in scope if you rely on them being installed e.g. via `ghcWithPackages`. `environment.packages` picks the `bin` output automatically, so for normal installation no intervention is required. Also, toplevel attributes like `pkgs.nixfmt` are not impacted negatively by this change.
 
+- `spamassassin` no longer supports the `Hashcash` module. The module needs to be removed from the `loadplugin` list if it was copied over from the default `initPreConf` option.
+
 ## Other Notable Changes {#sec-release-23.11-notable-changes}
 
 - The Cinnamon module now enables XDG desktop integration by default. If you are experiencing collisions related to xdg-desktop-portal-gtk you can safely remove `xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gtk ];` from your NixOS configuration.
diff --git a/nixos/lib/qemu-common.nix b/nixos/lib/qemu-common.nix
index a8ed27dd60919..4fff2e0a6f15e 100644
--- a/nixos/lib/qemu-common.nix
+++ b/nixos/lib/qemu-common.nix
@@ -19,7 +19,7 @@ rec {
     ];
 
   qemuSerialDevice =
-    if with pkgs.stdenv.hostPlatform; isx86 || isMips64 || isRiscV then "ttyS0"
+    if with pkgs.stdenv.hostPlatform; isx86 || isLoongArch64 || isMips64 || isRiscV then "ttyS0"
     else if (with pkgs.stdenv.hostPlatform; isAarch || isPower) then "ttyAMA0"
     else throw "Unknown QEMU serial device for system '${pkgs.stdenv.hostPlatform.system}'";
 
diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix
index 780a6b2a83a61..0f55ab8a09ce6 100644
--- a/nixos/modules/misc/version.nix
+++ b/nixos/modules/misc/version.nix
@@ -32,7 +32,7 @@ let
     VARIANT_ID = cfg.variant_id;
   };
 
-  initrdReleaseContents = osReleaseContents // {
+  initrdReleaseContents = (removeAttrs osReleaseContents [ "BUILD_ID" ]) // {
     PRETTY_NAME = "${osReleaseContents.PRETTY_NAME} (Initrd)";
   };
   initrdRelease = pkgs.writeText "initrd-release" (attrsToText initrdReleaseContents);
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 2e0eac237251d..43fcc68ded42a 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -725,6 +725,7 @@
   ./services/monitoring/alerta.nix
   ./services/monitoring/apcupsd.nix
   ./services/monitoring/arbtt.nix
+  ./services/monitoring/below.nix
   ./services/monitoring/bosun.nix
   ./services/monitoring/cadvisor.nix
   ./services/monitoring/cockpit.nix
diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix
index 764a67a160c1b..3dfc53c52612c 100644
--- a/nixos/modules/programs/gnupg.nix
+++ b/nixos/modules/programs/gnupg.nix
@@ -94,38 +94,110 @@ in
   };
 
   config = mkIf cfg.agent.enable {
+    environment.etc."gnupg/gpg-agent.conf".text = ''
+      pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
+    '';
+
     # This overrides the systemd user unit shipped with the gnupg package
     systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) {
-      serviceConfig.ExecStart = [ "" ''
-        ${cfg.package}/bin/gpg-agent --supervised \
-          --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry
-      '' ];
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache";
+        Documentation = "man:gpg-agent(1)";
+        Requires = [ "gpg-agent.socket" ];
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/gpg-agent --supervised";
+        ExecReload = "${cfg.package}/bin/gpgconf --reload gpg-agent";
+      };
     };
 
     systemd.user.sockets.gpg-agent = {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache";
+        Documentation = "man:gpg-agent(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent";
+        FileDescriptorName = "std";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     systemd.user.sockets.gpg-agent-ssh = mkIf cfg.agent.enableSSHSupport {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent (ssh-agent emulation)";
+        Documentation = "man:gpg-agent(1) man:ssh-add(1) man:ssh-agent(1) man:ssh(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent.ssh";
+        FileDescriptorName = "ssh";
+        Service = "gpg-agent.service";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     systemd.user.sockets.gpg-agent-extra = mkIf cfg.agent.enableExtraSocket {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache (restricted)";
+        Documentation = "man:gpg-agent(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent.extra";
+        FileDescriptorName = "extra";
+        Service = "gpg-agent.service";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     systemd.user.sockets.gpg-agent-browser = mkIf cfg.agent.enableBrowserSocket {
+      unitConfig = {
+        Description = "GnuPG cryptographic agent and passphrase cache (access for web browsers)";
+        Documentation = "man:gpg-agent(1)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.gpg-agent.browser";
+        FileDescriptorName = "browser";
+        Service = "gpg-agent.service";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
+    systemd.user.services.dirmngr = mkIf cfg.dirmngr.enable {
+      unitConfig = {
+        Description = "GnuPG network certificate management daemon";
+        Documentation = "man:dirmngr(8)";
+        Requires = "dirmngr.socket";
+      };
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/dirmngr --supervised";
+        ExecReload = "${cfg.package}/bin/gpgconf --reload dirmngr";
+      };
+    };
+
     systemd.user.sockets.dirmngr = mkIf cfg.dirmngr.enable {
+      unitConfig = {
+        Description = "GnuPG network certificate management daemon";
+        Documentation = "man:dirmngr(8)";
+      };
+      socketConfig = {
+        ListenStream = "%t/gnupg/S.dirmngr";
+        SocketMode = "0600";
+        DirectoryMode = "0700";
+      };
       wantedBy = [ "sockets.target" ];
     };
 
     services.dbus.packages = mkIf (cfg.agent.pinentryFlavor == "gnome3") [ pkgs.gcr ];
 
     environment.systemPackages = with pkgs; [ cfg.package ];
-    systemd.packages = [ cfg.package ];
 
     environment.interactiveShellInit = ''
       # Bind gpg-agent to this TTY if gpg commands are used.
diff --git a/nixos/modules/programs/starship.nix b/nixos/modules/programs/starship.nix
index cacad8eafe3da..9dca39da5edc0 100644
--- a/nixos/modules/programs/starship.nix
+++ b/nixos/modules/programs/starship.nix
@@ -43,21 +43,21 @@ in
 
   config = mkIf cfg.enable {
     programs.bash.${initOption} = ''
-      if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
+      if [[ $TERM != "dumb" ]]; then
         export STARSHIP_CONFIG=${settingsFile}
         eval "$(${pkgs.starship}/bin/starship init bash)"
       fi
     '';
 
     programs.fish.${initOption} = ''
-      if test "$TERM" != "dumb" -a \( -z "$INSIDE_EMACS" -o "$INSIDE_EMACS" = "vterm" \)
+      if test "$TERM" != "dumb"
         set -x STARSHIP_CONFIG ${settingsFile}
         eval (${pkgs.starship}/bin/starship init fish)
       end
     '';
 
     programs.zsh.${initOption} = ''
-      if [[ $TERM != "dumb" && (-z $INSIDE_EMACS || $INSIDE_EMACS == "vterm") ]]; then
+      if [[ $TERM != "dumb" ]]; then
         export STARSHIP_CONFIG=${settingsFile}
         eval "$(${pkgs.starship}/bin/starship init zsh)"
       fi
diff --git a/nixos/modules/security/lock-kernel-modules.nix b/nixos/modules/security/lock-kernel-modules.nix
index 674ba857818ca..333b648014263 100644
--- a/nixos/modules/security/lock-kernel-modules.nix
+++ b/nixos/modules/security/lock-kernel-modules.nix
@@ -22,12 +22,11 @@ with lib;
 
   config = mkIf config.security.lockKernelModules {
     boot.kernelModules = concatMap (x:
-      if x.device != null
-        then
-          if x.fsType == "vfat"
-            then [ "vfat" "nls-cp437" "nls-iso8859-1" ]
-            else [ x.fsType ]
-        else []) config.system.build.fileSystems;
+      optionals (x.device != null) (
+        if x.fsType == "vfat"
+        then [ "vfat" "nls-cp437" "nls-iso8859-1" ]
+        else [ x.fsType ])
+      ) config.system.build.fileSystems;
 
     systemd.services.disable-kernel-module-loading = {
       description = "Disable kernel module loading";
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
index 296b61fd703b1..9ac91bd0d3686 100644
--- a/nixos/modules/security/sudo.nix
+++ b/nixos/modules/security/sudo.nix
@@ -216,10 +216,10 @@ in
         ${concatStringsSep "\n" (
           lists.flatten (
             map (
-              rule: if (length rule.commands != 0) then [
+              rule: optionals (length rule.commands != 0) [
                 (map (user: "${toUserString user}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.users)
                 (map (group: "${toGroupString group}	${rule.host}=(${rule.runAs})	${toCommandsString rule.commands}") rule.groups)
-              ] else []
+              ]
             ) cfg.extraRules
           )
         )}
diff --git a/nixos/modules/services/audio/roon-bridge.nix b/nixos/modules/services/audio/roon-bridge.nix
index 70392b647cc66..027b0332fd1ea 100644
--- a/nixos/modules/services/audio/roon-bridge.nix
+++ b/nixos/modules/services/audio/roon-bridge.nix
@@ -70,12 +70,11 @@ in {
 
     users.groups.${cfg.group} = {};
     users.users.${cfg.user} =
-      if cfg.user == "roon-bridge" then {
+      optionalAttrs (cfg.user == "roon-bridge") {
         isSystemUser = true;
         description = "Roon Bridge user";
         group = cfg.group;
         extraGroups = [ "audio" ];
-      }
-      else {};
+      };
   };
 }
diff --git a/nixos/modules/services/audio/roon-server.nix b/nixos/modules/services/audio/roon-server.nix
index fbe74f63b9dac..8691c08b0d365 100644
--- a/nixos/modules/services/audio/roon-server.nix
+++ b/nixos/modules/services/audio/roon-server.nix
@@ -76,12 +76,11 @@ in {
 
     users.groups.${cfg.group} = {};
     users.users.${cfg.user} =
-      if cfg.user == "roon-server" then {
+      optionalAttrs (cfg.user == "roon-server") {
         isSystemUser = true;
         description = "Roon Server user";
         group = cfg.group;
         extraGroups = [ "audio" ];
-      }
-      else {};
+      };
   };
 }
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index 3a951f7cbc834..1620770e5b564 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -298,7 +298,7 @@ in
           let
             extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
             resticCmd = "${backup.package}/bin/restic${extraOptions}";
-            excludeFlags = if (backup.exclude != []) then ["--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}"] else [];
+            excludeFlags = optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}";
             filesFromTmpFile = "/run/restic-backups-${name}/includes";
             backupPaths =
               if (backup.dynamicFilesFrom == null)
diff --git a/nixos/modules/services/games/factorio.nix b/nixos/modules/services/games/factorio.nix
index 9b15cac149d59..b349ffa2375f7 100644
--- a/nixos/modules/services/games/factorio.nix
+++ b/nixos/modules/services/games/factorio.nix
@@ -294,6 +294,6 @@ in
       };
     };
 
-    networking.firewall.allowedUDPPorts = if cfg.openFirewall then [ cfg.port ] else [];
+    networking.firewall.allowedUDPPorts = optional cfg.openFirewall cfg.port;
   };
 }
diff --git a/nixos/modules/services/games/freeciv.nix b/nixos/modules/services/games/freeciv.nix
index f33ea5c08a270..bba27ae4cb5f7 100644
--- a/nixos/modules/services/games/freeciv.nix
+++ b/nixos/modules/services/games/freeciv.nix
@@ -16,7 +16,7 @@ let
     generate = name: value:
       let mkParam = k: v:
             if v == null then []
-            else if isBool v then if v then [("--"+k)] else []
+            else if isBool v then optional v ("--"+k)
             else [("--"+k) v];
           mkParams = k: v: map (mkParam k) (if isList v then v else [v]);
       in escapeShellArgs (concatLists (concatLists (mapAttrsToList mkParams value)));
diff --git a/nixos/modules/services/hardware/fwupd.nix b/nixos/modules/services/hardware/fwupd.nix
index 9ea1d0cf4165d..4e5913fd27515 100644
--- a/nixos/modules/services/hardware/fwupd.nix
+++ b/nixos/modules/services/hardware/fwupd.nix
@@ -50,7 +50,7 @@ let
     # to install it because it would create a cyclic dependency between
     # the outputs. We also need to enable the remote,
     # which should not be done by default.
-    if cfg.enableTestRemote then (enableRemote cfg.package.installedTests "fwupd-tests") else {}
+    lib.optionalAttrs cfg.enableTestRemote (enableRemote cfg.package.installedTests "fwupd-tests")
   );
 
 in {
diff --git a/nixos/modules/services/mail/davmail.nix b/nixos/modules/services/mail/davmail.nix
index 483f591a72687..9cdb435af4a11 100644
--- a/nixos/modules/services/mail/davmail.nix
+++ b/nixos/modules/services/mail/davmail.nix
@@ -91,6 +91,33 @@ in
           Restart = "on-failure";
           DynamicUser = "yes";
           LogsDirectory = "davmail";
+
+          CapabilityBoundingSet = [ "" ];
+          DeviceAllow = [ "" ];
+          LockPersonality = true;
+          NoNewPrivileges = true;
+          PrivateDevices = true;
+          PrivateTmp = true;
+          PrivateUsers = true;
+          ProtectClock = true;
+          ProtectControlGroups = true;
+          ProtectHome = true;
+          ProtectSystem = "strict";
+          ProtectHostname = true;
+          ProtectKernelLogs = true;
+          ProtectKernelModules = true;
+          ProtectKernelTunables = true;
+          ProtectProc = "invisible";
+          RemoveIPC = true;
+          RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+          RestrictNamespaces = true;
+          RestrictRealtime = true;
+          RestrictSUIDSGID = true;
+          SystemCallArchitectures = "native";
+          SystemCallFilter = "@system-service";
+          SystemCallErrorNumber = "EPERM";
+          UMask = "0077";
+
         };
       };
 
diff --git a/nixos/modules/services/mail/spamassassin.nix b/nixos/modules/services/mail/spamassassin.nix
index 49d1d93159856..072172e31451a 100644
--- a/nixos/modules/services/mail/spamassassin.nix
+++ b/nixos/modules/services/mail/spamassassin.nix
@@ -77,9 +77,9 @@ in
           loadplugin Mail::SpamAssassin::Plugin::Check
           #loadplugin Mail::SpamAssassin::Plugin::DCC
           loadplugin Mail::SpamAssassin::Plugin::DKIM
+          loadplugin Mail::SpamAssassin::Plugin::DMARC
           loadplugin Mail::SpamAssassin::Plugin::DNSEval
           loadplugin Mail::SpamAssassin::Plugin::FreeMail
-          loadplugin Mail::SpamAssassin::Plugin::Hashcash
           loadplugin Mail::SpamAssassin::Plugin::HeaderEval
           loadplugin Mail::SpamAssassin::Plugin::HTMLEval
           loadplugin Mail::SpamAssassin::Plugin::HTTPSMismatch
diff --git a/nixos/modules/services/misc/disnix.nix b/nixos/modules/services/misc/disnix.nix
index 1cdfeef57cefe..13c57ce6b85bf 100644
--- a/nixos/modules/services/misc/disnix.nix
+++ b/nixos/modules/services/misc/disnix.nix
@@ -87,8 +87,8 @@ in
         environment = {
           HOME = "/root";
         }
-        // (if config.environment.variables ? DYSNOMIA_CONTAINERS_PATH then { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; } else {})
-        // (if config.environment.variables ? DYSNOMIA_MODULES_PATH then { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; } else {});
+        // (optionalAttrs (config.environment.variables ? DYSNOMIA_CONTAINERS_PATH) { inherit (config.environment.variables) DYSNOMIA_CONTAINERS_PATH; })
+        // (optionalAttrs (config.environment.variables ? DYSNOMIA_MODULES_PATH) { inherit (config.environment.variables) DYSNOMIA_MODULES_PATH; });
 
         serviceConfig.ExecStart = "${cfg.package}/bin/disnix-service";
       };
diff --git a/nixos/modules/services/misc/docker-registry.nix b/nixos/modules/services/misc/docker-registry.nix
index 8ea81f9a7ee22..b0e910634637c 100644
--- a/nixos/modules/services/misc/docker-registry.nix
+++ b/nixos/modules/services/misc/docker-registry.nix
@@ -15,9 +15,7 @@ let
     storage = {
       cache.blobdescriptor = blobCache;
       delete.enabled = cfg.enableDelete;
-    } // (if cfg.storagePath != null
-          then { filesystem.rootdirectory = cfg.storagePath; }
-          else {});
+    } // (optionalAttrs (cfg.storagePath != null) { filesystem.rootdirectory = cfg.storagePath; });
     http = {
       addr = "${cfg.listenAddress}:${builtins.toString cfg.port}";
       headers.X-Content-Type-Options = ["nosniff"];
@@ -152,12 +150,10 @@ in {
     };
 
     users.users.docker-registry =
-      (if cfg.storagePath != null
-      then {
+      (optionalAttrs (cfg.storagePath != null) {
         createHome = true;
         home = cfg.storagePath;
-      }
-      else {}) // {
+      }) // {
         group = "docker-registry";
         isSystemUser = true;
       };
diff --git a/nixos/modules/services/monitoring/below.nix b/nixos/modules/services/monitoring/below.nix
new file mode 100644
index 0000000000000..92ee3882cac87
--- /dev/null
+++ b/nixos/modules/services/monitoring/below.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.services.below;
+  cfgContents = concatStringsSep "\n" (
+    mapAttrsToList (n: v: ''${n} = "${v}"'') (filterAttrs (_k: v: v != null) {
+      log_dir = cfg.dirs.log;
+      store_dir = cfg.dirs.store;
+      cgroup_filter_out = cfg.cgroupFilterOut;
+    })
+  );
+
+  mkDisableOption = n: mkOption {
+    type = types.bool;
+    default = true;
+    description = mdDoc "Whether to enable ${n}.";
+  };
+  optionalType = ty: x: mkOption (x // {
+    description = mdDoc x.description;
+    type = (types.nullOr ty);
+    default = null;
+  });
+  optionalPath = optionalType types.path;
+  optionalStr = optionalType types.str;
+  optionalInt = optionalType types.int;
+in {
+  options = {
+    services.below = {
+      enable = mkEnableOption (mdDoc "'below' resource monitor");
+
+      cgroupFilterOut = optionalStr {
+        description = "A regexp matching the full paths of cgroups whose data shouldn't be collected";
+        example = "user.slice.*";
+      };
+      collect = {
+        diskStats = mkDisableOption "dist_stat collection";
+        ioStats   = mkEnableOption (mdDoc "io.stat collection for cgroups");
+        exitStats = mkDisableOption "eBPF-based exitstats";
+      };
+      compression.enable = mkEnableOption (mdDoc "data compression");
+      retention = {
+        size = optionalInt {
+          description = ''
+            Size limit for below's data, in bytes. Data is deleted oldest-first, in 24h 'shards'.
+
+            ::: {.note}
+            The size limit may be exceeded by at most the size of the active shard, as:
+            - the active shard cannot be deleted;
+            - the size limit is only enforced when a new shard is created.
+            :::
+          '';
+        };
+        time = optionalInt {
+          description = ''
+            Retention time, in seconds.
+
+            ::: {.note}
+            As data is stored in 24 hour shards which are discarded as a whole,
+            only data expired by 24h (or more) is guaranteed to be discarded.
+            :::
+
+            ::: {.note}
+            If `retention.size` is set, data may be discarded earlier than the specified time.
+            :::
+          '';
+        };
+      };
+      dirs = {
+        log = optionalPath { description = "Where to store below's logs"; };
+        store = optionalPath {
+          description = "Where to store below's data";
+          example = "/var/lib/below";
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.below ];
+    # /etc/below.conf is also refered to by the `below` CLI tool,
+    #  so this can't be a store-only file whose path is passed to the service
+    environment.etc."below/below.conf".text = cfgContents;
+
+    systemd = {
+      packages = [ pkgs.below ];
+      services.below = {
+        # Workaround for https://github.com/NixOS/nixpkgs/issues/81138
+        wantedBy = [ "multi-user.target" ];
+        restartTriggers = [ cfgContents ];
+
+        serviceConfig.ExecStart = [
+          ""
+          ("${lib.getExe pkgs.below} record " + (concatStringsSep " " (
+            optional (!cfg.collect.diskStats) "--disable-disk-stat" ++
+            optional   cfg.collect.ioStats    "--collect-io-stat"   ++
+            optional (!cfg.collect.exitStats) "--disable-exitstats" ++
+            optional   cfg.compression.enable "--compress"          ++
+
+            optional (cfg.retention.size != null) "--store-size-limit ${toString cfg.retention.size}" ++
+            optional (cfg.retention.time != null) "--retain-for-s ${toString cfg.retention.time}"
+          )))
+        ];
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/network-filesystems/glusterfs.nix b/nixos/modules/services/network-filesystems/glusterfs.nix
index 5c3e197b687d4..ee03bada492dc 100644
--- a/nixos/modules/services/network-filesystems/glusterfs.nix
+++ b/nixos/modules/services/network-filesystems/glusterfs.nix
@@ -15,11 +15,11 @@ let
     rm -f /var/lib/glusterd/secure-access
   '';
 
-  restartTriggers = if (cfg.tlsSettings != null) then [
+  restartTriggers = optionals (cfg.tlsSettings != null) [
     config.environment.etc."ssl/glusterfs.pem".source
     config.environment.etc."ssl/glusterfs.key".source
     config.environment.etc."ssl/glusterfs.ca".source
-  ] else [];
+  ];
 
   cfg = config.services.glusterfs;
 
diff --git a/nixos/modules/services/networking/avahi-daemon.nix b/nixos/modules/services/networking/avahi-daemon.nix
index 3a7519c7230b3..bdbf9aad9accf 100644
--- a/nixos/modules/services/networking/avahi-daemon.nix
+++ b/nixos/modules/services/networking/avahi-daemon.nix
@@ -56,6 +56,15 @@ in
       '';
     };
 
+    package = mkOption {
+      type = types.package;
+      default = pkgs.avahi;
+      defaultText = literalExpression "pkgs.avahi";
+      description = lib.mdDoc ''
+        The avahi package to use for running the daemon.
+      '';
+    };
+
     hostName = mkOption {
       type = types.str;
       default = config.networking.hostName;
@@ -260,7 +269,7 @@ in
       (mkAfter [ "mdns" ]) # after dns
     ]);
 
-    environment.systemPackages = [ pkgs.avahi ];
+    environment.systemPackages = [ cfg.package ];
 
     environment.etc = (mapAttrs'
       (n: v: nameValuePair
@@ -286,19 +295,19 @@ in
       # return a sensible value.
       environment.LD_LIBRARY_PATH = config.system.nssModules.path;
 
-      path = [ pkgs.coreutils pkgs.avahi ];
+      path = [ pkgs.coreutils cfg.package ];
 
       serviceConfig = {
         NotifyAccess = "main";
         BusName = "org.freedesktop.Avahi";
         Type = "dbus";
-        ExecStart = "${pkgs.avahi}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}";
+        ExecStart = "${cfg.package}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}";
         ConfigurationDirectory = "avahi/services";
       };
     };
 
     services.dbus.enable = true;
-    services.dbus.packages = [ pkgs.avahi ];
+    services.dbus.packages = [ cfg.package ];
 
     networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 5353 ];
   };
diff --git a/nixos/modules/services/networking/ddclient.nix b/nixos/modules/services/networking/ddclient.nix
index 6b21ea5b52437..4985a2dd4b212 100644
--- a/nixos/modules/services/networking/ddclient.nix
+++ b/nixos/modules/services/networking/ddclient.nix
@@ -48,7 +48,7 @@ with lib;
     (mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ]
       (config:
         let value = getAttrFromPath [ "services" "ddclient" "domain" ] config;
-        in if value != "" then [ value ] else []))
+        in optional (value != "") value))
     (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "")
     (mkRemovedOptionModule [ "services" "ddclient" "password" ] "Use services.ddclient.passwordFile instead.")
     (mkRemovedOptionModule [ "services" "ddclient" "ipv6" ] "")
diff --git a/nixos/modules/services/networking/i2pd.nix b/nixos/modules/services/networking/i2pd.nix
index 3f6cb97296b50..c940324ad0964 100644
--- a/nixos/modules/services/networking/i2pd.nix
+++ b/nixos/modules/services/networking/i2pd.nix
@@ -169,15 +169,15 @@ let
         (boolOpt "enabled" proto.enable)
         (strOpt "address" proto.address)
         (intOpt "port" proto.port)
-        ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else [])
-        ++ (if proto ? auth then optionalNullBool "auth" proto.auth else [])
-        ++ (if proto ? user then optionalNullString "user" proto.user else [])
-        ++ (if proto ? pass then optionalNullString "pass" proto.pass else [])
-        ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else [])
-        ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else [])
-        ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else [])
-        ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else [])
-        ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []);
+        ] ++ (optionals (proto ? keys) (optionalNullString "keys" proto.keys))
+        ++ (optionals (proto ? auth) (optionalNullBool "auth" proto.auth))
+        ++ (optionals (proto ? user) (optionalNullString "user" proto.user))
+        ++ (optionals (proto ? pass) (optionalNullString "pass" proto.pass))
+        ++ (optionals (proto ? strictHeaders) (optionalNullBool "strictheaders" proto.strictHeaders))
+        ++ (optionals (proto ? hostname) (optionalNullString "hostname" proto.hostname))
+        ++ (optionals (proto ? outproxy) (optionalNullString "outproxy" proto.outproxy))
+        ++ (optionals (proto ? outproxyPort) (optionalNullInt "outproxyport" proto.outproxyPort))
+        ++ (optionals (proto ? outproxyEnable) (optionalNullBool "outproxy.enabled" proto.outproxyEnable));
         in (concatStringsSep "\n" protoOpts)
       ));
   in
@@ -192,21 +192,14 @@ let
         "type = client"
         (intOpt "port" tun.port)
         (strOpt "destination" tun.destination)
-        ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else [])
-        ++ (if tun ? keys then
-            optionalNullString "keys" tun.keys else [])
-        ++ (if tun ? address then
-            optionalNullString "address" tun.address else [])
-        ++ (if tun ? inbound.length then
-            optionalNullInt "inbound.length" tun.inbound.length else [])
-        ++ (if tun ? inbound.quantity then
-            optionalNullInt "inbound.quantity" tun.inbound.quantity else [])
-        ++ (if tun ? outbound.length then
-            optionalNullInt "outbound.length" tun.outbound.length else [])
-        ++ (if tun ? outbound.quantity then
-            optionalNullInt "outbound.quantity" tun.outbound.quantity else [])
-        ++ (if tun ? crypto.tagsToSend then
-            optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []);
+        ] ++ (optionals (tun ? destinationPort) (optionalNullInt "destinationport" tun.destinationPort))
+        ++ (optionals (tun ? keys) (optionalNullString "keys" tun.keys))
+        ++ (optionals (tun ? address) (optionalNullString "address" tun.address))
+        ++ (optionals (tun ? inbound.length) (optionalNullInt "inbound.length" tun.inbound.length))
+        ++ (optionals (tun ? inbound.quantity) (optionalNullInt "inbound.quantity" tun.inbound.quantity))
+        ++ (optionals (tun ? outbound.length) (optionalNullInt "outbound.length" tun.outbound.length))
+        ++ (optionals (tun ? outbound.quantity) (optionalNullInt "outbound.quantity" tun.outbound.quantity))
+        ++ (optionals (tun ? crypto.tagsToSend) (optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend));
         in concatStringsSep "\n" outTunOpts))
     (flip map
       (collect (tun: tun ? port && tun ? address) cfg.inTunnels)
@@ -215,14 +208,10 @@ let
         "type = server"
         (intOpt "port" tun.port)
         (strOpt "host" tun.address)
-      ] ++ (if tun ? destination then
-            optionalNullString "destination" tun.destination else [])
-        ++ (if tun ? keys then
-            optionalNullString "keys" tun.keys else [])
-        ++ (if tun ? inPort then
-            optionalNullInt "inport" tun.inPort else [])
-        ++ (if tun ? accessList then
-            optionalEmptyList "accesslist" tun.accessList else []);
+      ] ++ (optionals (tun ? destination) (optionalNullString "destination" tun.destination))
+        ++ (optionals (tun ? keys) (optionalNullString "keys" tun.keys))
+        ++ (optionals (tun ? inPort) (optionalNullInt "inport" tun.inPort))
+        ++ (optionals (tun ? accessList) (optionalEmptyList "accesslist" tun.accessList));
         in concatStringsSep "\n" inTunOpts))];
     in pkgs.writeText "i2pd-tunnels.conf" opts;
 
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index 9f68853f9fa83..0066c77438f41 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -757,9 +757,8 @@ in
 
     environment.etc."prosody/prosody.cfg.lua".text =
       let
-        httpDiscoItems = if (cfg.uploadHttp != null)
-            then [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}]
-            else [];
+        httpDiscoItems = optionals (cfg.uploadHttp != null)
+            [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}];
         mucDiscoItems = builtins.foldl'
             (acc: muc: [{ url = muc.domain; description = "${muc.domain} MUC endpoint";}] ++ acc)
             []
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 59980a4cef9c7..fb9774bafdeba 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -528,7 +528,7 @@ in
 
       };
 
-    networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else [];
+    networking.firewall.allowedTCPPorts = optionals cfg.openFirewall cfg.ports;
 
     security.pam.services.sshd =
       { startSession = true;
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index b1f0f7403243f..318ceb4e5094e 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -305,7 +305,7 @@ in
 
     # If you really have to access root via FTP use mkOverride or userlistDeny
     # = false and whitelist root
-    services.vsftpd.userlist = if cfg.userlistDeny then ["root"] else [];
+    services.vsftpd.userlist = optional cfg.userlistDeny "root";
 
     systemd = {
       tmpfiles.rules = optional cfg.anonymousUser
diff --git a/nixos/modules/services/networking/zerobin.nix b/nixos/modules/services/networking/zerobin.nix
index 9e07666f3e140..735d4fa25fb16 100644
--- a/nixos/modules/services/networking/zerobin.nix
+++ b/nixos/modules/services/networking/zerobin.nix
@@ -75,13 +75,12 @@ in
 
     config = mkIf (cfg.enable) {
       users.users.${cfg.user} =
-      if cfg.user == "zerobin" then {
+      optionalAttrs (cfg.user == "zerobin") {
         isSystemUser = true;
         group = cfg.group;
         home = cfg.dataDir;
         createHome = true;
-      }
-      else {};
+      };
       users.groups.${cfg.group} = {};
 
       systemd.services.zerobin = {
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index 2aa2964f88185..9e786eb2bf06c 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -769,7 +769,7 @@ in
           };
           options.SOCKSPort = mkOption {
             description = lib.mdDoc (descriptionGeneric "SOCKSPort");
-            default = if cfg.settings.HiddenServiceNonAnonymousMode == true then [{port = 0;}] else [];
+            default = lib.optionals cfg.settings.HiddenServiceNonAnonymousMode [{port = 0;}];
             defaultText = literalExpression ''
               if config.${opt.settings}.HiddenServiceNonAnonymousMode == true
               then [ { port = 0; } ]
@@ -897,8 +897,7 @@ in
       allowedTCPPorts =
         concatMap (o:
           if isInt o && o > 0 then [o]
-          else if o ? "port" && isInt o.port && o.port > 0 then [o.port]
-          else []
+          else optionals (o ? "port" && isInt o.port && o.port > 0) [o.port]
         ) (flatten [
           cfg.settings.ORPort
           cfg.settings.DirPort
diff --git a/nixos/modules/services/web-apps/dex.nix b/nixos/modules/services/web-apps/dex.nix
index f69f1749aeb83..bd041db007a1e 100644
--- a/nixos/modules/services/web-apps/dex.nix
+++ b/nixos/modules/services/web-apps/dex.nix
@@ -6,7 +6,7 @@ let
   cfg = config.services.dex;
   fixClient = client: if client ? secretFile then ((builtins.removeAttrs client [ "secretFile" ]) // { secret = client.secretFile; }) else client;
   filteredSettings = mapAttrs (n: v: if n == "staticClients" then (builtins.map fixClient v) else v) cfg.settings;
-  secretFiles = flatten (builtins.map (c: if c ? secretFile then [ c.secretFile ] else []) (cfg.settings.staticClients or []));
+  secretFiles = flatten (builtins.map (c: optional (c ? secretFile) c.secretFile) (cfg.settings.staticClients or []));
 
   settingsFormat = pkgs.formats.yaml {};
   configFile = settingsFormat.generate "config.yaml" filteredSettings;
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index 2ad6cd6aae194..2aab97438b7d6 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -91,9 +91,7 @@ let
 
   envFile = pkgs.writeText "mastodon.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
-      if value != null then [
-        "${name}=\"${toString value}\""
-      ] else []
+      lib.optional (value != null) ''${name}="${toString value}"''
     ) env))));
 
   mastodonTootctl = let
diff --git a/nixos/modules/services/web-apps/nifi.nix b/nixos/modules/services/web-apps/nifi.nix
index f643e24d81d99..5ce5610778364 100644
--- a/nixos/modules/services/web-apps/nifi.nix
+++ b/nixos/modules/services/web-apps/nifi.nix
@@ -13,9 +13,7 @@ let
 
   envFile = pkgs.writeText "nifi.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
-      if value != null then [
-        "${name}=\"${toString value}\""
-      ] else []
+      lib.optional (value != null) ''${name}="${toString value}"''
     ) env))));
 
   nifiEnv = pkgs.writeShellScriptBin "nifi-env" ''
diff --git a/nixos/modules/services/web-apps/peertube.nix b/nixos/modules/services/web-apps/peertube.nix
index 4ef2d7dce532c..4826b2cab6a32 100644
--- a/nixos/modules/services/web-apps/peertube.nix
+++ b/nixos/modules/services/web-apps/peertube.nix
@@ -52,9 +52,7 @@ let
 
   envFile = pkgs.writeText "peertube.env" (lib.concatMapStrings (s: s + "\n") (
     (lib.concatLists (lib.mapAttrsToList (name: value:
-      if value != null then [
-        "${name}=\"${toString value}\""
-      ] else []
+      lib.optional (value != null) ''${name}="${toString value}"''
     ) env))));
 
   peertubeEnv = pkgs.writeShellScriptBin "peertube-env" ''
diff --git a/nixos/modules/services/web-apps/pixelfed.nix b/nixos/modules/services/web-apps/pixelfed.nix
index b0c92aac979f6..a696d3f9323fa 100644
--- a/nixos/modules/services/web-apps/pixelfed.nix
+++ b/nixos/modules/services/web-apps/pixelfed.nix
@@ -24,7 +24,7 @@ let
     if [[ "$USER" != ${user} ]]; then
       sudo='exec /run/wrappers/bin/sudo -u ${user}'
     fi
-    $sudo ${cfg.phpPackage}/bin/php artisan "$@"
+    $sudo ${phpPackage}/bin/php artisan "$@"
   '';
   dbSocket = {
     "pgsql" = "/run/postgresql";
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 71036044a2dce..dc3fe163116e1 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -980,7 +980,7 @@ in
       ++ luks.cryptoModules
       # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged
       # remove once 'modprobe --show-depends xts' shows ecb as a dependency
-      ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []);
+      ++ (optional (builtins.elem "xts" luks.cryptoModules) "ecb");
 
     # copy the cryptsetup binary and it's dependencies
     boot.initrd.extraUtilsCommands = let
diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix
index c3949564d4bde..5df9942dbc049 100644
--- a/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixos/modules/virtualisation/nixos-containers.nix
@@ -800,14 +800,14 @@ in
       # declarative containers
       ++ (mapAttrsToList (name: cfg: nameValuePair "container@${name}" (let
           containerConfig = cfg // (
-          if cfg.enableTun then
+          optionalAttrs cfg.enableTun
             {
               allowedDevices = cfg.allowedDevices
                 ++ [ { node = "/dev/net/tun"; modifier = "rw"; } ];
               additionalCapabilities = cfg.additionalCapabilities
                 ++ [ "CAP_NET_ADMIN" ];
             }
-          else {});
+          );
         in
           recursiveUpdate unit {
             preStart = preStartScript containerConfig;
@@ -817,7 +817,7 @@ in
             unitConfig.RequiresMountsFor = lib.optional (!containerConfig.ephemeral) "${stateDirectory}/%i";
             environment.root = if containerConfig.ephemeral then "/run/nixos-containers/%i" else "${stateDirectory}/%i";
           } // (
-          if containerConfig.autoStart then
+          optionalAttrs containerConfig.autoStart
             {
               wantedBy = [ "machines.target" ];
               wants = [ "network.target" ];
@@ -828,7 +828,7 @@ in
               ];
               restartIfChanged = true;
             }
-          else {})
+          )
       )) config.containers)
     ));
 
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index f576676fa335c..4aac0fa90e8bd 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -18,6 +18,8 @@ let
 
   qemu = cfg.qemu.package;
 
+  hostPkgs = cfg.host.pkgs;
+
   consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
 
   driveOpts = { ... }: {
@@ -84,9 +86,9 @@ let
   # Shell script to start the VM.
   startVM =
     ''
-      #! ${cfg.host.pkgs.runtimeShell}
+      #! ${hostPkgs.runtimeShell}
 
-      export PATH=${makeBinPath [ cfg.host.pkgs.coreutils ]}''${PATH:+:}$PATH
+      export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
 
       set -e
 
@@ -97,7 +99,7 @@ let
         local size=$2
         local temp=$(mktemp)
         ${qemu}/bin/qemu-img create -f raw "$temp" "$size"
-        ${pkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
+        ${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
         ${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
         rm "$temp"
       }
@@ -142,17 +144,17 @@ let
           else ''
             (
               cd ${builtins.storeDir}
-              ${pkgs.erofs-utils}/bin/mkfs.erofs \
+              ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
                 --force-uid=0 \
                 --force-gid=0 \
                 -L ${nixStoreFilesystemLabel} \
                 -U eb176051-bd15-49b7-9e6b-462e0b467019 \
                 -T 0 \
                 --exclude-regex="$(
-                  <${pkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
+                  <${hostPkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
                     sed -e 's^.*/^^g' \
                   | cut -c -10 \
-                  | ${pkgs.python3}/bin/python ${./includes-to-excludes.py} )" \
+                  | ${hostPkgs.python3}/bin/python ${./includes-to-excludes.py} )" \
                 "$TMPDIR"/store.img \
                 . \
                 </dev/null >/dev/null
@@ -214,7 +216,7 @@ let
     '';
 
 
-  regInfo = pkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
+  regInfo = hostPkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
 
   # Use well-defined and persistent filesystem labels to identify block devices.
   rootFilesystemLabel = "nixos";
@@ -644,7 +646,7 @@ in
       package =
         mkOption {
           type = types.package;
-          default = cfg.host.pkgs.qemu_kvm;
+          default = hostPkgs.qemu_kvm;
           defaultText = literalExpression "config.virtualisation.host.pkgs.qemu_kvm";
           example = literalExpression "pkgs.qemu_test";
           description = lib.mdDoc "QEMU package to use.";
@@ -1180,14 +1182,14 @@ in
 
     services.qemuGuest.enable = cfg.qemu.guestAgent.enable;
 
-    system.build.vm = cfg.host.pkgs.runCommand "nixos-vm" {
+    system.build.vm = hostPkgs.runCommand "nixos-vm" {
       preferLocalBuild = true;
       meta.mainProgram = "run-${config.system.name}-vm";
     }
       ''
         mkdir -p $out/bin
         ln -s ${config.system.build.toplevel} $out/system
-        ln -s ${cfg.host.pkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
+        ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
       '';
 
     # When building a regular system configuration, override whatever
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index bfc6e11025d85..6e9c893c809e4 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -597,6 +597,7 @@ in {
   php = handleTest ./php {};
   php81 = handleTest ./php { php = pkgs.php81; };
   php82 = handleTest ./php { php = pkgs.php82; };
+  php83 = handleTest ./php { php = pkgs.php83; };
   phylactery = handleTest ./web-apps/phylactery.nix {};
   pict-rs = handleTest ./pict-rs.nix {};
   pinnwand = handleTest ./pinnwand.nix {};
@@ -670,6 +671,7 @@ in {
   samba = handleTest ./samba.nix {};
   samba-wsdd = handleTest ./samba-wsdd.nix {};
   sanoid = handleTest ./sanoid.nix {};
+  scaphandre = handleTest ./scaphandre.nix {};
   schleuder = handleTest ./schleuder.nix {};
   sddm = handleTest ./sddm.nix {};
   seafile = handleTest ./seafile.nix {};
diff --git a/nixos/tests/scaphandre.nix b/nixos/tests/scaphandre.nix
new file mode 100644
index 0000000000000..f0a411748503b
--- /dev/null
+++ b/nixos/tests/scaphandre.nix
@@ -0,0 +1,18 @@
+import ./make-test-python.nix {
+  name = "scaphandre";
+
+  nodes = {
+    scaphandre = { pkgs, ... } : {
+      boot.kernelModules = [ "intel_rapl_common" ];
+
+      environment.systemPackages = [ pkgs.scaphandre ];
+    };
+  };
+
+  testScript = { nodes, ... } : ''
+    scaphandre.start()
+    scaphandre.wait_until_succeeds(
+        "scaphandre stdout -t 4",
+    )
+  '';
+}
diff --git a/nixos/tests/virtualbox.nix b/nixos/tests/virtualbox.nix
index 1ebd99f5d75dc..062b125eb611e 100644
--- a/nixos/tests/virtualbox.nix
+++ b/nixos/tests/virtualbox.nix
@@ -519,4 +519,4 @@ in mapAttrs (mkVBoxTest false vboxVMs) {
     destroy_vm_test1()
     destroy_vm_test2()
   '';
-} // (if enableUnfree then unfreeTests else {})
+} // (lib.optionalAttrs enableUnfree unfreeTests)