···89# Design decision log
100011- `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.
···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
280000000000000000000000000
···89# Design decision log
1011+## Initial design
12+13- `system.services.<name>`. Alternatives considered
14 - `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.
15 - `services.abstract`: used in https://github.com/NixOS/nixpkgs/pull/267111, but too weird. Service modules should fit naturally into the configuration system.
···28 2. `systemd/system` configures SystemD _system units_.
29 - This reserves `modules/service` for actual service modules, at least until those are lifted out of NixOS, potentially
3031+## Configuration Data (`configData`) Design
32+33+Without a mechanism for adding files, all configuration had to go through `process.*`, requiring process restarts even when those would have been avoidable.
34+Many services implement automatic reloading or reloading on e.g. `SIGUSR1`, but those mechanisms need files to read. `configData` provides such files.
35+36+### Naming and Terminology
37+38+- **`configData` instead of `environment.etc`**: The name `configData` is service manager agnostic. While systemd system services can use `/etc`, other service managers may expose configuration data differently (e.g., different directory, relative paths).
39+40+- **`path` attribute**: Each `configData` entry automatically gets a `path` attribute set by the service manager implementation, allowing services to reference the location of their configuration files. These paths themselves are not subject to change from generation to generation; only their contents are.
41+42+- **`name` attribute**: In `environment.etc` this would be `target` but that's confusing, especially for symlinks, as it's not the symlink's target.
43+44+### Service Manager Integration
45+46+- **Portable base**: The `configData` interface is declared in `portable/config-data.nix`, making it available to all service manager implementations.
47+48+- **Systemd integration**: The systemd implementation (`systemd/system.nix`) maps `configData` entries to `environment.etc` entries under `/etc/system-services/`.
49+50+- **Path computation**: `systemd/config-data-path.nix` recursively computes unique paths for services and sub-services (e.g., `/etc/system-services/webserver/` vs `/etc/system-services/webserver-api/`).
51+ Fun fact: for the module system it is a completely normal module, despite its recursive definition.
52+ If we parameterize `/etc/system-services`, it will have to become an `importApply` style module nonetheless (function returning module).
53+54+- **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.
···1+# Tests in: ../../../../tests/modular-service-etc/test.nix
2+# This file is a function that returns a module.
3+pkgs:
4+{
5+ lib,
6+ name,
7+ config,
8+ options,
9+ ...
10+}:
11+let
12+ inherit (lib) mkOption types;
13+in
14+{
15+ options = {
16+ enable = mkOption {
17+ type = types.bool;
18+ default = true;
19+ description = ''
20+ Whether this configuration file should be generated.
21+ This option allows specific configuration files to be disabled.
22+ '';
23+ };
24+25+ name = mkOption {
26+ type = types.str;
27+ description = ''
28+ Name of the configuration file (relative to the service's configuration directory). Defaults to the attribute name.
29+ '';
30+ };
31+32+ path = mkOption {
33+ type = types.str;
34+ readOnly = true;
35+ description = ''
36+ The actual path where this configuration file will be available.
37+ This is determined by the service manager implementation.
38+39+ On NixOS it is an absolute path.
40+ Other service managers may provide a relative path, in order to be unprivileged and/or relocatable.
41+ '';
42+ };
43+44+ text = mkOption {
45+ default = null;
46+ type = types.nullOr types.lines;
47+ description = "Text content of the configuration file.";
48+ };
49+50+ source = mkOption {
51+ type = types.path;
52+ description = "Path of the source file.";
53+ };
54+ };
55+56+ config = {
57+ name = lib.mkDefault name;
58+ source = lib.mkIf (config.text != null) (
59+ let
60+ name' = "service-configdata-" + lib.replaceStrings [ "/" ] [ "-" ] name;
61+ in
62+ lib.mkDerivedConfig options.text (pkgs.writeText name')
63+ );
64+ };
65+}
···1+# Tests in: ../../../../tests/modular-service-etc/test.nix
2+# 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+}:
10+let
11+ inherit (lib) mkOption types;
12+ inherit (lib.modules) importApply;
13+in
14+{
15+ options = {
16+ configData = mkOption {
17+ default = { };
18+ example = lib.literalExpression ''
19+ {
20+ "server.conf" = {
21+ text = '''
22+ port = 8080
23+ workers = 4
24+ ''';
25+ };
26+ "ssl/cert.pem" = {
27+ source = ./cert.pem;
28+ };
29+ }
30+ '';
31+ description = ''
32+ Configuration data files for the service
33+34+ These files are made available to the service and can be updated without restarting the service process, enabling configuration reloading.
35+ The service manager implementation determines how these files are exposed to the service (e.g., via a specific directory path).
36+ This path is available in the `path` sub-option for each `configData.<name>` entry.
37+38+ This is particularly useful for services that support configuration reloading via signals (e.g., SIGHUP) or which pick up changes automatically, so that no downtime is required in order to reload the service.
39+ '';
40+41+ type = types.lazyAttrsOf (types.submodule (importApply ./config-data-item.nix pkgs));
42+ };
43+ };
44+}