Das Anlegen von einfacheren Timer-, Service- oder Mount-Units unter Systemd ist keine Geheimwissenschaft, braucht aber Übung und nicht selten korrigierende Detailrecherche. Systemd fordert Präzision und ist nicht auskunftsfreudig, wenn etwas nicht funktioniert. Wer aber die Prinzipien einmal beherrscht, wird durch zahlreiche Zeitoptionen, neue Möglichkeiten der Systemautomatisierung und minutiöse Details der Prozesssteuerung belohnt.
Jede Menge Anschauungsmaterial
Bevor man den ersten eigenen Timer oder Service baut, ist der Blick in vorhandene und funktionierende Beispiele nützlich. Der erste Artikel (ab Seite 40) hat nur gezeigt, wie „systemctl edit […]“ Detailänderungen erledigen kann. Wer eine Unit-Datei nur lesen will, verwendet (Beispiel)
systemctl cat samba.service
und der Befehl
sudo systemctl edit --full samba.service
erlaubt Änderungen an der Originaldatei (statt eine zusätzliche Override-Anweisung anzulegen).
Die Durchsicht einiger Dateien mit „systemctl cat“ ist für Einsteiger zwiespältig: Unit-Dateien sind kurz und mit ihrer Sektionsstruktur gut lesbar. Andererseits finden Sie in jeder Datei andere und neue Anweisungsdirektiven, deren Bedeutung und Relevanz sich nicht erschließt. Es ist keine schlechte Idee, mit Timer-Units zu starten. Erstens sind zeitgesteuerte Jobs ein häufiges Anliegen, zweitens sind die Unit-Dateien für Timer recht leicht nachvollziehbar: Die Sektionsstruktur „[Unit]“ (so beginnt jede Unit-Datei), „[Timer]“ (das ist der Unit-Typ und kann auch Service, Mount, Automount lauten) und „[Install]“ ist zuverlässig, und die ganze Datei enthält oft nur eine Handvoll relevanter Anweisungen. Unter „[Install]“ steht bei (System-)Timern immer:
[Install]
WantedBy=timers.target
Da oben unter „[Unit]“ nur die Zeile „Description=“ notwendig, aber beliebig zu füllen ist, geht es letztlich nur noch um die Zeitangaben unter „[Timer]“.
Timer: So einfach wie möglich
Der sicherste Weg, eigene Unit-Datei anzulegen, ist dieser Aufruf (Beispiel)
sudo systemctl edit --full --force auto-off.timer
Dann müssen Sie sich nicht um den richtigen Ort kümmern und Systemctl öffnet eine temporäre Datei „.#auto-off.timer
[Hex-Ziffer]“, die nach dem Speichern „/etc/systemd/system/auto-off.timer“ heißen wird. Hier tragen Sie die drei Sektionen „[Unit]“ mit beliebiger „Description“, „[Timer]“ und „[Install]“ mit der Standardanweisung „WantedBy=timers.target“ ein. Die Zeitangabe unter „[Timer]“ erledigen Sie mit der Anweisung „OnCalendar=“. Es gibt viele absolute und relative Zeitangaben, die Systemd-Timer akzeptieren. In diesem Beispiel tragen wir
OnCalendar=*-*-* 23:00:00
ein. Das bedeutet „jedes Jahr, jeden Monat, jeden Tag“ um 23 Uhr. Das ist schon alles, denn Systemd vermischt nichts: Was zu diesem Zeitpunkt konkret geschehen soll, muss in einer anderen Unit-Klasse definiert werden – als Service. Die Beziehung zwischen Timer und Service ist in den beiden Unit-Dateien nicht definiert. Sie entsteht schlicht dadurch, dass der Dateiname der Service-Unit exakt identisch sein muss mit jenem der Timer-Unit (hier „auto-off“) – daher:
sudo systemctl edit --full --force auto-off.service
In diese Datei muss nur der Titel „[Unit]“ mit der zweiten Zeile „Description=“ (Inhalt beliebig), darunter die Sektion „[Service]“ mit der Anweisung „ExecStart=“. Das ist der Befehl, der zu dem Zeitpunkt ausgeführt werden soll, die im Timer definiert ist. Dieses Beispiel fordert mit
ExecStart=/usr/sbin/rtcwake -m off -s 28800
einen Shutdown des Systems, das acht Stunden später wieder starten soll (28 800 Sekunden). Da der Shutdown um 23 Uhr erfolgt, läuft das System um 7 Uhr wieder an.
Das Beispiel mit Rtcwake ist vielleicht nicht das einfachst mögliche, alle Systemd-betreffenden Anweisungen sind aber auf das absolute Minimum reduziert, was ein funktionierender Timer benötigt. Sie müssen nach dem Speichern von Timer- und Service-Unit den Timer lediglich noch scharf schalten. Dies ist nur einmal zur Ersteinrichtung erforderlich:
sudo systemctl daemon-reload
sudo systemctl enable auto-off.timer
sudo systemctl start auto-off.timer
Um die Service-Unit müssen Sie sich nicht kümmern, das erledigt der Timer zur gegebenen Zeit. Die beiden neuen Units werden nach
systemctl status auto-off.timer
systemctl status auto-off.service
für den Service melden, dass er aktuell „tot“ ist, aber „Triggered“ vom Timer. Der Timer hingegen meldet sich als „aktiv“ und zeigt eine Frist, wann er den Service starten wird.

Timer: Elaborierte Optionen und Userkontext
Systemd-Timer kennen neben festen „OnCalendar=“-Zeiten zahlreiche relative Angaben wie „OnBootSec=10m“ (Beispiel für einen Timer, der zehn Minuten nach Anmeldung zuschlägt). Für periodische Wiederholungen gibt es vereinfachte Angaben wie „hourly“, „daily“, „weekly“ (also etwa „OnCalendar=daily“). Sehr hilfreich ist ferner, dass man mehrere „OnCalendar=“-Zeilen untereinander setzen kann. Und noch ein entscheidender Vorteil gegenüber Cron: Die Anweisung
Persistent=true
unterhalb der Sektion „[Timer]“ erledigt das, wofür neben Cron noch der Hilfsdienst Anacron arbeiten muss: Sie sorgt dafür, dass ein Termin nachgeholt wird, falls der Rechner zum geplanten Timer-Zeitpunkt nicht eingeschaltet war. Das folgende Beispiel geht auf weitere Timer-Details nicht näher ein, führt aber ein bislang vernachlässigtes Großthema ein. Die Aufgabe des Beispiel-Timers scheint einfach: Ein Systemd-Timer soll an jedem Monatsbeginn durch ein „notify-send“ an die Erledigung der Umsatzsteuervoranmeldung erinnern. Notify-send ist ein grafisches Tool und muss daher im Benutzerkontext laufen (theoretisch kann auch ein System-Timer plus Service grafische Programme starten, aber das ist knifflig und unzuverlässig).
Systemd-Timer und Dienste können auch mit Benutzerrecht angelegt und genutzt werden. Es funktionieren alle erläuterten Systemctl-Kommandos, hier aber ohne „sudo“ und mit dem Schalter „–user“. Beim Erstellen überlässt man die Wahl des richtigen Pfades am besten wieder Systemctl:
systemctl --user edit --full --force ustva.timer
Wie bei einer System-Unit erfolgt die Erstellung (oder Nachbearbeitung) der Init-Datei mit einer temporären Datei, hier im User-Pfad „~/.config/systemd/user“. Als die ersten beiden Zeilen stehen wieder das obligatorische „[Unit]“ und eine „Description“. Unter „[Timer]“ könnte nun etwa
[Timer]
OnCalendar=*-*-01 10:00:00
Persistent=true
folgen, um den Timer am ersten Tag jeden Monats um 10:00 auszulösen. Die „Persistent“-Zeile sorgt dafür, dass der Timer nachgeholt wird, falls der Rechner an diesem Ersten des Monats nicht läuft. Um die Flexibilität der Timer-Units zu zeigen, ändern wir den Zeitplan insofern, dass der Timer nur noch quartalsweise zuschlagen soll (Januar, April, Juli, Oktober). Das sind für kleine Solo-Selbständige die üblichen Termine für die Umsatzsteuer. Das ist ganz einfach mit diesen vier Zeilen erledigt:
OnCalendar=*-01-01 10:00:00
OnCalendar=*-04-01 10:00:00
OnCalendar=*-07-01 10:00:00
OnCalendar=*-10-01 10:00:00
In der letzten Sektion „[Install]“ ist bei Units, die im Benutzerkontext laufen, diese Anweisung erforderlich:
[Install]
WantedBy=default.target
Das ist ein wichtiger Unterschied zu System-Timern (immer timers.target).
Nun ist noch die zugehörige, gleichnamige Dienst-Unit zu erstellen:
systemctl --user edit --full --force ustva.service
In die Servicedatei kommt folgender Inhalt:
[Unit]
Description=Umsatzsteuer-Voranmeldung
Wants=graphical.target
After=graphical.target
[Service]
ExecStart=/usr/bin/notify-send „Umsatzsteuer bis 10. des Monats erledigen!“
Da ein grafisches Programm ausgelöst wird, ist in der Unit-Sektion das „graphical.target“ als benötigt (Wants) und gestartet (After) angegeben.

Path-Unit für Auto-Backup
Path-Units von Systemd können Ordner überwachen und Aufgaben automatisch auslösen, sobald sich Ordnerinhalte ändern. Die Kombination von Path-Unit und gleichnamiger Service-Unit ist ganz ähnlich wie bei Timer und Service, nur dass hier der Auslöse-Trigger kein zeitlicher, sondern eine Änderung im Dateisystem ist. Path-Units sollten gut überlegt sein, weil allzu umfangreiche Aktionen in frequentierten Ordern hohe und vielleicht unnötige Systemlast erzeugen. Bei Systemd muss man sich aber immerhin keine Gedanken machen, dass ein Service mehrfach läuft: Dies verhindert Systemd automatisch. Unser Beispiel zeigt, wie sich mit einer Path-Unit mehrere wichtige Ordner so überwachen lassen, dass bei allen Änderungen automatisch ein Rsync-Backup ausgelöst wird. Der erste Schritt ist wieder das Erzeugen der Unit-Datei, hier mit
sudo systemctl edit --full --force auto-sync.path
für die Path-Klasse. „[Unit]“-Header und „Description“ oben sind problemlos, unter „[Install]“ binden wir die Unit an das multi-user.target:
WantedBy=multi-user.target
Das liegt bei der hier genutzten Serverplatine nahe, genügt aber auch bei einem Desktopsystem, weil der Vorgang keine grafischen Ressourcen benötigt. Wesentlich sind die Pfadangaben nach „PathChanged=“. Wie die Abbildung zeigt, sind diverse solche Einträge möglich und oft sinnvoll, weil Path-Units keine Unterverzeichnisse berücksichtigen. „PathChanged“ reagiert auf alle Datenänderungen im angegebenen Pfad.
Notwendig – und im Vergleich zu Timer-Units etwas irritierend – ist in der Sektion „[Path]“ die explizite Angabe der zugehörigen Servicedatei (mit gleichem Namen):
Unit=auto-sync.service
Nach Speichern der Path-Unit legen Sie mit
sudo systemctl edit --full --force auto-sync.service
die Servicedatei an. Unser Beispiel sichert per SSH auf ein anderes Gerät im Netzwerk und erfordert ein geladenes „network.target“, was in der ersten Sektion unter „[Unit]“ vermerkt wird. Die entscheidende Anweisung unter „[Service]“ ist wie üblich „Exec-Start=“. Die zwei weiteren Anweisungen sind optional, aber sinnvoll: Sie begrenzen die Systemlast („CPUQuota“) und stellen mit „ExecCondition=“ eine Bedingung:
ExecCondition=/usr/bin/ping -c 1 192.168.0.110
Wenn der Rechner mit der genannten IP nicht erreichbar ist, stoppt der Service sofort. „ExecCondition“ ist sehr flexibel, insofern es den Exitcode („$?“) eines beliebigen Bash-Tools auswertet. Nur bei Exitcode „0“ startet der Dienst.
Nach dem Speichern der beiden Unit-Dateien folgt wieder die übliche Einrichtung:
sudo systemctl daemon-reload
sudo systemctl enable auto-sync.path
sudo systemctl start auto-sync.path
Die Service-Unit wird nicht aktiviert. Das erledigt künftig die Path-Unit automatisch, sobald sich in den hinterlegten Pfaden etwas ändert.

Automount: Wozu ist das gut?
Die Datei „/etc/fstab“ scheint eine Altlast, nachdem Systemd deren Laufwerkeinträge schon seit vielen Jahren in seine eigenen Mount-Units übersetzt (systemd-fstab-generator). Da aber von der Umstellung der systemkritischen Verzeichnisse Root („/“) und Boot („/boot“) auf Systemd-Mount-Units nach wie vor abgeraten wird, behält die „fstab“ vorerst weiter ihr Existenzrecht. Damit bringt es keinen Vorteil, Datenlaufwerke aus der „fstab“ zu nehmen und explizit als Systemd-Mount umzuwandeln.
Gute Motive für Mount- und Automount-Units gibt es aber für mobile USB-Datenträger. An das Automount von grafischen Systemen unter „/media/[User]“ kann man sich sicher gewöhnen, aber vielleicht will man den Mountpunkt selbst bestimmen? Noch überzeugender ist ein Mount-Automount-Gespann auf einem (Platinen-)Server ohne Desktop: Wer hier alle paar Wochen mal ein bestimmtes USB-Gerät anschließen will, kommt an einem manuellen Einhängen nicht vorbei.
Das kann Systemd deutlich vereinfachen. Eine Automount-Unit benötigt immer eine zugehörige Mount-Unit mit demselben Namen. Automount ist – ähnlich wie Timer und Path – ein Trigger, der beim Eintreten des Ereignisses die eigentliche Mount-Unit auslöst. Das Ereignis ist in diesem Fall der Zutritt in das Mountverzeichnis – ob per Klick im Dateimanager oder per Terminalbefehl (cd, ls, mc …), spielt keine Rolle.
Eine erste wichtige Vorbereitung ist der Mountpfad. Der sollte sprechend, aber nicht lang und kompliziert sein (Leerzeichen besser vermeiden), weil die Unit-Dateinamen den Pfad exakt wiedergeben müssen. Für einen Mountpfad „/srv/Archiv“ wären dann die Unit-Namen „srv-Archiv.mount“ und „srv-Archiv.automount“ zutreffend. Im Zweifel liefert Systemd sogar das Minitool
systemd-escape "/srv/Archiv/"
mit, um für einen Pfad den korrekten Unit-Namen zu ermitteln.
Ist der Mountpfad angelegt, kann der Automount-Trigger erstellt werden (hier im Systemkontext):
sudo systemctl edit --full --force srv-Archiv.automount
Die wenigen Zeilen können Sie der Abbildung rechts oben entnehmen. Entscheidend ist die Pfadangabe nach „Where=“, den die Unit künftig überwacht. Die Option „TimeOutIdleSec=“ ist optional, aber ressourcensparend. Unter „[Install]“ wird die Unit an das multi-user.target gebunden, was auch am grafischen Desktop ausreicht. Danach erstellt der Befehl
sudo systemctl edit --full --force srv-Archiv.mount
die zugehörige Mount-Unit. Die Angabe „Where=“ muss erneut den gewünschten Mountpfad definieren, während „What=“ die Hardware bezeichnet: Besser als etwa „/dev/sdb1“ ist die eindeutige UUID, die man vorher mit lsblk -f ermittelt. Der Rest ist nicht viel anders als beim Mountbefehl oder in fstab. Auf grafischen Systemen ist bei den „Options=“ der zusätzliche Eintrag „x-gvfs-show“ keine schlechte Idee, weil der Dateimanager Systemd-Automounts nur dann in der Navigation anzeigt.
Wichtig: Die Mount-Unit erhält keine „[Install]“-Sektion, da sie ausschließlich von der komplementären Automount-Unit gestartet werden soll.
Nach dem Speichern der Dateien müssen Sie den Automount-Trigger noch scharf schalten. Daher folgt abschließend die übliche Ersteinrichtung der Unit:
sudo systemctl daemon-reload
sudo systemctl enable srv-Archiv.automount
sudo systemctl start srv-Archiv.automount
Um die Mount-Unit müssen Sie sich nicht weiter kümmern.
Was passiert künftig, wenn Sie das USB-Laufwerk anschließen? Gar nichts! Automount ist keine Hardwareerkennung, sondern eine Ordnerüberwachung. Doch wird der Mountpfad betreten, dann feuert die Mount-Unit und – mit leichter Verzögerung – stehen die Inhalte des Datenträgers zur Verfügung! Noch ein Hinweis: Wenn Sie mit
systemd-unmount /srv/Archiv/
das Laufwerk auswerfen, wertet Systemd das als Ansage, dass Sie die Automount-Unit nicht mehr brauchen, und deaktiviert diese für die laufende Sitzung.

Schlussbemerkung
Der Heftschwerpunkt hat die Units von Systemd erläutert und ihre Verwaltung und Steuerung mit Systemctl. Nach der Vorstellung weiterer Verwaltungswerkzeuge ging es hier und zuletzt um das Erstellen eigener Unit-Files. Mit Timer, Path und Automount wurden Units mit Alltagsrelevanz mit mindestens je einem Beispiel erklärt. Ein Systemd-Experte sind Sie damit selbst bei gründlicher Lektüre nicht! Der Aufruf der Webseite www.freedesktop.org/software/systemd/man/ oder von lokalen Manpages (etwa „man systemctl“) kann da durchaus ernüchtern. Aber Sie haben jetzt alle Startvoraussetzungen, um die Dinge für konkrete Detailfragen einordnen zu können.
| [Unit] | Sektion 1 (notwendige und statische Startzeile „[Unit]“) |
|---|---|
| Description= | notwendig, aber Inhalt beliebig |
| Requires= | After= | Wants= | optionale Target-Angaben, falls die Unit z. B. „network.target“ oder „graphical“ benötigt |
| [Service] | [Timer] | [Path] | Mount] Sektion 2: Angabe der Unit-Klasse | |
| [Service] | |
| ExecStart= | notwendig: zu startendes Programm (oft auch Verweis auf Shell-Script) |
| Type= | optional, da der Standard „simple“ meist genügt |
| WorkingDirectory= | optional, aber oft nützlich, um Shell-Script zu ersparen |
| Restart= | optional: z. B. „on-failure“, um den Dienst nach Fehler neu zu starten |
| ExecCondition= | optional: Shell-Befehl wie ping o. test, der Errorcode liefert (Dienst startet nur bei $?=0) |
| User= | Group= | optional: höhere Sicherheit, weil der Systemdienst dann nur in diesem Konto laufen darf |
| [Timer] | |
| OnCalendar= | notwendig ist mindestens eine Zeitangabe, z. B. „*-*-* 15:00:00“ oder „hourly“ |
| OnBootSec= | alternative relative Zeitangabe – etwa „20m“ (20 Minuten nach Anmeldung) |
| Persistent= | optional: „true“ holt den Timer nach, falls der Rechner zur geplanten Zeit nicht lief |
| [Path] | |
| PathChanged= | notwendig ist mindestens eine Pfadangabe, „PathChanged“ reagiert auf alle Änderungen |
| PathExistsGlob= | erlaubt Filter nach Dateinamen oder Extensionen – etwa „/srv/data/*.pdf“ |
| [Mount] | |
| What= | notwendig: Gerätekennung wie „/dev/sda1“ oder „UUID=[…]“ |
| Where= | notwendig: Mountordner im Dateisystem |
| Type= | optional, aber zu empfehlen: Dateisystem wie „ext4“ oder „ntfs“ |
| Options= | optional, aber oft notwendig – etwa „username=sepp,password=sepp“ |
| [Automount] | |
| Where= | notwendig: muss exakt der Where-Zeile in der zugehörigen Mount-Unit entsprechen |
| TimeOutIdleSec= | optional: Abschaltzeit, falls kein Zugriff stattfindet – etwa „600“ (10 Minuten) |
| [Install] | Sektion 3: Unit installieren (an Target binden) |
| WantedBy= | Target-Klasse wie multi-user.target oder timers.target; Das angegebene Target erhält einen Symlink zu dieser Unit und startet es mit. Sektion 3 entfällt bei Mount-Units und Services, die von Timer- oder Path-Units gestartet werden |

