summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Hensing <robert@roberthensing.nl>2025-01-08 16:22:49 +0100
committerRobert Hensing <robert@roberthensing.nl>2025-01-08 17:26:21 +0100
commit1ee1ac47cc528474fecba733adab4b436b12b79f (patch)
treef8040db500d03e63f6ab470b03fc8d007a21f931
parentghostunnel.services.default: init (diff)
downloadnixpkgs-1ee1ac47cc528474fecba733adab4b436b12b79f.tar.gz
nixos/doc: Add modular services sectionorigin/modular-services
-rw-r--r--nixos/doc/manual/default.nix28
-rw-r--r--nixos/doc/manual/development/development.md1
-rw-r--r--nixos/doc/manual/development/modular-services.md91
-rw-r--r--nixos/doc/manual/redirects.json18
-rw-r--r--pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py2
5 files changed, 139 insertions, 1 deletions
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index 5b171420b24d..56e0ce5826eb 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -13,6 +13,7 @@ let
inherit (pkgs) buildPackages runCommand docbook_xsl_ns;
inherit (pkgs.lib)
+ evalModules
hasPrefix
removePrefix
flip
@@ -97,8 +98,35 @@ let
${testOptionsDoc.optionsJSON}/${common.outputPath}/options.json
sed -e '/@PYTHON_MACHINE_METHODS@/ {' -e 'r ${testDriverMachineDocstrings}/machine-methods.md' -e 'd' -e '}' \
-i ./development/writing-nixos-tests.section.md
+ substituteInPlace ./development/modular-services.md \
+ --replace-fail \
+ '@PORTABLE_SERVICE_OPTIONS@' \
+ ${portableServiceOptions.optionsJSON}/${common.outputPath}/options.json
+ substituteInPlace ./development/modular-services.md \
+ --replace-fail \
+ '@SYSTEMD_SERVICE_OPTIONS@' \
+ ${systemdServiceOptions.optionsJSON}/${common.outputPath}/options.json
'';
+ portableServiceOptions = buildPackages.nixosOptionsDoc {
+ inherit (evalModules { modules = [ ../../modules/system/service/portable/service.nix ]; }) options;
+ inherit revision warningsAreErrors;
+ transformOptions = opt: opt // {
+ # Clean up declaration sites to not refer to the NixOS source tree.
+ declarations = map stripAnyPrefixes opt.declarations;
+ };
+ };
+
+ systemdServiceOptions = buildPackages.nixosOptionsDoc {
+ inherit (evalModules { modules = [ ../../modules/system/service/systemd/service.nix ]; }) options;
+ # TODO: filter out options that are not systemd-specific, maybe also change option prefix to just `service-opt-`?
+ inherit revision warningsAreErrors;
+ transformOptions = opt: opt // {
+ # Clean up declaration sites to not refer to the NixOS source tree.
+ declarations = map stripAnyPrefixes opt.declarations;
+ };
+ };
+
in rec {
inherit (optionsDoc) optionsJSON optionsNix optionsDocBook;
diff --git a/nixos/doc/manual/development/development.md b/nixos/doc/manual/development/development.md
index 76f405c3b29c..37762afb41da 100644
--- a/nixos/doc/manual/development/development.md
+++ b/nixos/doc/manual/development/development.md
@@ -12,4 +12,5 @@ writing-documentation.chapter.md
nixos-tests.chapter.md
developing-the-test-driver.chapter.md
testing-installer.chapter.md
+modular-services.md
```
diff --git a/nixos/doc/manual/development/modular-services.md b/nixos/doc/manual/development/modular-services.md
new file mode 100644
index 000000000000..d0443081a488
--- /dev/null
+++ b/nixos/doc/manual/development/modular-services.md
@@ -0,0 +1,91 @@
+
+# Modular Services {#modular-services}
+
+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 <!-- FIXME PR link -->
+
+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.
+
+A *modular service* is a [module] that defines values for a core set of options, including which program to run.
+
+NixOS provides two options into which such modules can be plugged:
+
+- `system.services.<name>`
+- an option for user services (TBD)
+
+Crucially, these options have the type [`attrsOf`] [`submodule`].
+The name of the service is the attribute name corresponding to `attrsOf`.
+<!-- ^ This is how composition is *always* provided, instead of a difficult thing (but this is reference docs, not a changelog) -->
+The `submodule` is pre-loaded with two modules:
+- a generic module that is intended to be portable
+- a module with systemd-specific options, whose values or defaults derive from the generic module's option values.
+
+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:
+
+<!-- Not using typical example syntax, because reading this is *not* optional, and should it should not be folded closed. -->
+```nix
+{
+ system.services.httpd = {
+ imports = [ nixpkgs.modules.services.foo ];
+ foo.settings = {
+ # ...
+ };
+ };
+}
+```
+
+## Portability {#modular-service-portability}
+
+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:
+
+```nix
+{ config, options, lib, ... }: {
+ _class = "service";
+ config = {
+ process.executable = "${lib.getExe config.foo.program}";
+ } // lib.optionalAttrs (options?systemd) {
+ # ... systemd-specific definitions ...
+ };
+}
+```
+
+This way, the module can be loaded into a configuration manager that does not use systemd, and the `systemd` definitions will be ignored.
+Similarly, other configuration managers can declare their own options for services to customize.
+
+## Composition and Ownership {#modular-service-composition}
+
+Compared to traditional services, modular services are inherently more composable, by virtue of being modules and receiving a user-provided name when imported.
+However, composition can not end there, because services need to be able to interact with each other.
+This can be achieved in two ways:
+1. Users can link services together by providing the necessary NixOS configuration.
+2. Services can be compositions of other services.
+
+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.
+
+## Migration {#modular-service-migration}
+
+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.
+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.
+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.
+
+<!-- TODO example of a single-instance service -->
+
+## Portable Service Options {#modular-service-options-portable}
+
+```{=include=} options
+id-prefix: service-opt-
+list-id: service-options
+source: @PORTABLE_SERVICE_OPTIONS@
+```
+
+## Systemd-specific Service Options {#modular-service-options-systemd}
+
+```{=include=} options
+id-prefix: systemd-service-opt-
+list-id: systemd-service-options
+source: @SYSTEMD_SERVICE_OPTIONS@
+```
+
+[module]: https://nixos.org/manual/nixpkgs/stable/index.html#module-system
+<!-- TODO: more anchors -->
+[`attrsOf`]: #sec-option-types-composed
+[`submodule`]: #sec-option-types-submodule
diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json
index a7ebb49b62b8..1a3ce92bf7fd 100644
--- a/nixos/doc/manual/redirects.json
+++ b/nixos/doc/manual/redirects.json
@@ -2,6 +2,24 @@
"book-nixos-manual": [
"index.html#book-nixos-manual"
],
+ "modular-service-composition": [
+ "index.html#modular-service-composition"
+ ],
+ "modular-service-migration": [
+ "index.html#modular-service-migration"
+ ],
+ "modular-service-options-portable": [
+ "index.html#modular-service-options-portable"
+ ],
+ "modular-service-options-systemd": [
+ "index.html#modular-service-options-systemd"
+ ],
+ "modular-service-portability": [
+ "index.html#modular-service-portability"
+ ],
+ "modular-services": [
+ "index.html#modular-services"
+ ],
"module-services-crab-hole": [
"index.html#module-services-crab-hole"
],
diff --git a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py
index 1a891a1af238..554295aff50b 100644
--- a/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py
+++ b/pkgs/by-name/ni/nixos-render-docs/src/nixos_render_docs/redirects.py
@@ -114,7 +114,7 @@ class Redirects:
- The first element of an identifier's redirects list must denote its current location.
"""
xref_targets = {}
- ignored_identifier_patterns = ("opt-", "auto-generated-", "function-library-")
+ ignored_identifier_patterns = ("opt-", "auto-generated-", "function-library-", "service-opt-", "systemd-service-opt")
for id, target in initial_xref_targets.items():
# filter out automatically generated identifiers from module options and library documentation
if id.startswith(ignored_identifier_patterns):