+28
nixos/doc/manual/default.nix
+28
nixos/doc/manual/default.nix
···
14
14
inherit (pkgs) buildPackages runCommand docbook_xsl_ns;
15
15
16
16
inherit (pkgs.lib)
17
+
evalModules
17
18
hasPrefix
18
19
removePrefix
19
20
flip
···
116
117
${testOptionsDoc.optionsJSON}/${common.outputPath}/options.json
117
118
sed -e '/@PYTHON_MACHINE_METHODS@/ {' -e 'r ${testDriverMachineDocstrings}/machine-methods.md' -e 'd' -e '}' \
118
119
-i ./development/writing-nixos-tests.section.md
120
+
substituteInPlace ./development/modular-services.md \
121
+
--replace-fail \
122
+
'@PORTABLE_SERVICE_OPTIONS@' \
123
+
${portableServiceOptions.optionsJSON}/${common.outputPath}/options.json
124
+
substituteInPlace ./development/modular-services.md \
125
+
--replace-fail \
126
+
'@SYSTEMD_SERVICE_OPTIONS@' \
127
+
${systemdServiceOptions.optionsJSON}/${common.outputPath}/options.json
119
128
'';
129
+
130
+
portableServiceOptions = buildPackages.nixosOptionsDoc {
131
+
inherit (evalModules { modules = [ ../../modules/system/service/portable/service.nix ]; }) options;
132
+
inherit revision warningsAreErrors;
133
+
transformOptions = opt: opt // {
134
+
# Clean up declaration sites to not refer to the NixOS source tree.
135
+
declarations = map stripAnyPrefixes opt.declarations;
136
+
};
137
+
};
138
+
139
+
systemdServiceOptions = buildPackages.nixosOptionsDoc {
140
+
inherit (evalModules { modules = [ ../../modules/system/service/systemd/service.nix ]; }) options;
141
+
# TODO: filter out options that are not systemd-specific, maybe also change option prefix to just `service-opt-`?
142
+
inherit revision warningsAreErrors;
143
+
transformOptions = opt: opt // {
144
+
# Clean up declaration sites to not refer to the NixOS source tree.
145
+
declarations = map stripAnyPrefixes opt.declarations;
146
+
};
147
+
};
120
148
121
149
in
122
150
rec {
+1
nixos/doc/manual/development/development.md
+1
nixos/doc/manual/development/development.md
+91
nixos/doc/manual/development/modular-services.md
+91
nixos/doc/manual/development/modular-services.md
···
1
+
2
+
# Modular Services {#modular-services}
3
+
4
+
Status: in development. This functionality is new in NixOS 25.05, and significant changes should be expected. We'd love to hear your feedback in <https://github.com/NixOS/nixpkgs/pull/372170>
5
+
6
+
Traditionally, NixOS services were defined using sets of options *in* modules, not *as* modules. This made them non-modular, resulting in problems with composability, reuse, and portability.
7
+
8
+
A *modular service* is a [module] that defines values for a core set of options, including which program to run.
9
+
10
+
NixOS provides two options into which such modules can be plugged:
11
+
12
+
- `system.services.<name>`
13
+
- an option for user services (TBD)
14
+
15
+
Crucially, these options have the type [`attrsOf`] [`submodule`].
16
+
The name of the service is the attribute name corresponding to `attrsOf`.
17
+
<!-- ^ This is how composition is *always* provided, instead of a difficult thing (but this is reference docs, not a changelog) -->
18
+
The `submodule` is pre-loaded with two modules:
19
+
- a generic module that is intended to be portable
20
+
- a module with systemd-specific options, whose values or defaults derive from the generic module's option values.
21
+
22
+
So note that the default value of `system.services.<name>` is not a complete service. It requires that the user provide a value, and this is typically done by importing a module. For example:
23
+
24
+
<!-- Not using typical example syntax, because reading this is *not* optional, and should it should not be folded closed. -->
25
+
```nix
26
+
{
27
+
system.services.httpd = {
28
+
imports = [ nixpkgs.modules.services.foo ];
29
+
foo.settings = {
30
+
# ...
31
+
};
32
+
};
33
+
}
34
+
```
35
+
36
+
## Portability {#modular-service-portability}
37
+
38
+
It is possible to write service modules that are portable. This is done by either avoiding the `systemd` option tree, or by defining process-manager-specific definitions in an optional way:
39
+
40
+
```nix
41
+
{ config, options, lib, ... }: {
42
+
_class = "service";
43
+
config = {
44
+
process.executable = "${lib.getExe config.foo.program}";
45
+
} // lib.optionalAttrs (options?systemd) {
46
+
# ... systemd-specific definitions ...
47
+
};
48
+
}
49
+
```
50
+
51
+
This way, the module can be loaded into a configuration manager that does not use systemd, and the `systemd` definitions will be ignored.
52
+
Similarly, other configuration managers can declare their own options for services to customize.
53
+
54
+
## Composition and Ownership {#modular-service-composition}
55
+
56
+
Compared to traditional services, modular services are inherently more composable, by virtue of being modules and receiving a user-provided name when imported.
57
+
However, composition can not end there, because services need to be able to interact with each other.
58
+
This can be achieved in two ways:
59
+
1. Users can link services together by providing the necessary NixOS configuration.
60
+
2. Services can be compositions of other services.
61
+
62
+
These aren't mutually exclusive. In fact, it is a good practice when developing services to first write them as individual services, and then compose them into a higher-level composition. Each of these services is a valid modular service, including their composition.
63
+
64
+
## Migration {#modular-service-migration}
65
+
66
+
Many services could be migrated to the modular service system, but even when the modular service system is mature, it is not necessary to migrate all services.
67
+
For instance, many system-wide services are a mandatory part of a desktop system, and it doesn't make sense to have multiple instances of them.
68
+
Moving their logic into separate Nix files may still be beneficial for the efficient evaluation of configurations that don't use those services, but that is a rather minor benefit, unless modular services potentially become the standard way to define services.
69
+
70
+
<!-- TODO example of a single-instance service -->
71
+
72
+
## Portable Service Options {#modular-service-options-portable}
73
+
74
+
```{=include=} options
75
+
id-prefix: service-opt-
76
+
list-id: service-options
77
+
source: @PORTABLE_SERVICE_OPTIONS@
78
+
```
79
+
80
+
## Systemd-specific Service Options {#modular-service-options-systemd}
81
+
82
+
```{=include=} options
83
+
id-prefix: systemd-service-opt-
84
+
list-id: systemd-service-options
85
+
source: @SYSTEMD_SERVICE_OPTIONS@
86
+
```
87
+
88
+
[module]: https://nixos.org/manual/nixpkgs/stable/index.html#module-system
89
+
<!-- TODO: more anchors -->
90
+
[`attrsOf`]: #sec-option-types-composed
91
+
[`submodule`]: #sec-option-types-submodule
+18
nixos/doc/manual/redirects.json
+18
nixos/doc/manual/redirects.json
···
2
2
"book-nixos-manual": [
3
3
"index.html#book-nixos-manual"
4
4
],
5
+
"modular-service-composition": [
6
+
"index.html#modular-service-composition"
7
+
],
8
+
"modular-service-migration": [
9
+
"index.html#modular-service-migration"
10
+
],
11
+
"modular-service-options-portable": [
12
+
"index.html#modular-service-options-portable"
13
+
],
14
+
"modular-service-options-systemd": [
15
+
"index.html#modular-service-options-systemd"
16
+
],
17
+
"modular-service-portability": [
18
+
"index.html#modular-service-portability"
19
+
],
20
+
"modular-services": [
21
+
"index.html#modular-services"
22
+
],
5
23
"module-services-anubis": [
6
24
"index.html#module-services-anubis"
7
25
],
+1
-1
pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py
+1
-1
pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py
···
114
114
- The first element of an identifier's redirects list must denote its current location.
115
115
"""
116
116
xref_targets = {}
117
-
ignored_identifier_patterns = ("opt-", "auto-generated-", "function-library-")
117
+
ignored_identifier_patterns = ("opt-", "auto-generated-", "function-library-", "service-opt-", "systemd-service-opt")
118
118
for id, target in initial_xref_targets.items():
119
119
# filter out automatically generated identifiers from module options and library documentation
120
120
if id.startswith(ignored_identifier_patterns):