1
2# Writing and Reviewing Modular Services
3
4## Status
5
6Modular Services are, as of writing, a new feature with support in NixOS.
7It is in development, and be considerate of the fact that the intermediate outcome of RFC 163 is that we should try a module-based approach to portable services; it is not yet a widely agreed upon solution.
8
9## Relation to NixOS Modules
10
11- A modular service is not a replacement for a NixOS module, but may be in the future.
12- Using a modular service to implement a NixOS module is an expected use case, but exposes the NixOS module to a degree of uncertainty that is not acceptable for widely used modules yet.
13
14## Maintainership
15
16If you contribute a modular service, you must mark yourself as maintainer of the modular service.
17The maintainership of a modular service does not need to be the same as the maintainership of a NixOS module.
18If you are not a maintainer of the NixOS module, you should offer to join the NixOS module's `meta.maintainers` team, so that you are included in reviews and discussions, most of which also affect the modular service.
19The NixOS module maintainers have no obligation towards the modular service, except perhaps to notify you if they notice that the modular service breaks.
20
21## Minimum Standard
22
23Modular services **MUST** be accompanied by a **NixOS VM test** that exercises the modular service.
24
25Modular services **MUST** have a `meta.maintainers` module attribute that lists the maintainers of the modular service.
26
27## Reviewing Modular Services
28
29When reviewing a modular service, you should check the following. Details and rationale are provided below.
30
31```markdown
32- [ ] Has a NixOS VM test
33- [ ] Has a `meta.maintainers` attribute
34- [ ] Systemd-specific definitions are behind `optionalAttrs (options ? systemd)` to promote portability.
35- [ ] `_class = "service"`
36- [ ] Modular services provided through `passthru.services` must override the default of the package option using `finalAttrs.finalPackage`
37- [ ] Is the modular services infrastructure sufficient for this service? If one or more features are not covered, comment in https://github.com/NixOS/nixpkgs/issues/428084
38```
39
40## Details
41
42### NixOS VM test
43
44See the initial [Modular Services PR](https://github.com/NixOS/nixpkgs/pull/372170) for an [example](https://github.com/NixOS/nixpkgs/pull/372170/files#diff-e7fe16489cf3cd08ecc22b2c7896039d407a329b75691c046c95447423b3153f) of a NixOS VM test.
45TBD: describe best practices here.
46
47### `_class = "service"`
48
49A [`_class`](https://nixos.org/manual/nixpkgs/unstable/#module-system-lib-evalModules-param-class) declaration ensures a clear error when the module is accidentally imported into a configuration that isn't a modular service, such as a NixOS configuration.
50
51Provide it as the first attribute in the module:
52
53```nix
54# Non-module dependencies (`importApply`)
55{ writeScript, runtimeShell }:
56
57# Service module
58{ lib, config, ... }:
59{
60 _class = "service";
61
62 options = {
63 # ...
64 };
65 config = {
66 # ...
67 };
68}
69```
70
71### Overriding the package default
72
73When a modular service is provided through `passthru.services`, it must override the default of the package option using [`finalAttrs.finalPackage`](https://nixos.org/manual/nixpkgs/unstable/#mkderivation-recursive-attributes).
74If this is not possible, or if the module is not represented by a single package, consider exposing the modular service directly by file path only.
75
76Otherwise, since some packages are *defined* by an override, the modular service would launch a wrong package, if it builds at all.
77
78Example:
79
80`package.nix`
81```nix
82{
83 stdenv,
84 nixosTests,
85# ...
86}:
87stdenv.mkDerivation (finalAttrs: {
88 pname = "example";
89 # ...
90
91 passthru = {
92 services = {
93 default = {
94 imports = [ (lib.modules.importApply ./service.nix { inherit pkgs; }) ];
95 example.package = finalAttrs.finalPackage;
96 # ...
97 };
98 };
99 };
100})
101```