Aktuální situace: PHP2Shell. Dvě kritické zranitelnosti PHP (CVSS 9.2) umožňují spustit škodlivý kód bez přihlášení. Záplaty od 21. 10. 2026.Co nyní udělat →

Nastavení limitů paměti pomocí systemd a cgroups

php.net doporučuje jako přechodné opatření k PHP2Shell memory_limit=-1 a limit paměti mimo PHP. Jak tento limit nastavit pomocí systemd a cgroups, aby jeden worker nestrhl celou službu.

Stav 5 min čteníHardening a konfigurace

Stručně: Pro přechodné opatření k PHP2Shell memory_limit=-1 potřebuje PHP-FPM limit paměti mimo PHP. Pod systemd ho nastavíte pomocí MemoryHigh= jako brzdy a MemoryMax= jako tvrdého horního limitu v doplňkovém souboru pro službu FPM. ulimit, LimitAS= nebo LimitDATA= se podle php.net pro tento účel nehodí. Zkontrolujte také OOMPolicy=, aby jeden worker neukončil celou službu.

Doporučení, které zprvu překvapí

Mezi přechodnými opatřeními, která php.net zveřejnil 21. října 2026 k PHP2Shell, je jedno, nad kterým mnoho správců zůstalo stát: memory_limit=-1, tedy žádný limit paměti v samotném PHP. Po léta platilo za dobrou praxi pravý opak.

Souvislost s popisem zranitelnosti se nabízí. U CVE-2026-109618 může stav Out-of-Memory přerušit PHP ve chvíli, kdy jsou data požadavku inicializována jen částečně. Přechodné opatření přesouvá limit paměti z PHP do operačního systému. php.net ho výslovně váže na dvě podmínky: hodnota musí být uzamčena proti přepsání a limit musí pocházet z kontejneru nebo cgroup, ne z RLIMIT_AS nebo RLIMIT_DATA.

Kdo zavede jen první polovinu, má server bez jakéhokoli limitu paměti. Tento článek ukazuje druhou polovinu. I společně zůstává opatření přechodem: zranitelnost je opravena teprve s PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 nebo potvrzeným backportem.

cgroups ve dvou větách

Control groups, zkráceně cgroups, jsou funkce jádra Linuxu, která seskupuje procesy a omezuje jejich zdroje. systemd zakládá pro každou službu automaticky vlastní cgroup a patří do ní všechny procesy služby, u služby FPM tedy master i všichni workeři.

Volby paměti v systemd nastavují hodnoty paměťového řadiče cgroup v2. Zda váš systém používá cgroup v2, ukáže krátký příkaz:

# "cgroup2fs" znamená: cgroup v2 je aktivní
stat -fc %T /sys/fs/cgroup/

Aktuální verze běžných distribucí používají cgroup v2 standardně. Pokud příkaz ukáže něco jiného, vyjasněte to před přechodem, protože dokumentace systemd popisuje staré volby v1 jako zastaralé.

Nastavit systemd MemoryMax pro PHP-FPM

Limit patří do doplňkového souboru, takzvaného drop-in. Soubor jednotky z balíčku tak zůstane nezměněn a aktualizace vaše nastavení nepřepíše. Názvy služeb se podle distribuce liší; příklad používá Debian a Ubuntu s PHP 8.3.

# Otevře editor pro drop-in: sudo systemctl edit php8.3-fpm
# Výsledek: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue

Volby v přehledu, podle dokumentace systemd:

Volba Účinek Poznámka pro PHP-FPM
MemoryHigh= brzdný limit: procesy se silně zpomalí, paměť se získává zpět vlastní řídicí veličina, o něco pod MemoryMax
MemoryMax= absolutní limit: pokud ho nelze udržet, aktivuje se ve službě OOM killer poslední linie obrany
MemorySwapMax= limit pro odkládání 0 brání tomu, aby služba místo toho uhýbala do swapu
OOMPolicy= chování po události OOM: continue, stop nebo kill continue nechá ostatní workery běžet dál
LimitAS=, LimitDATA= rlimity na proces podle php.net pro toto opatření nevhodné

OOMPolicy= zaslouží zvláštní pozornost. U stop nebo kill systemd po události OOM ukončí celou službu a všechny weby této služby FPM vypadnou. Jaká hodnota platí bez vlastního údaje, určuje systémová výchozí DefaultOOMPolicy=. Platnou hodnotu proto ověřte, místo abyste ji předpokládali.

Poté službu restartujte a v poolu uzamkněte memory_limit:

sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Soubor poolu, např. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1

Jak uzamčení funguje, popisuje php_admin_value: uzamknutí nastavení proti přepsání.

Najít správnou hodnotu

Příliš těsný limit vyvolává přerušení v běžném provozu, příliš velkorysý sotva chrání. Výchozím bodem je skutečná spotřeba.

  1. Sledujte aktuální potřebu paměti všech procesů FPM po několik dní, i při špičkách zátěže.
  2. Zjistěte typickou potřebu jednoho workera a vynásobte ji pm.max_children všech poolů služby.
  3. Počítejte s příplatkem pro proces master, OPcache a špičky.
  4. MemoryHigh= nastavte těsně nad naměřenou špičku, MemoryMax= s malým odstupem nad ni.
  5. Hodnoty po týdnu dolaďte podle měření a událostí OOM.
# Paměť všech procesů PHP-FPM v MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Průběžný přehled všech cgroups seřazený podle paměti
systemd-cgtop -m

Hodnotu memory_limit si před přechodem poznamenejte. Po aktualizaci se můžete vrátit k pevnému limitu v PHP; jak ho rozumně zvolit, je v článku Správné nastavení memory_limit.

Sledovat: kdy limit zabere

Limit paměti, jehož spuštění nikdo nezaznamená, je tichým zdrojem chyb. Jádro počítá události OOM pro každou cgroup a systemd i protokol jádra je zaznamenávají.

# Počítadlo OOM cgroup služby (cesta u cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Hlášení OOM killeru v protokolu jádra
journalctl -k --since "-1 day" | grep -i "out of memory"

Pokud počítadlo oom_kill roste, jádro ukončuje workery. Může to ukazovat na příliš těsný limit, na chybnou aplikaci nebo na neobvyklé požadavky. V současné situaci se vyplatí takové události porovnat s přístupovými protokoly ve stejném čase.

Důležité

Limit paměti je přechodné opatření a nenahrazuje aktualizaci PHP. Plánujte aktualizaci souběžně a poté restartujte všechny procesy PHP. Přehled všech přechodných opatření je na stránce Okamžitá opatření, když zatím nemůžete záplatovat.

Co se uvnitř PHP při nedostatku paměti děje, vysvětluje memory_limit a Out-of-Memory. U Apache s mod_php platí stejná technika pro službu Apache, PHP však pak sdílí limit se samotným webovým serverem.

Závěr: nezapomeňte na druhou polovinu

memory_limit=-1 je vhodným přechodným opatřením jen spolu s vnějším limitem. systemd tento limit umožňuje několika řádky: MemoryHigh jako brzdu, MemoryMax jako tvrdý horní limit, MemorySwapMax proti uhýbání do swapu a vědomě zvolenou OOMPolicy. Předem měřte, poté sledujte a mějte na paměti, že všechny pooly jedné služby sdílejí limit.

Především ale plánujte konec opatření. Po aktualizaci na opravenou verzi PHP se vraťte k pevnému memory_limit. Limit cgroup si můžete ponechat jako dodatečné zajištění proti vymykajícím se procesům.

Jak konkrétně pomáhá PHP SOS?

  • Měřit: Zjistíme potřebu paměti vašich služeb PHP a odvodíme z ní vhodné limity.
  • Zavést: Nastavíme drop-iny s MemoryHigh, MemoryMax a vhodnou OOMPolicy, uzamkneme memory_limit v poolu a kontrolovaně restartujeme služby.
  • Sledovat: Události OOM zapojíme do monitoringu a hodnoty dolaďujeme.
  • Dovést k záplatě: Prostřednictvím služby záplatování PHP nasadíme aktualizaci a přechodné hodnoty poté uspořádaně vrátíme.

Časté dotazy

Proč má být memory_limit na -1? Není to nebezpečné?

Samo o sobě by bylo. php.net doporučuje memory_limit=-1 jako přechodné opatření výslovně spolu s limitem paměti přes kontejner nebo cgroup. Omezení se tím přesouvá z PHP do operačního systému. Bez tohoto vnějšího limitu byste hodnotu nastavovat neměli.

Mohu místo toho použít ulimit nebo LimitAS=?

Ne. php.net výslovně nedoporučuje RLIMIT_AS a RLIMIT_DATA a právě tyto limity nastavují ulimit -v, LimitAS= a LimitDATA=. Dokumentace systemd navíc upozorňuje, že LimitAS= omezuje adresní prostor, ne spotřebu paměti.

Co se stane, když PHP-FPM dosáhne limitu?

U MemoryHigh jádro procesy brzdí a získává paměť zpět. U MemoryMax se uvnitř služby aktivuje OOM killer a ukončí jeden proces, zpravidla velkého workera. Zda poté zastaví celá služba, určuje OOMPolicy=.

Platí limit pro každý pool zvlášť?

Ne. Všechny pooly jedné služby FPM sdílejí jednu cgroup, a tedy společný limit. Oddělené limity pro každý web vyžadují oddělené služby FPM nebo kontejnery.

Jak nastavím limit v Dockeru nebo Kubernetes?

Přes omezení paměti kontejneru, například volbou --memory u docker run nebo resources.limits.memory v Kubernetes. Obojí rovněž používá cgroups. Zásada zůstává stejná: limit mimo PHP, memory_limit v PHP uzamčený.

Zdroje

Okamžitý kontakt

Každá hodina se počítá. Napište nám hned.

Stručně popište systém, verzi PHP a pozorování. Ozveme se, vyjasníme situaci a budeme pracovat na dálku, dokud nebude incident uzavřen a zdokumentován.

Okamžitá pomoc