-
-
Notifications
You must be signed in to change notification settings - Fork 15.1k
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
doc: add systemd chapter #142282
base: master
Are you sure you want to change the base?
doc: add systemd chapter #142282
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
## Additional resources {#sec-systemd-additional-resources} | ||
|
||
[Systemd official website](https://systemd.io/) | ||
[Wikipedia](https://en.wikipedia.org/wiki/Systemd) | ||
[Archlinux Wiki](https://wiki.archlinux.org/title/Systemd) | ||
[Systemd announcement from Lennart Poettering (one of the 2 original developpers of systemd)](http://0pointer.de/blog/projects/systemd.html) | ||
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,32 @@ | ||||||
## Environment and secret maangement {#sec-systemd-environment} | ||||||
|
||||||
The `environment` attribute is used to pass environment variables to the systemd service. | ||||||
``` | ||||||
environment = { | ||||||
MY_ENV_VAR = "Something blue"; | ||||||
} | ||||||
``` | ||||||
|
||||||
A file can be passed as well with `environmentFile`. If both are passed, `environmentFile` takes precedence over `environment` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Environment variables should not contain secret data. | ||||||
The main reason being that environment variables are accessible to unpriviledged users. | ||||||
See [systemd doc](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment=) for a more detailed explanation. | ||||||
|
||||||
### Secret management {#sec-systemd-secrets} | ||||||
|
||||||
To pass secret data to a systemd service, systemd has the `LoadCredential` mechanism. | ||||||
By passing a file path to systemd, it will make sure that the file is available to the process under a specific path. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is probably a good idea to explicitly highlight that any path in the Nix store is world-readable so in order for this secret to be secret it needs to be managed outside of NixOS. |
||||||
The load credential attribute takes an id a colon and a file path | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
let's have a look at an [example](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L182) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a permalink like this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking about those, I thought it would be better to just include the code-snippet rather than a link. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's at lest do a permalink for now so that the line doesn't get lost and we can do snippets later. |
||||||
``` | ||||||
LoadCredential = "jwt_secret:${cfg.jwtSecretPath}"; | ||||||
``` | ||||||
Inside your application, the jwt secret will be available under `$CREDENTIALS_DIRECTORY/jwt_secret` environment variable. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Note that this requires your application to read secrets from a file. | ||||||
|
||||||
If your service can only read secrets through environment variables, the best alternative is to ask the end user to pass an `environmentFile` containing the secrets. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nice to document how that's done or link to an example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
If your service reads secrets from a configuration file, you can use the `script` attribute of your systemd service to populate that file with the secrets before starting the service. | ||||||
[example in nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L172) |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,26 @@ | ||||||
## Handling dependencies {#sec-systemd-handling-dependencies} | ||||||
|
||||||
When a systemd service A depends on another service B. | ||||||
There are 3 choices to make | ||||||
wants vs requires vs bindsTo: | ||||||
- use `wants = [ "B" ];`, if service A can start even if B fails. Systemd will try to start B but will start A even in case of a failure of B. | ||||||
- use `requires = [ "B" ];`, if service A will not start without B. Systemd will try to start B and will not start A in case B fails. | ||||||
- use `bindsTo = [ "B" ];`, if service A should be shutdown in case of a failure of B. | ||||||
|
||||||
After: | ||||||
- use `after = [ "B" ];`, if service A needs B to active before it is started. Note that with this setting, if you shutdown B, A will be shut down first. | ||||||
|
||||||
PartOf: | ||||||
- use `partOf = [ "B" ];`, if service A needs to re-started when service B is. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
Note that `wants,requires,bindsTo` are orthogonal to `after`. The most common occurence in nixpkgs is `requires` and `after`. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "and" makes it sound (to me) like the 2 most common occurrences are being listed
Suggested change
|
||||||
|
||||||
### Examples {#sec-systemd-handling-dependencies-examples} | ||||||
|
||||||
- Web-app dependency on a database. If the web-app cannot start without the database, use `requires` and `after`. If you would rather have it operate even without a database use `wants` and `after`. [example in nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L169) | ||||||
- Database migration and setup script for a web-app. It is common to make a service for database setup and migration. This service should be `partOf` the webapp service as everytime the webapp is restarted, this service should be restarted as well. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
[example in nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L214) | ||||||
- A web-app has two separate services, a UI and a server. The ui `requires` `after` the server. [example in nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L205) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is hard to parse as a sentence, also hard to keep it short :)
Suggested change
|
||||||
|
||||||
|
||||||
Note there are many other settings, for a complete reference and more detailed explanations, see the [systemd manual section](https://man.archlinux.org/man/systemd.unit.5#%5BUNIT%5D_SECTION_OPTIONS) |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,24 @@ | ||||||
## State management {#sec-systemd-state-management} | ||||||
|
||||||
State refers to files and directories for config or running a service. | ||||||
A special section is dedicated at the end regarding databases. | ||||||
|
||||||
TODO: section on RuntimeDir, StateDir, WorkingDir | ||||||
|
||||||
### Database connection {#sec-systemd-state-management-database-connection} | ||||||
|
||||||
Most databases have 2 modes of connection, over TCP or over Unix socket. Connecting over a unix socket can provide 2 advantages | ||||||
- 30% performance improvement as packets don't need to be encoder over TCP | ||||||
- The default authentication mode postgres on unix sockets is just with the user name. This will remove the need to manage the password secret | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though it being based on uid, this may be tricky across uid namespaces and therefore containers. I vaguely recall a nixpkgs issue or stackoverflow question about this, but I couldn't find it. |
||||||
|
||||||
Therefore the default for database connection for services is recommended to be with a unix socket. For postgres for example, the `host` needs to start with a `/`. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
[nixpkgs-example](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L98) | ||||||
|
||||||
### Database initialisation {#sec-systemd-state-management-database-initialisation} | ||||||
|
||||||
To initialise a database that a service depends on, it's customary to create another systemd service that will be `partOf` your main service. | ||||||
The initialisation service should be run by the database superuser so as not to require sudo. | ||||||
Typically it will involve creating the database and the user. | ||||||
There will be a step to see if the database is already created. | ||||||
That check is best implemented by going throug the created tables and exiting if the table already exists. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
[nixpkgs-example](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L220) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Systemd {#sec-systemd} | ||
|
||
Systemd is a building block of most nixos modules. | ||
|
||
From the [project's webpage](https://systemd.io/) | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't code but a quote. You should probably use |
||
systemd is a suite of basic building blocks for a Linux system. It provides a system and service manager that runs as PID 1 and starts the rest of the system. | ||
|
||
systemd provides aggressive parallelization capabilities, uses socket and D-Bus activation for starting services, offers on-demand starting of daemons, keeps track of processes using Linux control groups, maintains mount and automount points, and implements an elaborate transactional dependency-based service control logic. systemd supports SysV and LSB init scripts and works as a replacement for sysvinit. | ||
|
||
Other parts include a logging daemon, utilities to control basic system configuration like the hostname, date, locale, maintain a list of logged-in users and running containers and virtual machines, system accounts, runtime directories and settings, and daemons to manage simple network configuration, network time synchronization, log forwarding, and name resolution. | ||
``` | ||
|
||
This chapter aims at describing the most commonly used features of systemd in nixos. | ||
|
||
Index | ||
|
||
- usage in modules | ||
- Starting a service | ||
[ref](https://man.archlinux.org/man/systemd.service.5.en) | ||
- Handling dependencies | ||
Talk about `requires` `wants` `after` `partOf` and the likes | ||
[ref](https://man.archlinux.org/man/systemd.unit.5#%5BUNIT%5D_SECTION_OPTIONS) | ||
- Environment | ||
Talk about `environment` how to use it and not passing secrets | ||
[ref](https://man.archlinux.org/man/systemd.exec.5.en#ENVIRONMENT) | ||
- Managing state | ||
Talk about StateDir RunDir WorkingDir and when to use each | ||
- Initialisation of state and dependents (e.g. database) | ||
Talk about adding a systemd service to handle database initialisation | ||
- Database management | ||
Talk about connection via a unix socket for more performance and reduced secret management | ||
- Managing secrets | ||
Talk about LoadCredential and how to use it | ||
[ref](https://man.archlinux.org/man/systemd.exec.5.en#CREDENTIALS) | ||
- Security and principle of least privilege | ||
Talk about `DynamicUser` and user management | ||
[ref](https://man.archlinux.org/man/systemd.exec.5.en#USER/GROUP_IDENTITY) | ||
- Template units | ||
Show an example and explain why you would use them | ||
- Advanced topics | ||
- Capabilities | ||
[ref](https://man.archlinux.org/man/systemd.exec.5.en#CAPABILITIES) | ||
- Socket activation for zero downtime deploys | ||
|
||
```{=docbook} | ||
<xi:include href="systemd-handling-dependencies.section.xml" /> | ||
<xi:include href="systemd-state-management.section.xml" /> | ||
<xi:include href="systemd-environment-and-secrets.section.xml" /> | ||
<xi:include href="systemd-additional-resources.section.xml" /> | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-systemd-additional-resources"> | ||
<title>Additional resources</title> | ||
<para> | ||
<link xlink:href="https://systemd.io/">Systemd official | ||
website</link> | ||
<link xlink:href="https://en.wikipedia.org/wiki/Systemd">Wikipedia</link> | ||
<link xlink:href="https://wiki.archlinux.org/title/Systemd">Archlinux | ||
Wiki</link> | ||
<link xlink:href="http://0pointer.de/blog/projects/systemd.html">Systemd | ||
announcement from Lennart Poettering (one of the 2 original | ||
developpers of systemd)</link> | ||
</para> | ||
</section> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-systemd-environment"> | ||
<title>Environment and secret maangement</title> | ||
<para> | ||
The <literal>environment</literal> attribute is used to pass | ||
environment variables to the systemd service. | ||
</para> | ||
<programlisting> | ||
environment = { | ||
MY_ENV_VAR = "Something blue"; | ||
} | ||
</programlisting> | ||
<para> | ||
A file can be passed as well with | ||
<literal>environmentFile</literal>. If both are passed, | ||
<literal>environmentFile</literal> takes precedence over | ||
<literal>environment</literal> | ||
</para> | ||
<para> | ||
Environment variables should not contain secret data. The main | ||
reason being that environment variables are accessible to | ||
unpriviledged users. See | ||
<link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment=">systemd | ||
doc</link> for a more detailed explanation. | ||
</para> | ||
<section xml:id="sec-systemd-secrets"> | ||
<title>Secret management</title> | ||
<para> | ||
To pass secret data to a systemd service, systemd has the | ||
<literal>LoadCredential</literal> mechanism. By passing a file | ||
path to systemd, it will make sure that the file is available to | ||
the process under a specific path. The load credential attribute | ||
takes an id a colon and a file path let’s have a look at an | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L182">example</link> | ||
</para> | ||
<programlisting> | ||
LoadCredential = "jwt_secret:${cfg.jwtSecretPath}"; | ||
</programlisting> | ||
<para> | ||
Inside your application, the jwt secret will be available under | ||
<literal>$CREDENTIALS_DIRECTORY/jwt_secret</literal> environment | ||
variable. | ||
</para> | ||
<para> | ||
Note that this requires your application to read secrets from a | ||
file. | ||
</para> | ||
<para> | ||
If your service can only read secrets through environment | ||
variables, the best alternative is to ask the end user to pass an | ||
<literal>environmentFile</literal> containing the secrets. | ||
</para> | ||
<para> | ||
If your service reads secrets from a configuration file, you can | ||
use the <literal>script</literal> attribute of your systemd | ||
service to populate that file with the secrets before starting the | ||
service. | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L172">example | ||
in nixpkgs</link> | ||
</para> | ||
</section> | ||
</section> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-systemd-handling-dependencies"> | ||
<title>Handling dependencies</title> | ||
<para> | ||
When a systemd service A depends on another service B. There are 3 | ||
choices to make wants vs requires vs bindsTo: | ||
</para> | ||
<itemizedlist spacing="compact"> | ||
<listitem> | ||
<para> | ||
use <literal>wants = [ "B" ];</literal>, if service A | ||
can start even if B fails. Systemd will try to start B but will | ||
start A even in case of a failure of B. | ||
</para> | ||
</listitem> | ||
<listitem> | ||
<para> | ||
use <literal>requires = [ "B" ];</literal>, if service | ||
A will not start without B. Systemd will try to start B and will | ||
not start A in case B fails. | ||
</para> | ||
</listitem> | ||
<listitem> | ||
<para> | ||
use <literal>bindsTo = [ "B" ];</literal>, if service | ||
A should be shutdown in case of a failure of B. | ||
</para> | ||
</listitem> | ||
</itemizedlist> | ||
<para> | ||
After: | ||
</para> | ||
<itemizedlist spacing="compact"> | ||
<listitem> | ||
<para> | ||
use <literal>after = [ "B" ];</literal>, if service A | ||
needs B to active before it is started. Note that with this | ||
setting, if you shutdown B, A will be shut down first. | ||
</para> | ||
</listitem> | ||
</itemizedlist> | ||
<para> | ||
PartOf: | ||
</para> | ||
<itemizedlist spacing="compact"> | ||
<listitem> | ||
<para> | ||
use <literal>partOf = [ "B" ];</literal>, if service A | ||
needs to re-started when service B is. | ||
</para> | ||
</listitem> | ||
</itemizedlist> | ||
<para> | ||
Note that <literal>wants,requires,bindsTo</literal> are orthogonal | ||
to <literal>after</literal>. The most common occurence in nixpkgs is | ||
<literal>requires</literal> and <literal>after</literal>. | ||
</para> | ||
<section xml:id="sec-systemd-handling-dependencies-examples"> | ||
<title>Examples</title> | ||
<itemizedlist spacing="compact"> | ||
<listitem> | ||
<para> | ||
Web-app dependency on a database. If the web-app cannot start | ||
without the database, use <literal>requires</literal> and | ||
<literal>after</literal>. If you would rather have it operate | ||
even without a database use <literal>wants</literal> and | ||
<literal>after</literal>. | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L169">example | ||
in nixpkgs</link> | ||
</para> | ||
</listitem> | ||
<listitem> | ||
<para> | ||
Database migration and setup script for a web-app. It is | ||
common to make a service for database setup and migration. | ||
This service should be <literal>partOf</literal> the webapp | ||
service as everytime the webapp is restarted, this service | ||
should be restarted as well. | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L214">example | ||
in nixpkgs</link> | ||
</para> | ||
</listitem> | ||
<listitem> | ||
<para> | ||
A web-app has two separate services, a UI and a server. The ui | ||
<literal>requires</literal> <literal>after</literal> the | ||
server. | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L205">example | ||
in nixpkgs</link> | ||
</para> | ||
</listitem> | ||
</itemizedlist> | ||
<para> | ||
Note there are many other settings, for a complete reference and | ||
more detailed explanations, see the | ||
<link xlink:href="https://man.archlinux.org/man/systemd.unit.5#%5BUNIT%5D_SECTION_OPTIONS">systemd | ||
manual section</link> | ||
</para> | ||
</section> | ||
</section> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-systemd-state-management"> | ||
<title>State management</title> | ||
<para> | ||
State refers to files and directories for config or running a | ||
service. A special section is dedicated at the end regarding | ||
databases. | ||
</para> | ||
<para> | ||
TODO: section on RuntimeDir, StateDir, WorkingDir | ||
</para> | ||
<section xml:id="sec-systemd-state-management-database-connection"> | ||
<title>Database connection</title> | ||
<para> | ||
Most databases have 2 modes of connection, over TCP or over Unix | ||
socket. Connecting over a unix socket can provide 2 advantages | ||
</para> | ||
<itemizedlist spacing="compact"> | ||
<listitem> | ||
<para> | ||
30% performance improvement as packets don’t need to be | ||
encoder over TCP | ||
</para> | ||
</listitem> | ||
<listitem> | ||
<para> | ||
The default authentication mode postgres on unix sockets is | ||
just with the user name. This will remove the need to manage | ||
the password secret | ||
</para> | ||
</listitem> | ||
</itemizedlist> | ||
<para> | ||
Therefore the default for database connection for services is | ||
recommended to be with a unix socket. For postgres for example, | ||
the <literal>host</literal> needs to start with a | ||
<literal>/</literal>. | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L98">nixpkgs-example</link> | ||
</para> | ||
</section> | ||
<section xml:id="sec-systemd-state-management-database-initialisation"> | ||
<title>Database initialisation</title> | ||
<para> | ||
To initialise a database that a service depends on, it’s customary | ||
to create another systemd service that will be | ||
<literal>partOf</literal> your main service. The initialisation | ||
service should be run by the database superuser so as not to | ||
require sudo. Typically it will involve creating the database and | ||
the user. There will be a step to see if the database is already | ||
created. That check is best implemented by going throug the | ||
created tables and exiting if the table already exists. | ||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/web-apps/lemmy.nix#L220">nixpkgs-example</link> | ||
</para> | ||
</section> | ||
</section> |
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.