···7272 The binary name remains `webfontkitgenerator`.
7373 The `webfontkitgenerator` package is an alias to `webfont-bundler`.
74747575+- `python3Packages.triton` no longer takes an `enableRocm` argument and supports ROCm in all build configurations via runtime binding. In most cases no action will be needed. If triton is unable to find the HIP SDK add `rocmPackages.clr` as a build input or set the environment variable `HIP_PATH="${rocmPackages.clr}"`.
7676+7577- `inspircd` has been updated to the v4 release series. Please refer to the upstream documentation for [general information](https://docs.inspircd.org/4/overview/#v4-overview) and a list of [breaking changes](https://docs.inspircd.org/4/breaking-changes/).
76787779- `lima` package now only includes the guest agent for the host's architecture by default. If your guest VM's architecture differs from your Lima host's, you'll need to enable the `lima-additional-guestagents` package by setting `withAdditionalGuestAgents = true` when overriding lima with this input.
···9496 make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_3;` in your configuration.
95979698- `privatebin` has been updated to `2.0.0`. This release changes configuration defaults including switching the template and removing legacy features. See the [v2.0.0 changelog entry](https://github.com/PrivateBin/PrivateBin/releases/tag/2.0.0) for details on how to upgrade.
9999+100100+- `rocmPackages.triton` has been removed in favor of `python3Packages.triton`.
9710198102- `go-mockery` has been updated to v3. For migration instructions see the [upstream documentation](https://vektra.github.io/mockery/latest/v3/). If v2 is still required `go-mockery_v2` has been added but will be removed on or before 2029-12-31 in-line with it's [upstream support lifecycle](https://vektra.github.io/mockery/
99103
···85858686## Writing and Reviewing a Modular Service {#modular-service-review}
87878888-Refer to the contributor documentation in [`nixos/README-modular-services.md`](https://github.com/NixOS/nixpkgs/blob/master/nixos/README-modular-services.md).
8888+A typical service module consists of the following:
8989+9090+For more details, refer to the contributor documentation in [`nixos/README-modular-services.md`](https://github.com/NixOS/nixpkgs/blob/master/nixos/README-modular-services.md).
89919092## Portable Service Options {#modular-service-options-portable}
9193
+90
nixos/modules/system/service/README.md
···53535454- **Simple attribute structure**: Unlike `environment.etc`, `configData` uses a simpler structure with just `enable`, `name`, `text`, `source`, and `path` attributes. Complex ownership options were omitted for simplicity and portability.
5555 Per-service user creation is still TBD.
5656+5757+## No `pkgs` module argument
5858+5959+The modular service infrastructure avoids exposing `pkgs` as a module argument to service modules. Instead, derivations and builder functions are provided through lexical closure, making dependency relationships explicit and avoiding uncertainty about where dependencies come from.
6060+6161+### Benefits
6262+6363+- **Explicit dependencies**: Services declare what they need rather than implicitly depending on `pkgs`
6464+- **No interference**: Service modules can be reused in different contexts without assuming a specific `pkgs` instance. An unexpected `pkgs` version is not a failure mode anymore.
6565+- **Clarity**: With fewer ways to do things, there's no ambiguity about where dependencies come from (from the module, not the OS or service manager)
6666+6767+### Implementation
6868+6969+- **Portable layer**: Service modules in `portable/` do not receive `pkgs` as a module argument. Any required derivations must be provided by the caller.
7070+7171+- **Systemd integration**: The `systemd/system.nix` module imports `config-data.nix` as a function, providing `pkgs` in lexical closure:
7272+ ```nix
7373+ (import ../portable/config-data.nix { inherit pkgs; })
7474+ ```
7575+7676+- **Service modules**:
7777+ 1. Should explicitly declare their package dependencies as options rather than using `pkgs` defaults:
7878+ ```nix
7979+ {
8080+ # Bad: uses pkgs module argument
8181+ foo.package = mkOption {
8282+ default = pkgs.python3;
8383+ # ...
8484+ };
8585+ }
8686+ ```
8787+8888+ ```nix
8989+ {
9090+ # Good: caller provides the package
9191+ foo.package = mkOption {
9292+ type = types.package;
9393+ description = "Python package to use";
9494+ defaultText = lib.literalMD "The package that provided this module.";
9595+ };
9696+ }
9797+ ```
9898+9999+ 2. `passthru.services` can still provide a complete module using the package's lexical scope, making the module truly self-contained:
100100+101101+ **Package (`package.nix`):**
102102+ ```nix
103103+ {
104104+ lib,
105105+ writeScript,
106106+ runtimeShell,
107107+ # ... other dependencies
108108+ }:
109109+ stdenv.mkDerivation (finalAttrs: {
110110+ # ... package definition
111111+112112+ passthru.services.default = {
113113+ imports = [
114114+ (lib.modules.importApply ./service.nix {
115115+ inherit writeScript runtimeShell;
116116+ })
117117+ ];
118118+ someService.package = finalAttrs.finalPackage;
119119+ };
120120+ })
121121+ ```
122122+123123+ **Service module (`service.nix`):**
124124+ ```nix
125125+ # Non-module dependencies (importApply)
126126+ { writeScript, runtimeShell }:
127127+128128+ # Service module
129129+ {
130130+ lib,
131131+ config,
132132+ options,
133133+ ...
134134+ }:
135135+ {
136136+ # Service definition using writeScript, runtimeShell from lexical scope
137137+ process.argv = [
138138+ (writeScript "wrapper" ''
139139+ #!${runtimeShell}
140140+ # ... wrapper logic
141141+ '')
142142+ # ... other args
143143+ ];
144144+ }
145145+ ```
···11# Tests in: ../../../../tests/modular-service-etc/test.nix
22+33+# Non-modular context provided by the modular services integration.
44+{ pkgs }:
55+26# Configuration data support for portable services
37# This module provides configData for services, enabling configuration reloading
48# without terminating and restarting the service process.
59{
610 lib,
77- pkgs,
811 ...
912}:
1013let
+45-1
nixos/modules/system/service/portable/lib.nix
···11{ lib, ... }:
22let
33- inherit (lib) concatLists mapAttrsToList showOption;
33+ inherit (lib)
44+ concatLists
55+ mapAttrsToList
66+ showOption
77+ types
88+ ;
49in
510rec {
611 flattenMapServicesConfigToList =
···3035 assertion = ass.assertion;
3136 }) config.assertions
3237 );
3838+3939+ /**
4040+ This is the entrypoint for the portable part of modular services.
4141+4242+ It provides the various options that are consumed by service manager implementations.
4343+4444+ # Inputs
4545+4646+ `serviceManagerPkgs`: A Nixpkgs instance which will be used for built-in logic such as converting `configData.<path>.text` to a store path.
4747+4848+ `extraRootModules`: Modules to be loaded into the "root" service submodule, but not into its sub-`services`. That's the modules' own responsibility.
4949+5050+ `extraRootSpecialArgs`: Fixed module arguments that are provided in a similar manner to `extraRootModules`.
5151+5252+ # Output
5353+5454+ An attribute set.
5555+5656+ `serviceSubmodule`: a Module System option type which is a `submodule` with the portable modules and this function's inputs loaded into it.
5757+ */
5858+ configure =
5959+ {
6060+ serviceManagerPkgs,
6161+ extraRootModules ? [ ],
6262+ extraRootSpecialArgs ? { },
6363+ }:
6464+ let
6565+ modules = [
6666+ (lib.modules.importApply ./service.nix { pkgs = serviceManagerPkgs; })
6767+ ];
6868+ serviceSubmodule = types.submoduleWith {
6969+ class = "service";
7070+ modules = modules ++ extraRootModules;
7171+ specialArgs = extraRootSpecialArgs;
7272+ };
7373+ in
7474+ {
7575+ inherit serviceSubmodule;
7676+ };
3377}
+8-2
nixos/modules/system/service/portable/service.nix
···11+# Non-module arguments
22+# These are separate from the module arguments to avoid implicit dependencies.
33+# This makes service modules self-contains, allowing mixing of Nixpkgs versions.
44+{ pkgs }:
55+66+# The module
17{
28 lib,
39 ...
···1218 imports = [
1319 ../../../../../modules/generic/meta-maintainers.nix
1420 ../../../misc/assertions.nix
1515- ./config-data.nix
2121+ (lib.modules.importApply ./config-data.nix { inherit pkgs; })
1622 ];
1723 options = {
1824 services = mkOption {
1925 type = types.attrsOf (
2026 types.submoduleWith {
2127 modules = [
2222- ./service.nix
2828+ (lib.modules.importApply ./service.nix { inherit pkgs; })
2329 ];
2430 }
2531 );
···33{
44 config,
55 lib,
66- pkgs,
76 ...
87}:
98let
···1615 python-http-server = {
1716 package = mkOption {
1817 type = types.package;
1919- default = pkgs.python3;
2018 description = "Python package to use for the web server";
2119 };
2220···4644 ];
47454846 configData = {
4949- # This should probably just be {} if we were to put this module in production.
5050- "webroot" = lib.mkDefault {
5151- source = pkgs.runCommand "default-webroot" { } ''
5252- mkdir -p $out
5353- cat > $out/index.html << 'EOF'
5454- <!DOCTYPE html>
5555- <html>
5656- <head><title>Python Web Server</title></head>
5757- <body>
5858- <h1>Welcome to the Python Web Server</h1>
5959- <p>Serving from port ${toString config.python-http-server.port}</p>
6060- </body>
6161- </html>
6262- EOF
6363- '';
4747+ "webroot" = {
4848+ # Enable only if directory is set to use this path
4949+ enable = lib.mkDefault (config.python-http-server.directory == config.configData."webroot".path);
6450 };
6551 };
6652 };
+31-4
nixos/tests/modular-service-etc/test.nix
···1212 nodes = {
1313 server =
1414 { pkgs, ... }:
1515+ let
1616+ # Normally the package services.default attribute combines this, but we
1717+ # don't have that, because this is not a production service. Should it be?
1818+ python-http-server = {
1919+ imports = [ ./python-http-server.nix ];
2020+ python-http-server.package = pkgs.python3;
2121+ };
2222+ in
1523 {
1624 system.services.webserver = {
1725 # The python web server is simple enough that it doesn't need a reload signal.
1826 # Other services may need to receive a signal in order to re-read what's in `configData`.
1919- imports = [ ./python-http-server.nix ];
2727+ imports = [ python-http-server ];
2028 python-http-server = {
2129 port = 8080;
2230 };
23313232+ configData = {
3333+ "webroot" = {
3434+ source = pkgs.runCommand "webroot" { } ''
3535+ mkdir -p $out
3636+ cat > $out/index.html << 'EOF'
3737+ <!DOCTYPE html>
3838+ <html>
3939+ <head><title>Python Web Server</title></head>
4040+ <body>
4141+ <h1>Welcome to the Python Web Server</h1>
4242+ <p>Serving from port 8080</p>
4343+ </body>
4444+ </html>
4545+ EOF
4646+ '';
4747+ };
4848+ };
4949+2450 # Add a sub-service
2551 services.api = {
2626- imports = [ ./python-http-server.nix ];
5252+ imports = [ python-http-server ];
2753 python-http-server = {
2854 port = 8081;
2955 };
···147173 print(f"Before switch - webserver PID: {webserver_pid}, api PID: {api_pid}")
148174149175 # Switch to the specialisation with updated content
150150- switch_output = server.succeed("/run/current-system/specialisation/updated/bin/switch-to-configuration test")
151151- print(f"Switch output: {switch_output}")
176176+ # Capture both stdout and stderr, and show stderr in real-time for debugging
177177+ switch_output = server.succeed("/run/current-system/specialisation/updated/bin/switch-to-configuration test 2>&1 | tee /dev/stderr")
178178+ print(f"Switch output (stdout+stderr): {switch_output}")
152179153180 # Verify services are not mentioned in the switch output (indicating they weren't touched)
154181 assert "webserver.service" not in switch_output, f"webserver.service was mentioned in switch output: {switch_output}"
+2
nixos/tests/php/fpm-modular.nix
···11+# Run with:
22+# nix-build -A nixosTests.php.fpm-modular
13{ lib, php, ... }:
24{
35 name = "php-${php.version}-fpm-modular-nginx-test";
···7272 # (dependencies without cuda support).
7373 # Instead we should rely on overlays and nixpkgsFun.
7474 # (@SomeoneSerge)
7575- _tritonEffective ?
7676- if cudaSupport then
7777- triton-cuda
7878- else if rocmSupport then
7979- rocmPackages.triton
8080- else
8181- triton,
7575+ _tritonEffective ? if cudaSupport then triton-cuda else triton,
8276 triton-cuda,
83778478 # Disable MKLDNN on aarch64-darwin, it negatively impacts performance,
···11+From 9e4e58b647c17c5fa098c8a74e221f88d3cb1a43 Mon Sep 17 00:00:00 2001
22+From: Luna Nova <git@lunnova.dev>
33+Date: Sun, 24 Aug 2025 07:41:30 -0700
44+Subject: [PATCH] [AMD] Search HIP_PATH, hipconfig, and ROCM_PATH for
55+ libamdhip64
66+77+Search for libamdhip64 from HIP_PATH env var, hipconfig --path output,
88+and ROCM_PATH before looking in system-wide ldconfig or /opt/rocm.
99+1010+The system-wide ROCm path isn't guaranteed to be where the ROCm
1111+install we're building against is located, so follow typical ROCm
1212+lib behavior and look under env paths first.
1313+1414+This is especially important for non-FHS distros like NixOS
1515+where /opt/rocm never exists, but may be useful in more
1616+typical distros if multiple ROCm installs are present
1717+to ensure the right libamdhip64.so is picked up.
1818+---
1919+ third_party/amd/backend/driver.py | 28 ++++++++++++++++++++++++++++
2020+ 1 file changed, 28 insertions(+)
2121+2222+diff --git a/third_party/amd/backend/driver.py b/third_party/amd/backend/driver.py
2323+index af8e1a5c8097..57b0f7388c60 100644
2424+--- a/third_party/amd/backend/driver.py
2525++++ b/third_party/amd/backend/driver.py
2626+@@ -110,6 +110,34 @@ def _get_path_to_hip_runtime_dylib():
2727+ return f
2828+ paths.append(f)
2929+3030++ # HIP_PATH should point to HIP SDK root if set
3131++ env_hip_path = os.getenv("HIP_PATH")
3232++ if env_hip_path:
3333++ hip_lib_path = os.path.join(env_hip_path, "lib", lib_name)
3434++ if os.path.exists(hip_lib_path):
3535++ return hip_lib_path
3636++ paths.append(hip_lib_path)
3737++
3838++ # if available, `hipconfig --path` prints the HIP SDK root
3939++ try:
4040++ hip_root = subprocess.check_output(["hipconfig", "--path"]).decode().strip()
4141++ if hip_root:
4242++ hip_lib_path = os.path.join(hip_root, "lib", lib_name)
4343++ if os.path.exists(hip_lib_path):
4444++ return hip_lib_path
4545++ paths.append(hip_lib_path)
4646++ except (subprocess.CalledProcessError, FileNotFoundError):
4747++ # hipconfig may not be available
4848++ pass
4949++
5050++ # ROCm lib dir based on env var
5151++ env_rocm_path = os.getenv("ROCM_PATH")
5252++ if env_rocm_path:
5353++ rocm_lib_path = os.path.join(env_rocm_path, "lib", lib_name)
5454++ if os.path.exists(rocm_lib_path):
5555++ return rocm_lib_path
5656++ paths.append(rocm_lib_path)
5757++
5858+ # Afterwards try to search the loader dynamic library resolution paths.
5959+ libs = subprocess.check_output(["/sbin/ldconfig", "-p"]).decode(errors="ignore")
6060+ # each line looks like the following: