# Nastavení limitů paměti pomocí systemd a cgroups

> systemd MemoryMax pro PHP-FPM: limit paměti přes cgroup podle doporučení php.net k PHP2Shell, s MemoryHigh, OOMPolicy a kontrolními příkazy.

Zdroj: https://phpsos.de/cs/blog/speichergrenzen-systemd-cgroups/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**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:

```bash
# "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.

```ini
# 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`:

```bash
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
```

```ini
; 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í](https://phpsos.de/cs/blog/php-admin-value-sperren/).

## 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.

```bash
# 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](https://phpsos.de/cs/blog/memory-limit-richtig-setzen/).

## 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í.

```bash
# 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](https://phpsos.de/cs/php2shell/sofortmassnahmen-ohne-patch/).

Co se uvnitř PHP při nedostatku paměti děje, vysvětluje [memory_limit a Out-of-Memory](https://phpsos.de/cs/php/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](https://phpsos.de/cs/leistungen/php-patch-service/) 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

- [systemd.resource-control (MemoryMax, MemoryHigh)](https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html)
- [systemd.service (OOMPolicy)](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html)
- [php.net: bezpečnostní oznámení](https://www.php.net/)
- [Příručka PHP: memory_limit](https://www.php.net/manual/en/ini.core.php)
