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/development/option-types.section.md7
-rw-r--r--nixos/modules/config/nix.nix8
-rw-r--r--nixos/modules/config/users-groups.nix2
-rw-r--r--nixos/modules/hardware/all-firmware.nix5
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/programs/git.nix9
-rw-r--r--nixos/modules/programs/mininet.nix35
-rw-r--r--nixos/modules/programs/wayland/river.nix3
-rw-r--r--nixos/modules/services/audio/wyoming/faster-whisper.nix1
-rw-r--r--nixos/modules/services/audio/wyoming/piper.nix1
-rw-r--r--nixos/modules/services/backup/btrbk.nix4
-rw-r--r--nixos/modules/services/backup/postgresql-backup.nix5
-rw-r--r--nixos/modules/services/backup/restic.nix3
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix1
-rw-r--r--nixos/modules/services/display-managers/greetd.nix12
-rw-r--r--nixos/modules/services/games/teeworlds.nix2
-rw-r--r--nixos/modules/services/home-automation/home-assistant.nix18
-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.nix387
-rw-r--r--nixos/modules/services/monitoring/prometheus/default.nix19
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/kea.nix8
-rw-r--r--nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix22
-rw-r--r--nixos/modules/services/network-filesystems/drbd.nix4
-rw-r--r--nixos/modules/services/network-filesystems/kubo.nix2
-rw-r--r--nixos/modules/services/networking/ejabberd.nix6
-rw-r--r--nixos/modules/services/networking/kea.nix22
-rw-r--r--nixos/modules/services/networking/nebula.nix2
-rw-r--r--nixos/modules/services/networking/nix-serve.nix4
-rw-r--r--nixos/modules/services/networking/syncthing.nix11
-rw-r--r--nixos/modules/services/networking/teamspeak3.nix42
-rw-r--r--nixos/modules/services/torrent/transmission.nix8
-rw-r--r--nixos/modules/services/video/frigate.nix1
-rw-r--r--nixos/modules/services/web-apps/keycloak.nix14
-rw-r--r--nixos/modules/services/web-apps/mastodon.nix17
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix8
-rw-r--r--nixos/modules/services/web-apps/wordpress.nix2
-rw-r--r--nixos/modules/services/web-servers/caddy/default.nix6
-rw-r--r--nixos/modules/services/web-servers/tomcat.nix2
-rw-r--r--nixos/modules/system/boot/networkd.nix3
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix4
-rw-r--r--nixos/modules/tasks/network-interfaces.nix2
-rw-r--r--nixos/modules/virtualisation/lxd-agent.nix8
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/caddy.nix15
-rw-r--r--nixos/tests/fontconfig-default-fonts.nix1
-rw-r--r--nixos/tests/guix/basic.nix42
-rw-r--r--nixos/tests/guix/default.nix8
-rw-r--r--nixos/tests/guix/publish.nix95
-rw-r--r--nixos/tests/guix/scripts/add-existing-files-to-store.scm52
-rw-r--r--nixos/tests/guix/scripts/create-file-to-store.scm8
-rw-r--r--nixos/tests/home-assistant.nix4
-rw-r--r--nixos/tests/incus/virtual-machine.nix3
-rw-r--r--nixos/tests/kernel-generic.nix1
-rw-r--r--nixos/tests/munin.nix2
-rw-r--r--nixos/tests/nextcloud/default.nix2
-rw-r--r--nixos/tests/prometheus-exporters.nix4
-rw-r--r--nixos/tests/tomcat.nix3
58 files changed, 837 insertions, 147 deletions
diff --git a/nixos/doc/manual/development/option-types.section.md b/nixos/doc/manual/development/option-types.section.md
index 2ad3d6c4f9495..f9c7ac80018e4 100644
--- a/nixos/doc/manual/development/option-types.section.md
+++ b/nixos/doc/manual/development/option-types.section.md
@@ -13,6 +13,13 @@ merging is handled.
 `types.bool`
 
 :   A boolean, its values can be `true` or `false`.
+    All definitions must have the same value, after priorities. An error is thrown in case of a conflict.
+
+`types.boolByOr`
+
+:   A boolean, its values can be `true` or `false`.
+    The result is `true` if _any_ of multiple definitions is `true`.
+    In other words, definitions are merged with the logical _OR_ operator.
 
 `types.path`
 
diff --git a/nixos/modules/config/nix.nix b/nixos/modules/config/nix.nix
index cee4f54db0cb5..2769d8b25ef6f 100644
--- a/nixos/modules/config/nix.nix
+++ b/nixos/modules/config/nix.nix
@@ -109,13 +109,17 @@ let
         if pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform then ''
           echo "Ignoring validation for cross-compilation"
         ''
-        else ''
+        else
+        let
+          showCommand = if isNixAtLeast "2.20pre" then "config show" else "show-config";
+        in
+        ''
           echo "Validating generated nix.conf"
           ln -s $out ./nix.conf
           set -e
           set +o pipefail
           NIX_CONF_DIR=$PWD \
-            ${cfg.package}/bin/nix show-config ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
+            ${cfg.package}/bin/nix ${showCommand} ${optionalString (isNixAtLeast "2.3pre") "--no-net"} \
               ${optionalString (isNixAtLeast "2.4pre") "--option experimental-features nix-command"} \
             |& sed -e 's/^warning:/error:/' \
             | (! grep '${if cfg.checkAllErrors then "^error:" else "^error: unknown setting"}')
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 39aac9fb821bd..2aed620eb154c 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/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
index 6f58e848b38ae..a97c8c418c865 100644
--- a/nixos/modules/hardware/all-firmware.nix
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -48,10 +48,7 @@ in {
         alsa-firmware
         sof-firmware
         libreelec-dvb-firmware
-      ] ++ optional pkgs.stdenv.hostPlatform.isAarch raspberrypiWirelessFirmware
-        ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "4.13") [
-        rtl8723bs-firmware
-      ];
+      ] ++ optional pkgs.stdenv.hostPlatform.isAarch raspberrypiWirelessFirmware;
     })
     (mkIf cfg.enableAllFirmware {
       assertions = [{
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index fe2e0aed24842..0cc80f2906b3e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -677,6 +677,7 @@
   ./services/misc/gollum.nix
   ./services/misc/gpsd.nix
   ./services/misc/greenclip.nix
+  ./services/misc/guix
   ./services/misc/headphones.nix
   ./services/misc/heisenbridge.nix
   ./services/misc/homepage-dashboard.nix
diff --git a/nixos/modules/programs/git.nix b/nixos/modules/programs/git.nix
index 4e271a8c134b2..710dee349d590 100644
--- a/nixos/modules/programs/git.nix
+++ b/nixos/modules/programs/git.nix
@@ -58,6 +58,10 @@ in
         '';
       };
 
+      prompt = {
+        enable = mkEnableOption "automatically sourcing git-prompt.sh. This does not change $PS1; it simply provides relevant utility functions";
+      };
+
       lfs = {
         enable = mkEnableOption (lib.mdDoc "git-lfs");
 
@@ -89,6 +93,11 @@ in
         };
       };
     })
+    (mkIf (cfg.enable && cfg.prompt.enable) {
+      environment.interactiveShellInit = ''
+        source ${cfg.package}/share/bash-completion/completions/git-prompt.sh
+      '';
+    })
   ];
 
   meta.maintainers = with maintainers; [ figsoda ];
diff --git a/nixos/modules/programs/mininet.nix b/nixos/modules/programs/mininet.nix
index 01ffd811e70e2..3568736854d8e 100644
--- a/nixos/modules/programs/mininet.nix
+++ b/nixos/modules/programs/mininet.nix
@@ -6,39 +6,6 @@ with lib;
 
 let
   cfg = config.programs.mininet;
-
-  telnet = pkgs.runCommand "inetutils-telnet"
-    { }
-    ''
-      mkdir -p $out/bin
-      ln -s ${pkgs.inetutils}/bin/telnet $out/bin
-    '';
-
-  generatedPath = with pkgs; makeSearchPath "bin" [
-    iperf
-    ethtool
-    iproute2
-    socat
-    # mn errors out without a telnet binary
-    # pkgs.inetutils brings an undesired ifconfig into PATH see #43105
-    nettools
-    telnet
-  ];
-
-  pyEnv = pkgs.python3.withPackages (ps: [ ps.mininet-python ]);
-
-  mnexecWrapped = pkgs.runCommand "mnexec-wrapper"
-    { nativeBuildInputs = [ pkgs.makeWrapper pkgs.python3Packages.wrapPython ]; }
-    ''
-      makeWrapper ${pkgs.mininet}/bin/mnexec \
-        $out/bin/mnexec \
-        --prefix PATH : "${generatedPath}"
-
-      makeWrapper ${pyEnv}/bin/mn \
-        $out/bin/mn \
-        --prefix PYTHONPATH : "${pyEnv}/${pyEnv.sitePackages}" \
-        --prefix PATH : "${generatedPath}"
-    '';
 in
 {
   options.programs.mininet.enable = mkEnableOption (lib.mdDoc "Mininet");
@@ -47,6 +14,6 @@ in
 
     virtualisation.vswitch.enable = true;
 
-    environment.systemPackages = [ mnexecWrapped ];
+    environment.systemPackages = [ pkgs.mininet ];
   };
 }
diff --git a/nixos/modules/programs/wayland/river.nix b/nixos/modules/programs/wayland/river.nix
index 71232a7d2618c..84ee281f6004d 100644
--- a/nixos/modules/programs/wayland/river.nix
+++ b/nixos/modules/programs/wayland/river.nix
@@ -51,6 +51,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/audio/wyoming/faster-whisper.nix b/nixos/modules/services/audio/wyoming/faster-whisper.nix
index 2d56acdc1b4cd..8379f50f1905f 100644
--- a/nixos/modules/services/audio/wyoming/faster-whisper.nix
+++ b/nixos/modules/services/audio/wyoming/faster-whisper.nix
@@ -121,6 +121,7 @@ in
   in mkIf (cfg.servers != {}) {
     systemd.services = mapAttrs' (server: options:
       nameValuePair "wyoming-faster-whisper-${server}" {
+        inherit (options) enable;
         description = "Wyoming faster-whisper server instance ${server}";
         after = [
           "network-online.target"
diff --git a/nixos/modules/services/audio/wyoming/piper.nix b/nixos/modules/services/audio/wyoming/piper.nix
index ed50bd9f48e95..4944c958a2da7 100644
--- a/nixos/modules/services/audio/wyoming/piper.nix
+++ b/nixos/modules/services/audio/wyoming/piper.nix
@@ -116,6 +116,7 @@ in
   in mkIf (cfg.servers != {}) {
     systemd.services = mapAttrs' (server: options:
       nameValuePair "wyoming-piper-${server}" {
+        inherit (options) enable;
         description = "Wyoming Piper server instance ${server}";
         after = [
           "network-online.target"
diff --git a/nixos/modules/services/backup/btrbk.nix b/nixos/modules/services/backup/btrbk.nix
index 1e90ef54d33f9..3cbbf0f1bd5c0 100644
--- a/nixos/modules/services/backup/btrbk.nix
+++ b/nixos/modules/services/backup/btrbk.nix
@@ -13,7 +13,7 @@ let
     mkIf
     mkOption
     optionalString
-    sort
+    sortOn
     types
     ;
 
@@ -37,7 +37,7 @@ let
   genConfig = set:
     let
       pairs = mapAttrsToList (name: value: { inherit name value; }) set;
-      sortedPairs = sort (a: b: prioOf a < prioOf b) pairs;
+      sortedPairs = sortOn prioOf pairs;
     in
       concatMap genPair sortedPairs;
   genSection = sec: secName: value:
diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix
index d3c6f3104fc54..82067d8ade34d 100644
--- a/nixos/modules/services/backup/postgresql-backup.nix
+++ b/nixos/modules/services/backup/postgresql-backup.nix
@@ -17,8 +17,8 @@ let
 
       compressCmd = getAttr cfg.compression {
         "none" = "cat";
-        "gzip" = "${pkgs.gzip}/bin/gzip -c -${toString cfg.compressionLevel}";
-        "zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel}";
+        "gzip" = "${pkgs.gzip}/bin/gzip -c -${toString cfg.compressionLevel} --rsyncable";
+        "zstd" = "${pkgs.zstd}/bin/zstd -c -${toString cfg.compressionLevel} --rsyncable";
       };
 
       mkSqlPath = prefix: suffix: "${cfg.location}/${db}${prefix}.sql${suffix}";
@@ -178,4 +178,5 @@ in {
     })
   ];
 
+  meta.maintainers = with lib.maintainers; [ Scrumplex ];
 }
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix
index 87595f39796d9..d7cd6a29c52c6 100644
--- a/nixos/modules/services/backup/restic.nix
+++ b/nixos/modules/services/backup/restic.nix
@@ -391,10 +391,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/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
index e4d54b0cb0f49..11f30e6e49063 100644
--- a/nixos/modules/services/continuous-integration/jenkins/default.nix
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -241,6 +241,7 @@ in {
 
       serviceConfig = {
         User = cfg.user;
+        StateDirectory = mkIf (hasPrefix "/var/lib/jenkins" cfg.home) "jenkins";
       };
     };
   };
diff --git a/nixos/modules/services/display-managers/greetd.nix b/nixos/modules/services/display-managers/greetd.nix
index 89cb81f3a78f6..926ec1a963ba1 100644
--- a/nixos/modules/services/display-managers/greetd.nix
+++ b/nixos/modules/services/display-managers/greetd.nix
@@ -4,7 +4,7 @@ with lib;
 let
   cfg = config.services.greetd;
   tty = "tty${toString cfg.vt}";
-  settingsFormat = pkgs.formats.toml {};
+  settingsFormat = pkgs.formats.toml { };
 in
 {
   options.services.greetd = {
@@ -32,7 +32,7 @@ in
       '';
     };
 
-    vt = mkOption  {
+    vt = mkOption {
       type = types.int;
       default = 1;
       description = lib.mdDoc ''
@@ -102,12 +102,18 @@ in
 
     systemd.defaultUnit = "graphical.target";
 
+    # Create directories potentially required by supported greeters
+    # See https://github.com/NixOS/nixpkgs/issues/248323
+    systemd.tmpfiles.rules = [
+      "d '/var/cache/tuigreet' - greeter greeter - -"
+    ];
+
     users.users.greeter = {
       isSystemUser = true;
       group = "greeter";
     };
 
-    users.groups.greeter = {};
+    users.groups.greeter = { };
   };
 
   meta.maintainers = with maintainers; [ queezle ];
diff --git a/nixos/modules/services/games/teeworlds.nix b/nixos/modules/services/games/teeworlds.nix
index ffef440330c4e..bd0df1ffca578 100644
--- a/nixos/modules/services/games/teeworlds.nix
+++ b/nixos/modules/services/games/teeworlds.nix
@@ -100,7 +100,7 @@ in
 
       serviceConfig = {
         DynamicUser = true;
-        ExecStart = "${pkgs.teeworlds}/bin/teeworlds_srv -f ${teeworldsConf}";
+        ExecStart = "${pkgs.teeworlds-server}/bin/teeworlds_srv -f ${teeworldsConf}";
 
         # Hardening
         CapabilityBoundingSet = false;
diff --git a/nixos/modules/services/home-automation/home-assistant.nix b/nixos/modules/services/home-automation/home-assistant.nix
index 54fd3e17292f6..f2cda9e83575a 100644
--- a/nixos/modules/services/home-automation/home-assistant.nix
+++ b/nixos/modules/services/home-automation/home-assistant.nix
@@ -11,14 +11,12 @@ let
   # options shown in settings.
   # We post-process the result to add support for YAML functions, like secrets or includes, see e.g.
   # https://www.home-assistant.io/docs/configuration/secrets/
-  filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ null ])) cfg.config or {};
+  filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ null ])) (lib.recursiveUpdate customLovelaceModulesResources (cfg.config or {}));
   configFile = pkgs.runCommandLocal "configuration.yaml" { } ''
     cp ${format.generate "configuration.yaml" filteredConfig} $out
     sed -i -e "s/'\!\([a-z_]\+\) \(.*\)'/\!\1 \2/;s/^\!\!/\!/;" $out
   '';
-  lovelaceConfig = if (cfg.lovelaceConfig == null) then {}
-    else (lib.recursiveUpdate customLovelaceModulesResources cfg.lovelaceConfig);
-  lovelaceConfigFile = format.generate "ui-lovelace.yaml" lovelaceConfig;
+  lovelaceConfigFile = format.generate "ui-lovelace.yaml" cfg.lovelaceConfig;
 
   # Components advertised by the home-assistant package
   availableComponents = cfg.package.availableComponents;
@@ -77,7 +75,7 @@ let
   # Create parts of the lovelace config that reference lovelave modules as resources
   customLovelaceModulesResources = {
     lovelace.resources = map (card: {
-      url = "/local/nixos-lovelace-modules/${card.entrypoint or card.pname}.js?${card.version}";
+      url = "/local/nixos-lovelace-modules/${card.entrypoint or (card.pname + ".js")}?${card.version}";
       type = "module";
     }) cfg.customLovelaceModules;
   };
@@ -159,7 +157,7 @@ in {
       default = [];
       example = literalExpression ''
         with pkgs.home-assistant-custom-components; [
-          prometheus-sensor
+          prometheus_sensor
         ];
       '';
       description = lib.mdDoc ''
@@ -455,10 +453,10 @@ in {
           ln -s /etc/home-assistant/configuration.yaml "${cfg.configDir}/configuration.yaml"
         '';
         copyLovelaceConfig = if cfg.lovelaceConfigWritable then ''
+          rm -f "${cfg.configDir}/ui-lovelace.yaml"
           cp --no-preserve=mode ${lovelaceConfigFile} "${cfg.configDir}/ui-lovelace.yaml"
         '' else ''
-          rm -f "${cfg.configDir}/ui-lovelace.yaml"
-          ln -s /etc/home-assistant/ui-lovelace.yaml "${cfg.configDir}/ui-lovelace.yaml"
+          ln -fs /etc/home-assistant/ui-lovelace.yaml "${cfg.configDir}/ui-lovelace.yaml"
         '';
         copyCustomLovelaceModules = if cfg.customLovelaceModules != [] then ''
           mkdir -p "${cfg.configDir}/www"
@@ -470,8 +468,8 @@ in {
           mkdir -p "${cfg.configDir}/custom_components"
 
           # remove components symlinked in from below the /nix/store
-          components="$(find "${cfg.configDir}/custom_components" -maxdepth 1 -type l)"
-          for component in "$components"; do
+          readarray -d "" components < <(find "${cfg.configDir}/custom_components" -maxdepth 1 -type l -print0)
+          for component in "''${components[@]}"; do
             if [[ "$(readlink "$component")" =~ ^${escapeShellArg builtins.storeDir} ]]; then
               rm "$component"
             fi
diff --git a/nixos/modules/services/matrix/synapse.md b/nixos/modules/services/matrix/synapse.md
index 58be24204fcfe..f270be8c8d781 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 9cc769c2d0db7..50019d2a25cb5 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
new file mode 100644
index 0000000000000..2bfa3b77971f4
--- /dev/null
+++ b/nixos/modules/services/misc/guix/default.nix
@@ -0,0 +1,387 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.services.guix;
+
+  package = cfg.package.override { inherit (cfg) stateDir storeDir; };
+
+  guixBuildUser = id: {
+    name = "guixbuilder${toString id}";
+    group = cfg.group;
+    extraGroups = [ cfg.group ];
+    createHome = false;
+    description = "Guix build user ${toString id}";
+    isSystemUser = true;
+  };
+
+  guixBuildUsers = numberOfUsers:
+    builtins.listToAttrs (map
+      (user: {
+        name = user.name;
+        value = user;
+      })
+      (builtins.genList guixBuildUser numberOfUsers));
+
+  # A set of Guix user profiles to be linked at activation.
+  guixUserProfiles = {
+    # The current Guix profile that is created through `guix pull`.
+    "current-guix" = "\${XDG_CONFIG_HOME}/guix/current";
+
+    # The default Guix profile similar to $HOME/.nix-profile from Nix.
+    "guix-profile" = "$HOME/.guix-profile";
+  };
+
+  # All of the Guix profiles to be used.
+  guixProfiles = lib.attrValues guixUserProfiles;
+
+  serviceEnv = {
+    GUIX_LOCPATH = "${cfg.stateDir}/guix/profiles/per-user/root/guix-profile/lib/locale";
+    LC_ALL = "C.UTF-8";
+  };
+in
+{
+  meta.maintainers = with lib.maintainers; [ foo-dogsquared ];
+
+  options.services.guix = with lib; {
+    enable = mkEnableOption "Guix build daemon service";
+
+    group = mkOption {
+      type = types.str;
+      default = "guixbuild";
+      example = "guixbuild";
+      description = ''
+        The group of the Guix build user pool.
+      '';
+    };
+
+    nrBuildUsers = mkOption {
+      type = types.ints.unsigned;
+      description = ''
+        Number of Guix build users to be used in the build pool.
+      '';
+      default = 10;
+      example = 20;
+    };
+
+    extraArgs = mkOption {
+      type = with types; listOf str;
+      default = [ ];
+      example = [ "--max-jobs=4" "--debug" ];
+      description = ''
+        Extra flags to pass to the Guix daemon service.
+      '';
+    };
+
+    package = mkPackageOption pkgs "guix" {
+      extraDescription = ''
+        It should contain {command}`guix-daemon` and {command}`guix`
+        executable.
+      '';
+    };
+
+    storeDir = mkOption {
+      type = types.path;
+      default = "/gnu/store";
+      description = ''
+        The store directory where the Guix service will serve to/from. Take
+        note Guix cannot take advantage of substitutes if you set it something
+        other than {file}`/gnu/store` since most of the cached builds are
+        assumed to be in there.
+
+        ::: {.warning}
+        This will also recompile all packages because the normal cache no
+        longer applies.
+        :::
+      '';
+    };
+
+    stateDir = mkOption {
+      type = types.path;
+      default = "/var";
+      description = ''
+        The state directory where Guix service will store its data such as its
+        user-specific profiles, cache, and state files.
+
+        ::: {.warning}
+        Changing it to something other than the default will rebuild the
+        package.
+        :::
+      '';
+      example = "/gnu/var";
+    };
+
+    publish = {
+      enable = mkEnableOption "substitute server for your Guix store directory";
+
+      generateKeyPair = mkOption {
+        type = types.bool;
+        description = ''
+          Whether to generate signing keys in {file}`/etc/guix` which are
+          required to initialize a substitute server. Otherwise,
+          `--public-key=$FILE` and `--private-key=$FILE` can be passed in
+          {option}`services.guix.publish.extraArgs`.
+        '';
+        default = true;
+        example = false;
+      };
+
+      port = mkOption {
+        type = types.port;
+        default = 8181;
+        example = 8200;
+        description = ''
+          Port of the substitute server to listen on.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "guix-publish";
+        description = ''
+          Name of the user to change once the server is up.
+        '';
+      };
+
+      extraArgs = mkOption {
+        type = with types; listOf str;
+        description = ''
+          Extra flags to pass to the substitute server.
+        '';
+        default = [];
+        example = [
+          "--compression=zstd:6"
+          "--discover=no"
+        ];
+      };
+    };
+
+    gc = {
+      enable = mkEnableOption "automatic garbage collection service for Guix";
+
+      extraArgs = mkOption {
+        type = with types; listOf str;
+        default = [ ];
+        description = ''
+          List of arguments to be passed to {command}`guix gc`.
+
+          When given no option, it will try to collect all garbage which is
+          often inconvenient so it is recommended to set [some
+          options](https://guix.gnu.org/en/manual/en/html_node/Invoking-guix-gc.html).
+        '';
+        example = [
+          "--delete-generations=1m"
+          "--free-space=10G"
+          "--optimize"
+        ];
+      };
+
+      dates = lib.mkOption {
+        type = types.str;
+        default = "03:15";
+        example = "weekly";
+        description = ''
+          How often the garbage collection occurs. This takes the time format
+          from {manpage}`systemd.time(7)`.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable (lib.mkMerge [
+    {
+      environment.systemPackages = [ package ];
+
+      users.users = guixBuildUsers cfg.nrBuildUsers;
+      users.groups.${cfg.group} = { };
+
+      # Guix uses Avahi (through guile-avahi) both for the auto-discovering and
+      # advertising substitute servers in the local network.
+      services.avahi.enable = lib.mkDefault true;
+      services.avahi.publish.enable = lib.mkDefault true;
+      services.avahi.publish.userServices = lib.mkDefault true;
+
+      # It's similar to Nix daemon so there's no question whether or not this
+      # should be sandboxed.
+      systemd.services.guix-daemon = {
+        environment = serviceEnv;
+        script = ''
+          ${lib.getExe' package "guix-daemon"} \
+            --build-users-group=${cfg.group} \
+            ${lib.escapeShellArgs cfg.extraArgs}
+        '';
+        serviceConfig = {
+          OOMPolicy = "continue";
+          RemainAfterExit = "yes";
+          Restart = "always";
+          TasksMax = 8192;
+        };
+        unitConfig.RequiresMountsFor = [
+          cfg.storeDir
+          cfg.stateDir
+        ];
+        wantedBy = [ "multi-user.target" ];
+      };
+
+      # This is based from Nix daemon socket unit from upstream Nix package.
+      # Guix build daemon has support for systemd-style socket activation.
+      systemd.sockets.guix-daemon = {
+        description = "Guix daemon socket";
+        before = [ "multi-user.target" ];
+        listenStreams = [ "${cfg.stateDir}/guix/daemon-socket/socket" ];
+        unitConfig.RequiresMountsFor = [ cfg.storeDir cfg.stateDir ];
+        wantedBy = [ "sockets.target" ];
+      };
+
+      systemd.mounts = [{
+        description = "Guix read-only store directory";
+        before = [ "guix-daemon.service" ];
+        what = cfg.storeDir;
+        where = cfg.storeDir;
+        type = "none";
+        options = "bind,ro";
+
+        unitConfig.DefaultDependencies = false;
+        wantedBy = [ "guix-daemon.service" ];
+      }];
+
+      # Make transferring files from one store to another easier with the usual
+      # case being of most substitutes from the official Guix CI instance.
+      system.activationScripts.guix-authorize-keys = ''
+        for official_server_keys in ${package}/share/guix/*.pub; do
+          ${lib.getExe' package "guix"} archive --authorize < $official_server_keys
+        done
+      '';
+
+      # Link the usual Guix profiles to the home directory. This is useful in
+      # ephemeral setups where only certain part of the filesystem is
+      # persistent (e.g., "Erase my darlings"-type of setup).
+      system.userActivationScripts.guix-activate-user-profiles.text = let
+        linkProfileToPath = acc: profile: location: let
+          guixProfile = "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}";
+          in acc + ''
+            [ -d "${guixProfile}" ] && [ -L "${location}" ] || ln -sf "${guixProfile}" "${location}"
+          '';
+
+        activationScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles;
+      in ''
+        # Don't export this please! It is only expected to be used for this
+        # activation script and nothing else.
+        XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config}
+
+        # Linking the usual Guix profiles into the home directory.
+        ${activationScript}
+      '';
+
+      # GUIX_LOCPATH is basically LOCPATH but for Guix libc which in turn used by
+      # virtually every Guix-built packages. This is so that Guix-installed
+      # applications wouldn't use incompatible locale data and not touch its host
+      # system.
+      environment.sessionVariables.GUIX_LOCPATH = lib.makeSearchPath "lib/locale" guixProfiles;
+
+      # What Guix profiles export is very similar to Nix profiles so it is
+      # acceptable to list it here. Also, it is more likely that the user would
+      # want to use packages explicitly installed from Guix so we're putting it
+      # first.
+      environment.profiles = lib.mkBefore guixProfiles;
+    }
+
+    (lib.mkIf cfg.publish.enable {
+      systemd.services.guix-publish = {
+        description = "Guix remote store";
+        environment = serviceEnv;
+
+        # Mounts will be required by the daemon service anyways so there's no
+        # need add RequiresMountsFor= or something similar.
+        requires = [ "guix-daemon.service" ];
+        after = [ "guix-daemon.service" ];
+        partOf = [ "guix-daemon.service" ];
+
+        preStart = lib.mkIf cfg.publish.generateKeyPair ''
+          # Generate the keypair if it's missing.
+          [ -f "/etc/guix/signing-key.sec" ] && [ -f "/etc/guix/signing-key.pub" ] || \
+            ${lib.getExe' package "guix"} archive --generate-key || {
+              rm /etc/guix/signing-key.*;
+              ${lib.getExe' package "guix"} archive --generate-key;
+            }
+        '';
+        script = ''
+          ${lib.getExe' package "guix"} publish \
+            --user=${cfg.publish.user} --port=${builtins.toString cfg.publish.port} \
+            ${lib.escapeShellArgs cfg.publish.extraArgs}
+        '';
+
+        serviceConfig = {
+          Restart = "always";
+          RestartSec = 10;
+
+          ProtectClock = true;
+          ProtectHostname = true;
+          ProtectKernelTunables = true;
+          ProtectKernelModules = true;
+          ProtectControlGroups = true;
+          SystemCallFilter = [
+            "@system-service"
+            "@debug"
+            "@setuid"
+          ];
+
+          RestrictNamespaces = true;
+          RestrictAddressFamilies = [
+            "AF_UNIX"
+            "AF_INET"
+            "AF_INET6"
+          ];
+
+          # While the permissions can be set, it is assumed to be taken by Guix
+          # daemon service which it has already done the setup.
+          ConfigurationDirectory = "guix";
+
+          AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+          CapabilityBoundingSet = [
+            "CAP_NET_BIND_SERVICE"
+            "CAP_SETUID"
+            "CAP_SETGID"
+          ];
+        };
+        wantedBy = [ "multi-user.target" ];
+      };
+
+      users.users.guix-publish = lib.mkIf (cfg.publish.user == "guix-publish") {
+        description = "Guix publish user";
+        group = config.users.groups.guix-publish.name;
+        isSystemUser = true;
+      };
+      users.groups.guix-publish = {};
+    })
+
+    (lib.mkIf cfg.gc.enable {
+      # This service should be handled by root to collect all garbage by all
+      # users.
+      systemd.services.guix-gc = {
+        description = "Guix garbage collection";
+        startAt = cfg.gc.dates;
+        script = ''
+          ${lib.getExe' package "guix"} gc ${lib.escapeShellArgs cfg.gc.extraArgs}
+        '';
+
+        serviceConfig = {
+          Type = "oneshot";
+
+          PrivateDevices = true;
+          PrivateNetworks = true;
+          ProtectControlGroups = true;
+          ProtectHostname = true;
+          ProtectKernelTunables = true;
+          SystemCallFilter = [
+            "@default"
+            "@file-system"
+            "@basic-io"
+            "@system-service"
+          ];
+        };
+      };
+
+      systemd.timers.guix-gc.timerConfig.Persistent = true;
+    })
+  ]);
+}
diff --git a/nixos/modules/services/monitoring/prometheus/default.nix b/nixos/modules/services/monitoring/prometheus/default.nix
index a38855ccd4088..822a4946ba271 100644
--- a/nixos/modules/services/monitoring/prometheus/default.nix
+++ b/nixos/modules/services/monitoring/prometheus/default.nix
@@ -41,12 +41,12 @@ let
   # This becomes the main config file for Prometheus
   promConfig = {
     global = filterValidPrometheus cfg.globalConfig;
-    rule_files = map (promtoolCheck "check rules" "rules") (cfg.ruleFiles ++ [
-      (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules))
-    ]);
     scrape_configs = filterValidPrometheus cfg.scrapeConfigs;
     remote_write = filterValidPrometheus cfg.remoteWrite;
     remote_read = filterValidPrometheus cfg.remoteRead;
+    rule_files = optionals (!(cfg.enableAgentMode)) (map (promtoolCheck "check rules" "rules") (cfg.ruleFiles ++ [
+      (pkgs.writeText "prometheus.rules" (concatStringsSep "\n" cfg.rules))
+    ]));
     alerting = {
       inherit (cfg) alertmanagers;
     };
@@ -62,15 +62,20 @@ let
     promtoolCheck "check config ${lib.optionalString (cfg.checkConfig == "syntax-only") "--syntax-only"}" "prometheus.yml" yml;
 
   cmdlineArgs = cfg.extraFlags ++ [
-    "--storage.tsdb.path=${workingDir}/data/"
     "--config.file=${
       if cfg.enableReload
       then "/etc/prometheus/prometheus.yaml"
       else prometheusYml
     }"
     "--web.listen-address=${cfg.listenAddress}:${builtins.toString cfg.port}"
-    "--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity}"
-  ] ++ optional (cfg.webExternalUrl != null) "--web.external-url=${cfg.webExternalUrl}"
+  ] ++ (
+    if (cfg.enableAgentMode) then [
+      "--enable-feature=agent"
+    ] else [
+       "--alertmanager.notification-queue-capacity=${toString cfg.alertmanagerNotificationQueueCapacity }"
+       "--storage.tsdb.path=${workingDir}/data/"
+    ])
+    ++ optional (cfg.webExternalUrl != null) "--web.external-url=${cfg.webExternalUrl}"
     ++ optional (cfg.retentionTime != null) "--storage.tsdb.retention.time=${cfg.retentionTime}"
     ++ optional (cfg.webConfigFile != null) "--web.config.file=${cfg.webConfigFile}";
 
@@ -1619,6 +1624,8 @@ in
       '';
     };
 
+    enableAgentMode = mkEnableOption (lib.mdDoc "agent mode");
+
     configText = mkOption {
       type = types.nullOr types.lines;
       default = null;
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/kea.nix b/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
index 8b1cd47d0a409..3abb6ff6bdf8b 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/kea.nix
@@ -15,8 +15,8 @@ in {
       type = types.listOf types.str;
       example = literalExpression ''
         [
-          "/run/kea-dhcp4/kea-dhcp4.socket"
-          "/run/kea-dhcp6/kea-dhcp6.socket"
+          "/run/kea/kea-dhcp4.socket"
+          "/run/kea/kea-dhcp6.socket"
         ]
       '';
       description = lib.mdDoc ''
@@ -31,13 +31,15 @@ in {
     ];
     serviceConfig = {
       User = "kea";
+      DynamicUser = true;
       ExecStart = ''
         ${pkgs.prometheus-kea-exporter}/bin/kea-exporter \
           --address ${cfg.listenAddress} \
           --port ${toString cfg.port} \
           ${concatStringsSep " " cfg.controlSocketPaths}
       '';
-      SupplementaryGroups = [ "kea" ];
+      RuntimeDirectory = "kea";
+      RuntimeDirectoryPreserve = true;
       RestrictAddressFamilies = [
         # Need AF_UNIX to collect data
         "AF_UNIX"
diff --git a/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix b/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
index 4112774940139..b9ab305f7c082 100644
--- a/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
+++ b/nixos/modules/services/monitoring/prometheus/exporters/sabnzbd.nix
@@ -19,7 +19,11 @@ in
           };
           apiKeyFile = mkOption {
             type = types.str;
-            description = "File containing the API key.";
+            description = ''
+              The path to a file containing the API key.
+              The file is securely passed to the service by leveraging systemd credentials.
+              No special permissions need to be set on this file.
+            '';
             example = "/run/secrets/sabnzbd_apikey";
           };
         };
@@ -30,18 +34,24 @@ in
   serviceOpts =
     let
       servers = lib.zipAttrs cfg.servers;
-      apiKeys = lib.concatStringsSep "," (builtins.map (file: "$(cat ${file})") servers.apiKeyFile);
+      credentials = lib.imap0 (i: v: { name = "apikey-${toString i}"; path = v; }) servers.apiKeyFile;
     in
     {
+      serviceConfig.LoadCredential = builtins.map ({ name, path }: "${name}:${path}") credentials;
+
       environment = {
         METRICS_PORT = toString cfg.port;
         METRICS_ADDR = cfg.listenAddress;
         SABNZBD_BASEURLS = lib.concatStringsSep "," servers.baseUrl;
       };
 
-      script = ''
-        export SABNZBD_APIKEYS="${apiKeys}"
-        exec ${lib.getExe pkgs.prometheus-sabnzbd-exporter}
-      '';
+      script =
+        let
+          apiKeys = lib.concatStringsSep "," (builtins.map (cred: "$(< $CREDENTIALS_DIRECTORY/${cred.name})") credentials);
+        in
+        ''
+          export SABNZBD_APIKEYS="${apiKeys}"
+          exec ${lib.getExe pkgs.prometheus-sabnzbd-exporter}
+        '';
     };
 }
diff --git a/nixos/modules/services/network-filesystems/drbd.nix b/nixos/modules/services/network-filesystems/drbd.nix
index e74ed391d48e3..79a1b768b4615 100644
--- a/nixos/modules/services/network-filesystems/drbd.nix
+++ b/nixos/modules/services/network-filesystems/drbd.nix
@@ -55,8 +55,8 @@ let cfg = config.services.drbd; in
       wants = [ "systemd-udev.settle.service" ];
       wantedBy = [ "multi-user.target" ];
       serviceConfig = {
-        ExecStart = "${pkgs.drbd}/sbin/drbdadm up all";
-        ExecStop = "${pkgs.drbd}/sbin/drbdadm down all";
+        ExecStart = "${pkgs.drbd}/bin/drbdadm up all";
+        ExecStop = "${pkgs.drbd}/bin/drbdadm down all";
       };
     };
   };
diff --git a/nixos/modules/services/network-filesystems/kubo.nix b/nixos/modules/services/network-filesystems/kubo.nix
index bc746bed31f22..b6a81fedcfba3 100644
--- a/nixos/modules/services/network-filesystems/kubo.nix
+++ b/nixos/modules/services/network-filesystems/kubo.nix
@@ -366,6 +366,8 @@ in
         Group = cfg.group;
         StateDirectory = "";
         ReadWritePaths = optionals (!cfg.autoMount) [ "" cfg.dataDir ];
+        # Make sure the socket units are started before ipfs.service
+        Sockets = [ "ipfs-gateway.socket" "ipfs-api.socket" ];
       } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; };
     } // optionalAttrs (!cfg.startWhenNeeded) {
       wantedBy = [ "default.target" ];
diff --git a/nixos/modules/services/networking/ejabberd.nix b/nixos/modules/services/networking/ejabberd.nix
index 3feafc3bb3bd1..72dffac9365b6 100644
--- a/nixos/modules/services/networking/ejabberd.nix
+++ b/nixos/modules/services/networking/ejabberd.nix
@@ -125,6 +125,12 @@ in {
         if [ -z "$(ls -A '${cfg.spoolDir}')" ]; then
           touch "${cfg.spoolDir}/.firstRun"
         fi
+
+        if ! test -e ${cfg.spoolDir}/.erlang.cookie; then
+          touch ${cfg.spoolDir}/.erlang.cookie
+          chmod 600 ${cfg.spoolDir}/.erlang.cookie
+          dd if=/dev/random bs=16 count=1 | base64 > ${cfg.spoolDir}/.erlang.cookie
+        fi
       '';
 
       postStart = ''
diff --git a/nixos/modules/services/networking/kea.nix b/nixos/modules/services/networking/kea.nix
index 2f922a026a3a9..5ca705976c413 100644
--- a/nixos/modules/services/networking/kea.nix
+++ b/nixos/modules/services/networking/kea.nix
@@ -254,6 +254,8 @@ in
       DynamicUser = true;
       User = "kea";
       ConfigurationDirectory = "kea";
+      RuntimeDirectory = "kea";
+      RuntimeDirectoryPreserve = true;
       StateDirectory = "kea";
       UMask = "0077";
     };
@@ -288,8 +290,8 @@ in
       ];
 
       environment = {
-        KEA_PIDFILE_DIR = "/run/kea-ctrl-agent";
-        KEA_LOCKFILE_DIR = "/run/kea-ctrl-agent";
+        KEA_PIDFILE_DIR = "/run/kea";
+        KEA_LOCKFILE_DIR = "/run/kea";
       };
 
       restartTriggers = [
@@ -300,7 +302,6 @@ in
         ExecStart = "${package}/bin/kea-ctrl-agent -c /etc/kea/ctrl-agent.conf ${lib.escapeShellArgs cfg.ctrl-agent.extraArgs}";
         KillMode = "process";
         Restart = "on-failure";
-        RuntimeDirectory = "kea-ctrl-agent";
       } // commonServiceConfig;
     };
   })
@@ -329,8 +330,8 @@ in
       ];
 
       environment = {
-        KEA_PIDFILE_DIR = "/run/kea-dhcp4";
-        KEA_LOCKFILE_DIR = "/run/kea-dhcp4";
+        KEA_PIDFILE_DIR = "/run/kea";
+        KEA_LOCKFILE_DIR = "/run/kea";
       };
 
       restartTriggers = [
@@ -348,7 +349,6 @@ in
           "CAP_NET_BIND_SERVICE"
           "CAP_NET_RAW"
         ];
-        RuntimeDirectory = "kea-dhcp4";
       } // commonServiceConfig;
     };
   })
@@ -377,8 +377,8 @@ in
       ];
 
       environment = {
-        KEA_PIDFILE_DIR = "/run/kea-dhcp6";
-        KEA_LOCKFILE_DIR = "/run/kea-dhcp6";
+        KEA_PIDFILE_DIR = "/run/kea";
+        KEA_LOCKFILE_DIR = "/run/kea";
       };
 
       restartTriggers = [
@@ -394,7 +394,6 @@ in
         CapabilityBoundingSet = [
           "CAP_NET_BIND_SERVICE"
         ];
-        RuntimeDirectory = "kea-dhcp6";
       } // commonServiceConfig;
     };
   })
@@ -423,8 +422,8 @@ in
       ];
 
       environment = {
-        KEA_PIDFILE_DIR = "/run/kea-dhcp-ddns";
-        KEA_LOCKFILE_DIR = "/run/kea-dhcp-ddns";
+        KEA_PIDFILE_DIR = "/run/kea";
+        KEA_LOCKFILE_DIR = "/run/kea";
       };
 
       restartTriggers = [
@@ -439,7 +438,6 @@ in
         CapabilityBoundingSet = [
           "CAP_NET_BIND_SERVICE"
         ];
-        RuntimeDirectory = "kea-dhcp-ddns";
       } // commonServiceConfig;
     };
   })
diff --git a/nixos/modules/services/networking/nebula.nix b/nixos/modules/services/networking/nebula.nix
index e1a8c6740f57e..38e657d04437c 100644
--- a/nixos/modules/services/networking/nebula.nix
+++ b/nixos/modules/services/networking/nebula.nix
@@ -201,7 +201,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/nix-serve.nix b/nixos/modules/services/networking/nix-serve.nix
index 8c4352bc95e82..86bb603e12719 100644
--- a/nixos/modules/services/networking/nix-serve.nix
+++ b/nixos/modules/services/networking/nix-serve.nix
@@ -67,7 +67,9 @@ in
   };
 
   config = mkIf cfg.enable {
-    nix.settings.extra-allowed-users = [ "nix-serve" ];
+    nix.settings = lib.optionalAttrs (lib.versionAtLeast config.nix.package.version "2.4") {
+      extra-allowed-users = [ "nix-serve" ];
+    };
 
     systemd.services.nix-serve = {
       description = "nix-serve binary cache server";
diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix
index 6d9af6141f12c..0a493f0b2b95f 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 = [];
@@ -667,7 +676,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/teamspeak3.nix b/nixos/modules/services/networking/teamspeak3.nix
index f09ef1a959ed4..ff41539a6d9b7 100644
--- a/nixos/modules/services/networking/teamspeak3.nix
+++ b/nixos/modules/services/networking/teamspeak3.nix
@@ -50,7 +50,7 @@ in
       };
 
       defaultVoicePort = mkOption {
-        type = types.int;
+        type = types.port;
         default = 9987;
         description = lib.mdDoc ''
           Default UDP port for clients to connect to virtual servers - used for first virtual server, subsequent ones will open on incrementing port numbers by default.
@@ -67,7 +67,7 @@ in
       };
 
       fileTransferPort = mkOption {
-        type = types.int;
+        type = types.port;
         default = 30033;
         description = lib.mdDoc ''
           TCP port opened for file transfers.
@@ -84,10 +84,26 @@ in
       };
 
       queryPort = mkOption {
-        type = types.int;
+        type = types.port;
         default = 10011;
         description = lib.mdDoc ''
-          TCP port opened for ServerQuery connections.
+          TCP port opened for ServerQuery connections using the raw telnet protocol.
+        '';
+      };
+
+      querySshPort = mkOption {
+        type = types.port;
+        default = 10022;
+        description = lib.mdDoc ''
+          TCP port opened for ServerQuery connections using the SSH protocol.
+        '';
+      };
+
+      queryHttpPort = mkOption {
+        type = types.port;
+        default = 10080;
+        description = lib.mdDoc ''
+          TCP port opened for ServerQuery connections using the HTTP protocol.
         '';
       };
 
@@ -128,7 +144,9 @@ in
     ];
 
     networking.firewall = mkIf cfg.openFirewall {
-      allowedTCPPorts = [ cfg.fileTransferPort ] ++ optionals (cfg.openFirewallServerQuery) [ cfg.queryPort (cfg.queryPort + 11) ];
+      allowedTCPPorts = [ cfg.fileTransferPort ] ++ (map (port:
+        mkIf cfg.openFirewallServerQuery port
+      ) [cfg.queryPort cfg.querySshPort cfg.queryHttpPort]);
       # subsequent vServers will use the incremented voice port, let's just open the next 10
       allowedUDPPortRanges = [ { from = cfg.defaultVoicePort; to = cfg.defaultVoicePort + 10; } ];
     };
@@ -141,13 +159,19 @@ in
       serviceConfig = {
         ExecStart = ''
           ${ts3}/bin/ts3server \
-            dbsqlpath=${ts3}/lib/teamspeak/sql/ logpath=${cfg.logPath} \
-            ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \
+            dbsqlpath=${ts3}/lib/teamspeak/sql/ \
+            logpath=${cfg.logPath} \
+            license_accepted=1 \
             default_voice_port=${toString cfg.defaultVoicePort} \
-            ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \
             filetransfer_port=${toString cfg.fileTransferPort} \
+            query_port=${toString cfg.queryPort} \
+            query_ssh_port=${toString cfg.querySshPort} \
+            query_http_port=${toString cfg.queryHttpPort} \
+            ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \
+            ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \
             ${optionalString (cfg.queryIP != null) "query_ip=${cfg.queryIP}"} \
-            query_port=${toString cfg.queryPort} license_accepted=1
+            ${optionalString (cfg.queryIP != null) "query_ssh_ip=${cfg.queryIP}"} \
+            ${optionalString (cfg.queryIP != null) "query_http_ip=${cfg.queryIP}"} \
         '';
         WorkingDirectory = cfg.dataDir;
         User = user;
diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
index 5efb9334ea03e..06f1d6c5de354 100644
--- a/nixos/modules/services/torrent/transmission.nix
+++ b/nixos/modules/services/torrent/transmission.nix
@@ -333,10 +333,10 @@ in
             cfg.settings.watch-dir;
         StateDirectory = [
           "transmission"
-          "transmission/.config/transmission-daemon"
-          "transmission/.incomplete"
-          "transmission/Downloads"
-          "transmission/watch-dir"
+          "transmission/${settingsDir}"
+          "transmission/${incompleteDir}"
+          "transmission/${downloadsDir}"
+          "transmission/${watchDir}"
         ];
         StateDirectoryMode = mkDefault 750;
         # The following options are only for optimizing:
diff --git a/nixos/modules/services/video/frigate.nix b/nixos/modules/services/video/frigate.nix
index 8db2bfae80ac0..17c8d59c30958 100644
--- a/nixos/modules/services/video/frigate.nix
+++ b/nixos/modules/services/video/frigate.nix
@@ -358,6 +358,7 @@ in
       ];
       serviceConfig = {
         ExecStart = "${cfg.package.python.interpreter} -m frigate";
+        Restart = "on-failure";
 
         User = "frigate";
         Group = "frigate";
diff --git a/nixos/modules/services/web-apps/keycloak.nix b/nixos/modules/services/web-apps/keycloak.nix
index a7e4fab8ea287..c90ee78a3e04b 100644
--- a/nixos/modules/services/web-apps/keycloak.nix
+++ b/nixos/modules/services/web-apps/keycloak.nix
@@ -24,7 +24,6 @@ let
     maintainers
     catAttrs
     collect
-    splitString
     hasPrefix
     ;
 
@@ -335,7 +334,8 @@ in
             };
 
             hostname = mkOption {
-              type = str;
+              type = nullOr str;
+              default = null;
               example = "keycloak.example.com";
               description = lib.mdDoc ''
                 The hostname part of the public URL used as base for
@@ -457,7 +457,7 @@ in
 
       keycloakConfig = lib.generators.toKeyValue {
         mkKeyValue = lib.flip lib.generators.mkKeyValueDefault "=" {
-          mkValueString = v: with builtins;
+          mkValueString = v:
             if isInt v then toString v
             else if isString v then v
             else if true == v then "true"
@@ -486,6 +486,14 @@ in
             assertion = createLocalPostgreSQL -> config.services.postgresql.settings.standard_conforming_strings or true;
             message = "Setting up a local PostgreSQL db for Keycloak requires `standard_conforming_strings` turned on to work reliably";
           }
+          {
+            assertion = cfg.settings.hostname != null || cfg.settings.hostname-url or null != null;
+            message = "Setting the Keycloak hostname is required, see `services.keycloak.settings.hostname`";
+          }
+          {
+            assertion = !(cfg.settings.hostname != null && cfg.settings.hostname-url or null != null);
+            message = "`services.keycloak.settings.hostname` and `services.keycloak.settings.hostname-url` are mutually exclusive";
+          }
         ];
 
         environment.systemPackages = [ keycloakBuild ];
diff --git a/nixos/modules/services/web-apps/mastodon.nix b/nixos/modules/services/web-apps/mastodon.nix
index 7b00ce35eb1a7..40219edcd4470 100644
--- a/nixos/modules/services/web-apps/mastodon.nix
+++ b/nixos/modules/services/web-apps/mastodon.nix
@@ -711,31 +711,28 @@ in {
     systemd.services.mastodon-init-db = lib.mkIf cfg.automaticMigrations {
       script = lib.optionalString (!databaseActuallyCreateLocally) ''
         umask 077
-
-        export PGPASSFILE
-        PGPASSFILE=$(mktemp)
-        cat > $PGPASSFILE <<EOF
-        ${cfg.database.host}:${toString cfg.database.port}:${cfg.database.name}:${cfg.database.user}:$(cat ${cfg.database.passwordFile})
-        EOF
-
+        export PGPASSWORD="$(cat '${cfg.database.passwordFile}')"
       '' + ''
-        if [ `psql ${cfg.database.name} -c \
+        if [ `psql -c \
                 "select count(*) from pg_class c \
                 join pg_namespace s on s.oid = c.relnamespace \
                 where s.nspname not in ('pg_catalog', 'pg_toast', 'information_schema') \
                 and s.nspname not like 'pg_temp%';" | sed -n 3p` -eq 0 ]; then
+          echo "Seeding database"
           SAFETY_ASSURED=1 rails db:schema:load
           rails db:seed
         else
+          echo "Migrating database (this might be a noop)"
           rails db:migrate
         fi
       '' +  lib.optionalString (!databaseActuallyCreateLocally) ''
-        rm $PGPASSFILE
-        unset PGPASSFILE
+        unset PGPASSWORD
       '';
       path = [ cfg.package pkgs.postgresql ];
       environment = env // lib.optionalAttrs (!databaseActuallyCreateLocally) {
         PGHOST = cfg.database.host;
+        PGPORT = toString cfg.database.port;
+        PGDATABASE = cfg.database.name;
         PGUSER = cfg.database.user;
       };
       serviceConfig = {
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index d3d318304ee95..380923758161f 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -28,6 +28,7 @@ let
   phpPackage = cfg.phpPackage.buildEnv {
     extensions = { enabled, all }:
       (with all; enabled
+        ++ [ bz2 intl sodium ] # recommended
         ++ optional cfg.enableImagemagick imagick
         # Optionally enabled depending on caching settings
         ++ optional cfg.caching.apcu apcu
@@ -188,7 +189,7 @@ in {
     package = mkOption {
       type = types.package;
       description = lib.mdDoc "Which package to use for the Nextcloud instance.";
-      relatedPackages = [ "nextcloud26" "nextcloud27" ];
+      relatedPackages = [ "nextcloud26" "nextcloud27" "nextcloud28" ];
     };
     phpPackage = mkOption {
       type = types.package;
@@ -1134,10 +1135,13 @@ in {
               fastcgi_read_timeout ${builtins.toString cfg.fastcgiTimeout}s;
             '';
           };
-          "~ \\.(?:css|js|mjs|svg|gif|png|jpg|jpeg|ico|wasm|tflite|map|html|ttf|bcmap|mp4|webm)$".extraConfig = ''
+          "~ \\.(?:css|js|mjs|svg|gif|png|jpg|jpeg|ico|wasm|tflite|map|html|ttf|bcmap|mp4|webm|ogg|flac)$".extraConfig = ''
             try_files $uri /index.php$request_uri;
             expires 6M;
             access_log off;
+            location ~ \.mjs$ {
+              default_type text/javascript;
+            }
             location ~ \.wasm$ {
               default_type application/wasm;
             }
diff --git a/nixos/modules/services/web-apps/wordpress.nix b/nixos/modules/services/web-apps/wordpress.nix
index 5d2e775d45216..d4c987da1144c 100644
--- a/nixos/modules/services/web-apps/wordpress.nix
+++ b/nixos/modules/services/web-apps/wordpress.nix
@@ -34,7 +34,7 @@ let
       # copy additional plugin(s), theme(s) and language(s)
       ${concatStringsSep "\n" (mapAttrsToList (name: theme: "cp -r ${theme} $out/share/wordpress/wp-content/themes/${name}") cfg.themes)}
       ${concatStringsSep "\n" (mapAttrsToList (name: plugin: "cp -r ${plugin} $out/share/wordpress/wp-content/plugins/${name}") cfg.plugins)}
-      ${concatMapStringsSep "\n" (language: "cp -r ${language}/* $out/share/wordpress/wp-content/languages/") cfg.languages}
+      ${concatMapStringsSep "\n" (language: "cp -r ${language} $out/share/wordpress/wp-content/languages/") cfg.languages}
     '';
   };
 
diff --git a/nixos/modules/services/web-servers/caddy/default.nix b/nixos/modules/services/web-servers/caddy/default.nix
index cc89553fbb756..d50d8e34d50b4 100644
--- a/nixos/modules/services/web-servers/caddy/default.nix
+++ b/nixos/modules/services/web-servers/caddy/default.nix
@@ -154,7 +154,7 @@ in
       default = configFile;
       defaultText = "A Caddyfile automatically generated by values from services.caddy.*";
       example = literalExpression ''
-        pkgs.writeTextDir "Caddyfile" '''
+        pkgs.writeText "Caddyfile" '''
           example.com
 
           root * /var/www/wordpress
@@ -171,9 +171,9 @@ in
     };
 
     adapter = mkOption {
-      default = if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null;
+      default = if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then "caddyfile" else null;
       defaultText = literalExpression ''
-        if (builtins.baseNameOf cfg.configFile) == "Caddyfile" then "caddyfile" else null
+        if ((cfg.configFile != configFile) || (builtins.baseNameOf cfg.configFile) == "Caddyfile") then "caddyfile" else null
       '';
       example = literalExpression "nginx";
       type = with types; nullOr str;
diff --git a/nixos/modules/services/web-servers/tomcat.nix b/nixos/modules/services/web-servers/tomcat.nix
index 30d6b99fcfda1..8430265eabcef 100644
--- a/nixos/modules/services/web-servers/tomcat.nix
+++ b/nixos/modules/services/web-servers/tomcat.nix
@@ -8,7 +8,7 @@ in
 
 {
   meta = {
-    maintainers = with lib.maintainers; [ danbst ];
+    maintainers = with lib.maintainers; [ danbst anthonyroussel ];
   };
 
   ###### interface
diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix
index 33261021480f1..5ce04484710a8 100644
--- a/nixos/modules/system/boot/networkd.nix
+++ b/nixos/modules/system/boot/networkd.nix
@@ -597,6 +597,8 @@ let
           "DHCP"
           "DHCPServer"
           "LinkLocalAddressing"
+          "IPv6LinkLocalAddressGenerationMode"
+          "IPv6StableSecretAddress"
           "IPv4LLRoute"
           "DefaultRouteOnDevice"
           "LLMNR"
@@ -648,6 +650,7 @@ let
         (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6"])
         (assertValueOneOf "DHCPServer" boolValues)
         (assertValueOneOf "LinkLocalAddressing" ["yes" "no" "ipv4" "ipv6" "fallback" "ipv4-fallback"])
+        (assertValueOneOf "IPv6LinkLocalAddressGenerationMode" ["eui64" "none" "stable-privacy" "random"])
         (assertValueOneOf "IPv4LLRoute" boolValues)
         (assertValueOneOf "DefaultRouteOnDevice" boolValues)
         (assertValueOneOf "LLMNR" (boolValues ++ ["resolve"]))
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index 0e7d59b32075b..d7e8a67c4bc9d 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -90,8 +90,6 @@ let
 
   fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
 
-  needMakefs = lib.any (fs: fs.autoFormat) fileSystems;
-
   kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
   modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
   firmware = config.hardware.firmware;
@@ -430,7 +428,7 @@ in {
         "${cfg.package}/lib/systemd/systemd-fsck"
         "${cfg.package}/lib/systemd/systemd-hibernate-resume"
         "${cfg.package}/lib/systemd/systemd-journald"
-        (lib.mkIf needMakefs "${cfg.package}/lib/systemd/systemd-makefs")
+        "${cfg.package}/lib/systemd/systemd-makefs"
         "${cfg.package}/lib/systemd/systemd-modules-load"
         "${cfg.package}/lib/systemd/systemd-remount-fs"
         "${cfg.package}/lib/systemd/systemd-shutdown"
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 298add13437a0..53ffaa028038d 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1396,6 +1396,8 @@ in
       "net.ipv4.conf.all.forwarding" = mkDefault (any (i: i.proxyARP) interfaces);
       "net.ipv6.conf.all.disable_ipv6" = mkDefault (!cfg.enableIPv6);
       "net.ipv6.conf.default.disable_ipv6" = mkDefault (!cfg.enableIPv6);
+      # allow all users to do ICMP echo requests (ping)
+      "net.ipv4.ping_group_range" = mkDefault "0 2147483647";
       # networkmanager falls back to "/proc/sys/net/ipv6/conf/default/use_tempaddr"
       "net.ipv6.conf.default.use_tempaddr" = tempaddrValues.${cfg.tempAddresses}.sysctl;
     } // listToAttrs (forEach interfaces
diff --git a/nixos/modules/virtualisation/lxd-agent.nix b/nixos/modules/virtualisation/lxd-agent.nix
index 5bcc86e3bcbe9..59df658920f0f 100644
--- a/nixos/modules/virtualisation/lxd-agent.nix
+++ b/nixos/modules/virtualisation/lxd-agent.nix
@@ -56,7 +56,13 @@ in {
     systemd.services.lxd-agent = {
       enable = true;
       wantedBy = [ "multi-user.target" ];
-      path = [ pkgs.kmod pkgs.util-linux ];
+      path = [
+        pkgs.kmod
+        pkgs.util-linux
+
+        # allow `incus exec` to find system binaries
+        "/run/current-system/sw"
+      ];
 
       preStart = preStartScript;
 
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 44e99203856d5..69c3ad9fe0cc5 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -354,6 +354,7 @@ in {
   grow-partition = runTest ./grow-partition.nix;
   grub = handleTest ./grub.nix {};
   guacamole-server = handleTest ./guacamole-server.nix {};
+  guix = handleTest ./guix {};
   gvisor = handleTest ./gvisor.nix {};
   hadoop = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop; };
   hadoop_3_2 = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop_3_2; };
diff --git a/nixos/tests/caddy.nix b/nixos/tests/caddy.nix
index 5a0d3539394b6..41d8e57de4686 100644
--- a/nixos/tests/caddy.nix
+++ b/nixos/tests/caddy.nix
@@ -48,11 +48,19 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           };
         };
       };
+      specialisation.explicit-config-file.configuration = {
+        services.caddy.configFile = pkgs.writeText "Caddyfile" ''
+        localhost:80
+
+        respond "hello world"
+        '';
+      };
     };
   };
 
   testScript = { nodes, ... }:
     let
+      explicitConfigFile = "${nodes.webserver.system.build.toplevel}/specialisation/explicit-config-file";
       justReloadSystem = "${nodes.webserver.system.build.toplevel}/specialisation/config-reload";
       multipleConfigs = "${nodes.webserver.system.build.toplevel}/specialisation/multiple-configs";
       rfc42Config = "${nodes.webserver.system.build.toplevel}/specialisation/rfc42";
@@ -84,5 +92,12 @@ import ./make-test-python.nix ({ pkgs, ... }: {
           )
           webserver.wait_for_open_port(80)
           webserver.succeed("curl http://localhost | grep hello")
+
+      with subtest("explicit configFile"):
+          webserver.succeed(
+              "${explicitConfigFile}/bin/switch-to-configuration test >&2"
+          )
+          webserver.wait_for_open_port(80)
+          webserver.succeed("curl http://localhost | grep hello")
     '';
 })
diff --git a/nixos/tests/fontconfig-default-fonts.nix b/nixos/tests/fontconfig-default-fonts.nix
index 293dc43f91f38..209d348204b44 100644
--- a/nixos/tests/fontconfig-default-fonts.nix
+++ b/nixos/tests/fontconfig-default-fonts.nix
@@ -3,7 +3,6 @@ import ./make-test-python.nix ({ lib, ... }:
   name = "fontconfig-default-fonts";
 
   meta.maintainers = with lib.maintainers; [
-    jtojnar
   ];
 
   nodes.machine = { config, pkgs, ... }: {
diff --git a/nixos/tests/guix/basic.nix b/nixos/tests/guix/basic.nix
new file mode 100644
index 0000000000000..9b943b8965e60
--- /dev/null
+++ b/nixos/tests/guix/basic.nix
@@ -0,0 +1,42 @@
+# Take note the Guix store directory is empty. Also, we're trying to prevent
+# Guix from trying to downloading substitutes because of the restricted
+# access (assuming it's in a sandboxed environment).
+#
+# So this test is what it is: a basic test while trying to use Guix as much as
+# we possibly can (including the API) without triggering its download alarm.
+
+import ../make-test-python.nix ({ lib, pkgs, ... }: {
+  name = "guix-basic";
+  meta.maintainers = with lib.maintainers; [ foo-dogsquared ];
+
+  nodes.machine = { config, ... }: {
+    environment.etc."guix/scripts".source = ./scripts;
+    services.guix = {
+      enable = true;
+      gc.enable = true;
+    };
+  };
+
+  testScript = ''
+    import pathlib
+
+    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"):
+      machine.succeed("guix build --dry-run --verbosity=0 hello")
+      machine.succeed("guix show hello")
+
+    # This is to see if the Guix API is usable and mostly working.
+    with subtest("Guix API scripting"):
+      scripts_dir = pathlib.Path("/etc/guix/scripts")
+
+      text_msg = "Hello there, NixOS!"
+      text_store_file = machine.succeed(f"guix repl -- {scripts_dir}/create-file-to-store.scm '{text_msg}'")
+      assert machine.succeed(f"cat {text_store_file}") == text_msg
+
+      machine.succeed(f"guix repl -- {scripts_dir}/add-existing-files-to-store.scm {scripts_dir}")
+  '';
+})
diff --git a/nixos/tests/guix/default.nix b/nixos/tests/guix/default.nix
new file mode 100644
index 0000000000000..a017668c05a75
--- /dev/null
+++ b/nixos/tests/guix/default.nix
@@ -0,0 +1,8 @@
+{ system ? builtins.currentSystem
+, pkgs ? import ../../.. { inherit system; }
+}:
+
+{
+  basic = import ./basic.nix { inherit system pkgs; };
+  publish = import ./publish.nix { inherit system pkgs; };
+}
diff --git a/nixos/tests/guix/publish.nix b/nixos/tests/guix/publish.nix
new file mode 100644
index 0000000000000..6dbe8f99ebd68
--- /dev/null
+++ b/nixos/tests/guix/publish.nix
@@ -0,0 +1,95 @@
+# Testing out the substitute server with two machines in a local network. As a
+# bonus, we'll also test a feature of the substitute server being able to
+# advertise its service to the local network with Avahi.
+
+import ../make-test-python.nix ({ pkgs, lib, ... }: let
+  publishPort = 8181;
+  inherit (builtins) toString;
+in {
+  name = "guix-publish";
+
+  meta.maintainers = with lib.maintainers; [ foo-dogsquared ];
+
+  nodes = let
+    commonConfig = { config, ... }: {
+      # We'll be using '--advertise' flag with the
+      # substitute server which requires Avahi.
+      services.avahi = {
+        enable = true;
+        nssmdns = true;
+        publish = {
+          enable = true;
+          userServices = true;
+        };
+      };
+    };
+  in {
+    server = { config, lib, pkgs, ... }: {
+      imports = [ commonConfig ];
+
+      services.guix = {
+        enable = true;
+        publish = {
+          enable = true;
+          port = publishPort;
+
+          generateKeyPair = true;
+          extraArgs = [ "--advertise" ];
+        };
+      };
+
+      networking.firewall.allowedTCPPorts = [ publishPort ];
+    };
+
+    client = { config, lib, pkgs, ... }: {
+      imports = [ commonConfig ];
+
+      services.guix = {
+        enable = true;
+
+        extraArgs = [
+          # Force to only get all substitutes from the local server. We don't
+          # have anything in the Guix store directory and we cannot get
+          # anything from the official substitute servers anyways.
+          "--substitute-urls='http://server.local:${toString publishPort}'"
+
+          # Enable autodiscovery of the substitute servers in the local
+          # network. This machine shouldn't need to import the signing key from
+          # the substitute server since it is automatically done anyways.
+          "--discover=yes"
+        ];
+      };
+    };
+  };
+
+  testScript = ''
+    import pathlib
+
+    start_all()
+
+    scripts_dir = pathlib.Path("/etc/guix/scripts")
+
+    for machine in machines:
+      machine.wait_for_unit("multi-user.target")
+      machine.wait_for_unit("guix-daemon.service")
+      machine.wait_for_unit("avahi-daemon.service")
+
+    server.wait_for_unit("guix-publish.service")
+    server.wait_for_open_port(${toString publishPort})
+    server.succeed("curl http://localhost:${toString publishPort}/")
+
+    # Now it's the client turn to make use of it.
+    substitute_server = "http://server.local:${toString publishPort}"
+    client.wait_for_unit("network-online.target")
+    response = client.succeed(f"curl {substitute_server}")
+    assert "Guix Substitute Server" in response
+
+    # Authorizing the server to be used as a substitute server.
+    client.succeed(f"curl -O {substitute_server}/signing-key.pub")
+    client.succeed("guix archive --authorize < ./signing-key.pub")
+
+    # Since we're using the substitute server with the `--advertise` flag, we
+    # might as well check it.
+    client.succeed("avahi-browse --resolve --terminate _guix_publish._tcp | grep '_guix_publish._tcp'")
+  '';
+})
diff --git a/nixos/tests/guix/scripts/add-existing-files-to-store.scm b/nixos/tests/guix/scripts/add-existing-files-to-store.scm
new file mode 100644
index 0000000000000..fa47320b6a511
--- /dev/null
+++ b/nixos/tests/guix/scripts/add-existing-files-to-store.scm
@@ -0,0 +1,52 @@
+;; A simple script that adds each file given from the command-line into the
+;; store and checks them if it's the same.
+(use-modules (guix)
+             (srfi srfi-1)
+             (ice-9 ftw)
+             (rnrs io ports))
+
+;; This is based from tests/derivations.scm from Guix source code.
+(define* (directory-contents dir #:optional (slurp get-bytevector-all))
+         "Return an alist representing the contents of DIR"
+         (define prefix-len (string-length dir))
+         (sort (file-system-fold (const #t)
+                                 (lambda (path stat result)
+                                   (alist-cons (string-drop path prefix-len)
+                                               (call-with-input-file path slurp)
+                                               result))
+                                 (lambda (path stat result) result)
+                                 (lambda (path stat result) result)
+                                 (lambda (path stat result) result)
+                                 (lambda (path stat errno result) result)
+                                 '()
+                                 dir)
+               (lambda (e1 e2)
+                 (string<? (car e1) (car e2)))))
+
+(define* (check-if-same store drv path)
+         "Check if the given path and its store item are the same"
+         (let* ((filetype (stat:type (stat drv))))
+           (case filetype
+             ((regular)
+              (and (valid-path? store drv)
+                   (equal? (call-with-input-file path get-bytevector-all)
+                           (call-with-input-file drv get-bytevector-all))))
+             ((directory)
+              (and (valid-path? store drv)
+                   (equal? (directory-contents path)
+                           (directory-contents drv))))
+             (else #f))))
+
+(define* (add-and-check-item-to-store store path)
+         "Add PATH to STORE and check if the contents are the same"
+         (let* ((store-item (add-to-store store
+                                          (basename path)
+                                          #t "sha256" path))
+                (is-same (check-if-same store store-item path)))
+           (if (not is-same)
+             (exit 1))))
+
+(with-store store
+            (map (lambda (path)
+                   (add-and-check-item-to-store store (readlink* path)))
+                 (cdr (command-line))))
diff --git a/nixos/tests/guix/scripts/create-file-to-store.scm b/nixos/tests/guix/scripts/create-file-to-store.scm
new file mode 100644
index 0000000000000..467e4c4fd53f2
--- /dev/null
+++ b/nixos/tests/guix/scripts/create-file-to-store.scm
@@ -0,0 +1,8 @@
+;; A script that creates a store item with the given text and prints the
+;; resulting store item path.
+(use-modules (guix))
+
+(with-store store
+            (display (add-text-to-store store "guix-basic-test-text"
+                                        (string-join
+                                          (cdr (command-line))))))
diff --git a/nixos/tests/home-assistant.nix b/nixos/tests/home-assistant.nix
index e1588088ba198..05fb2fa1e06aa 100644
--- a/nixos/tests/home-assistant.nix
+++ b/nixos/tests/home-assistant.nix
@@ -43,7 +43,7 @@ in {
 
       # test loading custom components
       customComponents = with pkgs.home-assistant-custom-components; [
-        prometheus-sensor
+        prometheus_sensor
       ];
 
       # test loading lovelace modules
@@ -182,7 +182,7 @@ in {
         hass.wait_until_succeeds("journalctl -u home-assistant.service | grep -q 'We found a custom integration prometheus_sensor which has not been tested by Home Assistant'")
 
     with subtest("Check that lovelace modules are referenced and fetchable"):
-        hass.succeed("grep -q 'mini-graph-card-bundle.js' '${configDir}/ui-lovelace.yaml'")
+        hass.succeed("grep -q 'mini-graph-card-bundle.js' '${configDir}/configuration.yaml'")
         hass.succeed("curl --fail http://localhost:8123/local/nixos-lovelace-modules/mini-graph-card-bundle.js")
 
     with subtest("Check that optional dependencies are in the PYTHONPATH"):
diff --git a/nixos/tests/incus/virtual-machine.nix b/nixos/tests/incus/virtual-machine.nix
index bfa116679d43b..b7d130c8feb17 100644
--- a/nixos/tests/incus/virtual-machine.nix
+++ b/nixos/tests/incus/virtual-machine.nix
@@ -51,5 +51,8 @@ in
 
     with subtest("lxd-agent is started"):
         machine.succeed("incus exec ${instance-name} systemctl is-active lxd-agent")
+
+    with subtest("lxd-agent has a valid path"):
+        machine.succeed("incus exec ${instance-name} -- bash -c 'true'")
   '';
 })
diff --git a/nixos/tests/kernel-generic.nix b/nixos/tests/kernel-generic.nix
index 352deb521a478..72d31246b75d3 100644
--- a/nixos/tests/kernel-generic.nix
+++ b/nixos/tests/kernel-generic.nix
@@ -31,6 +31,7 @@ let
       linux_5_15_hardened
       linux_6_1_hardened
       linux_6_5_hardened
+      linux_6_6_hardened
       linux_rt_5_4
       linux_rt_5_10
       linux_rt_5_15
diff --git a/nixos/tests/munin.nix b/nixos/tests/munin.nix
index 4ec17e0339df0..e371b2dffa6b8 100644
--- a/nixos/tests/munin.nix
+++ b/nixos/tests/munin.nix
@@ -37,8 +37,10 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     with subtest("ensure munin-node starts and listens on 4949"):
         one.wait_for_unit("munin-node.service")
         one.wait_for_open_port(4949)
+
     with subtest("ensure munin-cron output is correct"):
         one.wait_for_file("/var/lib/munin/one/one-uptime-uptime-g.rrd")
         one.wait_for_file("/var/www/munin/one/index.html")
+        one.wait_for_file("/var/www/munin/one/one/diskstat_iops_vda-day.png", timeout=60)
   '';
 })
diff --git a/nixos/tests/nextcloud/default.nix b/nixos/tests/nextcloud/default.nix
index 19d04b28b4f99..84ac371537271 100644
--- a/nixos/tests/nextcloud/default.nix
+++ b/nixos/tests/nextcloud/default.nix
@@ -22,4 +22,4 @@ foldl
     };
   })
 { }
-  [ 26 27 ]
+  [ 26 27 28 ]
diff --git a/nixos/tests/prometheus-exporters.nix b/nixos/tests/prometheus-exporters.nix
index 7840130d4a364..e9f54208e95f9 100644
--- a/nixos/tests/prometheus-exporters.nix
+++ b/nixos/tests/prometheus-exporters.nix
@@ -431,8 +431,8 @@ let
     };
 
     kea = let
-      controlSocketPathV4 = "/run/kea-dhcp4/dhcp4.sock";
-      controlSocketPathV6 = "/run/kea-dhcp6/dhcp6.sock";
+      controlSocketPathV4 = "/run/kea/dhcp4.sock";
+      controlSocketPathV6 = "/run/kea/dhcp6.sock";
     in
     {
       exporterConfig = {
diff --git a/nixos/tests/tomcat.nix b/nixos/tests/tomcat.nix
index a5f219e104ad4..df5cb033b78f0 100644
--- a/nixos/tests/tomcat.nix
+++ b/nixos/tests/tomcat.nix
@@ -1,5 +1,6 @@
-import ./make-test-python.nix ({ pkgs, ... }: {
+import ./make-test-python.nix ({ lib, pkgs, ... }: {
   name = "tomcat";
+  meta.maintainers = [ lib.maintainers.anthonyroussel ];
 
   nodes.machine = { pkgs, ... }: {
     services.tomcat = {