about summary refs log tree commit diff
path: root/nixpkgs/nixos
diff options
context:
space:
mode:
authorAlyssa Ross <hi@alyssa.is>2023-10-31 22:19:45 +0100
committerAlyssa Ross <hi@alyssa.is>2023-10-31 22:19:45 +0100
commit78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc (patch)
treefd9cfb92edfaa37c919be8d24063b8a6c6d94c83 /nixpkgs/nixos
parent7e0c8fe656bbc2fcbdfc3e03a367d2c6ff389769 (diff)
parent0cbe9f69c234a7700596e943bfae7ef27a31b735 (diff)
downloadnixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.tar
nixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.tar.gz
nixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.tar.bz2
nixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.tar.lz
nixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.tar.xz
nixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.tar.zst
nixlib-78ba0c65b7bf9a64c12ca8c08f2e0220afdc8dbc.zip
Merge commit '0cbe9f69c234a7700596e943bfae7ef27a31b735' into HEAD
Diffstat (limited to 'nixpkgs/nixos')
-rw-r--r--nixpkgs/nixos/doc/manual/development/non-switchable-systems.section.md21
-rw-r--r--nixpkgs/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md1
-rw-r--r--nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md21
-rw-r--r--nixpkgs/nixos/lib/test-driver/default.nix5
-rwxr-xr-xnixpkgs/nixos/lib/test-driver/test_driver/__init__.py9
-rw-r--r--nixpkgs/nixos/lib/test-driver/test_driver/driver.py25
-rw-r--r--nixpkgs/nixos/lib/testing-python.nix1
-rw-r--r--nixpkgs/nixos/lib/testing/driver.nix13
-rw-r--r--nixpkgs/nixos/lib/testing/run.nix36
-rw-r--r--nixpkgs/nixos/maintainers/scripts/azure-new/examples/basic/system.nix1
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix6
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix10
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix6
-rw-r--r--nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix10
-rw-r--r--nixpkgs/nixos/modules/config/iproute2.nix15
-rw-r--r--nixpkgs/nixos/modules/config/mysql.nix4
-rw-r--r--nixpkgs/nixos/modules/config/nix-channel.nix11
-rw-r--r--nixpkgs/nixos/modules/config/users-groups.nix17
-rw-r--r--nixpkgs/nixos/modules/installer/tools/tools.nix22
-rw-r--r--nixpkgs/nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixpkgs/nixos/modules/misc/version.nix42
-rw-r--r--nixpkgs/nixos/modules/module-list.nix4
-rw-r--r--nixpkgs/nixos/modules/profiles/image-based-appliance.nix26
-rw-r--r--nixpkgs/nixos/modules/profiles/minimal.nix9
-rw-r--r--nixpkgs/nixos/modules/programs/cdemu.nix13
-rw-r--r--nixpkgs/nixos/modules/security/duosec.nix15
-rw-r--r--nixpkgs/nixos/modules/security/wrappers/default.nix57
-rw-r--r--nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix4
-rw-r--r--nixpkgs/nixos/modules/services/backup/borgmatic.nix2
-rw-r--r--nixpkgs/nixos/modules/services/backup/restic.nix40
-rw-r--r--nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix4
-rw-r--r--nixpkgs/nixos/modules/services/logging/syslog-ng.nix2
-rw-r--r--nixpkgs/nixos/modules/services/mail/mlmmj.nix17
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/certspotter.md74
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/certspotter.nix143
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/goss.md44
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/goss.nix86
-rw-r--r--nixpkgs/nixos/modules/services/monitoring/ups.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/gvpe.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix36
-rw-r--r--nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix2
-rw-r--r--nixpkgs/nixos/modules/services/networking/spiped.nix5
-rw-r--r--nixpkgs/nixos/modules/services/networking/ssh/sshd.nix86
-rw-r--r--nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix30
-rw-r--r--nixpkgs/nixos/modules/services/networking/tailscale.nix8
-rw-r--r--nixpkgs/nixos/modules/services/networking/unifi.nix4
-rw-r--r--nixpkgs/nixos/modules/services/system/nix-daemon.nix5
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md42
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix125
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/mattermost.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/plausible.nix6
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/shiori.nix9
-rw-r--r--nixpkgs/nixos/modules/services/web-apps/snipe-it.nix16
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/garage.nix2
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/nginx/default.nix1
-rw-r--r--nixpkgs/nixos/modules/services/web-servers/stargazer.nix8
-rw-r--r--nixpkgs/nixos/modules/system/activation/activatable-system.nix65
-rw-r--r--nixpkgs/nixos/modules/system/activation/activation-script.nix28
-rw-r--r--nixpkgs/nixos/modules/system/activation/switchable-system.nix55
-rw-r--r--nixpkgs/nixos/modules/system/boot/binfmt.nix51
-rw-r--r--nixpkgs/nixos/modules/system/boot/initrd-network.nix6
-rw-r--r--nixpkgs/nixos/modules/system/boot/initrd-ssh.nix11
-rw-r--r--nixpkgs/nixos/modules/system/boot/kernel.nix3
-rw-r--r--nixpkgs/nixos/modules/system/boot/networkd.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/timesyncd.nix45
-rw-r--r--nixpkgs/nixos/modules/tasks/encrypted-devices.nix64
-rw-r--r--nixpkgs/nixos/modules/tasks/filesystems/vfat.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix62
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix2
-rw-r--r--nixpkgs/nixos/modules/tasks/network-interfaces.nix18
-rw-r--r--nixpkgs/nixos/modules/virtualisation/nixos-containers.nix4
-rw-r--r--nixpkgs/nixos/release-combined.nix1
-rw-r--r--nixpkgs/nixos/release.nix4
-rw-r--r--nixpkgs/nixos/tests/activation/nix-channel.nix16
-rw-r--r--nixpkgs/nixos/tests/activation/var.nix18
-rw-r--r--nixpkgs/nixos/tests/all-tests.nix15
-rw-r--r--nixpkgs/nixos/tests/bittorrent.nix2
-rw-r--r--nixpkgs/nixos/tests/c2fmzq.nix75
-rw-r--r--nixpkgs/nixos/tests/containers-imperative.nix4
-rw-r--r--nixpkgs/nixos/tests/goss.nix53
-rw-r--r--nixpkgs/nixos/tests/grafana/provision/default.nix17
-rw-r--r--nixpkgs/nixos/tests/installer-systemd-stage-1.nix8
-rw-r--r--nixpkgs/nixos/tests/installer.nix4
-rw-r--r--nixpkgs/nixos/tests/netdata.nix4
-rw-r--r--nixpkgs/nixos/tests/nextcloud/basic.nix2
-rw-r--r--nixpkgs/nixos/tests/nixos-test-driver/timeout.nix15
-rw-r--r--nixpkgs/nixos/tests/non-switchable-system.nix15
-rw-r--r--nixpkgs/nixos/tests/opensearch.nix11
-rw-r--r--nixpkgs/nixos/tests/openssh.nix31
-rw-r--r--nixpkgs/nixos/tests/restic.nix18
-rw-r--r--nixpkgs/nixos/tests/stunnel.nix13
-rw-r--r--nixpkgs/nixos/tests/systemd-timesyncd.nix13
-rw-r--r--nixpkgs/nixos/tests/tsja.nix32
93 files changed, 1567 insertions, 443 deletions
diff --git a/nixpkgs/nixos/doc/manual/development/non-switchable-systems.section.md b/nixpkgs/nixos/doc/manual/development/non-switchable-systems.section.md
new file mode 100644
index 000000000000..87bb46c78909
--- /dev/null
+++ b/nixpkgs/nixos/doc/manual/development/non-switchable-systems.section.md
@@ -0,0 +1,21 @@
+# Non Switchable Systems {#sec-non-switchable-system}
+
+In certain systems, most notably image based appliances, updates are handled
+outside the system. This means that you do not need to rebuild your
+configuration on the system itself anymore.
+
+If you want to build such a system, you can use the `image-based-appliance`
+profile:
+
+```nix
+{ modulesPath, ... }: {
+  imports = [ "${modulesPath}/profiles/image-based-appliance.nix" ]
+}
+```
+
+The most notable deviation of this profile from a standard NixOS configuration
+is that after building it, you cannot switch *to* the configuration anymore.
+The profile sets `config.system.switch.enable = false;`, which excludes
+`switch-to-configuration`, the central script called by `nixos-rebuild`, from
+your system. Removing this script makes the image lighter and slightly more
+secure.
diff --git a/nixpkgs/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md b/nixpkgs/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md
index 82522b33740e..ccadb819e061 100644
--- a/nixpkgs/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md
+++ b/nixpkgs/nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md
@@ -55,4 +55,5 @@ explained in the next sections.
 ```{=include=} sections
 unit-handling.section.md
 activation-script.section.md
+non-switchable-systems.section.md
 ```
diff --git a/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md b/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md
index bd0d74a8885b..018e1e2f478b 100644
--- a/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md
+++ b/nixpkgs/nixos/doc/manual/release-notes/rl-2311.section.md
@@ -86,6 +86,8 @@
 
 - [pgBouncer](https://www.pgbouncer.org), a PostgreSQL connection pooler. Available as [services.pgbouncer](#opt-services.pgbouncer.enable).
 
+- [Goss](https://goss.rocks/), a YAML based serverspec alternative tool for validating a server's configuration. Available as [services.goss](#opt-services.goss.enable).
+
 - [trust-dns](https://trust-dns.org/), a Rust based DNS server built to be safe and secure from the ground up. Available as [services.trust-dns](#opt-services.trust-dns.enable).
 
 - [osquery](https://www.osquery.io/), a SQL powered operating system instrumentation, monitoring, and analytics.
@@ -111,6 +113,8 @@
 
 - [tuxedo-rs](https://github.com/AaronErhardt/tuxedo-rs), Rust utilities for interacting with hardware from TUXEDO Computers.
 
+- [certspotter](https://github.com/SSLMate/certspotter), a certificate transparency log monitor. Available as [services.certspotter](#opt-services.certspotter.enable).
+
 - [audiobookshelf](https://github.com/advplyr/audiobookshelf/), a self-hosted audiobook and podcast server. Available as [services.audiobookshelf](#opt-services.audiobookshelf.enable).
 
 - [ZITADEL](https://zitadel.com), a turnkey identity and access management platform. Available as [services.zitadel](#opt-services.zitadel.enable).
@@ -125,6 +129,8 @@
 
 - [Rosenpass](https://rosenpass.eu/), a service for post-quantum-secure VPNs with WireGuard. Available as [services.rosenpass](#opt-services.rosenpass.enable).
 
+- [c2FmZQ](https://github.com/c2FmZQ/c2FmZQ/), an application that can securely encrypt, store, and share files, including but not limited to pictures and videos. Available as [services.c2fmzq-server](#opt-services.c2fmzq-server.enable).
+
 ## Backward Incompatibilities {#sec-release-23.11-incompatibilities}
 
 - `network-online.target` has been fixed to no longer time out for systems with `networking.useDHCP = true` and `networking.useNetworkd = true`.
@@ -156,6 +162,10 @@
 
 - `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).
 
+- `maintainers/scripts/update-luarocks-packages` is now a proper package
+  `luarocks-packages-updater` that can be run to maintain out-of-tree luarocks
+  packages
+
 - The `users.users.<name>.passwordFile` has been renamed to `users.users.<name>.hashedPasswordFile` to avoid possible confusions. The option is in fact the file-based version of `hashedPassword`, not `password`, and expects a file containing the {manpage}`crypt(3)` hash of the user password.
 
 - The `services.ananicy.extraRules` option now has the type of `listOf attrs` instead of `string`.
@@ -236,8 +246,6 @@
 
 - `baloo`, the file indexer/search engine used by KDE now has a patch to prevent files from constantly being reindexed when the device ids of the their underlying storage changes. This happens frequently when using btrfs or LVM. The patch has not yet been accepted upstream but it provides a significantly improved experience. When upgrading, reset baloo to get a clean index: `balooctl disable ; balooctl purge ; balooctl enable`.
 
-- `services.ddclient` has been removed on the request of the upstream maintainer because it is unmaintained and has bugs. Please switch to a different software like `inadyn` or `knsupdate`.
-
 - The `vlock` program from the `kbd` package has been moved into its own package output and should now be referenced explicitly as `kbd.vlock` or replaced with an alternative such as the standalone `vlock` package or `physlock`.
 
 - `fileSystems.<name>.autoFormat` now uses `systemd-makefs`, which does not accept formatting options. Therefore, `fileSystems.<name>.formatOptions` has been removed.
@@ -335,8 +343,17 @@
 
 - `services.kea.{ctrl-agent,dhcp-ddns,dhcp,dhcp6}` now use separate runtime directories instead of `/run/kea` to work around the runtime directory being cleared on service start.
 
+- `mkDerivation` now rejects MD5 hashes.
+
+- The `junicode` font package has been updated to [major version 2](https://github.com/psb1558/Junicode-font/releases/tag/v2.001), which is now a font family. In particular, plain `Junicode.ttf` no longer exists. In addition, TrueType font files are now placed in `font/truetype` instead of `font/junicode-ttf`; this change does not affect use via `fonts.packages` NixOS option.
+
 ## Other Notable Changes {#sec-release-23.11-notable-changes}
 
+- A new option `system.switch.enable` was added. By default, this is option is
+  enabled. Disabling it makes the system unable to be reconfigured via
+  `nixos-rebuild`. This is good for image based appliances where updates are
+  handled outside the image.
+
 - 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.
 
 - GNOME, Pantheon, Cinnamon module no longer forces Qt applications to use Adwaita style since it was buggy and is no longer maintained upstream (specifically, Cinnamon now defaults to the gtk2 style instead, following the default in Linux Mint). If you still want it, you can add the following options to your configuration but it will probably be eventually removed:
diff --git a/nixpkgs/nixos/lib/test-driver/default.nix b/nixpkgs/nixos/lib/test-driver/default.nix
index 6e01e00b4355..09d80deb8546 100644
--- a/nixpkgs/nixos/lib/test-driver/default.nix
+++ b/nixpkgs/nixos/lib/test-driver/default.nix
@@ -11,6 +11,7 @@
 , tesseract4
 , vde2
 , extraPythonPackages ? (_ : [])
+, nixosTests
 }:
 
 python3Packages.buildPythonApplication {
@@ -31,6 +32,10 @@ python3Packages.buildPythonApplication {
     ++ (lib.optionals enableOCR [ imagemagick_light tesseract4 ])
     ++ extraPythonPackages python3Packages;
 
+  passthru.tests = {
+    inherit (nixosTests.nixos-test-driver) driver-timeout;
+  };
+
   doCheck = true;
   nativeCheckInputs = with python3Packages; [ mypy ruff black ];
   checkPhase = ''
diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/__init__.py b/nixpkgs/nixos/lib/test-driver/test_driver/__init__.py
index 371719d7a988..9daae1e941a6 100755
--- a/nixpkgs/nixos/lib/test-driver/test_driver/__init__.py
+++ b/nixpkgs/nixos/lib/test-driver/test_driver/__init__.py
@@ -77,6 +77,14 @@ def main() -> None:
         help="vlans to span by the driver",
     )
     arg_parser.add_argument(
+        "--global-timeout",
+        type=int,
+        metavar="GLOBAL_TIMEOUT",
+        action=EnvDefault,
+        envvar="globalTimeout",
+        help="Timeout in seconds for the whole test",
+    )
+    arg_parser.add_argument(
         "-o",
         "--output_directory",
         help="""The path to the directory where outputs copied from the VM will be placed.
@@ -103,6 +111,7 @@ def main() -> None:
         args.testscript.read_text(),
         args.output_directory.resolve(),
         args.keep_vm_state,
+        args.global_timeout,
     ) as driver:
         if args.interactive:
             history_dir = os.getcwd()
diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py
index 723c80717860..786821b0cc0d 100644
--- a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py
+++ b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py
@@ -1,6 +1,8 @@
 import os
 import re
+import signal
 import tempfile
+import threading
 from contextlib import contextmanager
 from pathlib import Path
 from typing import Any, Callable, ContextManager, Dict, Iterator, List, Optional, Union
@@ -41,6 +43,8 @@ class Driver:
     vlans: List[VLan]
     machines: List[Machine]
     polling_conditions: List[PollingCondition]
+    global_timeout: int
+    race_timer: threading.Timer
 
     def __init__(
         self,
@@ -49,9 +53,12 @@ class Driver:
         tests: str,
         out_dir: Path,
         keep_vm_state: bool = False,
+        global_timeout: int = 24 * 60 * 60 * 7,
     ):
         self.tests = tests
         self.out_dir = out_dir
+        self.global_timeout = global_timeout
+        self.race_timer = threading.Timer(global_timeout, self.terminate_test)
 
         tmp_dir = get_tmp_dir()
 
@@ -82,6 +89,7 @@ class Driver:
 
     def __exit__(self, *_: Any) -> None:
         with rootlog.nested("cleanup"):
+            self.race_timer.cancel()
             for machine in self.machines:
                 machine.release()
 
@@ -144,6 +152,10 @@ class Driver:
 
     def run_tests(self) -> None:
         """Run the test script (for non-interactive test runs)"""
+        rootlog.info(
+            f"Test will time out and terminate in {self.global_timeout} seconds"
+        )
+        self.race_timer.start()
         self.test_script()
         # TODO: Collect coverage data
         for machine in self.machines:
@@ -161,6 +173,19 @@ class Driver:
         with rootlog.nested("wait for all VMs to finish"):
             for machine in self.machines:
                 machine.wait_for_shutdown()
+            self.race_timer.cancel()
+
+    def terminate_test(self) -> None:
+        # This will be usually running in another thread than
+        # the thread actually executing the test script.
+        with rootlog.nested("timeout reached; test terminating..."):
+            for machine in self.machines:
+                machine.release()
+            # As we cannot `sys.exit` from another thread
+            # We can at least force the main thread to get SIGTERM'ed.
+            # This will prevent any user who caught all the exceptions
+            # to swallow them and prevent itself from terminating.
+            os.kill(os.getpid(), signal.SIGTERM)
 
     def create_machine(self, args: Dict[str, Any]) -> Machine:
         tmp_dir = get_tmp_dir()
diff --git a/nixpkgs/nixos/lib/testing-python.nix b/nixpkgs/nixos/lib/testing-python.nix
index 4904ad6e3591..f5222351518b 100644
--- a/nixpkgs/nixos/lib/testing-python.nix
+++ b/nixpkgs/nixos/lib/testing-python.nix
@@ -42,6 +42,7 @@ rec {
     , nodes ? {}
     , testScript
     , enableOCR ? false
+    , globalTimeout ? (60 * 60)
     , name ? "unnamed"
     , skipTypeCheck ? false
       # Skip linting (mainly intended for faster dev cycles)
diff --git a/nixpkgs/nixos/lib/testing/driver.nix b/nixpkgs/nixos/lib/testing/driver.nix
index cc97ca72083f..b6f01c38191d 100644
--- a/nixpkgs/nixos/lib/testing/driver.nix
+++ b/nixpkgs/nixos/lib/testing/driver.nix
@@ -94,6 +94,7 @@ let
         wrapProgram $out/bin/nixos-test-driver \
           --set startScripts "''${vmStartScripts[*]}" \
           --set testScript "$out/test-script" \
+          --set globalTimeout "${toString config.globalTimeout}" \
           --set vlans '${toString vlans}' \
           ${lib.escapeShellArgs (lib.concatMap (arg: ["--add-flags" arg]) config.extraDriverArgs)}
       '';
@@ -123,6 +124,18 @@ in
       defaultText = "hostPkgs.qemu_test";
     };
 
+    globalTimeout = mkOption {
+      description = mdDoc ''
+        A global timeout for the complete test, expressed in seconds.
+        Beyond that timeout, every resource will be killed and released and the test will fail.
+
+        By default, we use a 1 hour timeout.
+      '';
+      type = types.int;
+      default = 60 * 60;
+      example = 10 * 60;
+    };
+
     enableOCR = mkOption {
       description = mdDoc ''
         Whether to enable Optical Character Recognition functionality for
diff --git a/nixpkgs/nixos/lib/testing/run.nix b/nixpkgs/nixos/lib/testing/run.nix
index 0cd07d8afd21..9440c1acdfd8 100644
--- a/nixpkgs/nixos/lib/testing/run.nix
+++ b/nixpkgs/nixos/lib/testing/run.nix
@@ -16,6 +16,15 @@ in
       '';
     };
 
+    rawTestDerivation = mkOption {
+      type = types.package;
+      description = mdDoc ''
+        Unfiltered version of `test`, for troubleshooting the test framework and `testBuildFailure` in the test framework's test suite.
+        This is not intended for general use. Use `test` instead.
+      '';
+      internal = true;
+    };
+
     test = mkOption {
       type = types.package;
       # TODO: can the interactive driver be configured to access the network?
@@ -29,25 +38,26 @@ in
   };
 
   config = {
-    test = lib.lazyDerivation { # lazyDerivation improves performance when only passthru items and/or meta are used.
-      derivation = hostPkgs.stdenv.mkDerivation {
-        name = "vm-test-run-${config.name}";
+    rawTestDerivation = hostPkgs.stdenv.mkDerivation {
+      name = "vm-test-run-${config.name}";
 
-        requiredSystemFeatures = [ "kvm" "nixos-test" ];
+      requiredSystemFeatures = [ "kvm" "nixos-test" ];
 
-        buildCommand = ''
-          mkdir -p $out
+      buildCommand = ''
+        mkdir -p $out
 
-          # effectively mute the XMLLogger
-          export LOGFILE=/dev/null
+        # effectively mute the XMLLogger
+        export LOGFILE=/dev/null
 
-          ${config.driver}/bin/nixos-test-driver -o $out
-        '';
+        ${config.driver}/bin/nixos-test-driver -o $out
+      '';
 
-        passthru = config.passthru;
+      passthru = config.passthru;
 
-        meta = config.meta;
-      };
+      meta = config.meta;
+    };
+    test = lib.lazyDerivation { # lazyDerivation improves performance when only passthru items and/or meta are used.
+      derivation = config.rawTestDerivation;
       inherit (config) passthru meta;
     };
 
diff --git a/nixpkgs/nixos/maintainers/scripts/azure-new/examples/basic/system.nix b/nixpkgs/nixos/maintainers/scripts/azure-new/examples/basic/system.nix
index d283742701d1..d1044802e1f0 100644
--- a/nixpkgs/nixos/maintainers/scripts/azure-new/examples/basic/system.nix
+++ b/nixpkgs/nixos/maintainers/scripts/azure-new/examples/basic/system.nix
@@ -21,7 +21,6 @@ in
 
   virtualisation.azureImage.diskSize = 2500;
 
-  system.stateVersion = "20.03";
   boot.kernelPackages = pkgs.linuxPackages_latest;
 
   # test user doesn't have a password
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix
index 7b743d170bc6..62a6e1f9aa3a 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix
@@ -2,13 +2,13 @@
 # your system.  Help is available in the configuration.nix(5) man page
 # and in the NixOS manual (accessible by running ‘nixos-help’).
 
-{ config, pkgs, lib, ... }:
+{ config, pkgs, lib, modulesPath, ... }:
 
 {
   imports =
     [
       # Include the default lxd configuration.
-      ../../../modules/virtualisation/lxc-container.nix
+      "${modulesPath}/modules/virtualisation/lxc-container.nix"
       # Include the container-specific autogenerated configuration.
       ./lxd.nix
     ];
@@ -16,5 +16,5 @@
   networking.useDHCP = false;
   networking.interfaces.eth0.useDHCP = true;
 
-  system.stateVersion = "21.05"; # Did you read the comment?
+  system.stateVersion = "@stateVersion@"; # Did you read the comment?
 }
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix
index 3bd1320b2b68..b77f9f5aabe0 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-container-image.nix
@@ -13,11 +13,15 @@
   };
 
   # copy the config for nixos-rebuild
-  system.activationScripts.config = ''
+  system.activationScripts.config = let
+    config = pkgs.substituteAll {
+      src = ./lxd-container-image-inner.nix;
+      stateVersion = lib.trivial.release;
+    };
+  in ''
     if [ ! -e /etc/nixos/configuration.nix ]; then
       mkdir -p /etc/nixos
-      cat ${./lxd-container-image-inner.nix} > /etc/nixos/configuration.nix
-      ${lib.getExe pkgs.gnused} 's|../../../modules/virtualisation/lxc-container.nix|<nixpkgs/nixos/modules/virtualisation/lxc-container.nix>|g' -i /etc/nixos/configuration.nix
+      cp ${config} /etc/nixos/configuration.nix
     fi
   '';
 
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix
index a8f2c63ac5c6..c1c50b32ff5b 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix
@@ -2,13 +2,13 @@
 # your system.  Help is available in the configuration.nix(5) man page
 # and in the NixOS manual (accessible by running ‘nixos-help’).
 
-{ config, pkgs, lib, ... }:
+{ config, pkgs, lib, modulesPath, ... }:
 
 {
   imports =
     [
       # Include the default lxd configuration.
-      ../../../modules/virtualisation/lxd-virtual-machine.nix
+      "${modulesPath}/virtualisation/lxd-virtual-machine.nix"
       # Include the container-specific autogenerated configuration.
       ./lxd.nix
     ];
@@ -16,5 +16,5 @@
   networking.useDHCP = false;
   networking.interfaces.eth0.useDHCP = true;
 
-  system.stateVersion = "23.05"; # Did you read the comment?
+  system.stateVersion = "@stateVersion@"; # Did you read the comment?
 }
diff --git a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
index eb0d9217d402..0d96eea0e2d2 100644
--- a/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
+++ b/nixpkgs/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix
@@ -13,11 +13,15 @@
   };
 
   # copy the config for nixos-rebuild
-  system.activationScripts.config = ''
+  system.activationScripts.config = let
+    config = pkgs.substituteAll {
+      src = ./lxd-virtual-machine-image-inner.nix;
+      stateVersion = lib.trivial.release;
+    };
+  in ''
     if [ ! -e /etc/nixos/configuration.nix ]; then
       mkdir -p /etc/nixos
-      cat ${./lxd-virtual-machine-image-inner.nix} > /etc/nixos/configuration.nix
-      ${lib.getExe pkgs.gnused} 's|../../../modules/virtualisation/lxd-virtual-machine.nix|<nixpkgs/nixos/modules/virtualisation/lxd-virtual-machine.nix>|g' -i /etc/nixos/configuration.nix
+      cp ${config} /etc/nixos/configuration.nix
     fi
   '';
 
diff --git a/nixpkgs/nixos/modules/config/iproute2.nix b/nixpkgs/nixos/modules/config/iproute2.nix
index 7e4fb4d848e3..78bd07d680e2 100644
--- a/nixpkgs/nixos/modules/config/iproute2.nix
+++ b/nixpkgs/nixos/modules/config/iproute2.nix
@@ -18,15 +18,10 @@ in
   };
 
   config = mkIf cfg.enable {
-    environment.etc."iproute2/bpf_pinning" = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/bpf_pinning"; };
-    environment.etc."iproute2/ematch_map"  = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/ematch_map";  };
-    environment.etc."iproute2/group"       = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/group";       };
-    environment.etc."iproute2/nl_protos"   = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/nl_protos";   };
-    environment.etc."iproute2/rt_dsfield"  = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/rt_dsfield";  };
-    environment.etc."iproute2/rt_protos"   = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/rt_protos";   };
-    environment.etc."iproute2/rt_realms"   = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/rt_realms";   };
-    environment.etc."iproute2/rt_scopes"   = { mode = "0644"; text = fileContents "${pkgs.iproute2}/etc/iproute2/rt_scopes";   };
-    environment.etc."iproute2/rt_tables"   = { mode = "0644"; text = (fileContents "${pkgs.iproute2}/etc/iproute2/rt_tables")
-                                                                   + (optionalString (cfg.rttablesExtraConfig != "") "\n\n${cfg.rttablesExtraConfig}"); };
+    environment.etc."iproute2/rt_tables" = {
+      mode = "0644";
+      text = (fileContents "${pkgs.iproute2}/lib/iproute2/rt_tables")
+        + (optionalString (cfg.rttablesExtraConfig != "") "\n\n${cfg.rttablesExtraConfig}");
+    };
   };
 }
diff --git a/nixpkgs/nixos/modules/config/mysql.nix b/nixpkgs/nixos/modules/config/mysql.nix
index 2f13c56f2ae5..95c9ba76663e 100644
--- a/nixpkgs/nixos/modules/config/mysql.nix
+++ b/nixpkgs/nixos/modules/config/mysql.nix
@@ -429,11 +429,11 @@ in
       '';
     };
 
-    # Activation script to append the password from the password file
+    # preStart script to append the password from the password file
     # to the configuration files. It also fixes the owner of the
     # libnss-mysql-root.cfg because it is changed to root after the
     # password is appended.
-    system.activationScripts.mysql-auth-passwords = ''
+    systemd.services.mysql.preStart = ''
       if [[ -r ${cfg.passwordFile} ]]; then
         org_umask=$(umask)
         umask 0077
diff --git a/nixpkgs/nixos/modules/config/nix-channel.nix b/nixpkgs/nixos/modules/config/nix-channel.nix
index 3f8e088ede92..4abc846b0858 100644
--- a/nixpkgs/nixos/modules/config/nix-channel.nix
+++ b/nixpkgs/nixos/modules/config/nix-channel.nix
@@ -97,12 +97,9 @@ in
 
     nix.settings.nix-path = mkIf (! cfg.channel.enable) (mkDefault "");
 
-    system.activationScripts.nix-channel = mkIf cfg.channel.enable
-      (stringAfter [ "etc" "users" ] ''
-        # Subscribe the root user to the NixOS channel by default.
-        if [ ! -e "/root/.nix-channels" ]; then
-            echo "${config.system.defaultChannel} nixos" > "/root/.nix-channels"
-        fi
-      '');
+    systemd.tmpfiles.rules = lib.mkIf cfg.channel.enable [
+      "f /root/.nix-channels -"
+      ''w "/root/.nix-channels" - - - - "${config.system.defaultChannel} nixos\n"''
+    ];
   };
 }
diff --git a/nixpkgs/nixos/modules/config/users-groups.nix b/nixpkgs/nixos/modules/config/users-groups.nix
index 97268a8d83ef..b4251214876e 100644
--- a/nixpkgs/nixos/modules/config/users-groups.nix
+++ b/nixpkgs/nixos/modules/config/users-groups.nix
@@ -606,6 +606,14 @@ in {
           defaultText = literalExpression "config.users.users.\${name}.group";
           default = cfg.users.${name}.group;
         };
+        options.shell = mkOption {
+          type = types.passwdEntry types.path;
+          description = ''
+            The path to the user's shell in initrd.
+          '';
+          default = "${pkgs.shadow}/bin/nologin";
+          defaultText = literalExpression "\${pkgs.shadow}/bin/nologin";
+        };
       }));
     };
 
@@ -750,17 +758,20 @@ in {
     boot.initrd.systemd = lib.mkIf config.boot.initrd.systemd.enable {
       contents = {
         "/etc/passwd".text = ''
-          ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { uid, group }: let
+          ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { uid, group, shell }: let
             g = config.boot.initrd.systemd.groups.${group};
-          in "${n}:x:${toString uid}:${toString g.gid}::/var/empty:") config.boot.initrd.systemd.users)}
+          in "${n}:x:${toString uid}:${toString g.gid}::/var/empty:${shell}") config.boot.initrd.systemd.users)}
         '';
         "/etc/group".text = ''
           ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: { gid }: "${n}:x:${toString gid}:") config.boot.initrd.systemd.groups)}
         '';
+        "/etc/shells".text = lib.concatStringsSep "\n" (lib.unique (lib.mapAttrsToList (_: u: u.shell) config.boot.initrd.systemd.users)) + "\n";
       };
 
+      storePaths = [ "${pkgs.shadow}/bin/nologin" ];
+
       users = {
-        root = {};
+        root = { shell = lib.mkDefault "/bin/bash"; };
         nobody = {};
       };
 
diff --git a/nixpkgs/nixos/modules/installer/tools/tools.nix b/nixpkgs/nixos/modules/installer/tools/tools.nix
index d385e4a6b1c8..15e10128ac9a 100644
--- a/nixpkgs/nixos/modules/installer/tools/tools.nix
+++ b/nixpkgs/nixos/modules/installer/tools/tools.nix
@@ -224,12 +224,22 @@ in
         # accidentally delete configuration.nix.
         # system.copySystemConfiguration = true;
 
-        # This value determines the NixOS release from which the default
-        # settings for stateful data, like file locations and database versions
-        # on your system were taken. It's perfectly fine and recommended to leave
-        # this value at the release version of the first install of this system.
-        # Before changing this value read the documentation for this option
-        # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
+        # This option defines the first version of NixOS you have installed on this particular machine,
+        # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
+        #
+        # Most users should NEVER change this value after the initial install, for any reason,
+        # even if you've upgraded your system to a new NixOS release.
+        #
+        # This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
+        # so changing it will NOT upgrade your system.
+        #
+        # This value being lower than the current NixOS release does NOT mean your system is
+        # out of date, out of support, or vulnerable.
+        #
+        # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
+        # and migrated your data accordingly.
+        #
+        # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
         system.stateVersion = "${config.system.nixos.release}"; # Did you read the comment?
 
       }
diff --git a/nixpkgs/nixos/modules/installer/virtualbox-demo.nix b/nixpkgs/nixos/modules/installer/virtualbox-demo.nix
index 27a7651382b2..01931b2acfca 100644
--- a/nixpkgs/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixpkgs/nixos/modules/installer/virtualbox-demo.nix
@@ -21,7 +21,7 @@ with lib;
   services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" "modesetting" ];
 
   powerManagement.enable = false;
-  system.stateVersion = mkDefault "18.03";
+  system.stateVersion = lib.mkDefault lib.trivial.release;
 
   installer.cloneConfigExtra = ''
   # Let demo build as a trusted user.
diff --git a/nixpkgs/nixos/modules/misc/version.nix b/nixpkgs/nixos/modules/misc/version.nix
index 0a66eafe933e..45dbf45b3ae7 100644
--- a/nixpkgs/nixos/modules/misc/version.nix
+++ b/nixpkgs/nixos/modules/misc/version.nix
@@ -121,22 +121,32 @@ in
       default = cfg.release;
       defaultText = literalExpression "config.${opt.release}";
       description = lib.mdDoc ''
-        Every once in a while, a new NixOS release may change
-        configuration defaults in a way incompatible with stateful
-        data. For instance, if the default version of PostgreSQL
-        changes, the new version will probably be unable to read your
-        existing databases. To prevent such breakage, you should set the
-        value of this option to the NixOS release with which you want
-        to be compatible. The effect is that NixOS will use
-        defaults corresponding to the specified release (such as using
-        an older version of PostgreSQL).
-        It’s perfectly fine and recommended to leave this value at the
-        release version of the first install of this system.
-        Changing this option will not upgrade your system. In fact it
-        is meant to stay constant exactly when you upgrade your system.
-        You should only bump this option, if you are sure that you can
-        or have migrated all state on your system which is affected
-        by this option.
+        This option defines the first version of NixOS you have installed on this particular machine,
+        and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
+
+        For example, if NixOS version XX.YY ships with AwesomeDB version N by default, and is then
+        upgraded to version XX.YY+1, which ships AwesomeDB version N+1, the existing databases
+        may no longer be compatible, causing applications to fail, or even leading to data loss.
+
+        The `stateVersion` mechanism avoids this situation by making the default version of such packages
+        conditional on the first version of NixOS you've installed (encoded in `stateVersion`), instead of
+        simply always using the latest one.
+
+        Note that this generally only affects applications that can't upgrade their data automatically -
+        applications and services supporting automatic migrations will remain on latest versions when
+        you upgrade.
+
+        Most users should **never** change this value after the initial install, for any reason,
+        even if you've upgraded your system to a new NixOS release.
+
+        This value does **not** affect the Nixpkgs version your packages and OS are pulled from,
+        so changing it will **not** upgrade your system.
+
+        This value being lower than the current NixOS release does **not** mean your system is
+        out of date, out of support, or vulnerable.
+
+        Do **not** change this value unless you have manually inspected all the changes it would
+        make to your configuration, and migrated your data accordingly.
       '';
     };
 
diff --git a/nixpkgs/nixos/modules/module-list.nix b/nixpkgs/nixos/modules/module-list.nix
index c5c3c0d4719a..7611d113e668 100644
--- a/nixpkgs/nixos/modules/module-list.nix
+++ b/nixpkgs/nixos/modules/module-list.nix
@@ -768,12 +768,14 @@
   ./services/monitoring/below.nix
   ./services/monitoring/bosun.nix
   ./services/monitoring/cadvisor.nix
+  ./services/monitoring/certspotter.nix
   ./services/monitoring/cockpit.nix
   ./services/monitoring/collectd.nix
   ./services/monitoring/das_watchdog.nix
   ./services/monitoring/datadog-agent.nix
   ./services/monitoring/do-agent.nix
   ./services/monitoring/fusion-inventory.nix
+  ./services/monitoring/goss.nix
   ./services/monitoring/grafana-agent.nix
   ./services/monitoring/grafana-image-renderer.nix
   ./services/monitoring/grafana-reporter.nix
@@ -1233,6 +1235,7 @@
   ./services/web-apps/atlassian/jira.nix
   ./services/web-apps/audiobookshelf.nix
   ./services/web-apps/bookstack.nix
+  ./services/web-apps/c2fmzq-server.nix
   ./services/web-apps/calibre-web.nix
   ./services/web-apps/coder.nix
   ./services/web-apps/changedetection-io.nix
@@ -1406,6 +1409,7 @@
   ./system/activation/activatable-system.nix
   ./system/activation/activation-script.nix
   ./system/activation/specialisation.nix
+  ./system/activation/switchable-system.nix
   ./system/activation/bootspec.nix
   ./system/activation/top-level.nix
   ./system/boot/binfmt.nix
diff --git a/nixpkgs/nixos/modules/profiles/image-based-appliance.nix b/nixpkgs/nixos/modules/profiles/image-based-appliance.nix
new file mode 100644
index 000000000000..7e8b6f696d54
--- /dev/null
+++ b/nixpkgs/nixos/modules/profiles/image-based-appliance.nix
@@ -0,0 +1,26 @@
+# This profile sets up a sytem for image based appliance usage. An appliance is
+# installed as an image, cannot be re-built, has no Nix available, and is
+# generally not meant for interactive use. Updates to such an appliance are
+# handled by updating whole partition images via a tool like systemd-sysupdate.
+
+{ lib, modulesPath, ... }:
+
+{
+
+  # Appliances are always "minimal".
+  imports = [
+    "${modulesPath}/profiles/minimal.nix"
+  ];
+
+  # The system cannot be rebuilt.
+  nix.enable = false;
+  system.switch.enable = false;
+
+  # The system is static.
+  users.mutableUsers = false;
+
+  # The system avoids interpreters as much as possible to reduce its attack
+  # surface.
+  boot.initrd.systemd.enable = lib.mkDefault true;
+  networking.useNetworkd = lib.mkDefault true;
+}
diff --git a/nixpkgs/nixos/modules/profiles/minimal.nix b/nixpkgs/nixos/modules/profiles/minimal.nix
index bd1b2b452189..75f355b4a002 100644
--- a/nixpkgs/nixos/modules/profiles/minimal.nix
+++ b/nixpkgs/nixos/modules/profiles/minimal.nix
@@ -18,6 +18,15 @@ with lib;
 
   documentation.nixos.enable = mkDefault false;
 
+  # Perl is a default package.
+  environment.defaultPackages = mkDefault [ ];
+
+  # The lessopen package pulls in Perl.
+  programs.less.lessopen = mkDefault null;
+
+  # This pulls in nixos-containers which depends on Perl.
+  boot.enableContainers = mkDefault false;
+
   programs.command-not-found.enable = mkDefault false;
 
   services.logrotate.enable = mkDefault false;
diff --git a/nixpkgs/nixos/modules/programs/cdemu.nix b/nixpkgs/nixos/modules/programs/cdemu.nix
index d43f009f2f92..7eba4d29d83b 100644
--- a/nixpkgs/nixos/modules/programs/cdemu.nix
+++ b/nixpkgs/nixos/modules/programs/cdemu.nix
@@ -53,6 +53,19 @@ in {
       dbus.packages = [ pkgs.cdemu-daemon ];
     };
 
+    users.groups.${config.programs.cdemu.group} = {};
+
+    # Systemd User service
+    # manually adapted from example in source package:
+    # https://sourceforge.net/p/cdemu/code/ci/master/tree/cdemu-daemon/service-example/cdemu-daemon.service
+    systemd.user.services.cdemu-daemon.description = "CDEmu daemon";
+    systemd.user.services.cdemu-daemon.serviceConfig = {
+      Type = "dbus";
+      BusName = "net.sf.cdemu.CDEmuDaemon";
+      ExecStart = "${pkgs.cdemu-daemon}/bin/cdemu-daemon --config-file \"%h/.config/cdemu-daemon\"";
+      Restart = "no";
+    };
+
     environment.systemPackages =
       [ pkgs.cdemu-daemon pkgs.cdemu-client ]
       ++ optional cfg.gui pkgs.gcdemu
diff --git a/nixpkgs/nixos/modules/security/duosec.nix b/nixpkgs/nixos/modules/security/duosec.nix
index 02b11766b3c0..2a855a77e3a3 100644
--- a/nixpkgs/nixos/modules/security/duosec.nix
+++ b/nixpkgs/nixos/modules/security/duosec.nix
@@ -193,8 +193,11 @@ in
         source = "${pkgs.duo-unix.out}/bin/login_duo";
       };
 
-    system.activationScripts = {
-      login_duo = mkIf cfg.ssh.enable ''
+    systemd.services.login-duo = lib.mkIf cfg.ssh.enable {
+      wantedBy = [ "sysinit.target" ];
+      before = [ "sysinit.target" ];
+      unitConfig.DefaultDependencies = false;
+      script = ''
         if test -f "${cfg.secretKeyFile}"; then
           mkdir -m 0755 -p /etc/duo
 
@@ -209,7 +212,13 @@ in
           mv -fT "$conf" /etc/duo/login_duo.conf
         fi
       '';
-      pam_duo = mkIf cfg.pam.enable ''
+    };
+
+    systemd.services.pam-duo = lib.mkIf cfg.ssh.enable {
+      wantedBy = [ "sysinit.target" ];
+      before = [ "sysinit.target" ];
+      unitConfig.DefaultDependencies = false;
+      script = ''
         if test -f "${cfg.secretKeyFile}"; then
           mkdir -m 0755 -p /etc/duo
 
diff --git a/nixpkgs/nixos/modules/security/wrappers/default.nix b/nixpkgs/nixos/modules/security/wrappers/default.nix
index a8bb0650b11a..250f9775be14 100644
--- a/nixpkgs/nixos/modules/security/wrappers/default.nix
+++ b/nixpkgs/nixos/modules/security/wrappers/default.nix
@@ -275,33 +275,38 @@ in
       mrpx ${wrap.source},
     '') wrappers;
 
-    ###### wrappers activation script
-    system.activationScripts.wrappers =
-      lib.stringAfter [ "specialfs" "users" ]
-        ''
-          chmod 755 "${parentWrapperDir}"
-
-          # We want to place the tmpdirs for the wrappers to the parent dir.
-          wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
-          chmod a+rx "$wrapperDir"
-
-          ${lib.concatStringsSep "\n" mkWrappedPrograms}
-
-          if [ -L ${wrapperDir} ]; then
-            # Atomically replace the symlink
-            # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
-            old=$(readlink -f ${wrapperDir})
-            if [ -e "${wrapperDir}-tmp" ]; then
-              rm --force --recursive "${wrapperDir}-tmp"
-            fi
-            ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
-            mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
-            rm --force --recursive "$old"
-          else
-            # For initial setup
-            ln --symbolic "$wrapperDir" "${wrapperDir}"
+    systemd.services.suid-sgid-wrappers = {
+      description = "Create SUID/SGID Wrappers";
+      wantedBy = [ "sysinit.target" ];
+      before = [ "sysinit.target" ];
+      unitConfig.DefaultDependencies = false;
+      unitConfig.RequiresMountsFor = [ "/nix/store" "/run/wrappers" ];
+      serviceConfig.Type = "oneshot";
+      script = ''
+        chmod 755 "${parentWrapperDir}"
+
+        # We want to place the tmpdirs for the wrappers to the parent dir.
+        wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
+        chmod a+rx "$wrapperDir"
+
+        ${lib.concatStringsSep "\n" mkWrappedPrograms}
+
+        if [ -L ${wrapperDir} ]; then
+          # Atomically replace the symlink
+          # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
+          old=$(readlink -f ${wrapperDir})
+          if [ -e "${wrapperDir}-tmp" ]; then
+            rm --force --recursive "${wrapperDir}-tmp"
           fi
-        '';
+          ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
+          mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
+          rm --force --recursive "$old"
+        else
+          # For initial setup
+          ln --symbolic "$wrapperDir" "${wrapperDir}"
+        fi
+      '';
+    };
 
     ###### wrappers consistency checks
     system.checks = lib.singleton (pkgs.runCommandLocal
diff --git a/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix b/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
index 1fb67ecfe506..205e05f2ed17 100644
--- a/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
+++ b/nixpkgs/nixos/modules/services/audio/wyoming/faster-whisper.nix
@@ -37,6 +37,9 @@ in
             enable = mkEnableOption (mdDoc "Wyoming faster-whisper server");
 
             model = mkOption {
+              # Intersection between available and referenced models here:
+              # https://github.com/rhasspy/models/releases/tag/v1.0
+              # https://github.com/rhasspy/rhasspy3/blob/wyoming-v1/programs/asr/faster-whisper/server/wyoming_faster_whisper/download.py#L17-L27
               type = enum [
                 "tiny"
                 "tiny-int8"
@@ -44,7 +47,6 @@ in
                 "base-int8"
                 "small"
                 "small-int8"
-                "medium"
                 "medium-int8"
               ];
               default = "tiny-int8";
diff --git a/nixpkgs/nixos/modules/services/backup/borgmatic.nix b/nixpkgs/nixos/modules/services/backup/borgmatic.nix
index d3ba7628e85d..b27dd2817120 100644
--- a/nixpkgs/nixos/modules/services/backup/borgmatic.nix
+++ b/nixpkgs/nixos/modules/services/backup/borgmatic.nix
@@ -81,7 +81,7 @@ in
   config = mkIf cfg.enable {
 
     warnings = []
-      ++ optional (cfg.settings != null && cfg.settings.location != null)
+      ++ optional (cfg.settings != null && cfg.settings ? location)
         "`services.borgmatic.settings.location` is deprecated, please move your options out of sections to the global scope"
       ++ optional (catAttrs "location" (attrValues cfg.configurations) != [])
         "`services.borgmatic.configurations.<name>.location` is deprecated, please move your options out of sections to the global scope"
diff --git a/nixpkgs/nixos/modules/services/backup/restic.nix b/nixpkgs/nixos/modules/services/backup/restic.nix
index 78220e99c3d1..49a55d056014 100644
--- a/nixpkgs/nixos/modules/services/backup/restic.nix
+++ b/nixpkgs/nixos/modules/services/backup/restic.nix
@@ -23,25 +23,13 @@ in
 
         environmentFile = mkOption {
           type = with types; nullOr str;
-          # added on 2021-08-28, s3CredentialsFile should
-          # be removed in the future (+ remember the warning)
-          default = config.s3CredentialsFile;
+          default = null;
           description = lib.mdDoc ''
             file containing the credentials to access the repository, in the
             format of an EnvironmentFile as described by systemd.exec(5)
           '';
         };
 
-        s3CredentialsFile = mkOption {
-          type = with types; nullOr str;
-          default = null;
-          description = lib.mdDoc ''
-            file containing the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
-            for an S3-hosted repository, in the format of an EnvironmentFile
-            as described by systemd.exec(5)
-          '';
-        };
-
         rcloneOptions = mkOption {
           type = with types; nullOr (attrsOf (oneOf [ str bool ]));
           default = null;
@@ -113,12 +101,15 @@ in
         };
 
         paths = mkOption {
+          # This is nullable for legacy reasons only. We should consider making it a pure listOf
+          # after some time has passed since this comment was added.
           type = types.nullOr (types.listOf types.str);
-          default = null;
+          default = [ ];
           description = lib.mdDoc ''
-            Which paths to backup.  If null or an empty array, no
-            backup command will be run.  This can be used to create a
-            prune-only job.
+            Which paths to backup, in addition to ones specified via
+            `dynamicFilesFrom`.  If null or an empty array and
+            `dynamicFilesFrom` is also null, no backup command will be run.
+             This can be used to create a prune-only job.
           '';
           example = [
             "/var/lib/postgresql"
@@ -231,7 +222,7 @@ in
           description = lib.mdDoc ''
             A script that produces a list of files to back up.  The
             results of this command are given to the '--files-from'
-            option.
+            option. The result is merged with paths specified via `paths`.
           '';
           example = "find /home/matt/git -type d -name .git";
         };
@@ -297,7 +288,6 @@ in
   };
 
   config = {
-    warnings = mapAttrsToList (n: v: "services.restic.backups.${n}.s3CredentialsFile is deprecated, please use services.restic.backups.${n}.environmentFile instead.") (filterAttrs (n: v: v.s3CredentialsFile != null) config.services.restic.backups);
     assertions = mapAttrsToList (n: v: {
       assertion = (v.repository == null) != (v.repositoryFile == null);
       message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set";
@@ -310,10 +300,7 @@ in
             resticCmd = "${backup.package}/bin/restic${extraOptions}";
             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)
-              then optionalString (backup.paths != null) (concatStringsSep " " backup.paths)
-              else "--files-from ${filesFromTmpFile}";
+            doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != []);
             pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [
               (resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts))
               (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
@@ -348,7 +335,7 @@ in
             after = [ "network-online.target" ];
             serviceConfig = {
               Type = "oneshot";
-              ExecStart = (optionals (backupPaths != "") [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} ${backupPaths}" ])
+              ExecStart = (optionals doBackup [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ])
                 ++ pruneCmd;
               User = backup.user;
               RuntimeDirectory = "restic-backups-${name}";
@@ -366,8 +353,11 @@ in
               ${optionalString (backup.initialize) ''
                 ${resticCmd} snapshots || ${resticCmd} init
               ''}
+              ${optionalString (backup.paths != null && backup.paths != []) ''
+                cat ${pkgs.writeText "staticPaths" (concatStringsSep "\n" backup.paths)} >> ${filesFromTmpFile}
+              ''}
               ${optionalString (backup.dynamicFilesFrom != null) ''
-                ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} > ${filesFromTmpFile}
+                ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} >> ${filesFromTmpFile}
               ''}
             '';
           } // optionalAttrs (backup.dynamicFilesFrom != null || backup.backupCleanupCommand != null) {
diff --git a/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix b/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix
index 8ebe0fcaff54..945a373d1274 100644
--- a/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix
+++ b/nixpkgs/nixos/modules/services/blockchain/ethereum/erigon.nix
@@ -13,6 +13,8 @@ in {
     services.erigon = {
       enable = mkEnableOption (lib.mdDoc "Ethereum implementation on the efficiency frontier");
 
+      package = mkPackageOptionMD pkgs "erigon" { };
+
       extraArgs = mkOption {
         type = types.listOf types.str;
         description = lib.mdDoc "Additional arguments passed to Erigon";
@@ -92,7 +94,7 @@ in {
 
       serviceConfig = {
         LoadCredential = "ERIGON_JWT:${cfg.secretJwtPath}";
-        ExecStart = "${pkgs.erigon}/bin/erigon --config ${configFile} --authrpc.jwtsecret=%d/ERIGON_JWT ${lib.escapeShellArgs cfg.extraArgs}";
+        ExecStart = "${cfg.package}/bin/erigon --config ${configFile} --authrpc.jwtsecret=%d/ERIGON_JWT ${lib.escapeShellArgs cfg.extraArgs}";
         DynamicUser = true;
         Restart = "on-failure";
         StateDirectory = "erigon";
diff --git a/nixpkgs/nixos/modules/services/logging/syslog-ng.nix b/nixpkgs/nixos/modules/services/logging/syslog-ng.nix
index d22acbeaa70c..48d556b9459e 100644
--- a/nixpkgs/nixos/modules/services/logging/syslog-ng.nix
+++ b/nixpkgs/nixos/modules/services/logging/syslog-ng.nix
@@ -67,7 +67,7 @@ in {
       configHeader = mkOption {
         type = types.lines;
         default = ''
-          @version: 3.6
+          @version: 4.4
           @include "scl.conf"
         '';
         description = lib.mdDoc ''
diff --git a/nixpkgs/nixos/modules/services/mail/mlmmj.nix b/nixpkgs/nixos/modules/services/mail/mlmmj.nix
index 642f8b20fe35..3f07fabcf177 100644
--- a/nixpkgs/nixos/modules/services/mail/mlmmj.nix
+++ b/nixpkgs/nixos/modules/services/mail/mlmmj.nix
@@ -143,13 +143,11 @@ in
 
     environment.systemPackages = [ pkgs.mlmmj ];
 
-    system.activationScripts.mlmmj = ''
-          ${pkgs.coreutils}/bin/mkdir -p ${stateDir} ${spoolDir}/${cfg.listDomain}
-          ${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} ${spoolDir}
-          ${concatMapLines (createList cfg.listDomain) cfg.mailLists}
-          ${pkgs.postfix}/bin/postmap /etc/postfix/virtual
-          ${pkgs.postfix}/bin/postmap /etc/postfix/transport
-      '';
+    systemd.tmpfiles.rules = [
+      ''d "${stateDir}" -''
+      ''d "${spoolDir}/${cfg.listDomain}" -''
+      ''Z "${spoolDir}" - "${cfg.user}" "${cfg.group}" -''
+    ];
 
     systemd.services.mlmmj-maintd = {
       description = "mlmmj maintenance daemon";
@@ -158,6 +156,11 @@ in
         Group = cfg.group;
         ExecStart = "${pkgs.mlmmj}/bin/mlmmj-maintd -F -d ${spoolDir}/${cfg.listDomain}";
       };
+      preStart = ''
+        ${concatMapLines (createList cfg.listDomain) cfg.mailLists}
+        ${pkgs.postfix}/bin/postmap /etc/postfix/virtual
+        ${pkgs.postfix}/bin/postmap /etc/postfix/transport
+      '';
     };
 
     systemd.timers.mlmmj-maintd = {
diff --git a/nixpkgs/nixos/modules/services/monitoring/certspotter.md b/nixpkgs/nixos/modules/services/monitoring/certspotter.md
new file mode 100644
index 000000000000..9bf6e1d946a0
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/certspotter.md
@@ -0,0 +1,74 @@
+# Cert Spotter {#module-services-certspotter}
+
+Cert Spotter is a tool for monitoring [Certificate Transparency](https://en.wikipedia.org/wiki/Certificate_Transparency)
+logs.
+
+## Service Configuration {#modules-services-certspotter-service-configuration}
+
+A basic config that notifies you of all certificate changes for your
+domain would look as follows:
+
+```nix
+services.certspotter = {
+  enable = true;
+  # replace example.org with your domain name
+  watchlist = [ ".example.org" ];
+  emailRecipients = [ "webmaster@example.org" ];
+};
+
+# Configure an SMTP client
+programs.msmtp.enable = true;
+# Or you can use any other module that provides sendmail, like
+# services.nullmailer, services.opensmtpd, services.postfix
+```
+
+In this case, the leading dot in `".example.org"` means that Cert
+Spotter should monitor not only `example.org`, but also all of its
+subdomains.
+
+## Operation {#modules-services-certspotter-operation}
+
+**By default, NixOS configures Cert Spotter to skip all certificates
+issued before its first launch**, because checking the entire
+Certificate Transparency logs requires downloading tens of terabytes of
+data. If you want to check the *entire* logs for previously issued
+certificates, you have to set `services.certspotter.startAtEnd` to
+`false` and remove all previously saved log state in
+`/var/lib/certspotter/logs`. The downloaded logs aren't saved, so if you
+add a new domain to the watchlist and want Cert Spotter to go through
+the logs again, you will have to remove `/var/lib/certspotter/logs`
+again.
+
+After catching up with the logs, Cert Spotter will start monitoring live
+logs. As of October 2023, it uses around **20 Mbps** of traffic on
+average.
+
+## Hooks {#modules-services-certspotter-hooks}
+
+Cert Spotter supports running custom hooks instead of (or in addition
+to) sending emails. Hooks are shell scripts that will be passed certain
+environment variables.
+
+To see hook documentation, see Cert Spotter's man pages:
+
+```ShellSession
+nix-shell -p certspotter --run 'man 8 certspotter-script'
+```
+
+For example, you can remove `emailRecipients` and send email
+notifications manually using the following hook:
+
+```nix
+services.certspotter.hooks = [
+  (pkgs.writeShellScript "certspotter-hook" ''
+    function print_email() {
+      echo "Subject: [certspotter] $SUMMARY"
+      echo "Mime-Version: 1.0"
+      echo "Content-Type: text/plain; charset=US-ASCII"
+      echo
+      cat "$TEXT_FILENAME"
+    }
+    print_email | ${config.services.certspotter.sendmailPath} -i webmaster@example.org
+  '')
+];
+```
diff --git a/nixpkgs/nixos/modules/services/monitoring/certspotter.nix b/nixpkgs/nixos/modules/services/monitoring/certspotter.nix
new file mode 100644
index 000000000000..aafa29daa872
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/certspotter.nix
@@ -0,0 +1,143 @@
+{ config
+, lib
+, pkgs
+, ... }:
+
+let
+  cfg = config.services.certspotter;
+
+  configDir = pkgs.linkFarm "certspotter-config" (
+    lib.toList {
+      name = "watchlist";
+      path = pkgs.writeText "certspotter-watchlist" (builtins.concatStringsSep "\n" cfg.watchlist);
+    }
+    ++ lib.optional (cfg.emailRecipients != [ ]) {
+      name = "email_recipients";
+      path = pkgs.writeText "certspotter-email_recipients" (builtins.concatStringsSep "\n" cfg.emailRecipients);
+    }
+    # always generate hooks dir when no emails are provided to allow running cert spotter with no hooks/emails
+    ++ lib.optional (cfg.emailRecipients == [ ] || cfg.hooks != [ ]) {
+      name = "hooks.d";
+      path = pkgs.linkFarm "certspotter-hooks" (lib.imap1 (i: path: {
+        inherit path;
+        name = "hook${toString i}";
+      }) cfg.hooks);
+    });
+in
+{
+  options.services.certspotter = {
+    enable = lib.mkEnableOption "Cert Spotter, a Certificate Transparency log monitor";
+
+    package = lib.mkPackageOptionMD pkgs "certspotter" { };
+
+    startAtEnd = lib.mkOption {
+      type = lib.types.bool;
+      description = ''
+        Whether to skip certificates issued before the first launch of Cert Spotter.
+        Setting this to `false` will cause Cert Spotter to download tens of terabytes of data.
+      '';
+      default = true;
+    };
+
+    sendmailPath = lib.mkOption {
+      type = with lib.types; nullOr path;
+      description = ''
+        Path to the `sendmail` binary. By default, the local sendmail wrapper is used
+        (see {option}`services.mail.sendmailSetuidWrapper`}).
+      '';
+      example = lib.literalExpression ''"''${pkgs.system-sendmail}/bin/sendmail"'';
+    };
+
+    watchlist = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "Domain names to watch. To monitor a domain with all subdomains, prefix its name with `.` (e.g. `.example.org`).";
+      default = [ ];
+      example = [ ".example.org" "another.example.com" ];
+    };
+
+    emailRecipients = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "A list of email addresses to send certificate updates to.";
+      default = [ ];
+    };
+
+    hooks = lib.mkOption {
+      type = with lib.types; listOf path;
+      description = ''
+        Scripts to run upon the detection of a new certificate. See `man 8 certspotter-script` or
+        [the GitHub page](https://github.com/SSLMate/certspotter/blob/${pkgs.certspotter.src.rev or "master"}/man/certspotter-script.md)
+        for more info.
+      '';
+      default = [ ];
+      example = lib.literalExpression ''
+        [
+          (pkgs.writeShellScript "certspotter-hook" '''
+            echo "Event summary: $SUMMARY."
+          ''')
+        ]
+      '';
+    };
+
+    extraFlags = lib.mkOption {
+      type = with lib.types; listOf str;
+      description = "Extra command-line arguments to pass to Cert Spotter";
+      example = [ "-no_save" ];
+      default = [ ];
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    assertions = [
+      {
+        assertion = (cfg.emailRecipients != [ ]) -> (cfg.sendmailPath != null);
+        message = ''
+          You must configure the sendmail setuid wrapper (services.mail.sendmailSetuidWrapper)
+          or services.certspotter.sendmailPath
+        '';
+      }
+    ];
+
+    services.certspotter.sendmailPath = let
+      inherit (config.security) wrapperDir;
+      inherit (config.services.mail) sendmailSetuidWrapper;
+    in lib.mkMerge [
+      (lib.mkIf (sendmailSetuidWrapper != null) (lib.mkOptionDefault "${wrapperDir}/${sendmailSetuidWrapper.program}"))
+      (lib.mkIf (sendmailSetuidWrapper == null) (lib.mkOptionDefault null))
+    ];
+
+    users.users.certspotter = {
+      description = "Cert Spotter user";
+      group = "certspotter";
+      home = "/var/lib/certspotter";
+      isSystemUser = true;
+    };
+    users.groups.certspotter = { };
+
+    systemd.services.certspotter = {
+      description = "Cert Spotter - Certificate Transparency Monitor";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      environment.CERTSPOTTER_CONFIG_DIR = configDir;
+      environment.SENDMAIL_PATH = if cfg.sendmailPath != null then cfg.sendmailPath else "/run/current-system/sw/bin/false";
+      script = ''
+        export CERTSPOTTER_STATE_DIR="$STATE_DIRECTORY"
+        cd "$CERTSPOTTER_STATE_DIR"
+        ${lib.optionalString cfg.startAtEnd ''
+          if [[ ! -d logs ]]; then
+            # Don't download certificates issued before the first launch
+            exec ${cfg.package}/bin/certspotter -start_at_end ${lib.escapeShellArgs cfg.extraFlags}
+          fi
+        ''}
+        exec ${cfg.package}/bin/certspotter ${lib.escapeShellArgs cfg.extraFlags}
+      '';
+      serviceConfig = {
+        User = "certspotter";
+        Group = "certspotter";
+        StateDirectory = "certspotter";
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ chayleaf ];
+  meta.doc = ./certspotter.md;
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/goss.md b/nixpkgs/nixos/modules/services/monitoring/goss.md
new file mode 100644
index 000000000000..1e636aa3bdf3
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/goss.md
@@ -0,0 +1,44 @@
+# Goss {#module-services-goss}
+
+[goss](https://goss.rocks/) is a YAML based serverspec alternative tool
+for validating a server's configuration.
+
+## Basic Usage {#module-services-goss-basic-usage}
+
+A minimal configuration looks like this:
+
+```
+{
+  services.goss = {
+    enable = true;
+
+    environment = {
+      GOSS_FMT = "json";
+      GOSS_LOGLEVEL = "TRACE";
+    };
+
+    settings = {
+      addr."tcp://localhost:8080" = {
+        reachable = true;
+        local-address = "127.0.0.1";
+      };
+      command."check-goss-version" = {
+        exec = "${lib.getExe pkgs.goss} --version";
+        exit-status = 0;
+      };
+      dns.localhost.resolvable = true;
+      file."/nix" = {
+        filetype = "directory";
+        exists = true;
+      };
+      group.root.exists = true;
+      kernel-param."kernel.ostype".value = "Linux";
+      service.goss = {
+        enabled = true;
+        running = true;
+      };
+      user.root.exists = true;
+    };
+  };
+}
+```
diff --git a/nixpkgs/nixos/modules/services/monitoring/goss.nix b/nixpkgs/nixos/modules/services/monitoring/goss.nix
new file mode 100644
index 000000000000..64a8dad0703e
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/monitoring/goss.nix
@@ -0,0 +1,86 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.services.goss;
+
+  settingsFormat = pkgs.formats.yaml { };
+  configFile = settingsFormat.generate "goss.yaml" cfg.settings;
+
+in {
+  meta = {
+    doc = ./goss.md;
+    maintainers = [ lib.maintainers.anthonyroussel ];
+  };
+
+  options = {
+    services.goss = {
+      enable = lib.mkEnableOption (lib.mdDoc "Goss daemon");
+
+      package = lib.mkPackageOptionMD pkgs "goss" { };
+
+      environment = lib.mkOption {
+        type = lib.types.attrsOf lib.types.str;
+        default = { };
+        example = {
+          GOSS_FMT = "json";
+          GOSS_LOGLEVEL = "FATAL";
+          GOSS_LISTEN = ":8080";
+        };
+        description = lib.mdDoc ''
+          Environment variables to set for the goss service.
+
+          See <https://github.com/goss-org/goss/blob/master/docs/manual.md>
+        '';
+      };
+
+      settings = lib.mkOption {
+        type = lib.types.submodule { freeformType = settingsFormat.type; };
+        default = { };
+        example = {
+          addr."tcp://localhost:8080" = {
+            reachable = true;
+            local-address = "127.0.0.1";
+          };
+          service.goss = {
+            enabled = true;
+            running = true;
+          };
+        };
+        description = lib.mdDoc ''
+          The global options in `config` file in yaml format.
+
+          Refer to <https://github.com/goss-org/goss/blob/master/docs/goss-json-schema.yaml> for schema.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.goss = {
+      description = "Goss - Quick and Easy server validation";
+      unitConfig.Documentation = "https://github.com/goss-org/goss/blob/master/docs/manual.md";
+
+      after = [ "network-online.target" ];
+      wantedBy = [ "multi-user.target" ];
+      wants = [ "network-online.target" ];
+
+      environment = {
+        GOSS_FILE = configFile;
+      } // cfg.environment;
+
+      reloadTriggers = [ configFile ];
+
+      serviceConfig = {
+        DynamicUser = true;
+        ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+        ExecStart = "${cfg.package}/bin/goss serve";
+        Group = "goss";
+        Restart = "on-failure";
+        RestartSec = 5;
+        User = "goss";
+      };
+    };
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/monitoring/ups.nix b/nixpkgs/nixos/modules/services/monitoring/ups.nix
index bb11b6a1c1d0..efef2d777acd 100644
--- a/nixpkgs/nixos/modules/services/monitoring/ups.nix
+++ b/nixpkgs/nixos/modules/services/monitoring/ups.nix
@@ -239,11 +239,9 @@ in
 
     power.ups.schedulerRules = mkDefault "${pkgs.nut}/etc/upssched.conf.sample";
 
-    system.activationScripts.upsSetup = stringAfter [ "users" "groups" ]
-      ''
-        # Used to store pid files of drivers.
-        mkdir -p /var/state/ups
-      '';
+    systemd.tmpfiles.rules = [
+      "d /var/state/ups -"
+    ];
 
 
 /*
diff --git a/nixpkgs/nixos/modules/services/networking/gvpe.nix b/nixpkgs/nixos/modules/services/networking/gvpe.nix
index 2279ceee2f58..558f499022c8 100644
--- a/nixpkgs/nixos/modules/services/networking/gvpe.nix
+++ b/nixpkgs/nixos/modules/services/networking/gvpe.nix
@@ -29,7 +29,7 @@ let
 
       export PATH=$PATH:${pkgs.iproute2}/sbin
 
-      ip link set $IFNAME up
+      ip link set dev $IFNAME up
       ip address add ${cfg.ipAddress} dev $IFNAME
       ip route add ${cfg.subnet} dev $IFNAME
 
diff --git a/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix b/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
index 9c71a988f29c..6c30f89b7968 100644
--- a/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
+++ b/nixpkgs/nixos/modules/services/networking/iscsi/initiator.nix
@@ -52,25 +52,27 @@ in
     '';
     environment.etc."iscsi/initiatorname.iscsi".text = "InitiatorName=${cfg.name}";
 
-    system.activationScripts.iscsid = let
-      extraCfgDumper = optionalString (cfg.extraConfigFile != null) ''
-        if [ -f "${cfg.extraConfigFile}" ]; then
-          printf "\n# The following is from ${cfg.extraConfigFile}:\n"
-          cat "${cfg.extraConfigFile}"
-        else
-          echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2
-        fi
-      '';
-    in ''
-      (
-        cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source}
-        ${extraCfgDumper}
-      ) > /etc/iscsi/iscsid.conf
-    '';
-
     systemd.packages = [ cfg.package ];
 
-    systemd.services."iscsid".wantedBy = [ "multi-user.target" ];
+    systemd.services."iscsid" = {
+      wantedBy = [ "multi-user.target" ];
+      preStart =
+        let
+          extraCfgDumper = optionalString (cfg.extraConfigFile != null) ''
+            if [ -f "${cfg.extraConfigFile}" ]; then
+              printf "\n# The following is from ${cfg.extraConfigFile}:\n"
+              cat "${cfg.extraConfigFile}"
+            else
+              echo "Warning: services.openiscsi.extraConfigFile ${cfg.extraConfigFile} does not exist!" >&2
+            fi
+          '';
+        in ''
+          (
+            cat ${config.environment.etc."iscsi/iscsid.conf.fragment".source}
+            ${extraCfgDumper}
+          ) > /etc/iscsi/iscsid.conf
+        '';
+    };
     systemd.sockets."iscsid".wantedBy = [ "sockets.target" ];
 
     systemd.services."iscsi" = mkIf cfg.enableAutoLoginOut {
diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
index 82e68bf92af1..99ffbf56ccb0 100644
--- a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
+++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix
@@ -76,5 +76,5 @@ with lib;
     };
   };
 
-  meta.maintainers = with maintainers; [ patricksjackson ymarkus ];
+  meta.maintainers = with maintainers; [ arcuru ymarkus ];
 }
diff --git a/nixpkgs/nixos/modules/services/networking/spiped.nix b/nixpkgs/nixos/modules/services/networking/spiped.nix
index 3e01ace54ad1..547317dbcbe2 100644
--- a/nixpkgs/nixos/modules/services/networking/spiped.nix
+++ b/nixpkgs/nixos/modules/services/networking/spiped.nix
@@ -197,8 +197,9 @@ in
       script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`";
     };
 
-    system.activationScripts.spiped = optionalString (cfg.config != {})
-      "mkdir -p /var/lib/spiped";
+    systemd.tmpfiles.rules = lib.mkIf (cfg.config != { }) [
+      "d /var/lib/spiped -"
+    ];
 
     # Setup spiped config files
     environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec"
diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
index 1946e9e79e98..14bc59089adf 100644
--- a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix
@@ -12,22 +12,44 @@ let
     then cfgc.package
     else pkgs.buildPackages.openssh;
 
-  # reports boolean as yes / no
-  mkValueStringSshd = with lib; v:
-        if isInt           v then toString v
-        else if isString   v then v
-        else if true  ==   v then "yes"
-        else if false ==   v then "no"
-        else if isList     v then concatStringsSep "," v
-        else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}";
-
   # dont use the "=" operator
-  settingsFormat = (pkgs.formats.keyValue {
-      mkKeyValue = lib.generators.mkKeyValueDefault {
-      mkValueString = mkValueStringSshd;
-    } " ";});
+  settingsFormat =
+    let
+      # reports boolean as yes / no
+      mkValueString = with lib; v:
+            if isInt           v then toString v
+            else if isString   v then v
+            else if true  ==   v then "yes"
+            else if false ==   v then "no"
+            else throw "unsupported type ${builtins.typeOf v}: ${(lib.generators.toPretty {}) v}";
+
+      base = pkgs.formats.keyValue {
+        mkKeyValue = lib.generators.mkKeyValueDefault { inherit mkValueString; } " ";
+      };
+      # OpenSSH is very inconsistent with options that can take multiple values.
+      # For some of them, they can simply appear multiple times and are appended, for others the
+      # values must be separated by whitespace or even commas.
+      # Consult either sshd_config(5) or, as last resort, the OpehSSH source for parsing
+      # the options at servconf.c:process_server_config_line_depth() to determine the right "mode"
+      # for each. But fortunaly this fact is documented for most of them in the manpage.
+      commaSeparated = [ "Ciphers" "KexAlgorithms" "Macs" ];
+      spaceSeparated = [ "AuthorizedKeysFile" "AllowGroups" "AllowUsers" "DenyGroups" "DenyUsers" ];
+    in {
+      inherit (base) type;
+      generate = name: value:
+        let transformedValue = mapAttrs (key: val:
+          if isList val then
+            if elem key commaSeparated then concatStringsSep "," val
+            else if elem key spaceSeparated then concatStringsSep " " val
+            else throw "list value for unknown key ${key}: ${(lib.generators.toPretty {}) val}"
+          else
+            val
+          ) value;
+        in
+          base.generate name transformedValue;
+    };
 
-  configFile = settingsFormat.generate "sshd.conf-settings" cfg.settings;
+  configFile = settingsFormat.generate "sshd.conf-settings" (filterAttrs (n: v: v != null) cfg.settings);
   sshconf = pkgs.runCommand "sshd.conf-final" { } ''
     cat ${configFile} - >$out <<EOL
     ${cfg.extraConfig}
@@ -431,6 +453,42 @@ in
                 <https://infosec.mozilla.org/guidelines/openssh#modern-openssh-67>
               '';
             };
+            AllowUsers = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is allowed only for the listed users.
+                See {manpage}`sshd_config(5)` for details.
+              '';
+            };
+            DenyUsers = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is denied for all listed users. Takes
+                precedence over [](#opt-services.openssh.settings.AllowUsers).
+                See {manpage}`sshd_config(5)` for details.
+              '';
+            };
+            AllowGroups = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is allowed only for users part of the
+                listed groups.
+                See {manpage}`sshd_config(5)` for details.
+              '';
+            };
+            DenyGroups = mkOption {
+              type = with types; nullOr (listOf str);
+              default = null;
+              description = lib.mdDoc ''
+                If specified, login is denied for all users part of the listed
+                groups. Takes precedence over
+                [](#opt-services.openssh.settings.AllowGroups). See
+                {manpage}`sshd_config(5)` for details.
+              '';
+            };
           };
         });
       };
diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
index c51e8ad9f5fc..bfea89969728 100644
--- a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
+++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix
@@ -43,21 +43,21 @@ in  {
 
     # The swanctl command complains when the following directories don't exist:
     # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory
-    system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] ''
-      mkdir -p '/etc/swanctl/x509'     # Trusted X.509 end entity certificates
-      mkdir -p '/etc/swanctl/x509ca'   # Trusted X.509 Certificate Authority certificates
-      mkdir -p '/etc/swanctl/x509ocsp'
-      mkdir -p '/etc/swanctl/x509aa'   # Trusted X.509 Attribute Authority certificates
-      mkdir -p '/etc/swanctl/x509ac'   # Attribute Certificates
-      mkdir -p '/etc/swanctl/x509crl'  # Certificate Revocation Lists
-      mkdir -p '/etc/swanctl/pubkey'   # Raw public keys
-      mkdir -p '/etc/swanctl/private'  # Private keys in any format
-      mkdir -p '/etc/swanctl/rsa'      # PKCS#1 encoded RSA private keys
-      mkdir -p '/etc/swanctl/ecdsa'    # Plain ECDSA private keys
-      mkdir -p '/etc/swanctl/bliss'
-      mkdir -p '/etc/swanctl/pkcs8'    # PKCS#8 encoded private keys of any type
-      mkdir -p '/etc/swanctl/pkcs12'   # PKCS#12 containers
-    '';
+    systemd.tmpfiles.rules = [
+      "d /etc/swanctl/x509 -"     # Trusted X.509 end entity certificates
+      "d /etc/swanctl/x509ca -"   # Trusted X.509 Certificate Authority certificates
+      "d /etc/swanctl/x509ocsp -"
+      "d /etc/swanctl/x509aa -"   # Trusted X.509 Attribute Authority certificates
+      "d /etc/swanctl/x509ac -"   # Attribute Certificates
+      "d /etc/swanctl/x509crl -"  # Certificate Revocation Lists
+      "d /etc/swanctl/pubkey -"   # Raw public keys
+      "d /etc/swanctl/private -"  # Private keys in any format
+      "d /etc/swanctl/rsa -"      # PKCS#1 encoded RSA private keys
+      "d /etc/swanctl/ecdsa -"    # Plain ECDSA private keys
+      "d /etc/swanctl/bliss -"
+      "d /etc/swanctl/pkcs8 -"    # PKCS#8 encoded private keys of any type
+      "d /etc/swanctl/pkcs12 -"   # PKCS#12 containers
+    ];
 
     systemd.services.strongswan-swanctl = {
       description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl";
diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix
index 8b35cc8d6669..a5d171e0baab 100644
--- a/nixpkgs/nixos/modules/services/networking/tailscale.nix
+++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix
@@ -31,6 +31,12 @@ in {
 
     package = lib.mkPackageOptionMD pkgs "tailscale" {};
 
+    openFirewall = mkOption {
+      default = false;
+      type = types.bool;
+      description = lib.mdDoc "Whether to open the firewall for the specified port.";
+    };
+
     useRoutingFeatures = mkOption {
       type = types.enum [ "none" "client" "server" "both" ];
       default = "none";
@@ -113,6 +119,8 @@ in {
       "net.ipv6.conf.all.forwarding" = mkOverride 97 true;
     };
 
+    networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.port ];
+
     networking.firewall.checkReversePath = mkIf (cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both") "loose";
 
     networking.dhcpcd.denyInterfaces = [ cfg.interfaceName ];
diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix
index 37a739f41d48..6b6837109806 100644
--- a/nixpkgs/nixos/modules/services/networking/unifi.nix
+++ b/nixpkgs/nixos/modules/services/networking/unifi.nix
@@ -6,9 +6,9 @@ let
   cmd = ''
     @${cfg.jrePackage}/bin/java java \
         ${optionalString (lib.versionAtLeast (lib.getVersion cfg.jrePackage) "16")
-        "--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED "
+        ("--add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.time=ALL-UNNAMED "
         + "--add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED "
-        + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED"} \
+        + "--add-opens java.rmi/sun.rmi.transport=ALL-UNNAMED")} \
         ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \
         ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \
         -jar ${stateDir}/lib/ace.jar
diff --git a/nixpkgs/nixos/modules/services/system/nix-daemon.nix b/nixpkgs/nixos/modules/services/system/nix-daemon.nix
index c9df20196dbd..ce255cd8d0a4 100644
--- a/nixpkgs/nixos/modules/services/system/nix-daemon.nix
+++ b/nixpkgs/nixos/modules/services/system/nix-daemon.nix
@@ -249,11 +249,6 @@ in
 
     services.xserver.displayManager.hiddenUsers = attrNames nixbldUsers;
 
-    system.activationScripts.nix = stringAfter [ "etc" "users" ]
-      ''
-        install -m 0755 -d /nix/var/nix/{gcroots,profiles}/per-user
-      '';
-
     # Legacy configuration conversion.
     nix.settings = mkMerge [
       (mkIf (isNixAtLeast "2.3pre") { sandbox-fallback = false; })
diff --git a/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md
new file mode 100644
index 000000000000..236953bd4ff7
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.md
@@ -0,0 +1,42 @@
+# c2FmZQ {#module-services-c2fmzq}
+
+c2FmZQ is an application that can securely encrypt, store, and share files,
+including but not limited to pictures and videos.
+
+The service `c2fmzq-server` can be enabled by setting
+```
+{
+  services.c2fmzq-server.enable = true;
+}
+```
+This will spin up an instance of the server which is API-compatible with
+[Stingle Photos](https://stingle.org) and an experimental Progressive Web App
+(PWA) to interact with the storage via the browser.
+
+In principle the server can be exposed directly on a public interface and there
+are command line options to manage HTTPS certificates directly, but the module
+is designed to be served behind a reverse proxy or only accessed via localhost.
+
+```
+{
+  services.c2fmzq-server = {
+    enable = true;
+    bindIP = "127.0.0.1"; # default
+    port = 8080; # default
+  };
+
+  services.nginx = {
+    enable = true;
+    recommendedProxySettings = true;
+    virtualHosts."example.com" = {
+      enableACME = true;
+      forceSSL = true;
+      locations."/" = {
+        proxyPass = "http://127.0.0.1:8080";
+      };
+    };
+  };
+}
+```
+
+For more information, see <https://github.com/c2FmZQ/c2FmZQ/>.
diff --git a/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix
new file mode 100644
index 000000000000..2749c2a5a87a
--- /dev/null
+++ b/nixpkgs/nixos/modules/services/web-apps/c2fmzq-server.nix
@@ -0,0 +1,125 @@
+{ lib, pkgs, config, ... }:
+
+let
+  inherit (lib) mkEnableOption mkPackageOption mkOption types;
+
+  cfg = config.services.c2fmzq-server;
+
+  argsFormat = {
+    type = with lib.types; nullOr (oneOf [ bool int str ]);
+    generate = lib.cli.toGNUCommandLineShell { };
+  };
+in {
+  options.services.c2fmzq-server = {
+    enable = mkEnableOption "c2fmzq-server";
+
+    bindIP = mkOption {
+      type = types.str;
+      default = "127.0.0.1";
+      description = "The local address to use.";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8080;
+      description = "The local port to use.";
+    };
+
+    passphraseFile = mkOption {
+      type = types.str;
+      example = "/run/secrets/c2fmzq/pwfile";
+      description = "Path to file containing the database passphrase";
+    };
+
+    package = mkPackageOption pkgs "c2fmzq" { };
+
+    settings = mkOption {
+      type = types.submodule {
+        freeformType = argsFormat.type;
+
+        options = {
+          address = mkOption {
+            internal = true;
+            type = types.str;
+            default = "${cfg.bindIP}:${toString cfg.port}";
+          };
+
+          database = mkOption {
+            type = types.str;
+            default = "%S/c2fmzq-server/data";
+            description = "Path of the database";
+          };
+
+          verbose = mkOption {
+            type = types.ints.between 1 3;
+            default = 2;
+            description = "The level of logging verbosity: 1:Error 2:Info 3:Debug";
+          };
+        };
+      };
+      description = ''
+        Configuration for c2FmZQ-server passed as CLI arguments.
+        Run {command}`c2FmZQ-server help` for supported values.
+      '';
+      example = {
+        verbose = 3;
+        allow-new-accounts = true;
+        auto-approve-new-accounts = true;
+        encrypt-metadata = true;
+        enable-webapp = true;
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    systemd.services.c2fmzq-server = {
+      description = "c2FmZQ-server";
+      documentation = [ "https://github.com/c2FmZQ/c2FmZQ/blob/main/README.md" ];
+      wantedBy = [ "multi-user.target" ];
+      after = [ "network.target" "network-online.target" ];
+
+      serviceConfig = {
+        ExecStart = "${lib.getExe cfg.package} ${argsFormat.generate cfg.settings}";
+        AmbientCapabilities = "";
+        CapabilityBoundingSet = "";
+        DynamicUser = true;
+        Environment = "C2FMZQ_PASSPHRASE_FILE=%d/passphrase-file";
+        IPAccounting = true;
+        IPAddressAllow = cfg.bindIP;
+        IPAddressDeny = "any";
+        LoadCredential = "passphrase-file:${cfg.passphraseFile}";
+        LockPersonality = true;
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+        PrivateDevices = true;
+        PrivateIPC = true;
+        PrivateTmp = true;
+        PrivateUsers = true;
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        ProtectSystem = "strict";
+        RemoveIPC = true;
+        RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        RestrictSUIDSGID = true;
+        SocketBindAllow = cfg.port;
+        SocketBindDeny = "any";
+        StateDirectory = "c2fmzq-server";
+        SystemCallArchitectures = "native";
+        SystemCallFilter = [ "@system-service" "~@privileged @obsolete" ];
+      };
+    };
+  };
+
+  meta = {
+    doc = ./c2fmzq-server.md;
+    maintainers = with lib.maintainers; [ hmenke ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
index 66e5f1695a15..24f3b3331845 100644
--- a/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/mattermost.nix
@@ -287,9 +287,9 @@ in
 
       # The systemd service will fail to execute the preStart hook
       # if the WorkingDirectory does not exist
-      system.activationScripts.mattermost = ''
-        mkdir -p "${cfg.statePath}"
-      '';
+      systemd.tmpfiles.rules = [
+        ''d "${cfg.statePath}" -''
+      ];
 
       systemd.services.mattermost = {
         description = "Mattermost chat service";
diff --git a/nixpkgs/nixos/modules/services/web-apps/plausible.nix b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
index e5deb6cf511f..576b54a7edf2 100644
--- a/nixpkgs/nixos/modules/services/web-apps/plausible.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/plausible.nix
@@ -78,9 +78,9 @@ in {
     server = {
       disableRegistration = mkOption {
         default = true;
-        type = types.bool;
+        type = types.enum [true false "invite_only"];
         description = lib.mdDoc ''
-          Whether to prohibit creating an account in plausible's UI.
+          Whether to prohibit creating an account in plausible's UI or allow on `invite_only`.
         '';
       };
       secretKeybaseFile = mkOption {
@@ -209,7 +209,7 @@ in {
             # Configuration options from
             # https://plausible.io/docs/self-hosting-configuration
             PORT = toString cfg.server.port;
-            DISABLE_REGISTRATION = boolToString cfg.server.disableRegistration;
+            DISABLE_REGISTRATION = if isBool cfg.server.disableRegistration then boolToString cfg.server.disableRegistration else cfg.server.disableRegistration;
 
             RELEASE_TMP = "/var/lib/plausible/tmp";
             # Home is needed to connect to the node with iex
diff --git a/nixpkgs/nixos/modules/services/web-apps/shiori.nix b/nixpkgs/nixos/modules/services/web-apps/shiori.nix
index f0505e052e1c..71b5ad4d4c06 100644
--- a/nixpkgs/nixos/modules/services/web-apps/shiori.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/shiori.nix
@@ -29,6 +29,13 @@ in {
         default = 8080;
         description = lib.mdDoc "The port of the Shiori web application";
       };
+
+      webRoot = mkOption {
+        type = types.str;
+        default = "/";
+        example = "/shiori";
+        description = lib.mdDoc "The root of the Shiori web application";
+      };
     };
   };
 
@@ -40,7 +47,7 @@ in {
       environment.SHIORI_DIR = "/var/lib/shiori";
 
       serviceConfig = {
-        ExecStart = "${package}/bin/shiori serve --address '${address}' --port '${toString port}'";
+        ExecStart = "${package}/bin/shiori serve --address '${address}' --port '${toString port}' --webroot '${webRoot}'";
 
         DynamicUser = true;
         StateDirectory = "shiori";
diff --git a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
index 9cba5cb4fa9e..4fbf2bad750b 100644
--- a/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
+++ b/nixpkgs/nixos/modules/services/web-apps/snipe-it.nix
@@ -18,15 +18,19 @@ let
   inherit (snipe-it.passthru) phpPackage;
 
   # shell script for local administration
-  artisan = pkgs.writeScriptBin "snipe-it" ''
+  artisan = (pkgs.writeScriptBin "snipe-it" ''
     #! ${pkgs.runtimeShell}
-    cd ${snipe-it}
+    cd "${snipe-it}/share/php/snipe-it"
     sudo=exec
     if [[ "$USER" != ${user} ]]; then
       sudo='exec /run/wrappers/bin/sudo -u ${user}'
     fi
     $sudo ${phpPackage}/bin/php artisan $*
-  '';
+  '').overrideAttrs (old: {
+    meta = old.meta // {
+      mainProgram = "snipe-it";
+    };
+  });
 in {
   options.services.snipe-it = {
 
@@ -357,7 +361,7 @@ in {
     services.nginx = {
       enable = mkDefault true;
       virtualHosts."${cfg.hostName}" = mkMerge [ cfg.nginx {
-        root = mkForce "${snipe-it}/public";
+        root = mkForce "${snipe-it}/share/php/snipe-it/public";
         extraConfig = optionalString (cfg.nginx.addSSL || cfg.nginx.forceSSL || cfg.nginx.onlySSL || cfg.nginx.enableACME) "fastcgi_param HTTPS on;";
         locations = {
           "/" = {
@@ -394,7 +398,7 @@ in {
         RuntimeDirectory = "snipe-it/cache";
         RuntimeDirectoryMode = "0700";
       };
-      path = [ pkgs.replace-secret ];
+      path = [ pkgs.replace-secret artisan ];
       script =
         let
           isSecret  = v: isAttrs v && v ? _secret && (isString v._secret || builtins.isPath v._secret);
@@ -451,7 +455,7 @@ in {
           rm "${cfg.dataDir}"/bootstrap/cache/*.php || true
 
           # migrate db
-          ${phpPackage}/bin/php artisan migrate --force
+          ${lib.getExe artisan} migrate --force
 
           # A placeholder file for invalid barcodes
           invalid_barcode_location="${cfg.dataDir}/public/uploads/barcodes/invalid_barcode.gif"
diff --git a/nixpkgs/nixos/modules/services/web-servers/garage.nix b/nixpkgs/nixos/modules/services/web-servers/garage.nix
index 731d5315f23a..47b4c6ab416e 100644
--- a/nixpkgs/nixos/modules/services/web-servers/garage.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/garage.nix
@@ -86,7 +86,7 @@ in
       serviceConfig = {
         ExecStart = "${cfg.package}/bin/garage server";
 
-        StateDirectory = mkIf (hasPrefix "/var/lib/garage" cfg.settings.data_dir && hasPrefix "/var/lib/garage" cfg.settings.metadata_dir) "garage";
+        StateDirectory = mkIf (hasPrefix "/var/lib/garage" cfg.settings.data_dir || hasPrefix "/var/lib/garage" cfg.settings.metadata_dir) "garage";
         DynamicUser = lib.mkDefault true;
         ProtectHome = true;
         NoNewPrivileges = true;
diff --git a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
index 9eebd18855c7..f2e8585a9365 100644
--- a/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/nginx/default.nix
@@ -35,6 +35,7 @@ let
   compressMimeTypes = [
     "application/atom+xml"
     "application/geo+json"
+    "application/javascript" # Deprecated by IETF RFC 9239, but still widely used
     "application/json"
     "application/ld+json"
     "application/manifest+json"
diff --git a/nixpkgs/nixos/modules/services/web-servers/stargazer.nix b/nixpkgs/nixos/modules/services/web-servers/stargazer.nix
index f0c3cf8787eb..18f57363137c 100644
--- a/nixpkgs/nixos/modules/services/web-servers/stargazer.nix
+++ b/nixpkgs/nixos/modules/services/web-servers/stargazer.nix
@@ -204,11 +204,9 @@ in
     };
 
     # Create default cert store
-    system.activationScripts.makeStargazerCertDir =
-      lib.optionalAttrs (cfg.store == /var/lib/gemini/certs) ''
-        mkdir -p /var/lib/gemini/certs
-        chown -R ${cfg.user}:${cfg.group} /var/lib/gemini/certs
-      '';
+    systemd.tmpfiles.rules = lib.mkIf (cfg.store == /var/lib/gemini/certs) [
+      ''d /var/lib/gemini/certs - "${cfg.user}" "${cfg.group}" -''
+    ];
 
     users.users = lib.optionalAttrs (cfg.user == "stargazer") {
       stargazer = {
diff --git a/nixpkgs/nixos/modules/system/activation/activatable-system.nix b/nixpkgs/nixos/modules/system/activation/activatable-system.nix
index 7f6154794bd8..3d941596747b 100644
--- a/nixpkgs/nixos/modules/system/activation/activatable-system.nix
+++ b/nixpkgs/nixos/modules/system/activation/activatable-system.nix
@@ -1,52 +1,16 @@
-{ config, lib, pkgs, ... }:
+{ options, config, lib, pkgs, ... }:
 
 let
   inherit (lib)
     mkOption
-    optionalString
     types
     ;
 
-  perlWrapped = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
-
   systemBuilderArgs = {
     activationScript = config.system.activationScripts.script;
     dryActivationScript = config.system.dryActivationScript;
   };
 
-  systemBuilderCommands = ''
-    echo "$activationScript" > $out/activate
-    echo "$dryActivationScript" > $out/dry-activate
-    substituteInPlace $out/activate --subst-var-by out ''${!toplevelVar}
-    substituteInPlace $out/dry-activate --subst-var-by out ''${!toplevelVar}
-    chmod u+x $out/activate $out/dry-activate
-    unset activationScript dryActivationScript
-
-    mkdir $out/bin
-    substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
-      --subst-var out \
-      --subst-var-by toplevel ''${!toplevelVar} \
-      --subst-var-by coreutils "${pkgs.coreutils}" \
-      --subst-var-by distroId ${lib.escapeShellArg config.system.nixos.distroId} \
-      --subst-var-by installBootLoader ${lib.escapeShellArg config.system.build.installBootLoader} \
-      --subst-var-by localeArchive "${config.i18n.glibcLocales}/lib/locale/locale-archive" \
-      --subst-var-by perl "${perlWrapped}" \
-      --subst-var-by shell "${pkgs.bash}/bin/sh" \
-      --subst-var-by su "${pkgs.shadow.su}/bin/su" \
-      --subst-var-by systemd "${config.systemd.package}" \
-      --subst-var-by utillinux "${pkgs.util-linux}" \
-      ;
-
-    chmod +x $out/bin/switch-to-configuration
-    ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
-      if ! output=$(${perlWrapped}/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
-        echo "switch-to-configuration syntax is not valid:"
-        echo "$output"
-        exit 1
-      fi
-    ''}
-  '';
-
 in
 {
   options = {
@@ -60,6 +24,18 @@ in
         do, but for image based systems, this may not be needed or not be desirable.
       '';
     };
+    system.activatableSystemBuilderCommands = options.system.systemBuilderCommands // {
+      description = lib.mdDoc ''
+        Like `system.systemBuilderCommands`, but only for the commands that are
+        needed *both* when the system is activatable and when it isn't.
+
+        Disclaimer: This option might go away in the future. It might be
+        superseded by separating switch-to-configuration into a separate script
+        which will make this option superfluous. See
+        https://github.com/NixOS/nixpkgs/pull/263462#discussion_r1373104845 for
+        a discussion.
+      '';
+    };
     system.build.separateActivationScript = mkOption {
       type = types.package;
       description = ''
@@ -71,7 +47,18 @@ in
     };
   };
   config = {
-    system.systemBuilderCommands = lib.mkIf config.system.activatable systemBuilderCommands;
+    system.activatableSystemBuilderCommands = ''
+      echo "$activationScript" > $out/activate
+      echo "$dryActivationScript" > $out/dry-activate
+      substituteInPlace $out/activate --subst-var-by out ''${!toplevelVar}
+      substituteInPlace $out/dry-activate --subst-var-by out ''${!toplevelVar}
+      chmod u+x $out/activate $out/dry-activate
+      unset activationScript dryActivationScript
+    '';
+
+    system.systemBuilderCommands = lib.mkIf
+      config.system.activatable
+      config.system.activatableSystemBuilderCommands;
     system.systemBuilderArgs = lib.mkIf config.system.activatable
       (systemBuilderArgs // {
         toplevelVar = "out";
@@ -86,7 +73,7 @@ in
         })
         ''
           mkdir $out
-          ${systemBuilderCommands}
+          ${config.system.activatableSystemBuilderCommands}
         '';
   };
 }
diff --git a/nixpkgs/nixos/modules/system/activation/activation-script.nix b/nixpkgs/nixos/modules/system/activation/activation-script.nix
index c8407dd6779a..bc0b7266ce95 100644
--- a/nixpkgs/nixos/modules/system/activation/activation-script.nix
+++ b/nixpkgs/nixos/modules/system/activation/activation-script.nix
@@ -55,10 +55,6 @@ let
       # used as a garbage collection root.
       ln -sfn "$(readlink -f "$systemConfig")" /run/current-system
 
-      # Prevent the current configuration from being garbage-collected.
-      mkdir -p /nix/var/nix/gcroots
-      ln -sfn /run/current-system /nix/var/nix/gcroots/current-system
-
       exit $_status
     '';
 
@@ -233,23 +229,15 @@ in
   config = {
 
     system.activationScripts.stdio = ""; # obsolete
+    system.activationScripts.var = ""; # obsolete
 
-    system.activationScripts.var =
-      ''
-        # Various log/runtime directories.
-
-        mkdir -p /var/tmp
-        chmod 1777 /var/tmp
-
-        # Empty, immutable home directory of many system accounts.
-        mkdir -p /var/empty
-        # Make sure it's really empty
-        ${pkgs.e2fsprogs}/bin/chattr -f -i /var/empty || true
-        find /var/empty -mindepth 1 -delete
-        chmod 0555 /var/empty
-        chown root:root /var/empty
-        ${pkgs.e2fsprogs}/bin/chattr -f +i /var/empty || true
-      '';
+    systemd.tmpfiles.rules = [
+      # Prevent the current configuration from being garbage-collected.
+      "d /nix/var/nix/gcroots -"
+      "L+ /nix/var/nix/gcroots/current-system - - - - /run/current-system"
+      "D /var/empty 0555 root root -"
+      "h /var/empty - - - - +i"
+    ];
 
     system.activationScripts.usrbinenv = if config.environment.usrbinenv != null
       then ''
diff --git a/nixpkgs/nixos/modules/system/activation/switchable-system.nix b/nixpkgs/nixos/modules/system/activation/switchable-system.nix
new file mode 100644
index 000000000000..00bc18e48d1f
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/activation/switchable-system.nix
@@ -0,0 +1,55 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  perlWrapped = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
+
+in
+
+{
+
+  options = {
+    system.switch.enable = lib.mkOption {
+      type = lib.types.bool;
+      default = true;
+      description = lib.mdDoc ''
+        Whether to include the capability to switch configurations.
+
+        Disabling this makes the system unable to be reconfigured via `nixos-rebuild`.
+
+        This is good for image based appliances where updates are handled
+        outside the image. Reducing features makes the image lighter and
+        slightly more secure.
+      '';
+    };
+  };
+
+  config = lib.mkIf config.system.switch.enable {
+    system.activatableSystemBuilderCommands = ''
+      mkdir $out/bin
+      substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
+        --subst-var out \
+        --subst-var-by toplevel ''${!toplevelVar} \
+        --subst-var-by coreutils "${pkgs.coreutils}" \
+        --subst-var-by distroId ${lib.escapeShellArg config.system.nixos.distroId} \
+        --subst-var-by installBootLoader ${lib.escapeShellArg config.system.build.installBootLoader} \
+        --subst-var-by localeArchive "${config.i18n.glibcLocales}/lib/locale/locale-archive" \
+        --subst-var-by perl "${perlWrapped}" \
+        --subst-var-by shell "${pkgs.bash}/bin/sh" \
+        --subst-var-by su "${pkgs.shadow.su}/bin/su" \
+        --subst-var-by systemd "${config.systemd.package}" \
+        --subst-var-by utillinux "${pkgs.util-linux}" \
+        ;
+
+      chmod +x $out/bin/switch-to-configuration
+      ${lib.optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
+        if ! output=$(${perlWrapped}/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
+          echo "switch-to-configuration syntax is not valid:"
+          echo "$output"
+          exit 1
+        fi
+      ''}
+    '';
+  };
+
+}
diff --git a/nixpkgs/nixos/modules/system/boot/binfmt.nix b/nixpkgs/nixos/modules/system/boot/binfmt.nix
index 8c9483f01c10..d16152ab9dec 100644
--- a/nixpkgs/nixos/modules/system/boot/binfmt.nix
+++ b/nixpkgs/nixos/modules/system/boot/binfmt.nix
@@ -20,17 +20,13 @@ let
                  optionalString fixBinary "F";
   in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
 
-  activationSnippet = name: { interpreter, wrapInterpreterInShell, ... }: if wrapInterpreterInShell then ''
-    rm -f /run/binfmt/${name}
-    cat > /run/binfmt/${name} << 'EOF'
-    #!${pkgs.bash}/bin/sh
-    exec -- ${interpreter} "$@"
-    EOF
-    chmod +x /run/binfmt/${name}
-  '' else ''
-    rm -f /run/binfmt/${name}
-    ln -s ${interpreter} /run/binfmt/${name}
-  '';
+  mkInterpreter = name: { interpreter, wrapInterpreterInShell, ... }:
+    if wrapInterpreterInShell
+    then pkgs.writeShellScript "${name}-interpreter" ''
+           #!${pkgs.bash}/bin/sh
+           exec -- ${interpreter} "$@"
+         ''
+    else interpreter;
 
   getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs;
   getQemuArch = system: (lib.systems.elaborate { inherit system; }).qemuArch;
@@ -318,18 +314,25 @@ in {
 
     environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf"
       (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations));
-    system.activationScripts.binfmt = stringAfter [ "specialfs" ] ''
-      mkdir -p /run/binfmt
-      chmod 0755 /run/binfmt
-      ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet config.boot.binfmt.registrations)}
-    '';
-    systemd = lib.mkIf (config.boot.binfmt.registrations != {}) {
-      additionalUpstreamSystemUnits = [
-        "proc-sys-fs-binfmt_misc.automount"
-        "proc-sys-fs-binfmt_misc.mount"
-        "systemd-binfmt.service"
-      ];
-      services.systemd-binfmt.restartTriggers = [ (builtins.toJSON config.boot.binfmt.registrations) ];
-    };
+
+    systemd = lib.mkMerge [
+      ({ tmpfiles.rules = [
+          "d /run/binfmt 0755 -"
+        ] ++ lib.mapAttrsToList
+          (name: interpreter:
+            "L+ /run/binfmt/${name} - - - - ${interpreter}"
+          )
+          (lib.mapAttrs mkInterpreter config.boot.binfmt.registrations);
+      })
+
+      (lib.mkIf (config.boot.binfmt.registrations != {}) {
+        additionalUpstreamSystemUnits = [
+          "proc-sys-fs-binfmt_misc.automount"
+          "proc-sys-fs-binfmt_misc.mount"
+          "systemd-binfmt.service"
+        ];
+        services.systemd-binfmt.restartTriggers = [ (builtins.toJSON config.boot.binfmt.registrations) ];
+      })
+    ];
   };
 }
diff --git a/nixpkgs/nixos/modules/system/boot/initrd-network.nix b/nixpkgs/nixos/modules/system/boot/initrd-network.nix
index 5bf38b6fa200..5696cae8e65b 100644
--- a/nixpkgs/nixos/modules/system/boot/initrd-network.nix
+++ b/nixpkgs/nixos/modules/system/boot/initrd-network.nix
@@ -138,7 +138,7 @@ in
         # Bring up all interfaces.
         for iface in ${dhcpIfShellExpr}; do
           echo "bringing up network interface $iface..."
-          ip link set "$iface" up && ifaces="$ifaces $iface"
+          ip link set dev "$iface" up && ifaces="$ifaces $iface"
         done
 
         # Acquire DHCP leases.
@@ -152,8 +152,8 @@ in
 
     boot.initrd.postMountCommands = mkIf cfg.flushBeforeStage2 ''
       for iface in $ifaces; do
-        ip address flush "$iface"
-        ip link set "$iface" down
+        ip address flush dev "$iface"
+        ip link set dev "$iface" down
       done
     '';
 
diff --git a/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix b/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
index 60c5ff62ffff..3df14030ab68 100644
--- a/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
+++ b/nixpkgs/nixos/modules/system/boot/initrd-ssh.nix
@@ -164,13 +164,12 @@ in
           for instructions.
         '';
       }
-
-      {
-        assertion = config.boot.initrd.systemd.enable -> cfg.shell == null;
-        message = "systemd stage 1 does not support boot.initrd.network.ssh.shell";
-      }
     ];
 
+    warnings = lib.optional (config.boot.initrd.systemd.enable -> cfg.shell != null) ''
+      Please set 'boot.initrd.systemd.users.root.shell' instead of 'boot.initrd.network.ssh.shell'
+    '';
+
     boot.initrd.extraUtilsCommands = mkIf (!config.boot.initrd.systemd.enable) ''
       copy_bin_and_libs ${package}/bin/sshd
       cp -pv ${pkgs.glibc.out}/lib/libnss_files.so.* $out/lib
@@ -235,6 +234,8 @@ in
       users.sshd = { uid = 1; group = "sshd"; };
       groups.sshd = { gid = 1; };
 
+      users.root.shell = mkIf (config.boot.initrd.network.ssh.shell != null) config.boot.initrd.network.ssh.shell;
+
       contents."/etc/ssh/authorized_keys.d/root".text =
         concatStringsSep "\n" config.boot.initrd.network.ssh.authorizedKeys;
       contents."/etc/ssh/sshd_config".text = sshdConfig;
diff --git a/nixpkgs/nixos/modules/system/boot/kernel.nix b/nixpkgs/nixos/modules/system/boot/kernel.nix
index 9ea611919676..6b07686efcba 100644
--- a/nixpkgs/nixos/modules/system/boot/kernel.nix
+++ b/nixpkgs/nixos/modules/system/boot/kernel.nix
@@ -269,6 +269,9 @@ in
             "ata_piix"
             "pata_marvell"
 
+            # NVMe
+            "nvme"
+
             # Standard SCSI stuff.
             "sd_mod"
             "sr_mod"
diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix
index 4be040927540..b7ced5b0d346 100644
--- a/nixpkgs/nixos/modules/system/boot/networkd.nix
+++ b/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -1020,7 +1020,7 @@ let
           "MulticastToUnicast"
           "NeighborSuppression"
           "Learning"
-          "Hairpin"
+          "HairPin"
           "Isolated"
           "UseBPDU"
           "FastLeave"
@@ -1036,7 +1036,7 @@ let
         (assertValueOneOf "MulticastToUnicast" boolValues)
         (assertValueOneOf "NeighborSuppression" boolValues)
         (assertValueOneOf "Learning" boolValues)
-        (assertValueOneOf "Hairpin" boolValues)
+        (assertValueOneOf "HairPin" boolValues)
         (assertValueOneOf "Isolated" boolValues)
         (assertValueOneOf "UseBPDU" boolValues)
         (assertValueOneOf "FastLeave" boolValues)
diff --git a/nixpkgs/nixos/modules/system/boot/timesyncd.nix b/nixpkgs/nixos/modules/system/boot/timesyncd.nix
index a6604802c38c..7487cf97fe53 100644
--- a/nixpkgs/nixos/modules/system/boot/timesyncd.nix
+++ b/nixpkgs/nixos/modules/system/boot/timesyncd.nix
@@ -46,6 +46,28 @@ with lib;
       wantedBy = [ "sysinit.target" ];
       aliases = [ "dbus-org.freedesktop.timesync1.service" ];
       restartTriggers = [ config.environment.etc."systemd/timesyncd.conf".source ];
+
+      preStart = (
+        # Ensure that we have some stored time to prevent
+        # systemd-timesyncd to resort back to the fallback time.  If
+        # the file doesn't exist we assume that our current system
+        # clock is good enough to provide an initial value.
+        ''
+          if ! [ -f /var/lib/systemd/timesync/clock ]; then
+            test -d /var/lib/systemd/timesync || mkdir -p /var/lib/systemd/timesync
+            touch /var/lib/systemd/timesync/clock
+          fi
+        '' +
+        # workaround an issue of systemd-timesyncd not starting due to upstream systemd reverting their dynamic users changes
+        #  - https://github.com/NixOS/nixpkgs/pull/61321#issuecomment-492423742
+        #  - https://github.com/systemd/systemd/issues/12131
+        (lib.optionalString (versionOlder config.system.stateVersion "19.09") ''
+          if [ -L /var/lib/systemd/timesync ]; then
+            rm /var/lib/systemd/timesync
+            mv /var/lib/private/systemd/timesync /var/lib/systemd/timesync
+          fi
+        '')
+      );
     };
 
     environment.etc."systemd/timesyncd.conf".text = ''
@@ -59,28 +81,5 @@ with lib;
       group = "systemd-timesync";
     };
     users.groups.systemd-timesync.gid = config.ids.gids.systemd-timesync;
-
-    system.activationScripts.systemd-timesyncd-migration =
-      # workaround an issue of systemd-timesyncd not starting due to upstream systemd reverting their dynamic users changes
-      #  - https://github.com/NixOS/nixpkgs/pull/61321#issuecomment-492423742
-      #  - https://github.com/systemd/systemd/issues/12131
-      mkIf (versionOlder config.system.stateVersion "19.09") ''
-        if [ -L /var/lib/systemd/timesync ]; then
-          rm /var/lib/systemd/timesync
-          mv /var/lib/private/systemd/timesync /var/lib/systemd/timesync
-        fi
-      '';
-    system.activationScripts.systemd-timesyncd-init-clock =
-      # Ensure that we have some stored time to prevent systemd-timesyncd to
-      # resort back to the fallback time.
-      # If the file doesn't exist we assume that our current system clock is
-      # good enough to provide an initial value.
-      ''
-      if ! [ -f /var/lib/systemd/timesync/clock ]; then
-        test -d /var/lib/systemd/timesync || mkdir -p /var/lib/systemd/timesync
-        touch /var/lib/systemd/timesync/clock
-      fi
-      '';
   };
-
 }
diff --git a/nixpkgs/nixos/modules/tasks/encrypted-devices.nix b/nixpkgs/nixos/modules/tasks/encrypted-devices.nix
index 7837a34b4984..ab3ccddf682d 100644
--- a/nixpkgs/nixos/modules/tasks/encrypted-devices.nix
+++ b/nixpkgs/nixos/modules/tasks/encrypted-devices.nix
@@ -5,8 +5,22 @@ with lib;
 let
   fileSystems = config.system.build.fileSystems ++ config.swapDevices;
   encDevs = filter (dev: dev.encrypted.enable) fileSystems;
-  keyedEncDevs = filter (dev: dev.encrypted.keyFile != null) encDevs;
-  keylessEncDevs = filter (dev: dev.encrypted.keyFile == null) encDevs;
+
+  # With scripted initrd, devices with a keyFile have to be opened
+  # late, after file systems are mounted, because that could be where
+  # the keyFile is located. With systemd initrd, each individual
+  # systemd-cryptsetup@ unit has RequiresMountsFor= to delay until all
+  # the mount units for the key file are done; i.e. no special
+  # treatment is needed.
+  lateEncDevs =
+    if config.boot.initrd.systemd.enable
+    then { }
+    else filter (dev: dev.encrypted.keyFile != null) encDevs;
+  earlyEncDevs =
+    if config.boot.initrd.systemd.enable
+    then encDevs
+    else filter (dev: dev.encrypted.keyFile == null) encDevs;
+
   anyEncrypted =
     foldr (j: v: v || j.encrypted.enable) false encDevs;
 
@@ -39,11 +53,14 @@ let
         type = types.nullOr types.str;
         description = lib.mdDoc ''
           Path to a keyfile used to unlock the backing encrypted
-          device. At the time this keyfile is accessed, the
-          `neededForBoot` filesystems (see
-          `fileSystems.<name?>.neededForBoot`)
-          will have been mounted under `/mnt-root`,
-          so the keyfile path should usually start with "/mnt-root/".
+          device. When systemd stage 1 is not enabled, at the time
+          this keyfile is accessed, the `neededForBoot` filesystems
+          (see `utils.fsNeededForBoot`) will have been mounted under
+          `/mnt-root`, so the keyfile path should usually start with
+          "/mnt-root/". When systemd stage 1 is enabled,
+          `fsNeededForBoot` file systems will be mounted as needed
+          under `/sysroot`, and the keyfile will not be accessed until
+          its requisite mounts are done.
         '';
       };
     };
@@ -62,26 +79,41 @@ in
   };
 
   config = mkIf anyEncrypted {
-    assertions = map (dev: {
-      assertion = dev.encrypted.label != null;
-      message = ''
-        The filesystem for ${dev.mountPoint} has encrypted.enable set to true, but no encrypted.label set
-      '';
-    }) encDevs;
+    assertions = concatMap (dev: [
+      {
+        assertion = dev.encrypted.label != null;
+        message = ''
+          The filesystem for ${dev.mountPoint} has encrypted.enable set to true, but no encrypted.label set
+        '';
+      }
+      {
+        assertion =
+          config.boot.initrd.systemd.enable -> (
+            dev.encrypted.keyFile == null
+            || !lib.any (x: lib.hasPrefix x dev.encrypted.keyFile) ["/mnt-root" "$targetRoot"]
+          );
+        message = ''
+          Bad use of '/mnt-root' or '$targetRoot` in 'keyFile'.
+
+            When 'boot.initrd.systemd.enable' is enabled, file systems
+            are mounted at '/sysroot' instead of '/mnt-root'.
+        '';
+      }
+    ]) encDevs;
 
     boot.initrd = {
       luks = {
         devices =
           builtins.listToAttrs (map (dev: {
             name = dev.encrypted.label;
-            value = { device = dev.encrypted.blkDev; };
-          }) keylessEncDevs);
+            value = { device = dev.encrypted.blkDev; inherit (dev.encrypted) keyFile; };
+          }) earlyEncDevs);
         forceLuksSupportInInitrd = true;
       };
       postMountCommands =
         concatMapStrings (dev:
           "cryptsetup luksOpen --key-file ${dev.encrypted.keyFile} ${dev.encrypted.blkDev} ${dev.encrypted.label};\n"
-        ) keyedEncDevs;
+        ) lateEncDevs;
     };
   };
 }
diff --git a/nixpkgs/nixos/modules/tasks/filesystems/vfat.nix b/nixpkgs/nixos/modules/tasks/filesystems/vfat.nix
index e535e97759b2..9281b34633c2 100644
--- a/nixpkgs/nixos/modules/tasks/filesystems/vfat.nix
+++ b/nixpkgs/nixos/modules/tasks/filesystems/vfat.nix
@@ -21,7 +21,7 @@ in
         ln -sv dosfsck $out/bin/fsck.vfat
       '';
 
-    boot.initrd.systemd.extraBin = mkIf inInitrd [ pkgs.dosfstools ];
+    boot.initrd.systemd.initrdBin = mkIf inInitrd [ pkgs.dosfstools ];
 
   };
 }
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix b/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix
index da4aa916d655..e1ac7f24cb32 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces-scripted.nix
@@ -28,12 +28,12 @@ let
       SLAVES=$(ip link | grep 'master ${i}' | awk -F: '{print $2}')
       for I in $SLAVES; do
         UPDATED=0
-        ip link set "$I" nomaster
+        ip link set dev "$I" nomaster
       done
       [ "$UPDATED" -eq "1" ] && break
     done
-    ip link set "${i}" down 2>/dev/null || true
-    ip link del "${i}" 2>/dev/null || true
+    ip link set dev "${i}" down 2>/dev/null || true
+    ip link del dev "${i}" 2>/dev/null || true
   '';
 
   # warn that these attributes are deprecated (2017-2-2)
@@ -193,7 +193,7 @@ let
                 state="/run/nixos/network/addresses/${i.name}"
                 mkdir -p $(dirname "$state")
 
-                ip link set "${i.name}" up
+                ip link set dev "${i.name}" up
 
                 ${flip concatMapStrings ips (ip:
                   let
@@ -270,7 +270,7 @@ let
               ip tuntap add dev "${i.name}" mode "${i.virtualType}" user "${i.virtualOwner}"
             '';
             postStop = ''
-              ip link del ${i.name} || true
+              ip link del dev ${i.name} || true
             '';
           };
 
@@ -291,15 +291,15 @@ let
             script = ''
               # Remove Dead Interfaces
               echo "Removing old bridge ${n}..."
-              ip link show dev "${n}" >/dev/null 2>&1 && ip link del "${n}"
+              ip link show dev "${n}" >/dev/null 2>&1 && ip link del dev "${n}"
 
               echo "Adding bridge ${n}..."
               ip link add name "${n}" type bridge
 
               # Enslave child interfaces
               ${flip concatMapStrings v.interfaces (i: ''
-                ip link set "${i}" master "${n}"
-                ip link set "${i}" up
+                ip link set dev "${i}" master "${n}"
+                ip link set dev "${i}" up
               '')}
               # Save list of enslaved interfaces
               echo "${flip concatMapStrings v.interfaces (i: ''
@@ -316,7 +316,7 @@ let
                     for uri in qemu:///system lxc:///; do
                       for dom in $(${pkgs.libvirt}/bin/virsh -c $uri list --name); do
                         ${pkgs.libvirt}/bin/virsh -c $uri dumpxml "$dom" | \
-                        ${pkgs.xmlstarlet}/bin/xmlstarlet sel -t -m "//domain/devices/interface[@type='bridge'][source/@bridge='${n}'][target/@dev]" -v "concat('ip link set ',target/@dev,' master ',source/@bridge,';')" | \
+                        ${pkgs.xmlstarlet}/bin/xmlstarlet sel -t -m "//domain/devices/interface[@type='bridge'][source/@bridge='${n}'][target/@dev]" -v "concat('ip link set dev ',target/@dev,' master ',source/@bridge,';')" | \
                         ${pkgs.bash}/bin/bash
                       done
                     done
@@ -328,23 +328,23 @@ let
                 echo 2 >/sys/class/net/${n}/bridge/stp_state
               ''}
 
-              ip link set "${n}" up
+              ip link set dev "${n}" up
             '';
             postStop = ''
-              ip link set "${n}" down || true
-              ip link del "${n}" || true
+              ip link set dev "${n}" down || true
+              ip link del dev "${n}" || true
               rm -f /run/${n}.interfaces
             '';
             reload = ''
               # Un-enslave child interfaces (old list of interfaces)
               for interface in `cat /run/${n}.interfaces`; do
-                ip link set "$interface" nomaster up
+                ip link set dev "$interface" nomaster up
               done
 
               # Enslave child interfaces (new list of interfaces)
               ${flip concatMapStrings v.interfaces (i: ''
-                ip link set "${i}" master "${n}"
-                ip link set "${i}" up
+                ip link set dev "${i}" master "${n}"
+                ip link set dev "${i}" up
               '')}
               # Save list of enslaved interfaces
               echo "${flip concatMapStrings v.interfaces (i: ''
@@ -395,7 +395,7 @@ let
             postStop = ''
               echo "Cleaning Open vSwitch ${n}"
               echo "Shutting down internal ${n} interface"
-              ip link set ${n} down || true
+              ip link set dev ${n} down || true
               echo "Deleting flows for ${n}"
               ovs-ofctl --protocols=${v.openFlowVersion} del-flows ${n} || true
               echo "Deleting Open vSwitch ${n}"
@@ -433,10 +433,10 @@ let
               while [ ! -d "/sys/class/net/${n}" ]; do sleep 0.1; done;
 
               # Bring up the bond and enslave the specified interfaces
-              ip link set "${n}" up
+              ip link set dev "${n}" up
               ${flip concatMapStrings v.interfaces (i: ''
-                ip link set "${i}" down
-                ip link set "${i}" master "${n}"
+                ip link set dev "${i}" down
+                ip link set dev "${i}" master "${n}"
               '')}
             '';
             postStop = destroyBond n;
@@ -457,13 +457,13 @@ let
             path = [ pkgs.iproute2 ];
             script = ''
               # Remove Dead Interfaces
-              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
               ip link add link "${v.interface}" name "${n}" type macvlan \
                 ${optionalString (v.mode != null) "mode ${v.mode}"}
-              ip link set "${n}" up
+              ip link set dev "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}" || true
+              ip link delete dev "${n}" || true
             '';
           });
 
@@ -515,7 +515,7 @@ let
             path = [ pkgs.iproute2 ];
             script = ''
               # Remove Dead Interfaces
-              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
               ip link add name "${n}" type sit \
                 ${optionalString (v.remote != null) "remote \"${v.remote}\""} \
                 ${optionalString (v.local != null) "local \"${v.local}\""} \
@@ -526,10 +526,10 @@ let
                     optionalString (v.encapsulation.sourcePort != null)
                       "encap-sport ${toString v.encapsulation.sourcePort}"
                   }"}
-              ip link set "${n}" up
+              ip link set dev "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}" || true
+              ip link delete dev "${n}" || true
             '';
           });
 
@@ -549,16 +549,16 @@ let
             path = [ pkgs.iproute2 ];
             script = ''
               # Remove Dead Interfaces
-              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
               ip link add name "${n}" type ${v.type} \
                 ${optionalString (v.remote != null) "remote \"${v.remote}\""} \
                 ${optionalString (v.local != null) "local \"${v.local}\""} \
                 ${optionalString (v.ttl != null) "${ttlarg} ${toString v.ttl}"} \
                 ${optionalString (v.dev != null) "dev \"${v.dev}\""}
-              ip link set "${n}" up
+              ip link set dev "${n}" up
             '';
             postStop = ''
-              ip link delete "${n}" || true
+              ip link delete dev "${n}" || true
             '';
           });
 
@@ -577,17 +577,17 @@ let
             path = [ pkgs.iproute2 ];
             script = ''
               # Remove Dead Interfaces
-              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete "${n}"
+              ip link show dev "${n}" >/dev/null 2>&1 && ip link delete dev "${n}"
               ip link add link "${v.interface}" name "${n}" type vlan id "${toString v.id}"
 
               # We try to bring up the logical VLAN interface. If the master
               # interface the logical interface is dependent upon is not up yet we will
               # fail to immediately bring up the logical interface. The resulting logical
               # interface will brought up later when the master interface is up.
-              ip link set "${n}" up || true
+              ip link set dev "${n}" up || true
             '';
             postStop = ''
-              ip link delete "${n}" || true
+              ip link delete dev "${n}" || true
             '';
           });
 
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix b/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix
index cee23eb24406..2009c9a7e6e2 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces-systemd.nix
@@ -442,7 +442,7 @@ in
             postStop = ''
               echo "Cleaning Open vSwitch ${n}"
               echo "Shutting down internal ${n} interface"
-              ip link set ${n} down || true
+              ip link set dev ${n} down || true
               echo "Deleting flows for ${n}"
               ovs-ofctl --protocols=${v.openFlowVersion} del-flows ${n} || true
               echo "Deleting Open vSwitch ${n}"
diff --git a/nixpkgs/nixos/modules/tasks/network-interfaces.nix b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
index 853a2cb31432..d976f9951bb5 100644
--- a/nixpkgs/nixos/modules/tasks/network-interfaces.nix
+++ b/nixpkgs/nixos/modules/tasks/network-interfaces.nix
@@ -1406,18 +1406,12 @@ in
           val = tempaddrValues.${opt}.sysctl;
          in nameValuePair "net.ipv6.conf.${replaceStrings ["."] ["/"] i.name}.use_tempaddr" val));
 
-    # Set the host and domain names in the activation script.  Don't
-    # clear it if it's not configured in the NixOS configuration,
-    # since it may have been set by dhcpcd in the meantime.
-    system.activationScripts.hostname = let
-        effectiveHostname = config.boot.kernel.sysctl."kernel.hostname" or cfg.hostName;
-      in optionalString (effectiveHostname != "") ''
-        hostname "${effectiveHostname}"
-      '';
-    system.activationScripts.domain =
-      optionalString (cfg.domain != null) ''
-        domainname "${cfg.domain}"
-      '';
+    systemd.services.domainname = lib.mkIf (cfg.domain != null) {
+      wantedBy = [ "sysinit.target" ];
+      before = [ "sysinit.target" ];
+      unitConfig.DefaultDependencies = false;
+      serviceConfig.ExecStart = ''${pkgs.nettools}/bin/domainname "${cfg.domain}"'';
+    };
 
     environment.etc.hostid = mkIf (cfg.hostId != null) { source = hostidFile; };
     boot.initrd.systemd.contents."/etc/hostid" = mkIf (cfg.hostId != null) { source = hostidFile; };
diff --git a/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix b/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix
index aa85665af695..6fdb177b968b 100644
--- a/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixpkgs/nixos/modules/virtualisation/nixos-containers.nix
@@ -754,7 +754,7 @@ in
                   { services.postgresql.enable = true;
                     services.postgresql.package = pkgs.postgresql_14;
 
-                    system.stateVersion = "21.05";
+                    system.stateVersion = "${lib.trivial.release}";
                   };
               };
           }
@@ -906,4 +906,6 @@ in
       "tun"
     ];
   });
+
+  meta.buildDocsInSandbox = false;
 }
diff --git a/nixpkgs/nixos/release-combined.nix b/nixpkgs/nixos/release-combined.nix
index 149a924de4d0..9b4b92be6f3a 100644
--- a/nixpkgs/nixos/release-combined.nix
+++ b/nixpkgs/nixos/release-combined.nix
@@ -97,6 +97,7 @@ in rec {
         (onSystems ["x86_64-linux"] "nixos.tests.installer.simpleUefiSystemdBoot")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.simple")
         (onSystems ["x86_64-linux"] "nixos.tests.installer.swraid")
+        (onSystems ["x86_64-linux"] "nixos.tests.installer.zfsroot")
         (onSystems ["x86_64-linux"] "nixos.tests.nixos-rebuild-specialisations")
         (onFullSupported "nixos.tests.ipv6")
         (onFullSupported "nixos.tests.keymap.azerty")
diff --git a/nixpkgs/nixos/release.nix b/nixpkgs/nixos/release.nix
index 60f4cc94399c..2acc5ade7848 100644
--- a/nixpkgs/nixos/release.nix
+++ b/nixpkgs/nixos/release.nix
@@ -123,7 +123,7 @@ let
       build = configEvaled.config.system.build;
       kernelTarget = configEvaled.pkgs.stdenv.hostPlatform.linux-kernel.target;
     in
-      pkgs.symlinkJoin {
+      configEvaled.pkgs.symlinkJoin {
         name = "netboot";
         paths = [
           build.netbootRamdisk
@@ -398,7 +398,7 @@ in rec {
         modules = singleton ({ ... }:
           { fileSystems."/".device  = mkDefault "/dev/sda1";
             boot.loader.grub.device = mkDefault "/dev/sda";
-            system.stateVersion = mkDefault "18.03";
+            system.stateVersion = mkDefault lib.trivial.release;
           });
       }).config.system.build.toplevel;
       preferLocalBuild = true;
diff --git a/nixpkgs/nixos/tests/activation/nix-channel.nix b/nixpkgs/nixos/tests/activation/nix-channel.nix
new file mode 100644
index 000000000000..8416ff0347ac
--- /dev/null
+++ b/nixpkgs/nixos/tests/activation/nix-channel.nix
@@ -0,0 +1,16 @@
+{ lib, ... }:
+
+{
+
+  name = "activation-nix-channel";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes.machine = {
+    nix.channel.enable = true;
+  };
+
+  testScript = ''
+    print(machine.succeed("cat /root/.nix-channels"))
+  '';
+}
diff --git a/nixpkgs/nixos/tests/activation/var.nix b/nixpkgs/nixos/tests/activation/var.nix
new file mode 100644
index 000000000000..1a546a7671c5
--- /dev/null
+++ b/nixpkgs/nixos/tests/activation/var.nix
@@ -0,0 +1,18 @@
+{ lib, ... }:
+
+{
+
+  name = "activation-var";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes.machine = { };
+
+  testScript = ''
+    assert machine.succeed("stat -c '%a' /var/tmp") == "1777\n"
+    assert machine.succeed("stat -c '%a' /var/empty") == "555\n"
+    assert machine.succeed("stat -c '%U' /var/empty") == "root\n"
+    assert machine.succeed("stat -c '%G' /var/empty") == "root\n"
+    assert "i" in machine.succeed("lsattr -d /var/empty")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/all-tests.nix b/nixpkgs/nixos/tests/all-tests.nix
index 3531930d863a..2f6d5a8dae88 100644
--- a/nixpkgs/nixos/tests/all-tests.nix
+++ b/nixpkgs/nixos/tests/all-tests.nix
@@ -90,6 +90,14 @@ in {
     lib-extend = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./nixos-test-driver/lib-extend.nix {};
     node-name = runTest ./nixos-test-driver/node-name.nix;
     busybox = runTest ./nixos-test-driver/busybox.nix;
+    driver-timeout = pkgs.runCommand "ensure-timeout-induced-failure" {
+      failed = pkgs.testers.testBuildFailure ((runTest ./nixos-test-driver/timeout.nix).config.rawTestDerivation);
+    } ''
+      grep -F "timeout reached; test terminating" $failed/testBuildFailure.log
+      # The program will always be terminated by SIGTERM (143) if it waits for the deadline thread.
+      [[ 143 = $(cat $failed/testBuildFailure.exit) ]]
+      touch $out
+    '';
   };
 
   # NixOS vm tests and non-vm unit tests
@@ -153,6 +161,7 @@ in {
   budgie = handleTest ./budgie.nix {};
   buildbot = handleTest ./buildbot.nix {};
   buildkite-agents = handleTest ./buildkite-agents.nix {};
+  c2fmzq = handleTest ./c2fmzq.nix {};
   caddy = handleTest ./caddy.nix {};
   cadvisor = handleTestOn ["x86_64-linux"] ./cadvisor.nix {};
   cage = handleTest ./cage.nix {};
@@ -265,6 +274,8 @@ in {
   esphome = handleTest ./esphome.nix {};
   etc = pkgs.callPackage ../modules/system/etc/test.nix { inherit evalMinimalConfig; };
   activation = pkgs.callPackage ../modules/system/activation/test.nix { };
+  activation-var = runTest ./activation/var.nix;
+  activation-nix-channel = runTest ./activation/nix-channel.nix;
   etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
   etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
   etebase-server = handleTest ./etebase-server.nix {};
@@ -288,6 +299,7 @@ in {
   firewall-nftables = handleTest ./firewall.nix { nftables = true; };
   fish = handleTest ./fish.nix {};
   flannel = handleTestOn ["x86_64-linux"] ./flannel.nix {};
+  floorp = handleTest ./firefox.nix { firefoxPackage = pkgs.floorp; };
   fluentd = handleTest ./fluentd.nix {};
   fluidd = handleTest ./fluidd.nix {};
   fontconfig-default-fonts = handleTest ./fontconfig-default-fonts.nix {};
@@ -327,6 +339,7 @@ in {
   gollum = handleTest ./gollum.nix {};
   gonic = handleTest ./gonic.nix {};
   google-oslogin = handleTest ./google-oslogin {};
+  goss = handleTest ./goss.nix {};
   gotify-server = handleTest ./gotify-server.nix {};
   gotosocial = runTest ./web-apps/gotosocial.nix;
   grafana = handleTest ./grafana {};
@@ -577,6 +590,7 @@ in {
   node-red = handleTest ./node-red.nix {};
   nomad = handleTest ./nomad.nix {};
   non-default-filesystems = handleTest ./non-default-filesystems.nix {};
+  non-switchable-system = runTest ./non-switchable-system.nix;
   noto-fonts = handleTest ./noto-fonts.nix {};
   noto-fonts-cjk-qt-default-weight = handleTest ./noto-fonts-cjk-qt-default-weight.nix {};
   novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {};
@@ -847,6 +861,7 @@ in {
   trezord = handleTest ./trezord.nix {};
   trickster = handleTest ./trickster.nix {};
   trilium-server = handleTestOn ["x86_64-linux"] ./trilium-server.nix {};
+  tsja = handleTest ./tsja.nix {};
   tsm-client-gui = handleTest ./tsm-client-gui.nix {};
   txredisapi = handleTest ./txredisapi.nix {};
   tuptime = handleTest ./tuptime.nix {};
diff --git a/nixpkgs/nixos/tests/bittorrent.nix b/nixpkgs/nixos/tests/bittorrent.nix
index 11420cba9dce..4a73fea6a09d 100644
--- a/nixpkgs/nixos/tests/bittorrent.nix
+++ b/nixpkgs/nixos/tests/bittorrent.nix
@@ -148,7 +148,7 @@ in
       )
 
       # Bring down the initial seeder.
-      # tracker.stop_job("transmission")
+      tracker.stop_job("transmission")
 
       # Now download from the second client.  This can only succeed if
       # the first client created a NAT hole in the router.
diff --git a/nixpkgs/nixos/tests/c2fmzq.nix b/nixpkgs/nixos/tests/c2fmzq.nix
new file mode 100644
index 000000000000..d8ec816c7d29
--- /dev/null
+++ b/nixpkgs/nixos/tests/c2fmzq.nix
@@ -0,0 +1,75 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "c2FmZQ";
+  meta.maintainers = with lib.maintainers; [ hmenke ];
+
+  nodes.machine = {
+    services.c2fmzq-server = {
+      enable = true;
+      port = 8080;
+      passphraseFile = builtins.toFile "pwfile" "hunter2"; # don't do this on real deployments
+      settings = {
+        verbose = 3; # debug
+      };
+    };
+    environment = {
+      sessionVariables = {
+        C2FMZQ_PASSPHRASE = "lol";
+        C2FMZQ_API_SERVER = "http://localhost:8080";
+      };
+      systemPackages = [
+        pkgs.c2fmzq
+        (pkgs.writeScriptBin "c2FmZQ-client-wrapper" ''
+          #!${pkgs.expect}/bin/expect -f
+          spawn c2FmZQ-client {*}$argv
+          expect {
+            "Enter password:" { send "$env(PASSWORD)\r" }
+            "Type YES to confirm:" { send "YES\r" }
+            timeout { exit 1 }
+            eof { exit 0 }
+          }
+          interact
+        '')
+      ];
+    };
+  };
+
+  testScript = { nodes, ... }: ''
+    machine.start()
+    machine.wait_for_unit("c2fmzq-server.service")
+    machine.wait_for_open_port(8080)
+
+    with subtest("Create accounts for alice and bob"):
+        machine.succeed("PASSWORD=foobar c2FmZQ-client-wrapper -- -v 3 create-account alice@example.com")
+        machine.succeed("PASSWORD=fizzbuzz c2FmZQ-client-wrapper -- -v 3 create-account bob@example.com")
+
+    with subtest("Log in as alice"):
+        machine.succeed("PASSWORD=foobar c2FmZQ-client-wrapper -- -v 3 login alice@example.com")
+        msg = machine.succeed("c2FmZQ-client -v 3 status")
+        assert "Logged in as alice@example.com" in msg, f"ERROR: Not logged in as alice:\n{msg}"
+
+    with subtest("Create a new album, upload a file, and delete the uploaded file"):
+        machine.succeed("c2FmZQ-client -v 3 create-album 'Rarest Memes'")
+        machine.succeed("echo 'pls do not steal' > meme.txt")
+        machine.succeed("c2FmZQ-client -v 3 import meme.txt 'Rarest Memes'")
+        machine.succeed("c2FmZQ-client -v 3 sync")
+        machine.succeed("rm meme.txt")
+
+    with subtest("Share the album with bob"):
+        machine.succeed("c2FmZQ-client-wrapper -- -v 3 share 'Rarest Memes' bob@example.com")
+
+    with subtest("Log in as bob"):
+        machine.succeed("PASSWORD=fizzbuzz c2FmZQ-client-wrapper -- -v 3 login bob@example.com")
+        msg = machine.succeed("c2FmZQ-client -v 3 status")
+        assert "Logged in as bob@example.com" in msg, f"ERROR: Not logged in as bob:\n{msg}"
+
+    with subtest("Download the shared file"):
+        machine.succeed("c2FmZQ-client -v 3 download 'shared/Rarest Memes/meme.txt'")
+        machine.succeed("c2FmZQ-client -v 3 export 'shared/Rarest Memes/meme.txt' .")
+        msg = machine.succeed("cat meme.txt")
+        assert "pls do not steal\n" == msg, f"File content is not the same:\n{msg}"
+
+    with subtest("Test that PWA is served"):
+        msg = machine.succeed("curl -sSfL http://localhost:8080")
+        assert "c2FmZQ" in msg, f"Could not find 'c2FmZQ' in the output:\n{msg}"
+  '';
+})
diff --git a/nixpkgs/nixos/tests/containers-imperative.nix b/nixpkgs/nixos/tests/containers-imperative.nix
index 22b664a90e17..18bec1db78e8 100644
--- a/nixpkgs/nixos/tests/containers-imperative.nix
+++ b/nixpkgs/nixos/tests/containers-imperative.nix
@@ -21,9 +21,7 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
           modules = lib.singleton {
             nixpkgs = { inherit (config.nixpkgs) localSystem; };
 
-            containers.foo.config = {
-              system.stateVersion = "18.03";
-            };
+            containers.foo.config = {};
           };
 
           # The system is inherited from the host above.
diff --git a/nixpkgs/nixos/tests/goss.nix b/nixpkgs/nixos/tests/goss.nix
new file mode 100644
index 000000000000..6b772d19215e
--- /dev/null
+++ b/nixpkgs/nixos/tests/goss.nix
@@ -0,0 +1,53 @@
+import ./make-test-python.nix ({ pkgs, lib, ... }: {
+  name = "goss";
+  meta.maintainers = [ lib.maintainers.anthonyroussel ];
+
+  nodes.machine = {
+    environment.systemPackages = [ pkgs.jq ];
+
+    services.goss = {
+      enable = true;
+
+      environment = {
+        GOSS_FMT = "json";
+      };
+
+      settings = {
+        addr."tcp://localhost:8080" = {
+          reachable = true;
+          local-address = "127.0.0.1";
+        };
+        command."check-goss-version" = {
+          exec = "${lib.getExe pkgs.goss} --version";
+          exit-status = 0;
+        };
+        dns.localhost.resolvable = true;
+        file."/nix" = {
+          filetype = "directory";
+          exists = true;
+        };
+        group.root.exists = true;
+        kernel-param."kernel.ostype".value = "Linux";
+        service.goss = {
+          enabled = true;
+          running = true;
+        };
+        user.root.exists = true;
+      };
+    };
+  };
+
+  testScript = ''
+    import json
+
+    machine.wait_for_unit("goss.service")
+    machine.wait_for_open_port(8080)
+
+    with subtest("returns health status"):
+      result = json.loads(machine.succeed("curl -sS http://localhost:8080/healthz"))
+
+      assert len(result["results"]) == 10, f".results should be an array of 10 items, was {result['results']!r}"
+      assert result["summary"]["failed-count"] == 0, f".summary.failed-count should be zero, was {result['summary']['failed-count']}"
+      assert result["summary"]["test-count"] == 10, f".summary.test-count should be 10, was {result['summary']['test-count']}"
+    '';
+})
diff --git a/nixpkgs/nixos/tests/grafana/provision/default.nix b/nixpkgs/nixos/tests/grafana/provision/default.nix
index 96378452ade3..d33d16ce1209 100644
--- a/nixpkgs/nixos/tests/grafana/provision/default.nix
+++ b/nixpkgs/nixos/tests/grafana/provision/default.nix
@@ -22,15 +22,14 @@ let
       };
     };
 
-    system.activationScripts.setup-grafana = {
-      deps = [ "users" ];
-      text = ''
-        mkdir -p /var/lib/grafana/dashboards
-        chown -R grafana:grafana /var/lib/grafana
-        chmod 0700 -R /var/lib/grafana/dashboards
-        cp ${pkgs.writeText "test.json" (builtins.readFile ./test_dashboard.json)} /var/lib/grafana/dashboards/
-      '';
-    };
+    systemd.tmpfiles.rules =
+      let
+        dashboard = pkgs.writeText "test.json" (builtins.readFile ./test_dashboard.json);
+      in
+      [
+        "d /var/lib/grafana/dashboards 0700 grafana grafana -"
+        "C+ /var/lib/grafana/dashboards/test.json - - - - ${dashboard}"
+      ];
   };
 
   extraNodeConfs = {
diff --git a/nixpkgs/nixos/tests/installer-systemd-stage-1.nix b/nixpkgs/nixos/tests/installer-systemd-stage-1.nix
index 85155a6c682b..608a21ef6372 100644
--- a/nixpkgs/nixos/tests/installer-systemd-stage-1.nix
+++ b/nixpkgs/nixos/tests/installer-systemd-stage-1.nix
@@ -12,11 +12,11 @@
     btrfsSubvolDefault
     btrfsSubvolEscape
     btrfsSubvols
-    # encryptedFSWithKeyfile
+    encryptedFSWithKeyfile
     # grub1
-    # luksroot
-    # luksroot-format1
-    # luksroot-format2
+    luksroot
+    luksroot-format1
+    luksroot-format2
     # lvm
     separateBoot
     separateBootFat
diff --git a/nixpkgs/nixos/tests/installer.nix b/nixpkgs/nixos/tests/installer.nix
index 9ff1d8f5d039..15ece034898a 100644
--- a/nixpkgs/nixos/tests/installer.nix
+++ b/nixpkgs/nixos/tests/installer.nix
@@ -515,7 +515,7 @@ let
       enableOCR = true;
       preBootCommands = ''
         machine.start()
-        machine.wait_for_text("Passphrase for")
+        machine.wait_for_text("[Pp]assphrase for")
         machine.send_chars("supersecret\n")
       '';
     };
@@ -781,7 +781,7 @@ in {
         encrypted.enable = true;
         encrypted.blkDev = "/dev/vda3";
         encrypted.label = "crypt";
-        encrypted.keyFile = "/mnt-root/keyfile";
+        encrypted.keyFile = "/${if systemdStage1 then "sysroot" else "mnt-root"}/keyfile";
       };
     '';
   };
diff --git a/nixpkgs/nixos/tests/netdata.nix b/nixpkgs/nixos/tests/netdata.nix
index c5f7294f79ab..e3438f63404e 100644
--- a/nixpkgs/nixos/tests/netdata.nix
+++ b/nixpkgs/nixos/tests/netdata.nix
@@ -30,8 +30,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
     # check if netdata can read disk ops for root owned processes.
     # if > 0, successful. verifies both netdata working and
     # apps.plugin has elevated capabilities.
-    url = "http://localhost:19999/api/v1/data\?chart=users.pwrites"
-    filter = '[.data[range(10)][.labels | indices("root")[0]]] | add | . > 0'
+    url = "http://localhost:19999/api/v1/data\?chart=user.root_disk_physical_io"
+    filter = '[.data[range(10)][2]] | add | . < 0'
     cmd = f"curl -s {url} | jq -e '{filter}'"
     netdata.wait_until_succeeds(cmd)
 
diff --git a/nixpkgs/nixos/tests/nextcloud/basic.nix b/nixpkgs/nixos/tests/nextcloud/basic.nix
index b7af6d6d7364..ab1d8353dba0 100644
--- a/nixpkgs/nixos/tests/nextcloud/basic.nix
+++ b/nixpkgs/nixos/tests/nextcloud/basic.nix
@@ -37,8 +37,6 @@ in {
         "d /var/lib/nextcloud-data 0750 nextcloud nginx - -"
       ];
 
-      system.stateVersion = "22.11"; # stateVersion >=21.11 to make sure that we use OpenSSL3
-
       services.nextcloud = {
         enable = true;
         datadir = "/var/lib/nextcloud-data";
diff --git a/nixpkgs/nixos/tests/nixos-test-driver/timeout.nix b/nixpkgs/nixos/tests/nixos-test-driver/timeout.nix
new file mode 100644
index 000000000000..29bd85d2498e
--- /dev/null
+++ b/nixpkgs/nixos/tests/nixos-test-driver/timeout.nix
@@ -0,0 +1,15 @@
+{
+  name = "Test that sleep of 6 seconds fails a timeout of 5 seconds";
+  globalTimeout = 5;
+
+  nodes = {
+    machine = ({ pkgs, ... }: {
+    });
+  };
+
+  testScript = ''
+    start_all()
+    machine.wait_for_unit("multi-user.target")
+    machine.succeed("sleep 6")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/non-switchable-system.nix b/nixpkgs/nixos/tests/non-switchable-system.nix
new file mode 100644
index 000000000000..54bede75453b
--- /dev/null
+++ b/nixpkgs/nixos/tests/non-switchable-system.nix
@@ -0,0 +1,15 @@
+{ lib, ... }:
+
+{
+  name = "non-switchable-system";
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  nodes.machine = {
+    system.switch.enable = false;
+  };
+
+  testScript = ''
+    machine.succeed("test ! -e /run/current-system/bin/switch-to-configuration")
+  '';
+}
diff --git a/nixpkgs/nixos/tests/opensearch.nix b/nixpkgs/nixos/tests/opensearch.nix
index c0caf950cb9c..2887ac967765 100644
--- a/nixpkgs/nixos/tests/opensearch.nix
+++ b/nixpkgs/nixos/tests/opensearch.nix
@@ -31,14 +31,9 @@ in
       services.opensearch.dataDir = "/var/opensearch_test";
       services.opensearch.user = "open_search";
       services.opensearch.group = "open_search";
-      system.activationScripts.createDirectory = {
-        text = ''
-          mkdir -p "/var/opensearch_test"
-          chown open_search:open_search /var/opensearch_test
-          chmod 0700 /var/opensearch_test
-        '';
-        deps = [ "users" "groups" ];
-      };
+      systemd.tmpfiles.rules = [
+        "d /var/opensearch_test 0700 open_search open_search -"
+      ];
       users = {
         groups.open_search = {};
         users.open_search = {
diff --git a/nixpkgs/nixos/tests/openssh.nix b/nixpkgs/nixos/tests/openssh.nix
index 88d3e54ee76c..881eb9d7d91c 100644
--- a/nixpkgs/nixos/tests/openssh.nix
+++ b/nixpkgs/nixos/tests/openssh.nix
@@ -82,6 +82,19 @@ in {
         };
       };
 
+    server_allowedusers =
+      { ... }:
+
+      {
+        services.openssh = { enable = true; settings.AllowUsers = [ "alice" "bob" ]; };
+        users.groups = { alice = { }; bob = { }; carol = { }; };
+        users.users = {
+          alice = { isNormalUser = true; group = "alice"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
+          bob = { isNormalUser = true; group = "bob"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
+          carol = { isNormalUser = true; group = "carol"; openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; };
+        };
+      };
+
     client =
       { ... }: { };
 
@@ -147,5 +160,23 @@ in {
 
     with subtest("match-rules"):
         server_match_rule.succeed("ss -nlt | grep '127.0.0.1:22'")
+
+    with subtest("allowed-users"):
+        client.succeed(
+            "cat ${snakeOilPrivateKey} > privkey.snakeoil"
+        )
+        client.succeed("chmod 600 privkey.snakeoil")
+        client.succeed(
+            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil alice@server_allowedusers true",
+            timeout=30
+        )
+        client.succeed(
+            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil bob@server_allowedusers true",
+            timeout=30
+        )
+        client.fail(
+            "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i privkey.snakeoil carol@server_allowedusers true",
+            timeout=30
+        )
   '';
 })
diff --git a/nixpkgs/nixos/tests/restic.nix b/nixpkgs/nixos/tests/restic.nix
index 3b9ea2f85b1e..54fdc1d3995c 100644
--- a/nixpkgs/nixos/tests/restic.nix
+++ b/nixpkgs/nixos/tests/restic.nix
@@ -21,7 +21,10 @@ import ./make-test-python.nix (
       unpackPhase = "true";
       installPhase = ''
         mkdir $out
-        touch $out/some_file
+        echo some_file > $out/some_file
+        echo some_other_file > $out/some_other_file
+        mkdir $out/a_dir
+        echo a_file > $out/a_dir/a_file
       '';
     };
 
@@ -53,9 +56,13 @@ import ./make-test-python.nix (
               initialize = true;
             };
             remote-from-file-backup = {
-              inherit passwordFile paths exclude pruneOpts;
+              inherit passwordFile exclude pruneOpts;
               initialize = true;
               repositoryFile = pkgs.writeText "repositoryFile" remoteFromFileRepository;
+              paths = [ "/opt/a_dir" ];
+              dynamicFilesFrom = ''
+                find /opt -mindepth 1 -maxdepth 1 ! -name a_dir # all files in /opt except for a_dir
+              '';
             };
             rclonebackup = {
               inherit passwordFile paths exclude pruneOpts;
@@ -123,13 +130,18 @@ import ./make-test-python.nix (
           "systemctl start restic-backups-remote-from-file-backup.service",
           'restic-remote-from-file-backup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"',
 
+          # test that restoring that snapshot produces the same directory
+          "mkdir /tmp/restore-2",
+          "${pkgs.restic}/bin/restic -r ${remoteRepository} -p ${passwordFile} restore latest -t /tmp/restore-2",
+          "diff -ru ${testDir} /tmp/restore-2/opt",
+
           # test that rclonebackup produces a snapshot
           "systemctl start restic-backups-rclonebackup.service",
           'restic-rclonebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"',
 
           # test that custompackage runs both `restic backup` and `restic check` with reasonable commandlines
           "systemctl start restic-backups-custompackage.service",
-          "grep 'backup.* /opt' /root/fake-restic.log",
+          "grep 'backup' /root/fake-restic.log",
           "grep 'check.* --some-check-option' /root/fake-restic.log",
 
           # test that we can create four snapshots in remotebackup and rclonebackup
diff --git a/nixpkgs/nixos/tests/stunnel.nix b/nixpkgs/nixos/tests/stunnel.nix
index 22c087290fc7..07fba435d4df 100644
--- a/nixpkgs/nixos/tests/stunnel.nix
+++ b/nixpkgs/nixos/tests/stunnel.nix
@@ -17,11 +17,16 @@ let
     };
   };
   makeCert = { config, pkgs, ... }: {
-    system.activationScripts.create-test-cert = stringAfter [ "users" ] ''
-      ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName}
-      ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem )
-      chown stunnel /test-key.pem /test-key-and-cert.pem
+    systemd.services.create-test-cert = {
+      wantedBy = [ "sysinit.target" ];
+      before = [ "sysinit.target" ];
+      unitConfig.DefaultDependencies = false;
+      script = ''
+        ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa -nodes -out /test-cert.pem -keyout /test-key.pem -subj /CN=${config.networking.hostName}
+        ( umask 077; cat /test-key.pem /test-cert.pem > /test-key-and-cert.pem )
+        chown stunnel /test-key.pem /test-key-and-cert.pem
     '';
+    };
   };
   serverCommon = { pkgs, ... }: {
     networking.firewall.allowedTCPPorts = [ 443 ];
diff --git a/nixpkgs/nixos/tests/systemd-timesyncd.nix b/nixpkgs/nixos/tests/systemd-timesyncd.nix
index 43abd36c47d9..f38d06be1516 100644
--- a/nixpkgs/nixos/tests/systemd-timesyncd.nix
+++ b/nixpkgs/nixos/tests/systemd-timesyncd.nix
@@ -15,12 +15,13 @@ in {
       # create the path that should be migrated by our activation script when
       # upgrading to a newer nixos version
       system.stateVersion = "19.03";
-      system.activationScripts.simulate-old-timesync-state-dir = lib.mkBefore ''
-        rm -f /var/lib/systemd/timesync
-        mkdir -p /var/lib/systemd /var/lib/private/systemd/timesync
-        ln -s /var/lib/private/systemd/timesync /var/lib/systemd/timesync
-        chown systemd-timesync: /var/lib/private/systemd/timesync
-      '';
+      systemd.tmpfiles.rules = [
+        "r /var/lib/systemd/timesync -"
+        "d /var/lib/systemd -"
+        "d /var/lib/private/systemd/timesync -"
+        "L /var/lib/systemd/timesync - - - - /var/lib/private/systemd/timesync"
+        "d /var/lib/private/systemd/timesync - systemd-timesync systemd-timesync -"
+      ];
     });
   };
 
diff --git a/nixpkgs/nixos/tests/tsja.nix b/nixpkgs/nixos/tests/tsja.nix
new file mode 100644
index 000000000000..176783088d8d
--- /dev/null
+++ b/nixpkgs/nixos/tests/tsja.nix
@@ -0,0 +1,32 @@
+import ./make-test-python.nix ({ pkgs, lib, ...} : {
+  name = "tsja";
+  meta = {
+    maintainers = with lib.maintainers; [ chayleaf ];
+  };
+
+  nodes = {
+    master =
+      { config, ... }:
+
+      {
+        services.postgresql = {
+          enable = true;
+          extraPlugins = with config.services.postgresql.package.pkgs; [
+            tsja
+          ];
+        };
+      };
+  };
+
+  testScript = ''
+    start_all()
+    master.wait_for_unit("postgresql")
+    master.succeed("sudo -u postgres psql -f /run/current-system/sw/share/postgresql/extension/libtsja_dbinit.sql")
+    # make sure "日本語" is parsed as a separate lexeme
+    master.succeed("""
+      sudo -u postgres \\
+        psql -c "SELECT * FROM ts_debug('japanese', 'PostgreSQLで日本語のテキスト検索ができます。')" \\
+          | grep "{日本語}"
+    """)
+  '';
+})