...

Apache Worker Modelle: Prefork, Worker und Event im Vergleich

Apache Worker Modelle bestimmen, wie der Apache HTTP Server Anfragen parallel verarbeitet und Ressourcen nutzt – konkret über die MPMs Prefork, Worker und Event. In diesem Beitrag zeige ich, wie sich die drei Modelle technisch unterscheiden, welche Auswirkungen sie auf Leistung und Skalierung haben und welches Setup in realen Szenarien überzeugt.

Zentrale Punkte

Die folgenden Stichpunkte geben dir einen schnellen Überblick über die wichtigsten Unterschiede und Entscheidungen rund um die drei MPMs; danach gehe ich auf Details ein und liefere Praxiswissen.

  • Prefork: Prozessbasiert, hohe Isolation, hoher RAM-Bedarf.
  • Worker: Threads je Prozess, gute Skalierung, sensibel bei Keep-Alive.
  • Event: Event-Loop entkoppelt Verbindung und Anfrage, sehr effizient.
  • Tuning: StartServers, ThreadsPerChild, MaxRequestWorkers gezielt setzen.
  • HTTP/2: Funktioniert sinnvoll mit Worker und Event, nicht mit Prefork.

Was MPMs in Apache steuern

Mit den Multi-Processing Modules (MPMs) lege ich fest, ob Apache pro Anfrage Prozesse oder Threads nutzt und wie der Server Parallelität bereitstellt. Prefork erzeugt viele Prozesse mit je einem Thread, Worker wenige Prozesse mit vielen Threads, Event baut auf Worker auf und entkoppelt Verbindungen von der eigentlichen Bearbeitung. Diese Wahl wirkt sich direkt auf Speicher, CPU-Auslastung und Latenzen aus. Ich berücksichtige deshalb immer Sessions, Keep-Alive, Protokolle wie HTTP/2 und die verwendeten Module. Wer MPMs ignoriert, verschenkt messbare Performance und riskiert Engpässe.

Prefork: Prozessisolierung und Kompatibilität

Prefork setzt auf einzelne Prozesse je Anfrage und liefert damit starke Isolation. Stürzt ein Prozess ab, bleiben andere unberührt – das erhöht die Fehlertoleranz bei unsauberem Code oder alten Erweiterungen. Der Preis: Jeder Prozess bringt eigenen Overhead mit, sodass der RAM-Verbrauch pro paralleler Verbindung steigt. Bei 100 gleichzeitigen Anfragen entstehen 100 Prozesse, was ich nur bei geringer bis mittlerer Last akzeptabel finde. Ich nutze Prefork vor allem, wenn ich Module ohne Thread-Sicherheit einsetzen muss oder wenn legacy CGI-Skripte hohe Trennung erfordern.

Worker: Threads und hohe Parallelität

Beim Worker-Modell führen einzelne Prozesse mehrere Threads aus, wodurch der Speicherbedarf pro Anfrage sinkt. Diese Architektur erlaubt deutlich mehr Concurrency auf derselben Hardware und eignet sich für hohe Zugriffszahlen. Lange Keep-Alive-Verbindungen können jedoch Threads binden und damit Kapazität blockieren. In sauberen, thread-sicheren Setups – etwa mit PHP-FPM – erreiche ich mit Worker sehr gute RPS-Werte bei moderatem RAM-Einsatz. Ich setze Worker ein, wenn ich eine effiziente, threadbasierte Skalierung brauche und Keep-Alive sinnvoll gesteuert wird.

Event: Nicht-blockierende Keep-Alive-Strategie

Event baut auf dem Worker-Modell auf, behebt aber die Keep-Alive-Schwäche durch einen Event-Loop. Ein Thread bearbeitet nur die eigentliche Anfrage; das Halten der Verbindung übernimmt ein separater Mechanismus. Dadurch bleiben Threads frei, und die Maschine verarbeitet mehr gleichzeitige Sessions mit niedriger Latenz. Bei HTTP/2-Verbindungen überzeugt Event besonders, da Multiplexing und lange Verbindungen ohne Thread-Verschwendung laufen. In modernen Setups starte ich mit Event als Standardbasis und passe nur um, wenn Module oder Legacy-Anforderungen entgegenstehen.

Tabellarischer Vergleich der MPMs

Die folgende Tabelle verdichtet die zentralen Unterschiede, damit ich auf einen Blick einschätzen kann, welches Modell zur Last- und Modul-Situation passt. Vor dem Wechsel prüfe ich stets die Thread-Sicherheit aller Module und die erwartete Verbindungsdauer. Danach ordne ich MaxRequestWorkers, ThreadsPerChild und weitere Limits den verfügbaren Ressourcen zu. Die Tabelle hilft mir, erste Annahmen zu treffen, ersetzt aber keine Lasttests unter realen Bedingungen. Gerade bei Event lohnt eine Messung mit langen Keep-Alive-Phasen und HTTP/2, um die Vorteile sichtbar zu machen.

MPM Prozesse/Threads RAM-Verbrauch Zuverlässigkeit Typische Nutzung
Prefork 1 Thread je Prozess Hoch Hoch (gute Isolation) Geringe/mittlere Last, Module ohne Thread-Sicherheit, klassische CGI
Worker Mehrere Threads je Prozess Mittel Mittel Hohe Last mit thread-sicherem Stack, z. B. PHP-FPM
Event Threads + Event-Loop Niedrig Hoch Sehr hohe Last, lange Verbindungen, HTTP/2

Ich lese aus der Tabelle: Prefork punktet bei Abschirmung, Worker bei Effizienz und Event bei maximaler Auslastung unter gleichzeitigen Verbindungen. Für neue Projekte nutze ich Event, sofern keine Inkompatibilitäten dagegen sprechen. Für beständige Legacy-Stacks kann Prefork weiterhin sinnvoll sein. Wer erst migriert, erreicht mit Worker oft schon deutliche Fortschritte. Die Wahl bleibt am Ende eine Abwägung aus Modulen, Traffic-Profil und Hardware.

Leistung messen: Benchmarks und Metriken

Ohne Messung bleibt jede MPM-Entscheidung eine Vermutung. In Vergleichstests liefert Worker bei hoher Last bis zu rund 50 % mehr Requests pro Sekunde als Prefork; Event legt darüber hinaus zu, vor allem bei langen Keep-Alive-Phasen. Beim Speicher zeigen sich klare Unterschiede: Bei etwa 1000 gleichzeitigen Verbindungen landen Prefork-Setups grob bei 2–4 GB RAM, Worker bei 1–2 GB, Event meist unter 1 GB. Ich prüfe nicht nur RPS, sondern auch Time to First Byte, 95./99.-Perzentile und Fehlerquoten. Entscheidend ist das Lastprofil der Anwendung, denn kurze, schnelle Requests verhalten sich anders als Streaming oder WebSockets.

Tuning-Parameter erklärt: StartServers, ThreadsPerChild, MaxRequestWorkers

Ich beginne mit konservativen Werten und skaliere hoch, bis ich die gewünschte Auslastung treffe. Für Prefork setze ich MaxRequestWorkers anhand des verfügbaren Speichers und der Prozessgröße an; für Worker und Event plane ich ThreadsPerChild und die Zahl der Prozesse so, dass ThreadsPerChild × Prozesse = MaxRequestWorkers ergibt. Dabei achte ich auf genügend Puffer, damit Lastspitzen nicht zu 503-Fehlern führen. Ein sauberer StartServers-Wert verhindert unnötige Forks unter Kaltstartbedingungen. Wer tiefer einsteigen will, findet Hintergrundwissen zur Threadpool-Optimierung, das sich direkt in Apache-Setups übertragen lässt.

# Beispiel: Event (Debian/Ubuntu)
a2dismod mpm_prefork mpm_worker
a2enmod mpm_event
systemctl restart apache2

# Worker-Threading sinnvoll ausreizen
# /etc/apache2/mods-available/mpm_event.conf
ServerLimit           16
StartServers          4
ThreadsPerChild       50
MaxRequestWorkers     800
MaxConnectionsPerChild 0

Ich prüfe danach den Effekt mit Benchmarks und kontrolliere, ob die CPU ausreichend arbeiten kann, ohne in Kontextwechsel zu ertrinken. Parallel beobachte ich RAM-Trends, Swap-Aktivität und offene File-Deskriptoren. Werden Queues sichtbar voll, erhöhe ich vorsichtig MaxRequestWorkers oder reduziere Keep-Alive-Zeiten. Läuft alles rund, sichere ich die Konfiguration und dokumentiere die Grenzwerte.

Keep-Alive, HTTP/2 und Thread-Contention

Keep-Alive reduziert TCP-Handshakes, kann aber Threads binden – besonders beim Worker-MPM, das Verbindungen direkt auf Threads legt. Event löst genau diesen Effekt, indem es die Verbindung über einen Event-Loop abwickelt und Threads nur für aktive Arbeit nutzt. Für HTTP/2 setze ich deshalb Worker oder Event ein, weil Multiplexing sonst ausgebremst wird. In der Praxis beobachte ich gern die Warteschlangenlänge und prüfe, ob sich „Thread-Contention“ bemerkbar macht. Tipps dazu habe ich im Beitrag zu Thread-Contention zusammengefasst, den ich für tiefergehende Analysen heranziehe.

Ich passe außerdem KeepAliveTimeout an die Anwendung an, damit inaktive Verbindungen die Kapazität nicht binden. Die ideale Einstellung unterscheidet sich zwischen APIs, klassischen LAMP-Seiten und HTTP/2-basierten Frontends mit vielen Assets. Kommt viel Leerlauf vor, senke ich den Timeout und erhöhe MaxRequestWorkers leicht. Erwarte ich viele kurze Requests, halte ich Keep-Alive moderat, um TCP-Overhead zu sparen. Treten Wartezeiten auf, wechsle ich auf Event oder rüste zusätzliche Instanzen nach.

Praxis-Szenarien und Wahl des richtigen Modells

Für Legacy-Apps mit riskanten Modulen setze ich Prefork ein und profitiere von hoher Abschirmung. Bei moderner PHP-FPM-Architektur mit vielen gleichzeitigen Verbindungen liefert Worker schon sehr gute Ergebnisse. Event drückt die Latenz weiter und skaliert sauber bei langen Sessions, WebSockets und HTTP/2. Auf Shared-Hostings oder bei unklarem Code-Stand fahre ich mit Prefork sicherer, während ich auf VPS und dedizierter Hardware meist Event bevorzuge. Wer Alternativen zu Apache abwägt, findet im kompakten Webserver-Vergleich zusätzliche Entscheidungshilfen zu Nginx und LiteSpeed, die ich situativ prüfe.

Bei Traffic-Spitzen mit Burst-Charakter zahlt sich Event aus, da Threads nicht in Leerlauf verharren. Für CPU-lastige Apps limitiere ich MaxRequestWorkers, um die Maschine nicht zu überbuchen. Steht RAM knapp, verbanne ich Prefork und priorisiere Worker/Event. Bei Multi-Tenant-Umgebungen trennen Container oder cgroups die Services, sodass Worker/Event ihr Potenzial entfalten. Am Ende bestätigt die Messung, welches Modell im eigenen Stack die geringste Latenz liefert.

Konfiguration auf Ubuntu/Debian in der Praxis

Ich aktiviere und deaktiviere MPMs gezielt, teste die Auswirkung und halte Rollback-Optionen bereit. Unter Debian/Ubuntu nutze ich die bekannten Kommandos und prüfe anschließend die Statusausgabe. Danach feile ich an den mpm_*.conf-Dateien und protokolliere Änderungen versioniert. Vor dem Go-Live lasse ich Lastspitzen simulieren, um Deadlocks oder Speicherengpässe früh zu erkennen. Erst wenn Fehlerzähler und Perzentile stimmen, übernehme ich die Werte in Produktion.

# Prefork einschalten
a2dismod mpm_worker mpm_event
a2enmod mpm_prefork
systemctl restart apache2

# Worker einschalten
a2dismod mpm_prefork mpm_event
a2enmod mpm_worker
systemctl restart apache2

# Event einschalten
a2dismod mpm_prefork mpm_worker
a2enmod mpm_event
systemctl restart apache2

# Monitoring
apachectl status
htop
journalctl -u apache2 -f

Ich beobachte parallel Fehler-Logs, um Thread-Sicherheitsprobleme rasch zu finden. Für HTTP/2 prüfe ich, ob das Protokoll korrekt aushandelt und die TLS-Konfiguration passt. Bei auffälligen Latenzen vergleiche ich Prefork/Worker/Event im Wechsel und behalte die RAM-Entwicklung im Blick. Stimmt die Balance nicht, passe ich KeepAlive, Threadanzahl und Limits an. So erreiche ich verlässliche Antwortzeiten ohne Überbuchung.

Thread-Sicherheit und Modul-Kompatibilität

Die wichtigste Vorprüfung vor dem Wechsel von Prefork zu Worker/Event ist die Thread-Sicherheit aller Module. Klassiker: mod_php ist historisch eng mit Prefork verknüpft; in modernen Stacks setze ich stattdessen PHP-FPM via proxy_fcgi ein, damit Apache selbst threadbasiert skalieren kann. Auch Filter- und Auth-Module, selbstgeschriebene Module oder Integrationen (z. B. Bildverarbeitung) müssen als „thread safe“ gelten. Ich prüfe die geladenen Module, werte Release-Notes aus und führe unter Last einen Crash- und Race-Condition-Test durch. Für HTTP/2 gilt: Mit Prefork ist es praktisch keine Option – Worker/Event sind die Voraussetzung, damit Multiplexing und Priorisierung wirken.

Kapazitätsplanung: Speicherbudget realistisch kalkulieren

Ich dimensioniere MaxRequestWorkers nicht „gefühlt“, sondern anhand messbarer Prozess- und Thread-Größen. Vorgehen:

  • Testlast fahren, dann die Resident-Set-Size (RSS) pro Apache-Prozess messen.
  • Bei Worker/Event den pro Thread zusätzlichen Overhead berücksichtigen.
  • Puffer für Kernel, Page Cache, TLS-Session-Cache, Log-Puffer und Upstreams einplanen.
# Prozessgröße abschätzen (Beispiel)
ps -ylC apache2 --sort:rss | awk '{sum+=$8} END {print "RSS (kB) gesamt:",sum}'
ps -L -p <PID_eines_Apache-Kindprozesses> -o pid,tid,psr,stat,rss,cmd
pmap -x <PID> | tail -n 1  # Gesamtsumme je Prozess

Rechenbeispiel: Ein Event-Prozess belegt 25 MB, Threads benötigen im Mittel 1 MB. Bei 16 Prozessen und 50 Threads ergibt das grob 16 × 25 MB + 800 × 1 MB ≈ 1,2 GB. Ich setze MaxRequestWorkers = 800, lasse 30–40 % RAM frei und skaliere nach Messung hoch. Wer Prefork einsetzt, rechnet einfach „Prozessgröße × MaxRequestWorkers“ und bleibt konservativ.

Betriebssystem-Limits, Backlogs und Descriptoren

Apache kann nur so schnell sein wie die zugrunde liegende Plattform. Drei Punkte prüfe ich regelmäßig:

  • Dateideskriptoren: Ein Thread/Prozess öffnet Sockets, Dateien und Pipes. Ich erhöhe LimitNOFILE über systemd und verifiziere die Übernahme.
  • Accept-Backlog: Bei Verbindungsbursts vergrößere ich ListenBacklog und sorge für passende Kernel-Backlogs.
  • Socket-/Timeout-Tuning: RequestReadTimeout, Timeout und KeepAliveTimeout gezielt setzen, um „Slow Clients“ zu entschärfen.
# systemd-Override
systemctl edit apache2
[Service]
LimitNOFILE=65536

# Kernel-Parameter (temporär)
sysctl -w net.core.somaxconn=4096

# Apache: Backlog und Timeouts
Listen 0.0.0.0:443
ListenBacklog 1024
Timeout 60
RequestReadTimeout header=10-20,MinRate=1 body=10,MinRate=500
KeepAliveTimeout 5
MaxKeepAliveRequests 100

Ich halte Timeouts lieber etwas strenger und beobachte die Fehlerraten. Werden legitime lange Uploads erwartet, passe ich Werte gezielt pro VirtualHost an.

Graceful Reloads, Deployments und Container

Im Betrieb bevorzuge ich Reloads ohne Abriss bestehender Verbindungen. apachectl -k graceful oder systemctl reload lädt Konfigurationen neu, lässt laufende Requests aber sauber auslaufen – bei Prefork pro Prozess, bei Worker/Event pro Thread. In Container-Umgebungen plane ich kleinere ServerLimit/ThreadsPerChild, damit Pods zügig starten und beenden. Ich achte auf cgroup-Quotas: Werden CPU-Zeit oder RAM gekappt, muss MaxRequestWorkers entsprechend niedriger liegen, sonst verschiebt sich die Latenz in die 95./99.-Perzentile.

Proxy-/Upstream-Setups richtig dimensionieren

Viele Apache-Instanzen terminieren TLS und proxien danach an PHP-FPM, App-Server oder Microservices. Dabei verknüpfe ich Frontend-Kapazität (MaxRequestWorkers) mit den Upstream-Pools: Für PHP-FPM sind pm.max_children und pm.max_requests die harte Obergrenze. Ich halte das Verhältnis so, dass Apache nicht wesentlich mehr gleichzeitige Anfragen annimmt, als Upstreams verarbeiten können – sonst entstehen Warteschlangen und Timeouts. Für proxy_fcgi und proxy_http setze ich Timeouts explizit und prüfe, ob Keep-Alive zum Upstream sinnvoll ist oder nur Ressourcen bindet.

Monitoring und Diagnose mit dem Scoreboard

Die mod_status-Ausgabe verrät, wie gut das gewählte MPM arbeitet. Ich achte auf die Anteile folgender Zustände: Reading (eingehende Header), Sending (Antwort wird übertragen), Keepalive (verbindungsoffen ohne Arbeit), Waiting (frei). Hohe Anteile von Keepalive bei Worker deuten auf gebundene Threads hin – Event beseitigt genau das. Dauerhaft Reading kann auf langsame Clients oder falsche RequestReadTimeout-Werte hindeuten. Viele Closing/Logging-Zustände unter Spitzenlast sprechen für zu kleine Threadpools oder I/O-Engpässe im Logging.

Sicherheit und Robustheit: Slowloris & Co.

Gegen „Slowloris“-artige Angriffsmuster hilft die Kombination aus Event-MPM, knappen KeepAliveTimeouts und RequestReadTimeout. Prefork schützt zwar durch Prozessisolierung vor Modulabstürzen, bleibt aber anfällig für RAM-Erschöpfung bei vielen Verbindungen. Ich kombiniere Limits auf Webserver-Ebene mit vorgelagerten WAF/Rate-Limits, damit Apache gar nicht erst mit Millionen halboffener Sessions konfrontiert wird. Logs werte ich auf 95./99.-Perzentile aus, weil Angriffe die Verteilungsschwänze aufblähen.

Distribution-Defaults und typische Stolpersteine

Auf vielen Debian/Ubuntu-Installationen ist Event heute Standard. Trotzdem sind Default-Werte oft konservativ (z. B. ThreadsPerChild 25–50). Ich erhöhe diese erst nach Messung. Häufige Fehler:

  • MaxRequestWorkers höher als die verfügbaren File-Deskriptoren.
  • Nicht synchronisierte Limits zwischen Apache und PHP-FPM/App-Servern.
  • Zu hoher KeepAliveTimeout bei Worker mit vielen Mobil-Clients.
  • Fehlender Puffer für Log-I/O – Rotationsjobs blockieren kurzzeitig.

Ich dokumentiere Zielwerte (CPU-Auslastung, RAM, RPS, P95) und sichere die funktionierende Konfiguration versioniert. Erst dann erfolgt die Ausrollung.

Kurz zusammengefasst

Prefork liefert starke Isolation für Legacy-Stacks, kostet aber viel Speicher. Worker bietet eine gute Mitte mit Threads je Prozess und skaliert sauber, solange Keep-Alive nicht unnötig bindet. Event trennt Verbindung und Bearbeitung, erhöht die Auslastung und spielt seine Stärke bei HTTP/2 sowie langen Sessions aus. Ich messe systematisch, passe Limits an und wähle das MPM, das zum Code, zum Traffic-Profil und zur Hardware passt. Mit sauberem Tuning, klaren Messzielen und fokussiertem Monitoring holt Apache aus jedem der drei Modelle Leistung heraus.

Aktuelle Artikel