PHP-FPM Process Manager Tuning unter Linux
AI generated
$
/etc
Linux · PHP-FPM · Magento · Performance
PHP-FPM Process Manager Tuning unter Linux
pm.max_children richtig berechnen statt raten

Der PHP-FPM Process Manager entscheidet, wie viele parallele Requests ein Magento-Server verarbeiten kann, bevor Anfragen in der Warteschlange stehen bleiben oder der Arbeitsspeicher durch zu viele Worker aufgebraucht wird. Wer pm.max_children nach Bauchgefuehl setzt, riskiert entweder verschenkte Kapazitaet oder einen Out-of-Memory-Absturz unter Last.

18 Min. Lesezeit pm.max_children · static · dynamic · ondemand PHP-FPM 8.x · Linux · Magento 2

1. Warum der PHP-FPM Process Manager ueber die Kapazitaet entscheidet

Der PHP-FPM Process Manager ist die Komponente, die entscheidet, wie viele PHP-Worker-Prozesse gleichzeitig auf einem Linux-Server laufen duerfen. Jeder eingehende HTTP-Request an Magento wird von genau einem Worker-Prozess bearbeitet, solange dieser Prozess beschaeftigt ist, kann er keinen zweiten Request annehmen. Reicht die Anzahl der Worker nicht aus, stapeln sich Requests im Backlog des Unix-Sockets oder TCP-Ports, bis entweder ein Worker frei wird oder der Client einen Timeout erhaelt.

Das Problem in der Praxis: Viele Server laufen entweder mit den Standardwerten aus der Distribution, oder mit Werten, die einmal grosszuegig geschaetzt und nie wieder ueberprueft wurden. Beides fuehrt zu Problemen. Zu wenige Worker bremsen den Checkout unter Last aus, zu viele Worker konkurrieren um denselben Arbeitsspeicher und provozieren den OOM-Killer. Der PHP-FPM Process Manager muss deshalb auf Basis von echten Messwerten konfiguriert werden, nicht auf Basis von Vermutungen.

Dieser Artikel zeigt, wie man die passende Anzahl an Worker-Prozessen fuer einen Magento-Server unter Linux berechnet, welcher der drei Betriebsmodi fuer welches Lastprofil passt und wie man die Konfiguration mit der eingebauten Status-Seite und dem Slowlog laufend ueberpruft, statt sie einmalig zu setzen und zu vergessen.

2. Die drei Betriebsmodi: static, dynamic, ondemand

Der PHP-FPM Process Manager kennt drei Modi, die ueber die Direktive pm im Pool ausgewaehlt werden. Im Modus static startet PHP-FPM sofort exakt pm.max_children Worker-Prozesse und haelt diese Anzahl konstant, unabhaengig von der aktuellen Last. Das liefert die vorhersehbarste Speichernutzung, weil die maximale Prozessanzahl von Anfang an feststeht und sich waehrend des Betriebs nicht aendert.

Im Modus dynamic startet PHP-FPM mit pm.start_servers Worker-Prozessen und skaliert dann zwischen pm.min_spare_servers und pm.max_spare_servers je nach Auslastung, bis maximal pm.max_children erreicht ist. Dieser Modus passt sich an schwankende Last an, verbraucht in ruhigen Phasen weniger Speicher, erzeugt aber bei ploetzlichen Lastspitzen einen kurzen Verzoegerungsmoment, weil neue Worker-Prozesse erst gestartet werden muessen, bevor sie Requests annehmen koennen.

Der dritte Modus ondemand startet gar keine Worker im Leerlauf. Erst wenn ein Request eintrifft, wird ein Prozess erzeugt, nach pm.process_idle_timeout Sekunden ohne neue Anfrage wird er wieder beendet. Dieser Modus eignet sich fuer Staging-Umgebungen oder selten genutzte Zweitdomains, ist aber fuer ein produktives Magento-Frontend mit staendigem Traffic die falsche Wahl, weil jeder neue Prozessstart Zeit kostet, die auf einem viel besuchten Shop spuerbar wird.


; /etc/php/8.3/fpm/pool.d/magento.conf
; PHP-FPM Process Manager mode comparison — same pool, three variants

; Variant A: static — fixed worker count, most predictable memory usage
pm = static
pm.max_children = 40

; Variant B: dynamic — scales between min and max based on load
pm = dynamic
pm.max_children = 40
pm.start_servers = 12
pm.min_spare_servers = 8
pm.max_spare_servers = 20

; Variant C: ondemand — no idle workers, spawns on first request
pm = ondemand
pm.max_children = 40
pm.process_idle_timeout = 30s

3. Die Formel fuer pm.max_children

Der zentrale Fehler beim PHP-FPM Process Manager Tuning ist, pm.max_children ohne Bezug zum verfuegbaren Arbeitsspeicher zu setzen. Die korrekte Formel lautet: verfuegbarer RAM fuer PHP-FPM, geteilt durch den durchschnittlichen Speicherverbrauch eines einzelnen Worker-Prozesses unter realer Last. Der verfuegbare RAM ist dabei nicht der gesamte Server-Arbeitsspeicher, sondern der Anteil, der nach Abzug von MySQL, Redis, Nginx, Elasticsearch und dem Betriebssystem selbst uebrig bleibt.

Den durchschnittlichen Speicherverbrauch pro Worker misst man am zuverlaessigsten mit ps --sort=-rss -o pid,rss,cmd -C php-fpm8.3 waehrend einer realistischen Lastphase, nicht direkt nach dem Neustart des Dienstes, wenn der PHP-Opcode-Cache noch kalt ist. Ein Magento-2-Worker mit vollem Opcache und aktivem Objektmanager liegt haeufig zwischen 90 und 180 Megabyte RSS, abhaengig von installierten Modulen und der genutzten PHP-Version. Wer diesen Wert einmalig misst und danach jaehrlich neu validiert, trifft deutlich zuverlaessigere Entscheidungen als mit einer pauschalen Kopiervorlage aus einem Forum.

Ein Beispiel: Ein Server mit 16 GB RAM, von denen MySQL 6 GB, Redis 1 GB und das Betriebssystem samt Nginx etwa 1 GB beanspruchen, hat 8 GB fuer PHP-FPM uebrig. Bei durchschnittlich 130 MB pro Worker ergibt das rechnerisch 8000 MB / 130 MB, also rund 61 moegliche Worker. In der Praxis zieht man davon einen Sicherheitspuffer von 15 bis 20 Prozent ab, um Lastspitzen mit groesseren Requests abzufangen, was pm.max_children auf einen Wert um 48 bis 50 bringt.


#!/usr/bin/env bash
# measure-fpm-memory.sh — average RSS per PHP-FPM worker under real load
set -euo pipefail

FPM_PROCESS_NAME="php-fpm8.3"

# Sum RSS of all worker processes (excludes the master process by name filter)
total_kb=$(ps --no-headers -o rss -C "$FPM_PROCESS_NAME" | awk '{sum+=$1} END {print sum}')
worker_count=$(pgrep -c -f "$FPM_PROCESS_NAME: pool")

if [[ -z "$worker_count" || "$worker_count" -eq 0 ]]; then
  echo "[ERROR] No active workers found for $FPM_PROCESS_NAME" >&2
  exit 1
fi

avg_mb=$(( total_kb / worker_count / 1024 ))
echo "Active workers: $worker_count"
echo "Average RSS per worker: ${avg_mb} MB"
echo "Suggested pm.max_children for 8000 MB budget: $(( 8000 / avg_mb ))"

4. Eine vollstaendige Pool-Konfiguration fuer Magento

Neben pm und pm.max_children braucht ein produktiver PHP-FPM Process Manager weitere Feineinstellungen, die in der Praxis oft uebersehen werden. Die Direktive pm.max_requests beendet einen Worker nach einer festen Anzahl bearbeiteter Requests und startet ihn neu, das begrenzt Speicher-Leaks durch Extensions oder fehlerhafte Module, ohne dass ein Administrator eingreifen muss. Fuer Magento hat sich ein Wert zwischen 500 und 1000 bewaehrt, je nachdem wie speicherintensiv die installierten Third-Party-Module sind.

Getrennte Pools fuer unterschiedliche Anwendungsfaelle sind ein weiteres wichtiges Muster. Der Frontend-Pool fuer Storefront-Traffic sollte von einem separaten Pool fuer Cron-Jobs und Indexer-Prozesse getrennt werden, weil lang laufende Indexer-Aufgaben sonst Worker-Kapazitaet blockieren, die eigentlich fuer Kundenanfragen gedacht ist. Getrennte Unix-Sockets pro Pool erlauben es, in Nginx gezielt zu routen und im Monitoring pro Pool getrennte Metriken zu erfassen.


; /etc/php/8.3/fpm/pool.d/magento-frontend.conf
[magento-frontend]
user = magento
group = magento
listen = /run/php/magento-frontend.sock
listen.owner = www-data
listen.group = www-data

pm = static
pm.max_children = 48
pm.max_requests = 800

; Do not let a single slow request block indefinitely
request_terminate_timeout = 60s

pm.status_path = /fpm-status-frontend
ping.path = /fpm-ping

php_admin_value[memory_limit] = 756M
php_admin_value[opcache.max_accelerated_files] = 60000

5. Die Status-Seite als Live-Diagnose

Der eingebaute pm.status_path ist eines der am wenigsten genutzten Werkzeuge im PHP-FPM Process Manager. Aktiviert liefert er in Echtzeit die Anzahl aktiver und im Leerlauf befindlicher Worker, die Gesamtzahl bisheriger Requests, die Anzahl der Requests, die auf einen freien Worker warten mussten, sowie den Zeitpunkt des letzten Neustarts. Genau diese Werte zeigen, ob pm.max_children zu niedrig gesetzt ist, lange bevor Kunden Timeouts bemerken.

Der wichtigste einzelne Wert auf der Status-Seite ist listen queue. Steht diese Zahl regelmaessig ueber null, bedeutet das: Requests warten, weil alle konfigurierten Worker bereits beschaeftigt sind. Ein dauerhaft gefuelltes Listen-Queue ist das zuverlaessigste Fruehwarnsignal dafuer, dass pm.max_children erhoeht werden muss, noch bevor Nutzer ueberhaupt eine Verzoegerung spueren.


#!/usr/bin/env bash
# check-fpm-status.sh — poll the FPM status page and alert on queue backlog
set -euo pipefail

STATUS_URL="http://127.0.0.1/fpm-status-frontend?json"
MAX_QUEUE=5

response=$(curl -fsS "$STATUS_URL")
listen_queue=$(echo "$response" | jq -r '."listen queue"')
active=$(echo "$response" | jq -r '."active processes"')
idle=$(echo "$response" | jq -r '."idle processes"')

echo "Active: $active | Idle: $idle | Listen queue: $listen_queue"

if (( listen_queue > MAX_QUEUE )); then
  echo "[WARN] Listen queue exceeds threshold — consider raising pm.max_children" >&2
  exit 1
fi

6. Slowlog: langsame Requests aufspueren

Neben der Anzahl der Worker beeinflusst auch die Dauer einzelner Requests die Kapazitaet des PHP-FPM Process Manager. Ein Worker, der 8 Sekunden fuer einen langsamen Datenbank-Query braucht, blockiert waehrend dieser Zeit vollstaendig einen Slot, der sonst mehrere schnelle Requests haette bedienen koennen. Die Direktive request_slowlog_timeout zusammen mit slowlog schreibt bei jedem Request, der die konfigurierte Schwelle ueberschreitet, einen vollstaendigen PHP-Backtrace in eine separate Logdatei.

Dieser Backtrace zeigt exakt, in welcher Methode und Zeile der Code haengt, wenn ein Request als langsam markiert wird, ohne dass ein Profiler wie Xdebug im Produktivbetrieb aktiviert werden muss. In der Praxis findet man mit dem Slowlog haeufig ungecachte Preisregeln, fehlende Datenbank-Indizes oder externe API-Aufrufe ohne Timeout, die im Normalbetrieb kaum auffallen, unter Last aber ganze Worker-Pools blockieren koennen.


; /etc/php/8.3/fpm/pool.d/magento-frontend.conf (excerpt)
; Log full PHP backtrace for any request slower than 5 seconds
request_slowlog_timeout = 5s
slowlog = /var/log/php/magento-frontend-slow.log

; Read a captured trace with:
; tail -n 40 /var/log/php/magento-frontend-slow.log

7. systemd, Limits und der PHP-FPM-Service

Der PHP-FPM Process Manager laeuft unter Linux als systemd-Dienst, und dessen Unit-Datei setzt eigene Ressourcen-Grenzen, die unabhaengig von der Pool-Konfiguration greifen. Die Direktive LimitNOFILE in der systemd-Unit begrenzt die maximale Anzahl offener Dateideskriptoren pro Prozess, ein zu niedriger Wert fuehrt bei vielen gleichzeitigen Datenbank- und Cache-Verbindungen zu kryptischen "Too many open files"-Fehlern, die auf den ersten Blick nichts mit dem Process Manager zu tun zu haben scheinen.

Ein zweiter oft uebersehener Punkt: systemctl reload php8.3-fpm laedt die Konfiguration neu, ohne bestehende Worker abrupt zu beenden, waehrend systemctl restart alle Verbindungen sofort trennt. Fuer Aenderungen an der Pool-Konfiguration im laufenden Betrieb ist reload fast immer die richtige Wahl, weil aktive Requests dabei zu Ende laufen duerfen, bevor der alte Master-Prozess durch den neuen ersetzt wird.


# /etc/systemd/system/php8.3-fpm.service.d/override.conf
# systemctl edit php8.3-fpm — creates this drop-in file

[Service]
LimitNOFILE=65536
OOMScoreAdjust=-500

# Apply changes:
# systemctl daemon-reload
# systemctl restart php8.3-fpm

# Verify the effective limit for a running worker:
# cat /proc/$(pgrep -f "php-fpm8.3: pool magento-frontend" | head -1)/limits | grep "open files"

8. Typische Fehler beim Process Manager Tuning

Der haeufigste Fehler ist, pm.max_children auf einen viel zu hohen Wert zu setzen, in der Annahme, mehr Worker koennten nie schaden. Das Gegenteil ist der Fall: Wenn die Summe aus allen Worker-Prozessen mehr Speicher beansprucht, als physisch verfuegbar ist, greift der Linux-Kernel zum Swapping oder der OOM-Killer beendet zufaellig Prozesse, oft trifft es dabei nicht einmal den PHP-FPM-Worker selbst, sondern MySQL oder Redis. Ein zu hoher Wert ist damit kein sicherer Puffer, sondern ein latentes Ausfallrisiko fuer den gesamten Server.

Ein zweiter Fehler ist, den Modus dynamic mit sehr niedrigem pm.min_spare_servers zu betreiben und dabei zu erwarten, dass PHP-FPM Lastspitzen ohne spuerbare Verzoegerung abfaengt. Jeder neu gestartete Worker-Prozess muss PHP initialisieren, den Opcache anbinden und Autoloader-Klassen laden, was auf einem produktiven Magento-Server durchaus 100 bis 300 Millisekunden dauern kann. Wer regelmaessige, planbare Lastspitzen hat, etwa durch geplante Marketing-Kampagnen, faehrt mit static und einer grosszuegig genug bemessenen fixen Anzahl an Workern deutlich stabiler.

9. Die drei Modi im direkten Vergleich

Die Wahl des richtigen Modus fuer den PHP-FPM Process Manager haengt direkt vom Lastprofil des Servers ab. Die folgende Tabelle fasst zusammen, wann welcher Modus die bessere Wahl ist.

Modus Speicherverbrauch im Leerlauf Reaktion auf Lastspitzen Empfehlung
static Konstant hoch, vorhersehbar Sofort, keine Startverzoegerung Produktives Magento-Frontend
dynamic Niedrig bis mittel, schwankend Kurze Verzoegerung beim Hochskalieren Cron-Pools, wechselnde Last
ondemand Minimal, keine Idle-Worker Deutliche Verzoegerung beim Kaltstart Staging, seltene Zweitdomains

Fuer die meisten produktiven Magento-Installationen mit gleichmaessig hohem Traffic ist static die robustere Wahl, weil der PHP-FPM Process Manager dabei keine Zeit mit dem Starten neuer Prozesse verliert. Ein hybrider Ansatz mit getrennten Pools, static fuer den Frontend-Storefront und ondemand fuer selten aufgerufene Backend-Werkzeuge, kombiniert die Vorteile beider Modi, ohne Kompromisse bei der Storefront-Performance einzugehen.

Mironsoft

Linux-Server-Tuning und Performance-Optimierung fuer Magento

PHP-FPM richtig konfiguriert statt geraten?

Wir analysieren Ihre bestehende PHP-FPM Process Manager Konfiguration, berechnen die passende Worker-Anzahl anhand echter Speicher-Messwerte und richten Monitoring fuer Status-Seite und Slowlog ein.

Kapazitaets-Analyse

Speicherverbrauch pro Worker messen und pm.max_children korrekt berechnen

Pool-Aufteilung

Getrennte Pools fuer Storefront, Cron und Indexer sauber konfigurieren

Monitoring

Status-Seite und Slowlog in bestehendes Monitoring integrieren

10. Zusammenfassung

Der PHP-FPM Process Manager ist keine einmalige Einstellung, sondern ein laufend zu ueberpruefender Bestandteil der Server-Kapazitaetsplanung. Die Formel aus verfuegbarem RAM geteilt durch gemessenen Speicherverbrauch pro Worker liefert einen belastbaren Ausgangswert fuer pm.max_children, statt eine geschaetzte Zahl aus einem Forum zu uebernehmen. Der Modus static passt fuer die meisten produktiven Magento-Frontends am besten, weil er keine Verzoegerung durch neu startende Worker-Prozesse erzeugt.

Status-Seite und Slowlog liefern die Messdaten, um die Konfiguration nicht nur einmalig zu setzen, sondern kontinuierlich zu validieren. Ein gefuelltes Listen-Queue zeigt, dass mehr Worker gebraucht werden, ein voller Slowlog zeigt, welche konkreten Codepfade Worker-Kapazitaet unnoetig lange blockieren. Wer beide Signale in ein bestehendes Monitoring einbindet, erkennt Kapazitaetsprobleme, bevor Kunden im Checkout Timeouts erleben.

PHP-FPM Process Manager Tuning — Das Wichtigste auf einen Blick

Formel

Verfuegbarer RAM geteilt durch gemessene RSS pro Worker, abzueglich 15 bis 20 Prozent Sicherheitspuffer.

Modus-Wahl

static fuer gleichmaessig hohen Traffic, ondemand nur fuer selten genutzte Zweitdomains.

Monitoring

pm.status_path und slowlog aktivieren, Listen-Queue als Fruehwarnsignal ueberwachen.

Betrieb

systemctl reload statt restart fuer Konfigurationsaenderungen im laufenden Betrieb nutzen.

11. FAQ: PHP-FPM Process Manager Tuning unter Linux

1Was macht der PHP-FPM Process Manager genau?
Er verwaltet, wie viele PHP-Worker-Prozesse gleichzeitig laufen und wie sie je nach Modus gestartet, skaliert oder beendet werden.
2Wie berechne ich pm.max_children richtig?
Verfuegbaren RAM durch gemessene RSS pro Worker teilen, dann 15 bis 20 Prozent Sicherheitspuffer abziehen.
3Welcher Modus ist fuer Magento am besten?
Static fuer gleichmaessig hohen Traffic, dynamic fuer Cron-Pools, ondemand nur fuer selten genutzte Zweitdomains.
4Was zeigt die pm.status_path Seite?
Aktive und Leerlauf-Worker, Requestzahlen und vor allem die Listen-Queue als Fruehwarnsignal.
5Wozu dient der Slowlog?
Schreibt bei langsamen Requests einen vollstaendigen PHP-Backtrace, ohne dass ein Profiler aktiviert werden muss.
6Warum getrennte Pools fuer Frontend und Cron?
Lang laufende Cron- und Indexer-Prozesse blockieren sonst Worker, die fuer Kundenanfragen gedacht sind.
7Was passiert bei zu hoch gesetztem pm.max_children?
Zu viele Worker koennen Swapping oder den OOM-Killer ausloesen, oft trifft es dabei MySQL oder Redis statt PHP-FPM.
8Was bewirkt pm.max_requests?
Startet einen Worker nach einer festen Anzahl Requests automatisch neu und begrenzt so Speicher-Leaks.
9Reload oder restart fuer Konfigurationsaenderungen?
Reload laesst aktive Requests zu Ende laufen, restart trennt sofort. Fuer laufenden Betrieb ist reload vorzuziehen.
10Wie haengt LimitNOFILE damit zusammen?
systemd begrenzt offene Dateideskriptoren pro Prozess. Ein zu niedriger Wert fuehrt zu Too-many-open-files-Fehlern.