···1+2+# Modular Services
3+4+This directory defines a modular service infrastructure for NixOS.
5+See the [Modular Services chapter] in the manual [[source]](../../doc/manual/development/modular-services.md).
6+7+[Modular Services chapter]: https://nixos.org/manual/nixos/unstable/#modular-services
8+9+# Design decision log
10+11+- `system.services.<name>`. Alternatives considered
12+ - `systemServices`: similar to does not allow importing a composition of services into `system`. Not sure if that's a good idea in the first place, but I've kept the possibility open.
13+ - `services.abstract`: used in https://github.com/NixOS/nixpkgs/pull/267111, but too weird. Service modules should fit naturally into the configuration system.
14+ Also "abstract" is wrong, because it has submodules - in other words, evalModules results, concrete services - not abstract at all.
15+ - `services.modular`: only slightly better than `services.abstract`, but still weird
16+17+- No `daemon.*` options. https://github.com/NixOS/nixpkgs/pull/267111/files#r1723206521
18+19+- For now, do not add an `enable` option, because it's ambiguous. Does it disable at the Nix level (not generate anything) or at the systemd level (generate a service that is disabled)?
20+21+- Move all process options into a `process` option tree. Putting this at the root is messy, because we also have sub-services at that level. Those are rather distinct. Grouping them "by kind" should raise fewer questions.
22+23+- `modules/system/service/systemd/system.nix` has `system` twice. Not great, but
24+ - they have different meanings
25+ 1. These are system-provided modules, provided by the configuration manager
26+ 2. `systemd/system` configures SystemD _system units_.
27+ - This reserves `modules/service` for actual service modules, at least until those are lifted out of NixOS, potentially
28+
···1+{
2+ lib,
3+ config,
4+ systemdPackage,
5+ ...
6+}:
7+let
8+ inherit (lib) mkOption types;
9+in
10+{
11+ imports = [
12+ ../portable/service.nix
13+ (lib.mkAliasOptionModule [ "systemd" "service" ] [ "systemd" "services" "" ])
14+ (lib.mkAliasOptionModule [ "systemd" "socket" ] [ "systemd" "sockets" "" ])
15+ ];
16+ options = {
17+ systemd.services = mkOption {
18+ description = ''
19+ This module configures systemd services, with the notable difference that their unit names will be prefixed with the abstract service name.
20+21+ This option's value is not suitable for reading, but you can define a module here that interacts with just the unit configuration in the host system configuration.
22+23+ Note that this option contains _deferred_ modules.
24+ This means that the module has not been combined with the system configuration yet, no values can be read from this option.
25+ What you can do instead is define a module that reads from the module arguments (such as `config`) that are available when the module is merged into the system configuration.
26+ '';
27+ type = types.lazyAttrsOf (
28+ types.deferredModuleWith {
29+ staticModules = [
30+ # TODO: Add modules for the purpose of generating documentation?
31+ ];
32+ }
33+ );
34+ default = { };
35+ };
36+ systemd.sockets = mkOption {
37+ description = ''
38+ Declares systemd socket units. Names will be prefixed by the service name / path.
39+40+ See {option}`systemd.services`.
41+ '';
42+ type = types.lazyAttrsOf types.deferredModule;
43+ default = { };
44+ };
45+46+ # Also import systemd logic into sub-services
47+ # extends the portable `services` option
48+ services = mkOption {
49+ type = types.attrsOf (
50+ types.submoduleWith {
51+ class = "service";
52+ modules = [
53+ ./service.nix
54+ ];
55+ specialArgs = {
56+ inherit systemdPackage;
57+ };
58+ }
59+ );
60+ };
61+ };
62+ config = {
63+ # Note that this is the systemd.services option above, not the system one.
64+ systemd.services."" = {
65+ # TODO description;
66+ wantedBy = lib.mkDefault [ "multi-user.target" ];
67+ serviceConfig = {
68+ Type = lib.mkDefault "simple";
69+ Restart = lib.mkDefault "always";
70+ RestartSec = lib.mkDefault "5";
71+ ExecStart = [
72+ (systemdPackage.functions.escapeSystemdExecArgs (
73+ [ config.process.executable ] ++ config.process.args
74+ ))
75+ ];
76+ };
77+ };
78+ };
79+}