···72 The binary name remains `webfontkitgenerator`.
73 The `webfontkitgenerator` package is an alias to `webfont-bundler`.
740075- `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/).
7677- `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.
···94 make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_3;` in your configuration.
9596- `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.
009798- `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/
99
···72 The binary name remains `webfontkitgenerator`.
73 The `webfontkitgenerator` package is an alias to `webfont-bundler`.
7475+- `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}"`.
76+77- `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/).
7879- `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.
···96 make the required changes to your database, if needed, then upgrade by setting `services.netbox.package = pkgs.netbox_4_3;` in your configuration.
9798- `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.
99+100+- `rocmPackages.triton` has been removed in favor of `python3Packages.triton`.
101102- `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/
103
···8586## Writing and Reviewing a Modular Service {#modular-service-review}
8788-Refer to the contributor documentation in [`nixos/README-modular-services.md`](https://github.com/NixOS/nixpkgs/blob/master/nixos/README-modular-services.md).
008990## Portable Service Options {#modular-service-options-portable}
91
···8586## Writing and Reviewing a Modular Service {#modular-service-review}
8788+A typical service module consists of the following:
89+90+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).
9192## Portable Service Options {#modular-service-options-portable}
93
+90
nixos/modules/system/service/README.md
···5354- **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.
55 Per-service user creation is still TBD.
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
···5354- **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.
55 Per-service user creation is still TBD.
56+57+## No `pkgs` module argument
58+59+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.
60+61+### Benefits
62+63+- **Explicit dependencies**: Services declare what they need rather than implicitly depending on `pkgs`
64+- **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.
65+- **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)
66+67+### Implementation
68+69+- **Portable layer**: Service modules in `portable/` do not receive `pkgs` as a module argument. Any required derivations must be provided by the caller.
70+71+- **Systemd integration**: The `systemd/system.nix` module imports `config-data.nix` as a function, providing `pkgs` in lexical closure:
72+ ```nix
73+ (import ../portable/config-data.nix { inherit pkgs; })
74+ ```
75+76+- **Service modules**:
77+ 1. Should explicitly declare their package dependencies as options rather than using `pkgs` defaults:
78+ ```nix
79+ {
80+ # Bad: uses pkgs module argument
81+ foo.package = mkOption {
82+ default = pkgs.python3;
83+ # ...
84+ };
85+ }
86+ ```
87+88+ ```nix
89+ {
90+ # Good: caller provides the package
91+ foo.package = mkOption {
92+ type = types.package;
93+ description = "Python package to use";
94+ defaultText = lib.literalMD "The package that provided this module.";
95+ };
96+ }
97+ ```
98+99+ 2. `passthru.services` can still provide a complete module using the package's lexical scope, making the module truly self-contained:
100+101+ **Package (`package.nix`):**
102+ ```nix
103+ {
104+ lib,
105+ writeScript,
106+ runtimeShell,
107+ # ... other dependencies
108+ }:
109+ stdenv.mkDerivation (finalAttrs: {
110+ # ... package definition
111+112+ passthru.services.default = {
113+ imports = [
114+ (lib.modules.importApply ./service.nix {
115+ inherit writeScript runtimeShell;
116+ })
117+ ];
118+ someService.package = finalAttrs.finalPackage;
119+ };
120+ })
121+ ```
122+123+ **Service module (`service.nix`):**
124+ ```nix
125+ # Non-module dependencies (importApply)
126+ { writeScript, runtimeShell }:
127+128+ # Service module
129+ {
130+ lib,
131+ config,
132+ options,
133+ ...
134+ }:
135+ {
136+ # Service definition using writeScript, runtimeShell from lexical scope
137+ process.argv = [
138+ (writeScript "wrapper" ''
139+ #!${runtimeShell}
140+ # ... wrapper logic
141+ '')
142+ # ... other args
143+ ];
144+ }
145+ ```
···1# Tests in: ../../../../tests/modular-service-etc/test.nix
00002# Configuration data support for portable services
3# This module provides configData for services, enabling configuration reloading
4# without terminating and restarting the service process.
5{
6 lib,
7- pkgs,
8 ...
9}:
10let
···1# Tests in: ../../../../tests/modular-service-etc/test.nix
2+3+# Non-modular context provided by the modular services integration.
4+{ pkgs }:
5+6# Configuration data support for portable services
7# This module provides configData for services, enabling configuration reloading
8# without terminating and restarting the service process.
9{
10 lib,
011 ...
12}:
13let
···1{ lib, ... }:
2let
3+ inherit (lib)
4+ concatLists
5+ mapAttrsToList
6+ showOption
7+ types
8+ ;
9in
10rec {
11 flattenMapServicesConfigToList =
···35 assertion = ass.assertion;
36 }) config.assertions
37 );
38+39+ /**
40+ This is the entrypoint for the portable part of modular services.
41+42+ It provides the various options that are consumed by service manager implementations.
43+44+ # Inputs
45+46+ `serviceManagerPkgs`: A Nixpkgs instance which will be used for built-in logic such as converting `configData.<path>.text` to a store path.
47+48+ `extraRootModules`: Modules to be loaded into the "root" service submodule, but not into its sub-`services`. That's the modules' own responsibility.
49+50+ `extraRootSpecialArgs`: Fixed module arguments that are provided in a similar manner to `extraRootModules`.
51+52+ # Output
53+54+ An attribute set.
55+56+ `serviceSubmodule`: a Module System option type which is a `submodule` with the portable modules and this function's inputs loaded into it.
57+ */
58+ configure =
59+ {
60+ serviceManagerPkgs,
61+ extraRootModules ? [ ],
62+ extraRootSpecialArgs ? { },
63+ }:
64+ let
65+ modules = [
66+ (lib.modules.importApply ./service.nix { pkgs = serviceManagerPkgs; })
67+ ];
68+ serviceSubmodule = types.submoduleWith {
69+ class = "service";
70+ modules = modules ++ extraRootModules;
71+ specialArgs = extraRootSpecialArgs;
72+ };
73+ in
74+ {
75+ inherit serviceSubmodule;
76+ };
77}
···3{
4 config,
5 lib,
6- pkgs,
7 ...
8}:
9let
···16 python-http-server = {
17 package = mkOption {
18 type = types.package;
19- default = pkgs.python3;
20 description = "Python package to use for the web server";
21 };
22···46 ];
4748 configData = {
49- # This should probably just be {} if we were to put this module in production.
50- "webroot" = lib.mkDefault {
51- source = pkgs.runCommand "default-webroot" { } ''
52- mkdir -p $out
53- cat > $out/index.html << 'EOF'
54- <!DOCTYPE html>
55- <html>
56- <head><title>Python Web Server</title></head>
57- <body>
58- <h1>Welcome to the Python Web Server</h1>
59- <p>Serving from port ${toString config.python-http-server.port}</p>
60- </body>
61- </html>
62- EOF
63- '';
64 };
65 };
66 };
···3{
4 config,
5 lib,
06 ...
7}:
8let
···15 python-http-server = {
16 package = mkOption {
17 type = types.package;
018 description = "Python package to use for the web server";
19 };
20···44 ];
4546 configData = {
47+ "webroot" = {
48+ # Enable only if directory is set to use this path
49+ enable = lib.mkDefault (config.python-http-server.directory == config.configData."webroot".path);
00000000000050 };
51 };
52 };
+31-4
nixos/tests/modular-service-etc/test.nix
···12 nodes = {
13 server =
14 { pkgs, ... }:
0000000015 {
16 system.services.webserver = {
17 # The python web server is simple enough that it doesn't need a reload signal.
18 # Other services may need to receive a signal in order to re-read what's in `configData`.
19- imports = [ ./python-http-server.nix ];
20 python-http-server = {
21 port = 8080;
22 };
2300000000000000000024 # Add a sub-service
25 services.api = {
26- imports = [ ./python-http-server.nix ];
27 python-http-server = {
28 port = 8081;
29 };
···147 print(f"Before switch - webserver PID: {webserver_pid}, api PID: {api_pid}")
148149 # Switch to the specialisation with updated content
150- switch_output = server.succeed("/run/current-system/specialisation/updated/bin/switch-to-configuration test")
151- print(f"Switch output: {switch_output}")
0152153 # Verify services are not mentioned in the switch output (indicating they weren't touched)
154 assert "webserver.service" not in switch_output, f"webserver.service was mentioned in switch output: {switch_output}"
···12 nodes = {
13 server =
14 { pkgs, ... }:
15+ let
16+ # Normally the package services.default attribute combines this, but we
17+ # don't have that, because this is not a production service. Should it be?
18+ python-http-server = {
19+ imports = [ ./python-http-server.nix ];
20+ python-http-server.package = pkgs.python3;
21+ };
22+ in
23 {
24 system.services.webserver = {
25 # The python web server is simple enough that it doesn't need a reload signal.
26 # Other services may need to receive a signal in order to re-read what's in `configData`.
27+ imports = [ python-http-server ];
28 python-http-server = {
29 port = 8080;
30 };
3132+ configData = {
33+ "webroot" = {
34+ source = pkgs.runCommand "webroot" { } ''
35+ mkdir -p $out
36+ cat > $out/index.html << 'EOF'
37+ <!DOCTYPE html>
38+ <html>
39+ <head><title>Python Web Server</title></head>
40+ <body>
41+ <h1>Welcome to the Python Web Server</h1>
42+ <p>Serving from port 8080</p>
43+ </body>
44+ </html>
45+ EOF
46+ '';
47+ };
48+ };
49+50 # Add a sub-service
51 services.api = {
52+ imports = [ python-http-server ];
53 python-http-server = {
54 port = 8081;
55 };
···173 print(f"Before switch - webserver PID: {webserver_pid}, api PID: {api_pid}")
174175 # Switch to the specialisation with updated content
176+ # Capture both stdout and stderr, and show stderr in real-time for debugging
177+ switch_output = server.succeed("/run/current-system/specialisation/updated/bin/switch-to-configuration test 2>&1 | tee /dev/stderr")
178+ print(f"Switch output (stdout+stderr): {switch_output}")
179180 # Verify services are not mentioned in the switch output (indicating they weren't touched)
181 assert "webserver.service" not in switch_output, f"webserver.service was mentioned in switch output: {switch_output}"
+2
nixos/tests/php/fpm-modular.nix
···001{ lib, php, ... }:
2{
3 name = "php-${php.version}-fpm-modular-nginx-test";
···1+# Run with:
2+# nix-build -A nixosTests.php.fpm-modular
3{ lib, php, ... }:
4{
5 name = "php-${php.version}-fpm-modular-nginx-test";
···72 # (dependencies without cuda support).
73 # Instead we should rely on overlays and nixpkgsFun.
74 # (@SomeoneSerge)
75- _tritonEffective ?
76- if cudaSupport then
77- triton-cuda
78- else if rocmSupport then
79- rocmPackages.triton
80- else
81- triton,
82 triton-cuda,
8384 # Disable MKLDNN on aarch64-darwin, it negatively impacts performance,
···72 # (dependencies without cuda support).
73 # Instead we should rely on overlays and nixpkgsFun.
74 # (@SomeoneSerge)
75+ _tritonEffective ? if cudaSupport then triton-cuda else triton,
00000076 triton-cuda,
7778 # Disable MKLDNN on aarch64-darwin, it negatively impacts performance,
···1+From 9e4e58b647c17c5fa098c8a74e221f88d3cb1a43 Mon Sep 17 00:00:00 2001
2+From: Luna Nova <git@lunnova.dev>
3+Date: Sun, 24 Aug 2025 07:41:30 -0700
4+Subject: [PATCH] [AMD] Search HIP_PATH, hipconfig, and ROCM_PATH for
5+ libamdhip64
6+7+Search for libamdhip64 from HIP_PATH env var, hipconfig --path output,
8+and ROCM_PATH before looking in system-wide ldconfig or /opt/rocm.
9+10+The system-wide ROCm path isn't guaranteed to be where the ROCm
11+install we're building against is located, so follow typical ROCm
12+lib behavior and look under env paths first.
13+14+This is especially important for non-FHS distros like NixOS
15+where /opt/rocm never exists, but may be useful in more
16+typical distros if multiple ROCm installs are present
17+to ensure the right libamdhip64.so is picked up.
18+---
19+ third_party/amd/backend/driver.py | 28 ++++++++++++++++++++++++++++
20+ 1 file changed, 28 insertions(+)
21+22+diff --git a/third_party/amd/backend/driver.py b/third_party/amd/backend/driver.py
23+index af8e1a5c8097..57b0f7388c60 100644
24+--- a/third_party/amd/backend/driver.py
25++++ b/third_party/amd/backend/driver.py
26+@@ -110,6 +110,34 @@ def _get_path_to_hip_runtime_dylib():
27+ return f
28+ paths.append(f)
29+30++ # HIP_PATH should point to HIP SDK root if set
31++ env_hip_path = os.getenv("HIP_PATH")
32++ if env_hip_path:
33++ hip_lib_path = os.path.join(env_hip_path, "lib", lib_name)
34++ if os.path.exists(hip_lib_path):
35++ return hip_lib_path
36++ paths.append(hip_lib_path)
37++
38++ # if available, `hipconfig --path` prints the HIP SDK root
39++ try:
40++ hip_root = subprocess.check_output(["hipconfig", "--path"]).decode().strip()
41++ if hip_root:
42++ hip_lib_path = os.path.join(hip_root, "lib", lib_name)
43++ if os.path.exists(hip_lib_path):
44++ return hip_lib_path
45++ paths.append(hip_lib_path)
46++ except (subprocess.CalledProcessError, FileNotFoundError):
47++ # hipconfig may not be available
48++ pass
49++
50++ # ROCm lib dir based on env var
51++ env_rocm_path = os.getenv("ROCM_PATH")
52++ if env_rocm_path:
53++ rocm_lib_path = os.path.join(env_rocm_path, "lib", lib_name)
54++ if os.path.exists(rocm_lib_path):
55++ return rocm_lib_path
56++ paths.append(rocm_lib_path)
57++
58+ # Afterwards try to search the loader dynamic library resolution paths.
59+ libs = subprocess.check_output(["/sbin/ldconfig", "-p"]).decode(errors="ignore")
60+ # each line looks like the following:
···264 );
265 mpi = self.openmpi;
266267- triton-llvm = triton-llvm.overrideAttrs {
268- src = fetchFromGitHub {
269- owner = "llvm";
270- repo = "llvm-project";
271- # make sure this matches triton llvm rel branch hash for now
272- # https://github.com/triton-lang/triton/blob/release/3.2.x/cmake/llvm-hash.txt
273- rev = "86b69c31642e98f8357df62c09d118ad1da4e16a";
274- hash = "sha256-W/mQwaLGx6/rIBjdzUTIbWrvGjdh7m4s15f70fQ1/hE=";
275- };
276- pname = "triton-llvm-rocm";
277- patches = [ ]; # FIXME: https://github.com/llvm/llvm-project//commit/84837e3cc1cf17ed71580e3ea38299ed2bfaa5f6.patch doesn't apply, may need to rebase
278- };
279-280- triton = pyPackages.callPackage ./triton { rocmPackages = self; };
281-282 ## Meta ##
283 # Emulate common ROCm meta layout
284 # These are mainly for users. I strongly suggest NOT using these in nixpkgs derivations
···454 };
455 }
456 // lib.optionalAttrs config.allowAliases {
0000457 rocm-thunk = throw ''
458 'rocm-thunk' has been removed. It's now part of the ROCm runtime.
459 ''; # Added 2025-3-16
···264 );
265 mpi = self.openmpi;
266000000000000000267 ## Meta ##
268 # Emulate common ROCm meta layout
269 # These are mainly for users. I strongly suggest NOT using these in nixpkgs derivations
···439 };
440 }
441 // lib.optionalAttrs config.allowAliases {
442+ triton = throw ''
443+ 'rocmPackages.triton' has been removed. Please use python3Packages.triton
444+ ''; # Added 2025-08-24
445+446 rocm-thunk = throw ''
447 'rocm-thunk' has been removed. It's now part of the ROCm runtime.
448 ''; # Added 2025-3-16