# Nastavenie pamäťových hraníc pomocou systemd a cgroups

> systemd MemoryMax pre PHP-FPM: nastavenie pamäťovej hranice cez cgroup, ako ho pri PHP2Shell odporúča php.net, s MemoryHigh, OOMPolicy a kontrolnými príkazmi.

Zdroj: https://phpsos.de/sk/blog/speichergrenzen-systemd-cgroups/  
Stav: 25.10.2026  
Poskytovateľ: PHP SOS, Núdzová pomoc pri PHP a bezpečnosť serverov. Okamžitá pomoc cez WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20de%C5%88%2C%20PHP%20SOS%2C%20potrebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20serverom%20s%20PHP.%20Spolo%C4%8Dnos%C5%A5%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**Stručne:** Pre prechodné opatrenie k PHP2Shell `memory_limit=-1` potrebuje PHP-FPM pamäťovú hranicu mimo PHP. Pod systemd ju nastavíte pomocou `MemoryHigh=` ako brzdy a `MemoryMax=` ako tvrdej hornej hranice v doplňujúcom súbore pre službu FPM. `ulimit`, `LimitAS=` alebo `LimitDATA=` sú na to podľa php.net nevhodné. Skontrolujte aj `OOMPolicy=`, aby jeden worker neukončil celú službu.

## Odporúčanie, ktoré najprv prekvapí

Medzi prechodnými opatreniami, ktoré php.net zverejnil 21. októbra 2026 k PHP2Shell, je jedno, nad ktorým mnohí administrátori zastali: `memory_limit=-1`, teda žiadna pamäťová hranica v samotnom PHP. Roky platilo za dobrú prax opak.

Súvislosť s opisom zraniteľnosti je zrejmá. Pri CVE-2026-109618 môže stav nedostatku pamäte (out-of-memory) prerušiť PHP v čase, keď sú údaje požiadavky inicializované len čiastočne. Prechodné opatrenie presúva pamäťovú hranicu z PHP do operačného systému. php.net ho výslovne spája s dvoma podmienkami: hodnota musí byť zablokovaná proti prepísaniu a hranica musí pochádzať z kontajnera alebo cgroup, nie z `RLIMIT_AS` alebo `RLIMIT_DATA`.

Kto zavedie len prvú polovicu, má server bez akejkoľvek pamäťovej hranice. Tento príspevok ukazuje druhú polovicu. Ani spolu opatrenie nepresahuje prechod: zraniteľnosť je opravená až s PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 alebo potvrdeným backportom.

## cgroups v dvoch vetách

Control Groups, skrátene cgroups, sú funkcia jadra Linuxu, ktorá zoskupuje procesy a obmedzuje ich zdroje. systemd vytvára pre každú službu automaticky vlastnú cgroup a patria do nej všetky procesy služby, pri službe FPM teda master a všetci workeri.

Pamäťové voľby systemd nastavujú hodnoty pamäťového kontroléra cgroup v2. Či váš systém používa cgroup v2, ukáže krátky príkaz:

```bash
# "cgroup2fs" znamená: cgroup v2 je aktívna
stat -fc %T /sys/fs/cgroup/
```

Aktuálne verzie bežných distribúcií používajú cgroup v2 štandardne. Ak príkaz ukáže niečo iné, vyjasnite to pred prechodom, pretože dokumentácia systemd opisuje staré voľby v1 ako zastarané.

## Nastavenie systemd MemoryMax pre PHP-FPM

Hranica patrí do doplňujúceho súboru, takzvaného drop-in. Súbor unit balíka tak zostane nezmenený a aktualizácia vaše nastavenie neprepíše. Názvy služieb sa podľa distribúcie líšia; príklad používa Debian a Ubuntu s PHP 8.3.

```ini
# Otvorí editor pre drop-in: sudo systemctl edit php8.3-fpm
# Výsledok: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
```

Voľby v prehľade, podľa dokumentácie systemd:

| Voľba | Účinok | Poznámka pre PHP-FPM |
|---|---|---|
| `MemoryHigh=` | hranica tlmenia: procesy sa silne spomalia, pamäť sa získava späť | vlastná riadiaca veličina, o niečo pod `MemoryMax` |
| `MemoryMax=` | absolútna hranica: ak sa nedá dodržať, aktivuje sa OOM-killer v službe | posledná obranná línia |
| `MemorySwapMax=` | hranica pre odkladanie | `0` bráni tomu, aby služba namiesto toho unikla do swapu |
| `OOMPolicy=` | správanie po udalosti OOM: `continue`, `stop` alebo `kill` | `continue` nechá ostatných workerov bežať |
| `LimitAS=`, `LimitDATA=` | rlimity na proces | podľa php.net pre toto opatrenie nevhodné |

`OOMPolicy=` si zaslúži osobitnú pozornosť. Pri `stop` alebo `kill` systemd po udalosti OOM ukončí celú službu a vypadnú všetky weby tejto služby FPM. Ktorá hodnota platí bez vlastného údaja, určuje systémový štandard `DefaultOOMPolicy=`. Účinnú hodnotu preto overte, nepredpokladajte ju.

Potom službu reštartujte a v poole zablokujte `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
; Súbor poolu, napr. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
```

Ako blokovanie funguje, opisuje [php_admin_value: zablokovanie nastavení proti prepísaniu](https://phpsos.de/sk/blog/php-admin-value-sperren/).

## Nájdenie správnej hodnoty

Príliš tesná hranica vyvoláva prerušenia v bežnej prevádzke, príliš štedrá sotva chráni. Východiskom je skutočná spotreba.

1. Sledovať aktuálnu potrebu pamäte všetkých procesov FPM niekoľko dní, aj pri špičkách zaťaženia.
2. Zistiť typickú potrebu jedného workera a vynásobiť ju `pm.max_children` všetkých poolov služby.
3. Započítať prirážku pre master proces, OPcache a špičky.
4. `MemoryHigh=` nastaviť tesne nad nameranú špičkovú hodnotu, `MemoryMax=` s rezervou nad ňu.
5. Hodnoty po týždni dolaďiť podľa meraní a udalostí OOM.

```bash
# Pamäť všetkých procesov PHP-FPM v MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Priebežný prehľad všetkých cgroups zoradený podľa pamäte
systemd-cgtop -m
```

Hodnotu `memory_limit` si pred prechodom zapíšte. Po aktualizácii sa môžete vrátiť k pevnej hranici v PHP; ako ju zmysluplne zvoliť, uvádza [Správne nastavenie memory_limit](https://phpsos.de/sk/blog/memory-limit-richtig-setzen/).

## Sledovanie: kedy hranica zaberie

Pamäťová hranica, ktorej spustenia si nikto nevšimne, je tichým zdrojom chýb. Jadro počíta udalosti OOM pre každú cgroup a systemd aj protokol jadra ich zaznamenávajú.

```bash
# Počítadlo OOM cgroup služby (cesta pri cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Hlásenia OOM-killera v protokole jadra
journalctl -k --since "-1 day" | grep -i "out of memory"
```

Ak počítadlo `oom_kill` rastie, jadro ukončuje workerov. To môže poukazovať na príliš tesnú hranicu, na chybnú aplikáciu alebo na nezvyčajné požiadavky. V aktuálnej situácii sa oplatí také udalosti porovnať s prístupovými protokolmi z rovnakého času.

> **Dôležité:**
> Pamäťová hranica je prechodné opatrenie a nenahrádza aktualizáciu PHP. Plánujte aktualizáciu paralelne a potom reštartujte všetky procesy PHP. Všetky prechodné opatrenia v prehľade sú pri [Okamžité opatrenia, ak ešte nemôžete záplatovať](https://phpsos.de/sk/php2shell/sofortmassnahmen-ohne-patch/).

Čo sa vo vnútri PHP deje pri nedostatku pamäte, vysvetľuje [memory_limit a Out-of-Memory](https://phpsos.de/sk/php/out-of-memory/). Pri Apache s mod_php platí rovnaká technika pre službu Apache, PHP však potom zdieľa hranicu so samotným webovým serverom.

## Záver: nezabudnúť na druhú polovicu

`memory_limit=-1` je prijateľným prechodným opatrením len spolu s vonkajšou hranicou. systemd túto hranicu umožňuje niekoľkými riadkami: `MemoryHigh` ako brzdu, `MemoryMax` ako tvrdú hornú hranicu, `MemorySwapMax` proti úniku do swapu a vedome zvolenú `OOMPolicy`. Najprv merajte, potom sledujte a majte na pamäti, že všetky pooly jednej služby zdieľajú hranicu.

Predovšetkým však: naplánujte koniec opatrenia. Po aktualizácii na opravenú verziu PHP sa vrátite k pevnému `memory_limit`. Hranicu cgroup si môžete ponechať ako dodatočné zabezpečenie proti rozbujneným procesom.

## Ako konkrétne pomáha PHP SOS?

- **Merať:** zachytíme pamäťovú potrebu vašich služieb PHP a odvodíme z nej vhodné hranice.
- **Zaviesť:** nastavíme drop-iny s `MemoryHigh`, `MemoryMax` a vhodnou `OOMPolicy`, zablokujeme `memory_limit` v poole a služby riadene reštartujeme.
- **Sledovať:** udalosti OOM zapojíme do monitoringu a hodnoty dolaďujeme.
- **Doviesť k záplate:** cez [službu záplat PHP](https://phpsos.de/sk/leistungen/php-patch-service/) nasadíme aktualizáciu a prechodné hodnoty potom usporiadane vrátime.


## Časté otázky

**Prečo má byť memory_limit na -1? Nie je to nebezpečné?**  
Samo osebe by bolo. php.net odporúča memory_limit=-1 ako prechodné opatrenie výslovne spolu s pamäťovou hranicou cez kontajner alebo cgroup. Obmedzenie sa tak presúva z PHP do operačného systému. Bez tejto vonkajšej hranice by ste hodnotu nastavovať nemali.

**Môžem namiesto toho použiť ulimit alebo LimitAS=?**  
Nie. php.net výslovne odrádza od RLIMIT_AS a RLIMIT_DATA a práve tieto hranice nastavujú ulimit -v, LimitAS= a LimitDATA=. Dokumentácia systemd navyše upozorňuje, že LimitAS= obmedzuje adresový priestor, nie spotrebu pamäte.

**Čo sa stane, keď PHP-FPM dosiahne hranicu?**  
Pri MemoryHigh jadro procesy spomalí a získa pamäť späť. Pri MemoryMax sa vo vnútri služby aktivuje OOM-killer a ukončí proces, spravidla veľký worker. Či sa potom zastaví celá služba, určuje OOMPolicy=.

**Platí hranica pre každý pool osobitne?**  
Nie. Všetky pooly jednej služby FPM zdieľajú jednu cgroup a teda spoločnú hranicu. Oddelené hranice pre každý web vyžadujú oddelené služby FPM alebo kontajnery.

**Ako hranicu nastavím v Dockeri alebo Kubernetes?**  
Cez obmedzenie pamäte kontajnera, napríklad voľbu --memory pri docker run alebo resources.limits.memory v Kubernetes. Obe takisto využívajú cgroups. Zásada ostáva rovnaká: hranica mimo PHP, memory_limit v PHP zablokované.

## 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é správy](https://www.php.net/)
- [Príručka PHP: memory_limit](https://www.php.net/manual/en/ini.core.php)
