Docker · Prozesse · PID 1 · Supervisor · Best Practices
One Process per Container
wann die Regel hilft – und wann nicht

„Ein Prozess pro Container" ist eines der meistzitierten Docker-Prinzipien – und eines der am häufigsten falsch angewendeten. Was das Prinzip wirklich bedeutet, wo es Sinn macht und wann Supervisor, s6-overlay oder dumb-init die bessere Entscheidung sind.

12 Min. Lesezeit PID 1 · Supervisor · s6-overlay · dumb-init · tini Docker Engine · Docker Compose · Production Patterns

1. Woher kommt das One-Process-Prinzip?

Das One Process per Container-Prinzip entstammt der Unix-Philosophie: Ein Programm soll eine Aufgabe gut erledigen und nichts darüber hinaus. Auf Docker übertragen bedeutet es: Ein Container soll für eine klar definierte Funktion zuständig sein. Der Gedanke dahinter ist Isolation und Ersetzbarkeit – wenn der Datenbankprozess abstürzt, soll nur der Datenbankcontainer neu starten, nicht auch der Webserver, der zufällig im selben Container läuft. Dieser Gedanke ist grundsätzlich richtig und trägt zur Wartbarkeit von Container-Stacks bei.

Das Problem entsteht, wenn das Prinzip wortwörtlich interpretiert wird: genau ein einziger Betriebssystem-Prozess darf im Container laufen. Diese Interpretation ist weder die Intention der Docker-Dokumentation noch praktisch sinnvoll. PHP-FPM etwa startet einen Master-Prozess und mehrere Worker-Prozesse – das sind viele Prozesse, aber trotzdem one concern per container: der Container ist für das Ausführen von PHP-Code zuständig. Nginx startet ebenfalls einen Master und mehrere Worker. Das ist normal und kein Verstoß gegen das Prinzip.

2. Das PID-1-Problem: warum es wichtiger ist als die Regel

Unabhängig davon, ob man One Process per Container strikt befolgt oder nicht, ist das PID-1-Problem das tatsächlich kritische Thema beim Prozessmanagement in Docker. PID 1 ist der erste Prozess im Container, und er hat eine Sonderstellung im Linux-Kernel: Wenn PID 1 stirbt, wird der gesamte Container beendet. Und PID 1 ist für das Reaping von Zombie-Prozessen verantwortlich – Child-Prozessen, die beendet wurden, aber deren Elternprozess noch keinen wait()-Syscall ausgeführt hat.

Wenn eine normale Anwendung als PID 1 läuft, kann sie Probleme haben: Viele Anwendungen verarbeiten SIGTERM nicht korrekt, wenn sie direkt als PID 1 gestartet werden – weil Linux an PID 1 standardmäßig keine Signals sendet, die ohne expliziten Handler zur Terminierung führen würden. Das bedeutet: docker stop sendet SIGTERM an PID 1, die Anwendung reagiert nicht, Docker wartet 10 Sekunden und sendet dann SIGKILL – ein harter Kill ohne Graceful Shutdown. Das One Process per Container-Verständnis muss daher PID-1-Handling einschließen.


# Demonstrate PID 1 problem — what happens without proper init
# WRONG: Application as PID 1 — may not handle signals correctly
FROM php:8.4-fpm-alpine
CMD ["php-fpm"]
# php-fpm receives SIGTERM as PID 1 — but handles it correctly (has signal handlers)

# WRONG: Shell as PID 1 — shell does not forward signals to children
FROM php:8.4-fpm-alpine
CMD ["/bin/sh", "-c", "php-fpm -F"]
# docker stop → SIGTERM to /bin/sh → sh ignores it → 10s timeout → SIGKILL

# RIGHT: Use exec to replace shell with the actual process
FROM php:8.4-fpm-alpine
CMD ["php-fpm", "-F"]  # exec form — PID 1 is php-fpm directly

# RIGHT: Use dumb-init as a minimal init system
FROM php:8.4-fpm-alpine
RUN apk add --no-cache dumb-init
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["php-fpm", "-F"]
# dumb-init as PID 1: forwards signals, reaps zombies, proper shutdown

3. Was das Prinzip wirklich bedeutet

Das One Process per Container-Prinzip bedeutet korrekt interpretiert: Ein Container hat eine einzige Verantwortlichkeit (Single Responsibility). Ein Container ist für das Ausführen von PHP-FPM zuständig – und nur dafür. Er ist nicht gleichzeitig für das Ausführen von Nginx, Redis und einem Cronjob zuständig. Diese Verantwortlichkeitstrennung ermöglicht unabhängiges Skalieren (mehr PHP-FPM-Container, ohne Nginx zu skalieren), unabhängige Updates und klare Restart-Policies.

Das ist etwas anderes als die Aussage, ein Container dürfe nur einen einzigen Betriebssystem-Prozess haben. PHP-FPM mit Master und Workern ist one concern: PHP ausführen. Nginx mit Master und Workern ist one concern: HTTP ausliefern. Wenn Supervisor in einem Container sowohl PHP-FPM als auch einen Queue-Worker verwaltet, die beide konzeptionell zur selben Applikationsschicht gehören und zusammen deployed werden müssen, ist das in vielen Teams eine pragmatische und akzeptable Entscheidung – solange die Grenzen klar dokumentiert sind.

4. Wann One Process per Container die richtige Wahl ist

Das One Process per Container-Prinzip entfaltet seinen Nutzen am stärksten, wenn Dienste unabhängig skaliert, aktualisiert oder neu gestartet werden müssen. Eine Magento-Installation mit hohem Traffic braucht möglicherweise zehn PHP-FPM-Container, aber nur einen Nginx-Container – das ist nur möglich, wenn beide getrennte Dienste sind. Ein Queue-Worker, der intensiv CPU verbraucht, soll skaliert werden können, ohne damit auch die Anzahl der PHP-FPM-Worker zu erhöhen.

Ebenfalls stark für One Process per Container: unterschiedliche Lebensdauern von Prozessen. Ein Cronjob läuft kurz und endet, ein Webserver läuft dauerhaft. Wenn beide im selben Container laufen, stirbt der Container, sobald der Cronjob beendet ist – was Neustart-Policies kompliziert. Separate Container mit eigenen Restart-Policies (restart: unless-stopped für den Webserver, restart: no für den Einmal-Cronjob) sind die saubere Lösung.

5. Wo das Prinzip zu weit geht

Das One Process per Container-Prinzip wird übertrieben, wenn es zu einer Proliferation von Containern führt, die eigentlich immer zusammen deployed, skaliert und neu gestartet werden müssen. Eine PHP-Applikation mit einem eng gekoppelten Background-Worker, der denselben Code teilt, auf denselben Share-Memory-Bereich zugreift und immer in derselben Version deployed werden muss, ist ein Kandidat für einen Container mit zwei Prozessen – verwaltet durch einen leichtgewichtigen Process Manager.

Ein weiteres typisches Szenario, wo One Process per Container zu weit geht: die Kombination aus Anwendung und Health-Check-Prozess, Log-Forwarder oder Metrics-Collector. Diese Sidecar-Prozesse sind konzeptionell an die Hauptanwendung gebunden und verursachen oft mehr Overhead als Nutzen, wenn sie in eigene Container ausgelagert werden. In Kubernetes gibt es dafür das Sidecar-Container-Konzept – in Docker Compose ist ein minimal-init-System mit mehreren eng verwandten Prozessen oft die pragmatischere Lösung.


# supervisord.conf — manage multiple related processes in one container
[supervisord]
nodaemon=true    ; Keep supervisor as PID 1 in the foreground
user=root
logfile=/dev/null
pidfile=/tmp/supervisord.pid

[program:php-fpm]
command=/usr/local/sbin/php-fpm -F
autostart=true
autorestart=true
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:queue-worker]
command=/usr/local/bin/php /var/www/html/bin/magento queue:consumers:start async.operations.all --single-thread
autostart=true
autorestart=true
priority=20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

# Dockerfile: use supervisor as the container entrypoint
# FROM php:8.4-fpm-alpine
# RUN apk add --no-cache supervisor
# COPY supervisord.conf /etc/supervisord.conf
# CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

6. Supervisor und s6-overlay: mehrere Prozesse sauber verwalten

Wenn mehrere Prozesse in einem Container betrieben werden sollen, ist Supervisor ein bewährtes Werkzeug. Supervisor läuft als PID 1 im Vordergrund (nodaemon=true), überwacht alle konfigurierten Prozesse, startet sie bei Abstürzen neu und leitet Logs korrekt an stdout/stderr weiter. Der SIGTERM-Handler von Supervisor sorgt für einen geordneten Graceful-Shutdown aller verwalteten Prozesse bei docker stop. Das löst das PID-1-Problem und das Zombie-Reaping-Problem gleichzeitig.

s6-overlay ist eine modernere Alternative zu Supervisor für Docker-Container, die auf dem s6-Init-System aufbaut. Es ist schlanker als Supervisor und besonders verbreitet in Community-Basisimages wie den linuxserver.io-Images. s6-overlay trennt Init-Phasen, Service-Definitionen und Shutdown-Sequenzen sauber und ist flexibler bei bedingten Service-Starts. Für einfache Szenarien ist Supervisor ausreichend; für komplexe Init-Logik mit mehreren Abhängigkeiten und Startup-Reihenfolgen ist s6-overlay die stärkere Wahl – sofern man bereit ist, sich mit seiner Konfigurationsstruktur auseinanderzusetzen.

7. dumb-init und tini: minimale Init-Systeme für Docker

Für Szenarien, in denen tatsächlich nur ein einziger Hauptprozess im Container läuft, aber das PID-1-Problem trotzdem adressiert werden muss, sind dumb-init und tini die leichtgewichtige Lösung. dumb-init ist ein minimales Init-System, das als PID 1 gestartet wird, Signals korrekt an seinen Child-Prozess weiterleitet und Zombie-Prozesse reapet. Es hat keine Service-Management-Funktionen – es ist ausschließlich ein PID-1-Wrapper.

tini ist funktional ähnlich wie dumb-init und ist seit Docker 1.13 als --init-Flag in die Docker-Engine integriert: docker run --init my-image startet tini automatisch als PID 1. In Docker Compose kann init: true auf Service-Ebene gesetzt werden. Für die meisten One Process per Container-Szenarien ist das ausreichend: Die Anwendung läuft korrekt, Signals werden weitergeleitet, Zombies werden aufgeräumt – ohne dass Supervisor oder s6-overlay ins Image müssen.

8. Signal-Handling und Graceful Shutdown

Korrektes Signal-Handling ist die wichtigste Eigenschaft, die ein Container-Prozess haben muss – wichtiger als die Frage, ob es genau ein oder mehrere Prozesse sind. Bei docker stop sendet Docker SIGTERM an PID 1 und wartet standardmäßig zehn Sekunden. Wenn der Prozess nicht reagiert, folgt SIGKILL. Für Magento bedeutet ein harter Kill mitten in einer aktiven Datenbankverbindung potenzielle Datenbeschädigung oder korrupte Cache-Strukturen.

PHP-FPM versteht SIGTERM und fährt Workers nach Abschluss laufender Anfragen sauber herunter. Nginx ebenfalls. Das Problem entsteht, wenn Shell-Skripte als CMD-Eintrag fungieren: Die Shell leitet SIGTERM nicht an ihre Child-Prozesse weiter. Die Lösung ist konsequente Verwendung der Exec-Form für CMD (["php-fpm", "-F"] statt sh -c "php-fpm -F"). Alternativ kann das STOPSIGNAL-Dockerfile-Directive auf ein Signal gesetzt werden, das die Anwendung zuverlässig verarbeitet.

9. Prozessmanagement-Ansätze im Vergleich

Die Wahl des richtigen Prozessmanagement-Ansatzes hängt von der Anzahl der Prozesse, der Komplexität der Init-Logik und dem Team-Know-how ab. Die Tabelle zeigt die wichtigsten Optionen für Docker-Container.

Ansatz PID-1-Safe Multi-Prozess Komplexität
Direkter Prozess (Exec-Form) Abhängig von der App Nein Minimal
dumb-init / tini Ja Nein (nur wrapper) Sehr gering
docker run --init Ja Nein Keine Image-Änderung
Supervisor Ja Ja Mittel
s6-overlay Ja Ja Hoch

Für die meisten Magento-Container mit PHP-FPM, nginx und Redis als separate Dienste ist die Kombination aus direkter Exec-Form und init: true in Compose ausreichend. Supervisor wird sinnvoll, wenn eng gekoppelte Worker-Prozesse neben dem Hauptdienst laufen müssen. s6-overlay kommt ins Spiel bei komplexen Init-Reihenfolgen und bedingten Service-Starts.

Mironsoft

Container-Architektur, Prozessmanagement und Production-Hardening

Container-Prozesse korrekt strukturieren?

Wir analysieren eure Container-Architektur auf PID-1-Probleme, fehlende Signal-Handler und suboptimale Prozessstrukturen – und implementieren die richtige Lösung von dumb-init bis Supervisor.

PID-1-Audit

Container auf Signal-Handling und Zombie-Reaping analysieren und absichern

Graceful Shutdown

docker stop ohne SIGKILL: STOPSIGNAL, Exec-Form und Shutdown-Timeouts korrekt konfigurieren

Prozess-Architektur

Supervisor- oder s6-overlay-Setup für Multi-Prozess-Container im Produktionsbetrieb

10. Zusammenfassung

Das One Process per Container-Prinzip ist ein wertvolles Designziel, aber kein absolutes Gesetz. Korrekt verstanden bedeutet es: Ein Container hat eine einzige Verantwortlichkeit und kann unabhängig skaliert, aktualisiert und neu gestartet werden. Das wichtigere technische Problem – das PID-1-Problem mit fehlendem Signal-Handling und Zombie-Reaping – ist orthogonal zur Frage der Prozessanzahl und muss in jedem Container adressiert werden.

Für einfache Fälle genügt die Exec-Form für CMD zusammen mit init: true in Docker Compose. Für eng gekoppelte Prozesse ist Supervisor mit nodaemon=true die pragmatische Lösung. s6-overlay empfiehlt sich für komplexe Init-Szenarien. Das Wichtigste: Container sollen bei docker stop sauber herunterfahren – Graceful Shutdown setzt voraus, dass PID 1 SIGTERM versteht und weiterleitet. Wer das sicherstellt, hat die wichtigste Eigenschaft eines produktionstauglichen Containers erfüllt.

One Process per Container — Das Wichtigste auf einen Blick

Das echte Prinzip

One Concern per Container, nicht wortwörtlich ein Betriebssystem-Prozess. PHP-FPM mit Master und Workern ist eine Verantwortlichkeit.

PID-1-Problem

Shell als PID 1 leitet SIGTERM nicht weiter. Exec-Form für CMD oder dumb-init/tini als minimales Init-System verwenden.

Graceful Shutdown

init: true in Compose oder dumb-init im Dockerfile. STOPSIGNAL korrekt setzen. docker stop wartet 10s – dann SIGKILL.

Multi-Prozess

Supervisor mit nodaemon=true für eng gekoppelte Prozesse. s6-overlay für komplexe Init-Reihenfolgen. Beide lösen PID-1 und Zombie-Reaping.

11. FAQ: One Process per Container

1Bedeutet One Process per Container wirklich ein einziger OS-Prozess?
Nein. Eine Verantwortlichkeit pro Container. PHP-FPM mit Workers ist eine Verantwortlichkeit. Das Prinzip richtet sich gegen Container, die Webserver, DB und Worker gleichzeitig betreiben.
2Was ist das PID-1-Problem in Docker?
PID 1 bekommt Signals anders als andere Prozesse. Ohne SIGTERM-Handler: docker stop → wartet 10s → SIGKILL. Keine Chance für Graceful Shutdown.
3Was macht dumb-init?
Minimales Init als PID 1. Leitet Signals weiter, reapet Zombies. Kein Service-Management. Reicht für Single-Process-Containers.
4dumb-init vs. tini?
tini ist in Docker integriert: init: true in Compose, kein Image-Change nötig. dumb-init muss ins Image, erlaubt aber gezieltere Konfiguration.
5Wann Supervisor in Docker einsetzen?
Wenn mehrere eng gekoppelte Prozesse immer zusammen deployed werden. PHP-FPM plus Queue-Worker desselben Codes – sinnvoller Kandidat für Supervisor.
6Warum Shell als CMD ein Problem?
Shell leitet SIGTERM nicht weiter. Anwendung bekommt kein Signal, kein Graceful Shutdown. Exec-Form oder exec in Shell-Skripten als Lösung.
7Supervisor vs. s6-overlay?
Supervisor: einfachere ini-Konfiguration, gut dokumentiert. s6-overlay: schlanker, komplexe Init-Reihenfolgen, steilere Lernkurve. Für einfache Fälle Supervisor.
8Wie lange wartet docker stop?
Standard 10 Sekunden. stop_grace_period: 30s in Compose oder -t 30 beim Befehl erhöht den Timeout. Für PHP-FPM mit langen Requests empfehlen sich 30-60s.
9Was ist STOPSIGNAL im Dockerfile?
Definiert das Signal bei docker stop. Standard: SIGTERM. Nginx reagiert schneller auf SIGQUIT. STOPSIGNAL SIGQUIT im Dockerfile ändert das Verhalten image-weit.
10Zwei unabhängige Dienste in einem Container?
Technisch möglich, architektonisch nicht empfohlen. Unabhängiges Skalieren und Updaten nicht mehr möglich. Unterläuft alle Vorteile von Container-Orchestrierung.