- Typischer Nutzwert
- Hoch
- Aufwand
- Hoch
- Risiko
- Hoch
- Hosting / Umgebung
- VPS/dedizierter Server oder eigener Plesk-Host; im klassischen Shared Hosting Anbieteraufgabe
- Voraussetzungen
- Root-/Administratorzugriff, repräsentative Lastmessung und geprüftes Rollback
- Aussagekraft
- hoch
- Zuletzt geprüft
- 17. August 2026
Kurzfassung: drei Prüfgrößen statt einer Zauberzahl
pm.max_children ist die Höchstzahl gleichzeitig bearbeiteter PHP-Anfragen eines Pools. Der Wert darf weder das RAM-Budget sprengen noch mehr Parallelität freigeben, als CPU, Datenbank und externe Dienste unter Last stabil verarbeiten. Page-Cache-Treffer erreichen PHP-FPM gar nicht und gehören deshalb nicht in die PHP-Anfragerate.
RAM-Grenze = floor(PHP-RAM-Budget / P95-PSS je Worker)
Lastbedarf = ceil(Spitzenrate ungecachter Requests/s × mittlere FPM-Laufzeit in s × Reservefaktor)
CPU-Bedarf in Cores = Spitzenrate ungecachter Requests/s × mittlere CPU-Zeit je Request in s
Startwert pm.max_children = mindestens Lastbedarf, aber höchstens getestete RAM-/CPU-/DB-Grenze
Ein sinnvoller Reservefaktor liegt als Startpunkt bei 1,2 bis 1,5. Das Ergebnis ist eine prüfbare Ausgangskonfiguration, kein dauerhaft „optimaler“ Wert.
Was die FPM-Parameter tatsächlich bewirken
| Parameter | Wirkung | Praxisregel |
|---|---|---|
pm |
static hält genau pm.max_children Worker; dynamic regelt zwischen Start-/Reservewerten und dem Maximum; ondemand startet Worker erst bei Bedarf. |
dynamic für einen dauerhaft aktiven Shop, ondemand für viele selten besuchte Pools. static nur bei sehr gleichmäßiger, gut vermessener Last. |
pm.max_children |
Maximale Zahl gleichzeitig bedienter Anfragen pro Pool. | Nach RAM und Last begrenzen. Bei mehreren Pools zählt die Summe ihrer möglichen Speicherbelegung. |
pm.start_servers |
Bei dynamic sofort gestartete Worker. |
Zwischen pm.min_spare_servers und pm.max_spare_servers; ungefähr die normale Parallelität vorhalten. |
pm.min_spare_servers |
Gewünschte Mindestzahl untätiger Worker bei dynamic. |
So wählen, dass normale Lastspitzen nicht erst Prozesse starten müssen. |
pm.max_spare_servers |
Gewünschte Höchstzahl untätiger Worker bei dynamic. |
Nicht unnötig hoch; untätige Worker belegen weiterhin RAM. |
pm.process_idle_timeout |
Beendet bei ondemand lange untätige Worker. |
10–30 s ist für viele kleine Hosting-Pools ein brauchbarer Startbereich; Startlatenz beobachten. |
pm.max_requests |
Startet einen Worker nach dieser Zahl von Anfragen neu. | 300–1000 ist ein üblicher Testbereich bei Drittanbieter-Code. Das kaschiert höchstens schleichendes Wachstum und ersetzt keine Ursachenanalyse. |
request_slowlog_timeout / slowlog |
Schreibt nach einer Schwelle einen PHP-Backtrace langsamer Anfragen. | Gezielt aktivieren, Log schützen und rotieren; so wird aus „mehr Worker“ eine Ursachenanalyse. |
pm.status_path |
Liefert unter anderem Queue, aktive/untätige Prozesse, Maximalwerte und max children reached. |
Nur intern oder für bekannte IPs freigeben; die Statusseite kann URLs und Ressourcendaten offenlegen. |
Nicht verwechseln: memory_limit begrenzt den Speicher einer einzelnen PHP-Anfrage. Es ist weder der normale Verbrauch eines Workers noch ein geeigneter Divisor für die FPM-Rechnung.
Schritt 1: das echte RAM-Budget bestimmen
Rechne durchgehend in MiB (1 GiB = 1024 MiB). Ermittle während einer repräsentativen Spitzenphase den tatsächlichen Bedarf von Betriebssystem, Webserver, MariaDB/MySQL einschließlich Verbindungs- und temporärer Puffer, Redis, OPcache, Monitoring, Backups und sonstigen Diensten. Ziehe zusätzlich eine Sicherheitsreserve ab.
PHP-RAM-Budget = Gesamt-RAM
− Betriebssystem und Webserver
− Datenbankbudget
− Redis/OPcache/weitere Dienste
− Jobs, Backups und Monitoring
− Sicherheitsreserve
Auf einem All-in-one-Server sind 10–20 % Reserve ein vernünftiger Anfang. Swap ist kein zusätzliches FPM-Budget: Dauerhaftes Swapping macht PHP langsam und erhöht das Risiko einer Lastspirale.
Schritt 2: Worker unter realer Last messen
Messe nicht nur die Startgröße eines untätigen Workers. Erzeuge repräsentative Frontend-, Login-, Suche-, Warenkorb-, Checkout-, REST-, Admin- und Bildverarbeitungsanfragen und sammle viele Messpunkte. Nutze für die Rechnung vorzugsweise das 95. Perzentil (P95), damit ein einzelner Ausreißer nicht den ganzen Pool bestimmt, typische schwere Anfragen aber berücksichtigt werden.
RSS ist leicht verfügbar, zählt gemeinsam genutzte Seiten bei mehreren Prozessen jedoch mehrfach. PSS verteilt diese Seiten anteilig und ist für eine Gesamtrechnung genauer. Unter Linux zeigt /proc/<PID>/smaps_rollup den PSS-Wert; benötigt werden passende Rechte:
# Pools und Prozess-IDs finden
pgrep -af "php-fpm: pool"
# PSS eines ausgewählten Workers, Beispiel-PID ersetzen
awk '/^Pss:/{printf "%.1f MiB\n", $2/1024}' /proc/12345/smaps_rollup
# RSS als konservativer Fallback für einen Plesk-Pool
ps -eo pid,rss,args | grep '[p]hp-fpm: pool example.com'
Bei vielen Messpunkten P50 und P95 je Pool bilden. Import-, Backup- oder WP-CLI-Prozesse sind keine FPM-Worker, müssen aber im separaten Systembudget stehen.
Schritt 3: RAM-Grenze und Lastbedarf berechnen
Für einen einzelnen Pool:
max_children_RAM = floor(PHP-RAM-Budget / P95-PSS je Worker)
Für mehrere Pools ist die gewichtete Summe entscheidend:
Σ (pm.max_children je Pool × P95-PSS dieses Pools) ≤ PHP-RAM-Budget
Die benötigte Parallelität lässt sich mit einer Form von Little’s Law abschätzen:
children_Last = ceil(Spitzen-RPS ohne Page-Cache × mittlere FPM-Laufzeit × 1,2 bis 1,5)
Schritt 4: CPU-Cores und CPU-Zeit einbeziehen
Die Core-Zahl wurde nicht vergessen: Sie ergibt nur keinen festen Faktor „Worker pro Core“. Ein CPU-lastiger Worker kann eine Verarbeitungseinheit vollständig belegen; wartet ein Worker dagegen auf Datenbank, Dateisystem oder Netzwerk, können mehr Worker als Cores sinnvoll sein. Mehr Prozesse schaffen aber keine zusätzliche Rechenleistung.
nproc zeigt unter Linux die für den aktuellen Prozess verfügbaren Verarbeitungseinheiten und berücksichtigt je nach Umgebung auch CPU-Quoten. Eine vCPU ist eine logische Verarbeitungseinheit und nicht zwingend ein exklusiver physischer Core; auf virtuellen Hosts zusätzlich CPU-Steal beziehungsweise Host-Konkurrenz beobachten.
# Für den aktuellen Prozess verfügbare logische CPUs/vCPUs
nproc
PHP-CPU-Budget = (verfügbare CPUs × Zielauslastung) − CPU-Grundlast anderer Dienste
CPU-Bedarf = Spitzen-RPS ohne Page-Cache × mittlere CPU-Sekunden je Request
max_RPS_CPU = PHP-CPU-Budget / mittlere CPU-Sekunden je Request
CPU-Zeit und Laufzeit sind verschieden: Eine Anfrage kann 0,65 s dauern, aber nur 0,18 CPU-Sekunden verbrauchen und den Rest auf MariaDB oder eine API warten. CPU-Zeit mit APM/Profiler oder geeigneten Prozessmetriken unter repräsentativer Last messen. Für einen All-in-one-Server ist beispielsweise 70–75 % dauerhafte Gesamtauslastung ein konservativer Startkorridor; Spitzen und das gewünschte Fehlerbudget entscheiden.
Erst wenn CPU-Bedarf ≤ PHP-CPU-Budget gilt, darf der aus der Lastformel benötigte Poolwert als Ziel dienen. Andernfalls Code, Queries, Cache-Strategie oder CPU-Kapazität verbessern. Ein größeres pm.max_children verschiebt die Warteschlange dann nur von FPM in den CPU-Scheduler oder die Datenbank.
Beispiel 1: typischer All-in-one-Webserver mit 8 GiB
Annahmen: 8192 MiB Gesamt-RAM; 1280 MiB für Linux, Nginx/Apache und Monitoring; 2048 MiB Datenbankbudget; 512 MiB Redis/OPcache/sonstige Dienste; 1024 MiB Sicherheitsreserve. Für PHP bleiben 3328 MiB. Gemessen wurden 100 MiB P95-PSS je Worker. nproc liefert 4; bei 70 % Zielauslastung stehen insgesamt 2,8 Core-Sekunden pro Sekunde zur Verfügung. Nach 0,8 Core-Sekunden Grundlast anderer Dienste bleiben 2,0 für PHP.
RAM-Grenze = floor(3328 / 100) = 33 Worker
Lastbedarf = ceil(12 ungecachte RPS × 0,45 s × 1,3) = 8 Worker
CPU-Bedarf = 12 RPS × 0,12 CPU-s = 1,44 Cores
CPU-Kapazität = 2,0 Core-s/s / 0,12 CPU-s = 16,7 RPS
Die erwarteten 12 RPS liegen unter der CPU-Kapazität. Ein konservativer Start mit 16 statt der 33 RAM-möglichen Worker lässt Luft für kurze Admin-/Shop-Spitzen, ohne die Rechengrenze auszureizen. Würde dieselbe Anfrage 0,25 CPU-s benötigen, wären bereits 3,0 Cores erforderlich; mehr Worker wären dann die falsche Maßnahme.
pm = dynamic
pm.max_children = 16
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 8
pm.max_requests = 500
Steigt die Queue bei gesunder CPU-/DB-Auslastung, in kleinen Schritten auf 20 oder 24 erhöhen. Bleiben max active processes und Lastbedarf dauerhaft deutlich niedriger, kann der Wert sinken.
Beispiel 2: großer WooCommerce-Shop mit 32 GiB
Annahmen: 32768 MiB Gesamt-RAM; 3072 MiB Betriebssystem/Webserver; 12288 MiB Datenbank; 2048 MiB Redis/OPcache; 2048 MiB Jobs/Monitoring; 4096 MiB Reserve. PHP-Budget: 9216 MiB. P95-PSS: 180 MiB je Shop-Worker. nproc liefert 16; bei 75 % Zielauslastung entsprechen 12 Core-Sekunden pro Sekunde dem gemeinsamen Ziel. Nach 3,5 Core-Sekunden Grundlast für Datenbank, Webserver und übrige Dienste bleiben 8,5 für PHP.
RAM-Grenze = floor(9216 / 180) = 51 Worker
Lastbedarf = ceil(35 ungecachte RPS × 0,65 s × 1,3) = 30 Worker
CPU-Bedarf = 35 RPS × 0,18 CPU-s = 6,3 Cores
CPU-Kapazität = 8,5 Core-s/s / 0,18 CPU-s = 47,2 RPS
Die CPU-Rechnung lässt die erwarteten 35 RPS mit Reserve zu. Nach erfolgreichem Lasttest kann deshalb ein Startwert von 40 passen. Die verbleibende RAM-Luft deckt schwerere Checkout-/Admin-Anfragen ab; die 51 sind nur die theoretische RAM-Obergrenze. Steigt die mittlere CPU-Zeit je Anfrage auf 0,30 s, sinkt die CPU-Kapazität auf rund 28 RPS – dann helfen 51 Worker nicht.
pm = dynamic
pm.max_children = 40
pm.start_servers = 12
pm.min_spare_servers = 12
pm.max_spare_servers = 24
pm.max_requests = 500
Checkout, eingeloggte Konten, REST/API und Admin separat beobachten. Ein anonymer Page-Cache-Test sagt über diese Pfade wenig aus.
Beispiel 3: eigener Plesk-Server mit vielen Hosting-Kunden
Das ist aus Sicht des Betreibers kein gewöhnliches Shared-Hosting-Konto, sondern ein Multi-Tenant-Server mit Administratorzugriff. Plesk verwaltet typischerweise getrennte Pools pro Website. Deshalb darf nicht jeder Pool so gerechnet werden, als gehöre ihm der gesamte Server.
Beispielbudget: 10 GiB (= 10240 MiB) für alle FPM-Pools eines 32-GiB-Hosts. Eine mögliche gewichtete Verteilung:
| Klasse | Pools × Worker | P95-PSS | Maximalbudget |
|---|---|---|---|
16 kleine Websites, ondemand |
16 × 3 | 70 MiB | 3360 MiB |
3 mittlere Websites, ondemand oder dynamic |
3 × 6 | 100 MiB | 1800 MiB |
1 Shop, dynamic |
1 × 20 | 180 MiB | 3600 MiB |
| Summe | 86 mögliche Worker | gewichtet | 8760 MiB |
Die verbleibenden 1480 MiB sind Poolreserve; die allgemeine Serverreserve wurde bereits vor dem 10-GiB-PHP-Budget abgezogen. Kleine, selten besuchte Domains profitieren von ondemand und beispielsweise pm.max_children = 3; ein Shop erhält bewusst mehr Parallelität. Dieselbe gewichtete Planung ist für CPU nötig: Die Summe gleichzeitig aktiver Pools darf unter CPU-lastigen Requests nicht so groß werden, dass alle verfügbaren vCPUs dauerhaft gesättigt sind. Seltene I/O-Wartezeiten erlauben Überbuchung, ersetzen aber keinen gemeinsamen CPU-Lasttest.
In Plesk die Werte unter Service-Pakete → Paket → PHP-Einstellungen → PHP-FPM-Einstellungen pflegen und mit Aktualisieren & synchronisieren ausrollen. Gesperrte oder abweichende Abonnements sowie Domain-Overrides gesondert prüfen. Plesk-generierte Pooldateien nicht direkt bearbeiten, weil eine Neukonfiguration Änderungen überschreiben kann. Außerdem Budgets über alle verwendeten PHP-Versionen und FPM-Dienste summieren.
Monitoring: wann erhöhen, wann nicht?
| Beobachtung | Interpretation | Nächster Schritt |
|---|---|---|
listen queue > 0 und max children reached steigt; RAM, CPU und DB haben Reserve |
Pool wahrscheinlich zu klein | In 10–25-%-Schritten erhöhen und erneut testen. |
| Queue wächst, CPU ist gesättigt | Mehr Worker verschärfen CPU-Konkurrenz | PHP profilieren, Cache verbessern oder CPU-Kapazität erhöhen. |
| Queue wächst, DB-Latenz/Locks steigen | Datenbank ist Engpass | Queries, Indizes, Locks und DB-Budget untersuchen. |
| Kein Queue-Aufbau, viele untätige Worker | Zu viel RAM dauerhaft gebunden | Spare-Werte oder Maximum senken; bei vielen kleinen Pools ondemand prüfen. |
| Worker wachsen mit der Lebensdauer | Mögliches Speicherleck oder fragmentierender Drittanbieter-Code | Ursache profilieren; pm.max_requests nur als Schutznetz einsetzen. |
Die FPM-Statusseite zeigt hierfür listen queue, max listen queue, aktive/untätige Prozesse, max active processes, max children reached und langsame Anfragen. Sie darf nicht öffentlich erreichbar sein.
Sicher ausrollen und Rollback
- Alte Poolwerte und Messdaten sichern; Syntax und Dienstnamen der installierten PHP-Version prüfen.
- Neue Werte zuerst auf Staging oder in einem kontrollierten Lastfenster anwenden. In Plesk über GUI/Service-Paket arbeiten.
- Konfiguration testen und FPM möglichst kontrolliert neu laden; keine ungetesteten Neustarts im Spitzenverkehr.
- Mindestens P95-Latenz, Fehlerrate, Queue, aktive Worker, CPU, DB-Latenz, RAM und Swap vergleichen.
- Bei Regression die gesicherten Werte wiederherstellen, erneut laden und Cache-/Funktionspfade prüfen.
Erfolg bedeutet: keine anhaltende Queue, keine OOM-/Swap-Spirale, bessere oder stabile P95-Latenz und ausreichend Reserve unter echter Spitzenlast. Ein lediglich höherer pm.max_children-Wert ist noch kein Erfolg.
Quellen und Prüfstand
- PHP-Handbuch: FPM-Konfiguration
- PHP-Handbuch: FPM-Statusseite
- Linux-Kernel: PSS in /proc
- GNU Coreutils: verfügbare CPUs mit nproc
- Plesk: pm.max_children berechnen
- Plesk: PHP-Einstellungen in Service-Paketen
- WordPress: PHP-Optimierung
Inhalt fachlich geprüft am 17. August 2026. Konkrete Werte sind Ausgangspunkte, keine Universaldosierung.