Links
- Homepage
- https://systemd.io/
- Source
- https://github.com/systemd/systemd/
- Misc
- NEWS file (raw) - Fun to read which new features are added.
systemd is possibly my favorite “Linux-only” program (or set of programs/utilities). It lets you harness a ton of Linux-kernel-specific features without having to write any C. Like, I use it to manage cgroups, network namespaces, and even to set alarm clock reminders. It’s got great documentation that’s also helped me learn about features like eBPF.
Look at man systemd.directives(7)
for a list of interesting parameters.
Resource control
See man systemd.resource-control(5)
.
Here’s an example of limiting IO for a service that’s repairing the Nix store:
systemctl set-property repairing-store.service --runtime IOReadBandwidthMax="/nix/store/ 1M"
Here, /nix/store/
is used to resolve the backing device where read bandwidth will be limited.
You can then see how many bytes have been read with something like:
watch --interval 0.2 systemctl show repairing-store.service --property=IOReadBytes
systemd-run shell
systemd-run
can be used to create shells with limited resources.
Here’s an example where the maximum number of processes is limited:
systemd-run --user --shell -p TasksMax=10
And here’s an example where the $HOME
directory is mounted read-only, and the Downloads
directory is replaced with a temporary file system:
systemd-run --user --shell -p BindReadOnlyPaths="$HOME" -p TemporaryFileSystem="$HOME/Downloads/"
These examples didn’t require special privileges, but some properties like IPAddressDeny=any
won’t work unless run using the system service manager.
Here’s an example that blocks internet:
$ systemd-run --uid=$(whoami) --shell -p IPAddressDeny=any (pkttyagent:971142): GLib-ERROR **: 16:23:42.326: creating thread 'gmain': Error creating thread: Resource temporarily unavailable Running as unit: run-u7876.service Press ^] three times within 1s to disconnect TTY. $ ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. ^C --- 8.8.8.8 ping statistics --- 4 packets transmitted, 0 received, 100% packet loss, time 3109ms
Though you can get a similar result without higher privileges by using the PrivateNetwork=
directive:
systemd-run --user --shell -p PrivateNetwork=true
I’m surprised this works without elevated privileges (it probably requires unprivileged userns clone), but this is pretty useful.
Note that trying to use NetworkNamespacePath=
will not work with --user
from when I tried on 2023-12-18.
(Which is good, by the way, since some network namespaces might be privileged in some way.)
You can, however, escape PrivateNetwork=true
using NetworkNamespacePath=
, but it seems like you can only go to the root namespace.
machinectl shell
$ machinectl shell --uid=someuser Connected to the local host. Press ^] three times within 1s to exit session. bash-5.2$ whoami whoami someuser bash-5.2$ pwd pwd /home/someuser bash-5.2$ logout Connection to the local host terminated.
systemd-run
systemd-run
is a handy way to run commands as different users, or with different resource control settings, or at specific times.
Sometimes I prefer it to sudo
because it can tell me the resource usage of a specific command.
Here’s an example of doing a dry run of Nix garbage collection, scheduled in 1 minute.
systemd-run --on-active=1m --timer-property=AccuracySec=1s --unit=nix-gc-dry-run --remain-after-exit nix-collect-garbage --delete-older-than 90d --dry-run
$ systemctl status nix-gc-dry-run ● nix-gc-dry-run.service - /run/current-system/sw/bin/nix-collect-garbage --delete-older-than 90d --dry-run Loaded: loaded (/run/systemd/transient/nix-gc-dry-run.service; transient) Transient: yes Active: active (exited) since Thu 2024-02-22 13:37:00 PST; 35s ago TriggeredBy: ● nix-gc-dry-run.timer Process: 1932545 ExecStart=/run/current-system/sw/bin/nix-collect-garbage --delete-older-than 90d --dry-run (code=exited, status=0/SUCCESS) Main PID: 1932545 (code=exited, status=0/SUCCESS) IP: 0B in, 0B out CPU: 32ms Feb 22 13:37:00 ░░░░░ systemd[1]: Started /run/current-system/sw/bin/nix-collect-garbage …y-run. Feb 22 13:37:00 ░░░░░ nix-collect-garbage[1932545]: removing old generations of profile /ni…nels Feb 22 13:37:00 ░░░░░ nix-collect-garbage[1932545]: removing old generations of profile /ni…nels Feb 22 13:37:00 ░░░░░ nix-collect-garbage[1932545]: removing old generations of profile /ni…file Feb 22 13:37:00 ░░░░░ nix-collect-garbage[1932545]: removing old generations of profile /ni…stem Feb 22 13:37:00 ░░░░░ nix-collect-garbage[1932545]: removing old generations of profile /ni…nels Hint: Some lines were ellipsized, use -l to show in full. $
To “garbage collect” the RemainAfterExit
service, run systemctl status nix-gc-dry-run
.
systemd-nspawn
Here’s an example of pulling Debian 12 from Debian’s cloud images.
I’m using genericcloud
since it’s smaller, and I’m using --verify=no
because systemd-nspawn expects SHA256SUMS
whereas cloud.debian.org
provides SHA512SUMS
.
$ machinectl pull-raw --verify=no https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 Enqueued transfer job 1. Press C-c to continue download in background. Pulling 'https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2', saving as 'debian-12-genericcloud-amd64'. HTTP request to https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.nspawn failed with code 404. Settings file could not be retrieved, proceeding without. HTTP request to https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.verity failed with code 404. Verity integrity file could not be retrieved, proceeding without. HTTP request to https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.roothash failed with code 404. Root hash file could not be retrieved, proceeding without. HTTP request to https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.roothash.p7s failed with code 404. Root hash signature file could not be retrieved, proceeding without. Downloading 267.1M for https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2. Got 1% of https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2. ░░░░░░░ left at ░░░░░░/s. ... Got 99% of https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2. ░░ left at ░░░░░░. Acquired 267.1M. Download of https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 complete. Unpacking QCOW2 file. Created new local image 'debian-12-genericcloud-amd64'. Operation completed successfully. Exiting.
And here’s an example of starting it up and logging in. There’s a couple errors for some reason.
# systemd-nspawn -M debian-12-genericcloud-amd64 Spawning container debian-12-genericcloud-amd64 on /var/lib/machines/debian-12-genericcloud-amd64.raw. Press Ctrl-] three times within 1s to kill container. -bash: id: command not found -bash: [: : integer expression expected root@debian-12-genericcloud-amd64:~# passwd New password: hunter2 Retype new password: ******* passwd: password updated successfully root@debian-12-genericcloud-amd64:~# logout Container debian-12-genericcloud-amd64 exited successfully. $ machinectl start debian-12-genericcloud-amd64 $ machinectl list MACHINE CLASS SERVICE OS VERSION ADDRESSES debian-12-genericcloud-amd64 container systemd-nspawn debian 12 - 1 machines listed. $ machinectl login debian-12-genericcloud-amd64 Connected to machine debian-12-genericcloud-amd64. Press ^] three times within 1s to exit session. Debian GNU/Linux 12 debian-12-genericcloud-amd64 pts/1 debian-12-genericcloud-amd64 login: root Password: Linux debian-12-genericcloud-amd64 6.6.4 #1-NixOS ░░░░░░░░ The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. root@debian-12-genericcloud-amd64:~# which apt /usr/bin/apt root@debian-12-genericcloud-amd64:~#