Aktuelle Lage: PHP2Shell. Zwei kritische PHP-Lücken (CVSS 9.2) erlauben Schadcode ohne Anmeldung. Patches seit 21.10.2026.Was jetzt zu tun ist →

Speichergrenzen mit systemd und cgroups setzen

php.net empfiehlt als PHP2Shell-Übergangsmaßnahme memory_limit=-1 und eine Speichergrenze außerhalb von PHP. So setzen Sie diese Grenze mit systemd und cgroups, ohne dass ein einzelner Worker den ganzen Dienst mitreißt.

Stand 6 Min. LesezeitHärtung und Konfiguration

Kurz gesagt: Für die PHP2Shell-Übergangsmaßnahme memory_limit=-1 braucht PHP-FPM eine Speichergrenze außerhalb von PHP. Unter systemd setzen Sie sie mit MemoryHigh= als Bremse und MemoryMax= als harte Obergrenze in einer Ergänzungsdatei für den FPM-Dienst. ulimit, LimitAS= oder LimitDATA= sind dafür laut php.net ungeeignet. Prüfen Sie außerdem OOMPolicy=, damit ein einzelner Worker nicht den ganzen Dienst beendet.

Eine Empfehlung, die erst einmal verwundert

Unter den Übergangsmaßnahmen, die php.net am 21. Oktober 2026 zu PHP2Shell veröffentlicht hat, steht eine, die viele Administratoren stutzen ließ: memory_limit=-1, also keine Speichergrenze in PHP selbst. Jahrelang galt das Gegenteil als gute Praxis.

Ein Zusammenhang mit der Beschreibung der Lücke liegt nahe. Bei CVE-2026-109618 kann ein Out-of-Memory-Zustand PHP unterbrechen, während Anfragedaten nur teilweise initialisiert sind. Die Übergangsmaßnahme verlagert die Speichergrenze aus PHP heraus in das Betriebssystem. php.net verbindet sie ausdrücklich mit zwei Bedingungen: Der Wert muss gegen Überschreiben gesperrt sein, und die Grenze muss über Container oder cgroup kommen, nicht über RLIMIT_AS oder RLIMIT_DATA.

Wer nur die erste Hälfte umsetzt, hat einen Server ohne jede Speichergrenze. Dieser Beitrag zeigt die zweite Hälfte. Auch zusammen bleibt die Maßnahme ein Übergang: Behoben ist die Lücke erst mit PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 oder einem bestätigten Backport.

cgroups in zwei Sätzen erklärt

Control Groups, kurz cgroups, sind eine Funktion des Linux-Kernels, die Prozesse gruppiert und deren Ressourcen begrenzt. systemd legt für jeden Dienst automatisch eine eigene cgroup an, und alle Prozesse des Dienstes, beim FPM-Dienst also Master und sämtliche Worker, gehören dazu.

Die Speicheroptionen von systemd setzen Werte des cgroup-v2-Speichercontrollers. Ob Ihr System cgroup v2 nutzt, zeigt ein kurzer Befehl:

# "cgroup2fs" bedeutet: cgroup v2 ist aktiv
stat -fc %T /sys/fs/cgroup/

Aktuelle Versionen der gängigen Distributionen nutzen cgroup v2 standardmäßig. Zeigt der Befehl etwas anderes, klären Sie das vor der Umstellung, denn die systemd-Dokumentation beschreibt die alten v1-Optionen als veraltet.

systemd MemoryMax für PHP-FPM setzen

Die Grenze kommt in eine Ergänzungsdatei, ein sogenanntes Drop-in. So bleibt die Unit-Datei des Pakets unverändert, und ein Update überschreibt Ihre Einstellung nicht. Dienstnamen weichen je Distribution ab; das Beispiel nutzt Debian und Ubuntu mit PHP 8.3.

# Öffnet einen Editor für das Drop-in: sudo systemctl edit php8.3-fpm
# Ergebnis: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue

Die Optionen im Überblick, nach der Dokumentation von systemd:

Option Wirkung Hinweis für PHP-FPM
MemoryHigh= Drosselgrenze: Prozesse werden stark gebremst, Speicher wird zurückgeholt die eigentliche Steuergröße, etwas unter MemoryMax
MemoryMax= absolute Grenze: ist sie nicht zu halten, wird der OOM-Killer im Dienst aktiv letzte Verteidigungslinie
MemorySwapMax= Grenze für Auslagerung 0 verhindert, dass der Dienst stattdessen in den Swap ausweicht
OOMPolicy= Verhalten nach einem OOM-Ereignis: continue, stop oder kill continue lässt die übrigen Worker weiterlaufen
LimitAS=, LimitDATA= rlimits pro Prozess laut php.net für diese Maßnahme ungeeignet

OOMPolicy= verdient besondere Aufmerksamkeit. Bei stop oder kill beendet systemd nach einem OOM-Ereignis den gesamten Dienst, und alle Websites dieses FPM-Dienstes fallen aus. Welcher Wert ohne eigene Angabe gilt, legt der Systemstandard DefaultOOMPolicy= fest. Prüfen Sie den wirksamen Wert deshalb, statt ihn anzunehmen.

Danach den Dienst neu starten und im Pool memory_limit sperren:

sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Pool-Datei, z. B. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1

Wie das Sperren funktioniert, beschreibt php_admin_value: Einstellungen gegen Überschreiben sperren.

Den richtigen Wert finden

Eine zu knappe Grenze erzeugt Abbrüche im Normalbetrieb, eine zu großzügige schützt kaum. Ausgangspunkt ist der tatsächliche Verbrauch.

  1. Den aktuellen Speicherbedarf aller FPM-Prozesse über einige Tage beobachten, auch zu Lastspitzen.
  2. Den typischen Bedarf eines Workers ermitteln und mit pm.max_children aller Pools des Dienstes multiplizieren.
  3. Einen Aufschlag für den Master-Prozess, OPcache und Spitzen einplanen.
  4. MemoryHigh= knapp über dem gemessenen Spitzenwert setzen, MemoryMax= mit etwas Abstand darüber.
  5. Werte nach einer Woche anhand der Messungen und OOM-Ereignisse nachjustieren.
# Speicher aller PHP-FPM-Prozesse in MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Laufende Übersicht aller cgroups nach Speicher sortiert
systemd-cgtop -m

Den Wert für memory_limit vor der Umstellung sollten Sie notieren. Nach dem Update können Sie zu einer festen Grenze in PHP zurückkehren; wie Sie diese sinnvoll wählen, steht unter memory_limit richtig setzen.

Überwachen: Wann die Grenze greift

Eine Speichergrenze, deren Auslösen niemand bemerkt, ist eine stille Fehlerquelle. Der Kernel zählt OOM-Ereignisse je cgroup, und systemd sowie das Kernel-Protokoll vermerken sie.

# OOM-Zähler der cgroup des Dienstes (Pfad bei cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Meldungen des OOM-Killers im Kernel-Protokoll
journalctl -k --since "-1 day" | grep -i "out of memory"

Steigt der Zähler oom_kill, beendet der Kernel Worker. Das kann auf eine zu knappe Grenze hinweisen, auf eine fehlerhafte Anwendung oder auf ungewöhnliche Anfragen. In der aktuellen Lage lohnt es, solche Ereignisse mit den Zugriffsprotokollen zur selben Zeit abzugleichen.

Wichtig

Die Speichergrenze ist eine Übergangsmaßnahme und ersetzt nicht das PHP-Update. Planen Sie das Update parallel, und starten Sie danach alle PHP-Prozesse neu. Alle Übergangsmaßnahmen im Überblick stehen unter Sofortmaßnahmen, wenn Sie noch nicht patchen können.

Was im Inneren von PHP bei Speichermangel passiert, erklärt memory_limit und Out-of-Memory. Bei Apache mit mod_php gilt dieselbe Technik für den Apache-Dienst, allerdings teilt sich PHP die Grenze dann mit dem Webserver selbst.

Fazit: Die zweite Hälfte nicht vergessen

memory_limit=-1 ist nur zusammen mit einer äußeren Grenze eine vertretbare Übergangsmaßnahme. systemd macht diese Grenze mit wenigen Zeilen möglich: MemoryHigh als Bremse, MemoryMax als harte Obergrenze, MemorySwapMax gegen das Ausweichen in den Swap und eine bewusst gewählte OOMPolicy. Messen Sie vorher, beobachten Sie danach, und behalten Sie im Blick, dass alle Pools eines Dienstes sich die Grenze teilen.

Vor allem aber: Planen Sie das Ende der Maßnahme. Nach dem Update auf eine behobene PHP-Version kehren Sie zu einem festen memory_limit zurück. Die cgroup-Grenze können Sie als zusätzliche Absicherung gegen ausufernde Prozesse behalten.

Wie hilft PHP SOS konkret?

  • Messen: Wir erfassen den Speicherbedarf Ihrer PHP-Dienste und leiten daraus passende Grenzen ab.
  • Umsetzen: Wir setzen Drop-ins mit MemoryHigh, MemoryMax und passender OOMPolicy, sperren memory_limit im Pool und starten die Dienste kontrolliert neu.
  • Beobachten: OOM-Ereignisse binden wir in die Überwachung ein und justieren die Werte nach.
  • Zum Patch führen: Über den PHP-Patch-Service spielen wir das Update ein und nehmen die Übergangswerte danach geordnet zurück.

Häufige Fragen

Warum soll memory_limit auf -1 stehen? Ist das nicht gefährlich?

Allein wäre es das. php.net empfiehlt memory_limit=-1 als Übergangsmaßnahme ausdrücklich zusammen mit einer Speichergrenze über Container oder cgroup. Die Begrenzung wandert damit aus PHP heraus in das Betriebssystem. Ohne diese äußere Grenze sollten Sie den Wert nicht setzen.

Kann ich stattdessen ulimit oder LimitAS= verwenden?

Nein. php.net rät ausdrücklich von RLIMIT_AS und RLIMIT_DATA ab, und genau diese Grenzen setzen ulimit -v, LimitAS= und LimitDATA=. Die systemd-Dokumentation weist zudem darauf hin, dass LimitAS= den Adressraum begrenzt, nicht den Speicherverbrauch.

Was passiert, wenn PHP-FPM die Grenze erreicht?

Bei MemoryHigh bremst der Kernel die Prozesse und holt Speicher zurück. Bei MemoryMax wird innerhalb des Dienstes der OOM-Killer aktiv und beendet einen Prozess, meist einen großen Worker. Ob danach der ganze Dienst stoppt, regelt OOMPolicy=.

Gilt die Grenze für jeden Pool einzeln?

Nein. Alle Pools eines FPM-Dienstes teilen sich eine cgroup und damit eine gemeinsame Grenze. Getrennte Grenzen je Website brauchen getrennte FPM-Dienste oder Container.

Wie setze ich die Grenze in Docker oder Kubernetes?

Über die Speicherbegrenzung des Containers, etwa die Option --memory bei docker run oder resources.limits.memory in Kubernetes. Beide nutzen ebenfalls cgroups. Der Grundsatz bleibt gleich: Grenze außerhalb von PHP, memory_limit in PHP gesperrt.

Quellen

Sofort-Kontakt

Jede Stunde zählt. Schreiben Sie uns jetzt.

Schildern Sie kurz System, PHP-Version und Beobachtung. Wir melden uns, klären die Lage und arbeiten remote, bis der Vorfall abgeschlossen und dokumentiert ist.

Sofort-Hilfe