|
| 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 |
0 commit comments