# Postaviti granice memorije pomoću systemd-a i cgroupa

> systemd MemoryMax za PHP-FPM: postavite granicu memorije putem cgroupa kako php.net preporučuje za PHP2Shell, s MemoryHigh, OOMPolicy i naredbama za provjeru.

Izvor: https://phpsos.de/hr/blog/speichergrenzen-systemd-cgroups/  
Stanje: 25.10.2026  
Pružatelj: PHP SOS, PHP hitna pomoć i sigurnost poslužitelja. Hitna pomoć putem WhatsAppa: https://wa.me/491736494914?text=Pozdrav%20PHP%20SOS%2C%20trebamo%20pomo%C4%87%20s%20na%C5%A1im%20PHP%20poslu%C5%BEiteljem.%20Tvrtka%3A%20%E2%80%A6%20Web-stranica%3A%20%E2%80%A6

**Ukratko:** Za prijelaznu mjeru za PHP2Shell `memory_limit=-1` PHP-FPM treba granicu memorije izvan PHP-a. Pod systemd-om postavljate je pomoću `MemoryHigh=` kao kočnice i `MemoryMax=` kao tvrde gornje granice u dopunskoj datoteci za FPM uslugu. `ulimit`, `LimitAS=` ili `LimitDATA=` za to su prema php.net-u neprikladni. Provjerite i `OOMPolicy=` kako jedan worker ne bi prekinuo cijelu uslugu.

## Preporuka koja se isprva čini začuđujućom

Među prijelaznim mjerama koje je php.net 21. listopada 2026. objavio za PHP2Shell nalazi se jedna zbog koje su mnogi administratori zastali: `memory_limit=-1`, dakle bez granice memorije u samom PHP-u. Godinama se suprotno smatralo dobrom praksom.

Veza s opisom ranjivosti nameće se sama. Kod CVE-2026-109618 stanje nedostatka memorije (out of memory) može prekinuti PHP dok su podaci zahtjeva tek djelomično inicijalizirani. Prijelazna mjera seli granicu memorije iz PHP-a u operacijski sustav. php.net je izričito veže uz dva uvjeta: vrijednost mora biti zaključana od nadjačavanja, a granica mora dolaziti putem kontejnera ili cgroupa, a ne putem `RLIMIT_AS` ili `RLIMIT_DATA`.

Tko provede samo prvu polovicu, ima poslužitelj bez ikakve granice memorije. Ovaj članak pokazuje drugu polovicu. Ni zajedno mjera ne prestaje biti prijelaz: ranjivost je otklonjena tek s PHP-om 8.5.12, 8.4.27, 8.3.36, 8.2.35 ili potvrđenim backportom.

## cgroupe u dvije rečenice

Control Groups, skraćeno cgroupe, funkcija su Linux jezgre koja grupira procese i ograničava njihove resurse. systemd za svaku uslugu automatski izrađuje vlastitu cgroup i svi procesi usluge, kod FPM usluge dakle master i svi workeri, pripadaju joj.

Opcije memorije u systemd-u postavljaju vrijednosti kontrolera memorije cgroup v2. Koristi li Vaš sustav cgroup v2, pokazuje kratka naredba:

```bash
# "cgroup2fs" znači: cgroup v2 je aktivan
stat -fc %T /sys/fs/cgroup/
```

Aktualne verzije uobičajenih distribucija zadano koriste cgroup v2. Ako naredba pokaže nešto drugo, to razjasnite prije prelaska, jer dokumentacija systemd-a stare v1 opcije opisuje kao zastarjele.

## Postaviti systemd MemoryMax za PHP-FPM

Granica ide u dopunsku datoteku, takozvani drop-in. Tako unit datoteka paketa ostaje nepromijenjena i ažuriranje ne prepisuje Vašu postavku. Nazivi usluga razlikuju se po distribuciji; primjer koristi Debian i Ubuntu s PHP-om 8.3.

```ini
# Otvara uređivač za drop-in: sudo systemctl edit php8.3-fpm
# Rezultat: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
```

Opcije ukratko, prema dokumentaciji systemd-a:

| Opcija | Učinak | Napomena za PHP-FPM |
|---|---|---|
| `MemoryHigh=` | granica prigušenja: procesi se snažno usporavaju, memorija se vraća | stvarna upravljačka veličina, malo ispod `MemoryMax` |
| `MemoryMax=` | apsolutna granica: ako se ne može održati, u usluzi postaje aktivan OOM killer | posljednja linija obrane |
| `MemorySwapMax=` | granica za izmještanje | `0` sprječava da usluga umjesto toga pobjegne u swap |
| `OOMPolicy=` | ponašanje nakon OOM događaja: `continue`, `stop` ili `kill` | `continue` ostavlja preostale workere da rade |
| `LimitAS=`, `LimitDATA=` | rlimiti po procesu | prema php.net-u neprikladni za ovu mjeru |

`OOMPolicy=` zaslužuje osobitu pažnju. Kod `stop` ili `kill` systemd nakon OOM događaja prekida cijelu uslugu i sve web-stranice te FPM usluge ispadaju. Koja vrijednost vrijedi bez vlastitog navoda, određuje zadana vrijednost sustava `DefaultOOMPolicy=`. Zato provjerite djelotvornu vrijednost umjesto da je pretpostavite.

Zatim ponovno pokrenite uslugu i u poolu zaključajte `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
; Datoteka poola, npr. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
```

Kako funkcionira zaključavanje, opisuje članak [php_admin_value: zaključati postavke od nadjačavanja](https://phpsos.de/hr/blog/php-admin-value-sperren/).

## Pronaći pravu vrijednost

Preuska granica izaziva prekide u redovitom radu, preširoka jedva štiti. Polazište je stvarna potrošnja.

1. Nekoliko dana promatrati trenutačnu potrebu za memorijom svih FPM procesa, i u vršnim opterećenjima.
2. Utvrditi tipičnu potrebu jednog workera i pomnožiti je s `pm.max_children` svih poolova usluge.
3. Predvidjeti dodatak za master proces, OPcache i vrhove.
4. `MemoryHigh=` postaviti malo iznad izmjerenog vrha, `MemoryMax=` s malim razmakom iznad toga.
5. Nakon tjedan dana vrijednosti dotjerati prema mjerenjima i OOM događajima.

```bash
# Memorija svih PHP-FPM procesa u MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Tekući pregled svih cgroupa poredanih po memoriji
systemd-cgtop -m
```

Vrijednost za `memory_limit` prije prelaska trebali biste zabilježiti. Nakon ažuriranja možete se vratiti na čvrstu granicu u PHP-u; kako je smisleno odabrati, piše u članku [Pravilno postaviti memory_limit](https://phpsos.de/hr/blog/memory-limit-richtig-setzen/).

## Nadzirati: kada granica djeluje

Granica memorije čije aktiviranje nitko ne primijeti tihi je izvor pogrešaka. Jezgra broji OOM događaje po cgroupi, a systemd i zapisnik jezgre bilježe ih.

```bash
# OOM brojač cgroupe usluge (putanja kod cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Poruke OOM killera u zapisniku jezgre
journalctl -k --since "-1 day" | grep -i "out of memory"
```

Ako brojač `oom_kill` raste, jezgra prekida workere. To može upućivati na preusku granicu, na pogrešnu aplikaciju ili na neobične zahtjeve. U aktualnoj situaciji isplati se takve događaje uskladiti s pristupnim zapisnicima iz istog vremena.

> **Važno:**
> Granica memorije je prijelazna mjera i ne zamjenjuje PHP ažuriranje. Planirajte ažuriranje paralelno i nakon njega ponovno pokrenite sve PHP procese. Sve prijelazne mjere ukratko nalaze se na stranici [Hitne mjere ako još ne možete zakrpati](https://phpsos.de/hr/php2shell/sofortmassnahmen-ohne-patch/).

Što se unutar PHP-a događa pri nedostatku memorije, objašnjava [memory_limit i Out-of-Memory](https://phpsos.de/hr/php/out-of-memory/). Kod Apachea s mod_php-om ista tehnika vrijedi za Apache uslugu, ali PHP tada granicu dijeli sa samim web-poslužiteljem.

## Zaključak: ne zaboraviti drugu polovicu

`memory_limit=-1` opravdana je prijelazna mjera samo zajedno s vanjskom granicom. systemd tu granicu omogućuje s nekoliko redaka: `MemoryHigh` kao kočnicu, `MemoryMax` kao tvrdu gornju granicu, `MemorySwapMax` protiv bijega u swap i svjesno odabran `OOMPolicy`. Izmjerite prije, promatrajte poslije i imajte na umu da svi poolovi jedne usluge dijele granicu.

Ponajprije pak: planirajte kraj mjere. Nakon ažuriranja na ispravljenu verziju PHP-a vraćate se na čvrst `memory_limit`. cgroup granicu možete zadržati kao dodatno osiguranje od otrgnutih procesa.

## Kako PHP SOS konkretno pomaže?

- **Mjeriti:** bilježimo potrebe za memorijom Vaših PHP usluga i iz toga izvodimo prikladne granice.
- **Provesti:** postavljamo drop-inove s `MemoryHigh`, `MemoryMax` i prikladnim `OOMPolicy`, zaključavamo `memory_limit` u poolu i kontrolirano ponovno pokrećemo usluge.
- **Promatrati:** OOM događaje uključujemo u nadzor i dotjerujemo vrijednosti.
- **Dovesti do zakrpe:** putem [PHP Patch usluge](https://phpsos.de/hr/leistungen/php-patch-service/) provodimo ažuriranje i nakon toga uredno povlačimo prijelazne vrijednosti.


## Česta pitanja

**Zašto memory_limit treba biti -1? Nije li to opasno?**  
Samo za sebe bilo bi. php.net memory_limit=-1 kao prijelaznu mjeru izričito preporučuje zajedno s granicom memorije putem kontejnera ili cgroupa. Ograničenje se time iz PHP-a seli u operacijski sustav. Bez te vanjske granice ne biste smjeli postaviti tu vrijednost.

**Mogu li umjesto toga koristiti ulimit ili LimitAS=?**  
Ne. php.net izričito odgovara od RLIMIT_AS i RLIMIT_DATA, a upravo te granice postavljaju ulimit -v, LimitAS= i LimitDATA=. Dokumentacija systemd-a uz to upozorava da LimitAS= ograničava adresni prostor, a ne potrošnju memorije.

**Što se događa kad PHP-FPM dosegne granicu?**  
Kod MemoryHigh jezgra usporava procese i vraća memoriju. Kod MemoryMax unutar usluge postaje aktivan OOM killer i prekida jedan proces, najčešće velikog workera. Zaustavlja li se nakon toga cijela usluga, određuje OOMPolicy=.

**Vrijedi li granica za svaki pool zasebno?**  
Ne. Svi poolovi jedne FPM usluge dijele jednu cgroup i time zajedničku granicu. Odvojene granice po web-stranici traže odvojene FPM usluge ili kontejnere.

**Kako postaviti granicu u Dockeru ili Kubernetesu?**  
Putem ograničenja memorije kontejnera, primjerice opcijom --memory kod docker run ili resources.limits.memory u Kubernetesu. Oba također koriste cgroupe. Načelo ostaje isto: granica izvan PHP-a, memory_limit u PHP-u zaključan.

## Izvori

- [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: sigurnosne obavijesti](https://www.php.net/)
- [PHP priručnik: memory_limit](https://www.php.net/manual/en/ini.core.php)
