-
-
Notifications
You must be signed in to change notification settings - Fork 14.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modular services #372170
Open
roberth
wants to merge
6
commits into
master
Choose a base branch
from
modular-services
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,054
−2
Open
Modular services #372170
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c718392
Add modular services, system.services
roberth cd93c59
format
roberth b07d4c6
Add assertions and warnings to modular services
roberth 30c57d5
format
roberth 398f612
ghostunnel.services.default: init
roberth 1ee1ac4
nixos/doc: Add modular services section
roberth File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
# Modular Services | ||
|
||
This directory defines a modular service infrastructure for NixOS. | ||
See the [Modular Services chapter] in the manual [[source]](../../doc/manual/development/modular-services.md). | ||
|
||
[Modular Services chapter]: https://nixos.org/manual/nixos/unstable/#modular-services | ||
|
||
# Design decision log | ||
|
||
- `system.services.<name>`. Alternatives considered | ||
- `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. | ||
- `services.abstract`: used in https://github.com/NixOS/nixpkgs/pull/267111, but too weird. Service modules should fit naturally into the configuration system. | ||
Also "abstract" is wrong, because it has submodules - in other words, evalModules results, concrete services - not abstract at all. | ||
- `services.modular`: only slightly better than `services.abstract`, but still weird | ||
|
||
- No `daemon.*` options. https://github.com/NixOS/nixpkgs/pull/267111/files#r1723206521 | ||
|
||
- 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)? | ||
|
||
- 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. | ||
|
||
- `modules/system/service/systemd/system.nix` has `system` twice. Not great, but | ||
- they have different meanings | ||
1. These are system-provided modules, provided by the configuration manager | ||
2. `systemd/system` configures SystemD _system units_. | ||
- This reserves `modules/service` for actual service modules, at least until those are lifted out of NixOS, potentially | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ lib, ... }: | ||
let | ||
inherit (lib) concatLists mapAttrsToList showOption; | ||
in | ||
rec { | ||
flattenMapServicesConfigToList = | ||
f: loc: config: | ||
f loc config | ||
++ concatLists ( | ||
mapAttrsToList ( | ||
k: v: | ||
flattenMapServicesConfigToList f ( | ||
loc | ||
++ [ | ||
"services" | ||
k | ||
] | ||
) v | ||
) config.services | ||
); | ||
|
||
getWarnings = flattenMapServicesConfigToList ( | ||
loc: config: map (msg: "in ${showOption loc}: ${msg}") config.warnings | ||
); | ||
|
||
getAssertions = flattenMapServicesConfigToList ( | ||
loc: config: | ||
map (ass: { | ||
message = "in ${showOption loc}: ${ass.message}"; | ||
assertion = ass.assertion; | ||
}) config.assertions | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
{ | ||
lib, | ||
config, | ||
options, | ||
... | ||
}: | ||
let | ||
inherit (lib) mkOption types; | ||
pathOrStr = types.coercedTo types.path (x: "${x}") types.str; | ||
program = | ||
types.coercedTo ( | ||
types.package | ||
// { | ||
# require mainProgram for this conversion | ||
check = v: v.type or null == "derivation" && v ? meta.mainProgram; | ||
} | ||
) lib.getExe pathOrStr | ||
// { | ||
description = "main program, path or command"; | ||
descriptionClass = "conjunction"; | ||
}; | ||
in | ||
{ | ||
# https://nixos.org/manual/nixos/unstable/#modular-services | ||
_class = "service"; | ||
imports = [ | ||
../../../misc/assertions.nix | ||
]; | ||
options = { | ||
services = mkOption { | ||
type = types.attrsOf ( | ||
types.submoduleWith { | ||
modules = [ | ||
./service.nix | ||
]; | ||
} | ||
); | ||
description = '' | ||
A collection of [modular services](https://nixos.org/manual/nixos/unstable/#modular-services) that are configured in one go. | ||
|
||
You could consider the sub-service relationship to be an ownership relation. | ||
It **does not** automatically create any other relationship between services (e.g. systemd slices), unless perhaps such a behavior is explicitly defined and enabled in another option. | ||
''; | ||
default = { }; | ||
visible = "shallow"; | ||
}; | ||
process = { | ||
executable = mkOption { | ||
type = program; | ||
description = '' | ||
The path to the executable that will be run when the service is started. | ||
''; | ||
}; | ||
args = lib.mkOption { | ||
type = types.listOf pathOrStr; | ||
description = '' | ||
Arguments to pass to the `executable`. | ||
''; | ||
default = [ ]; | ||
}; | ||
}; | ||
}; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to keep the distinction. I had cases where I wanted one or the other.