Wer von Docker Compose kommt, kennt das Muster: ein Container als Dienst, der beim Booten hochfährt und nach einem Absturz neu startet. Unter Podman übernimmt diese Rolle nicht Compose, sondern systemd, und seit einiger Zeit beschreibt man solche Dienste deklarativ mit Quadlet. Sie schreiben eine kurze .container-Datei, systemd generiert daraus eine vollwertige Service-Unit. In Podman 6.0 (Juni 2026) ist Quadlet der Standardweg und noch enger integriert. Dieser Beitrag stellt einen einzelnen rootless Dienst auf die Beine: eine .container-Datei ablegen, daemon-reload, starten, prüfen, autostart-fähig machen.
Was Quadlet macht und wann es passt
Quadlet ist ein systemd-Generator, der zur Laufzeit aus einer .container-Datei eine .service-Unit erzeugt. Sie pflegen keine handgeschriebenen Units mit langen ExecStart=podman run ...-Zeilen mehr, sondern eine kompakte, ini-artige Beschreibung im vertrauten Compose-Stil (Image, Ports, Volumes, Umgebungsvariablen). Das passt genau dann, wenn Sie einen oder wenige Dienste auf einem Host rootless und autostart-fähig betreiben wollen, etwa auf einem VPS oder Self-Hosting-Server. Für Orchestrierung über mehrere Knoten bleibt Kubernetes das Mittel der Wahl.
Rootless bedeutet: Der Container läuft im User-Namespace Ihres normalen Logins, nicht als root. Entsprechend arbeiten wir durchgehend mit systemctl --user, nicht mit dem System-systemd.
Ein Hinweis zu Podman 6.0 vorweg: cgroup v1 wird nicht mehr unterstützt, ebenso wenig CNI, slirp4netns und iptables. Rootless-Netzwerk läuft über pasta, Firewalling über nftables. Auf einem aktuellen Linux (cgroup v2, netavark/pasta) ist das der Normalfall, aber auf sehr alten Hosts sollten Sie das vor dem Upgrade prüfen.
Der minimale Dienst
Wir betreiben einen einfachen Webserver (Caddy) als rootless Service. Legen Sie die Quadlet-Datei im rootless-Suchpfad ab:
mkdir -p ~/.config/containers/systemd
Datei ~/.config/containers/systemd/web.container:
[Unit]
Description=Caddy web server (rootless, Quadlet)
After=network-online.target
Wants=network-online.target
[Container]
Image=docker.io/library/caddy:2
PublishPort=8080:80
Volume=web-content:/usr/share/caddy:Z
Environment=TZ=UTC
[Service]
Restart=on-failure
[Install]
WantedBy=default.target
Danach den Generator einlesen lassen und den Dienst starten:
systemctl --user daemon-reload
systemctl --user start web.service
systemctl --user status web.service
status sollte active (running) zeigen. Ein kurzer Test:
curl -I http://localhost:8080
Caddy antwortet auf Port 8080; das benannte Volume web-content legt Podman beim ersten Start automatisch an.
Die Bausteine der .container-Datei
[Container]ist der Kern.Image=gibt das Image an (voll qualifiziert, inklusive Registry, um Mehrdeutigkeiten zu vermeiden).PublishPort=8080:80mappt Host-Port 8080 auf Container-Port 80.Volume=web-content:/usr/share/caddy:Zbindet ein benanntes Volume ein; das:Zsetzt das SELinux-Label passend, falls Ihr System SELinux nutzt.Environment=setzt Umgebungsvariablen.[Service]enthält klassische systemd-Service-Optionen.Restart=on-failuresorgt für den Neustart nach einem Absturz (das Pendant zur Compose-Restart-Policy).[Install]mitWantedBy=default.targetverankert den Dienst so, dass er beim Hochfahren der User-Session mitstartet.
Der Ablauf im Ueberblick:
web.container (~/.config/containers/systemd/)
|
| systemctl --user daemon-reload
v
+---------------------+
| Quadlet-Generator | erzeugt Unit zur Laufzeit
+----------+----------+
|
v
web.service (transient)
|
| systemctl --user start web.service
v
+---------------------+
| rootless Container | caddy:2 -> Host :8080
+---------------------+
Autostart ohne aktives Login
WantedBy=default.target allein genügt nicht, wenn niemand eingeloggt ist: Der User-Manager läuft standardmäßig nur, solange eine Session offen ist. Damit Ihr Dienst nach einem Reboot auch ohne Login startet, aktivieren Sie Linger für Ihren Benutzer:
loginctl enable-linger
Ab jetzt startet systemd Ihren User-Manager beim Boot und hält ihn nach dem Logout am Leben, sodass der Container dauerhaft läuft.
Drei typische Stolperfallen
1. Servicename ist der Dateiname, nicht der Container-Name. Aus web.container wird web.service. Sie steuern den Dienst also mit systemctl --user start web.service, nicht über einen im Container gesetzten Namen. Verwechslungen führen zu „Unit not found“.
2. Kein systemctl enable für Quadlet-Dienste. Die generierten Units sind aus Sicht von systemd transient und lassen sich nicht mit systemctl enable für den Boot verankern. Der Autostart wird ausschließlich über [Install] WantedBy= in der .container-Datei plus daemon-reload gesteuert. Ein enable-Versuch schlägt fehl.
3. Ports unter 1024 gehen rootless nicht ohne Weiteres. Als normaler Benutzer dürfen Sie privilegierte Ports (< 1024) standardmäßig nicht binden. Deshalb PublishPort=8080:80 statt 80:80. Wollen Sie den Dienst nach außen auf Port 80/443 anbieten, setzen Sie einen Reverse Proxy davor oder passen net.ipv4.ip_unprivileged_port_start an. Für den Einstieg bleiben Sie bei einem hohen Host-Port.
Wie es weitergeht
Damit steht ein sauberes, rootless Muster, das sich auf jeden weiteren Dienst überträgt: .container-Datei, daemon-reload, --user start, enable-linger. Naheliegende nächste Schritte sind eigene Netzwerke (.network) und Volumes (.volume) als weitere Quadlet-Dateien sowie das Zusammenfassen mehrerer Container in einem Pod (.pod). Die Details stehen in der offiziellen Dokumentation: podman-systemd.unit (Quadlet) und die Podman-Dokumentation.
Hinweis: Die Beiträge dieses Blogs werden unter Einsatz von KI erstellt und vor der Veröffentlichung redaktionell geprüft. Die redaktionelle Verantwortung trägt Emre Yurtbay (siehe Impressum).