# Nastavitev pomnilniških mej s systemd in cgroups

> systemd MemoryMax za PHP-FPM: pomnilniška meja prek cgroup, kot jo php.net priporoča za PHP2Shell, z MemoryHigh, OOMPolicy in ukazi za preverjanje.

Vir: https://phpsos.de/sl/blog/speichergrenzen-systemd-cgroups/  
Stanje: 25.10.2026  
Ponudnik: PHP SOS, Nujna pomoč za PHP in varnost strežnikov. Takojšnja pomoč po WhatsAppu: https://wa.me/491736494914?text=Pozdravljeni%2C%20PHP%20SOS%2C%20potrebujemo%20pomo%C4%8D%20z%20na%C5%A1im%20stre%C5%BEnikom%20PHP.%20Podjetje%3A%20%E2%80%A6%20Spletna%20stran%3A%20%E2%80%A6

**Na kratko:** Za prehodni ukrep PHP2Shell `memory_limit=-1` PHP-FPM potrebuje pomnilniško mejo zunaj PHP. Pod systemd jo nastavite z `MemoryHigh=` kot zavoro in `MemoryMax=` kot trdo zgornjo mejo v dopolnilni datoteki za storitev FPM. `ulimit`, `LimitAS=` ali `LimitDATA=` so za to po php.net neprimerni. Preverite še `OOMPolicy=`, da en sam delavec ne konča celotne storitve.

## Priporočilo, ki sprva preseneti

Med prehodnimi ukrepi, ki jih je php.net 21. oktobra 2026 objavil za PHP2Shell, je eden, ki je številne skrbnike presenetil: `memory_limit=-1`, torej brez pomnilniške meje v samem PHP. Leta je veljalo nasprotno kot dobra praksa.

Povezava z opisom ranljivosti je očitna. Pri CVE-2026-109618 lahko stanje brez pomnilnika (out-of-memory) prekine PHP, medtem ko so podatki zahteve inicializirani le delno. Prehodni ukrep pomnilniško mejo iz PHP preseli v operacijski sistem. php.net ga izrecno veže na dva pogoja: vrednost mora biti zaklenjena pred prepisovanjem, meja pa mora priti prek vsebnika ali cgroup, ne prek `RLIMIT_AS` ali `RLIMIT_DATA`.

Kdor izvede samo prvo polovico, ima strežnik brez kakršne koli pomnilniške meje. Ta prispevek pokaže drugo polovico. Tudi skupaj ukrep ostaja prehod: ranljivost je odpravljena šele s PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 ali potrjenim backportom.

## cgroups v dveh stavkih

Control Groups, na kratko cgroups, so funkcija jedra Linux, ki procese združuje v skupine in omejuje njihove vire. systemd za vsako storitev samodejno ustvari lasten cgroup in vsi procesi storitve, pri storitvi FPM torej master in vsi delavci, mu pripadajo.

Pomnilniške možnosti systemd nastavljajo vrednosti krmilnika pomnilnika cgroup v2. Ali vaš sistem uporablja cgroup v2, pokaže kratek ukaz:

```bash
# "cgroup2fs" pomeni: cgroup v2 je aktiven
stat -fc %T /sys/fs/cgroup/
```

Aktualne različice običajnih distribucij privzeto uporabljajo cgroup v2. Če ukaz pokaže kaj drugega, to razjasnite pred prehodom, saj dokumentacija systemd stare možnosti v1 opisuje kot zastarele.

## Nastavitev systemd MemoryMax za PHP-FPM

Meja pride v dopolnilno datoteko, tako imenovani drop-in. Tako datoteka enote paketa ostane nespremenjena in posodobitev vaše nastavitve ne prepiše. Imena storitev se po distribuciji razlikujejo; primer uporablja Debian in Ubuntu s PHP 8.3.

```ini
# Odpre urejevalnik 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
```

Možnosti v pregledu, po dokumentaciji systemd:

| Možnost | Učinek | Namig za PHP-FPM |
|---|---|---|
| `MemoryHigh=` | meja dušenja: procesi se močno zavirajo, pomnilnik se vrača | pravi krmilni parameter, nekoliko pod `MemoryMax` |
| `MemoryMax=` | absolutna meja: če je ni mogoče držati, se v storitvi aktivira OOM-killer | zadnja obrambna linija |
| `MemorySwapMax=` | meja za izmenjavo | `0` prepreči, da bi storitev namesto tega zaplavala v swap |
| `OOMPolicy=` | obnašanje po dogodku OOM: `continue`, `stop` ali `kill` | `continue` pusti preostale delavce teči naprej |
| `LimitAS=`, `LimitDATA=` | rlimiti na proces | po php.net za ta ukrep neprimerni |

`OOMPolicy=` si zasluži posebno pozornost. Pri `stop` ali `kill` systemd po dogodku OOM konča celotno storitev in vse spletne strani te storitve FPM odpovedo. Katera vrednost velja brez lastne navedbe, določa sistemska privzeta vrednost `DefaultOOMPolicy=`. Veljavno vrednost zato preverite, namesto da jo predpostavljate.

Nato storitev znova zaženite in v poolu zaklenite `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 zaklepanje deluje, opisuje [php_admin_value: zaklepanje nastavitev pred prepisovanjem](https://phpsos.de/sl/blog/php-admin-value-sperren/).

## Iskanje prave vrednosti

Prekratka meja povzroča prekinitve v običajnem delovanju, preveč radodarna komaj ščiti. Izhodišče je dejanska poraba.

1. Opazujte trenutno pomnilniško potrebo vseh procesov FPM več dni, tudi ob konicah obremenitve.
2. Ugotovite tipično potrebo delavca in jo pomnožite s `pm.max_children` vseh poolov storitve.
3. Načrtujte pribitek za proces master, OPcache in konice.
4. `MemoryHigh=` nastavite tik nad izmerjeno konico, `MemoryMax=` z nekaj razmika nad tem.
5. Vrednosti po enem tednu popravite glede na meritve in dogodke OOM.

```bash
# Pomnilnik vseh 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}'
# Tekoči pregled vseh cgroups, razvrščen po pomnilniku
systemd-cgtop -m
```

Vrednost za `memory_limit` si pred prehodom zabeležite. Po posodobitvi se lahko vrnete k fiksni meji v PHP; kako jo smiselno izberete, piše pod [Pravilna nastavitev memory_limit](https://phpsos.de/sl/blog/memory-limit-richtig-setzen/).

## Spremljanje: kdaj meja deluje

Pomnilniška meja, katere sprožitve nihče ne opazi, je tihi vir napak. Jedro šteje dogodke OOM za vsak cgroup, systemd in dnevnik jedra pa jih zabeležita.

```bash
# Števec OOM cgroupa storitve (pot pri cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Sporočila OOM-killerja v dnevniku jedra
journalctl -k --since "-1 day" | grep -i "out of memory"
```

Če števec `oom_kill` narašča, jedro končuje delavce. To lahko kaže na prekratko mejo, napačno aplikacijo ali nenavadne zahteve. V trenutnih razmerah se take dogodke splača primerjati z dostopnimi dnevniki ob istem času.

> **Pomembno:**
> Pomnilniška meja je prehodni ukrep in posodobitve PHP ne nadomešča. Posodobitev načrtujte vzporedno in nato znova zaženite vse procese PHP. Vsi prehodni ukrepi v pregledu so pod [Takojšnji ukrepi, če popravka še ne morete namestiti](https://phpsos.de/sl/php2shell/sofortmassnahmen-ohne-patch/).

Kaj se ob pomanjkanju pomnilnika dogaja v notranjosti PHP, pojasnjuje [memory_limit in out-of-memory](https://phpsos.de/sl/php/out-of-memory/). Pri Apache z mod_php enaka tehnika velja za storitev Apache, vendar si PHP potem mejo deli s samim spletnim strežnikom.

## Sklep: druge polovice ne pozabite

`memory_limit=-1` je upravičen prehodni ukrep samo skupaj z zunanjo mejo. systemd to mejo omogoči z nekaj vrsticami: `MemoryHigh` kot zavora, `MemoryMax` kot trda zgornja meja, `MemorySwapMax` proti umiku v swap in zavestno izbran `OOMPolicy`. Izmerite vnaprej, opazujte potem in imejte v mislih, da si vsi pooli ene storitve mejo delijo.

Predvsem pa: načrtujte konec ukrepa. Po posodobitvi na popravljeno različico PHP se vrnite k fiksnemu `memory_limit`. Mejo cgroup lahko obdržite kot dodatno zaščito pred ušlimi procesi.

## Kako PHP SOS pomaga konkretno?

- **Merjenje:** zajamemo pomnilniško potrebo vaših storitev PHP in iz tega izpeljemo ustrezne meje.
- **Izvedba:** nastavimo drop-ine z `MemoryHigh`, `MemoryMax` in ustreznim `OOMPolicy`, v poolu zaklenemo `memory_limit` in storitve nadzorovano znova zaženemo.
- **Opazovanje:** dogodke OOM vključimo v spremljanje in vrednosti popravimo.
- **Pot do popravka:** prek [storitve popravljanja PHP](https://phpsos.de/sl/leistungen/php-patch-service/) namestimo posodobitev in nato prehodne vrednosti urejeno umaknemo.


## Pogosta vprašanja

**Zakaj naj bo memory_limit nastavljen na -1? Ali ni to nevarno?**  
Samo po sebi bi bilo. php.net memory_limit=-1 kot prehodni ukrep izrecno priporoča skupaj z mejo pomnilnika prek vsebnika ali cgroup. Omejitev se tako iz PHP preseli v operacijski sistem. Brez te zunanje meje vrednosti ne nastavljajte.

**Ali lahko namesto tega uporabim ulimit ali LimitAS=?**  
Ne. php.net izrecno odsvetuje RLIMIT_AS in RLIMIT_DATA, prav ti meji pa nastavljajo ulimit -v, LimitAS= in LimitDATA=. Dokumentacija systemd poleg tega opozarja, da LimitAS= omejuje naslovni prostor, ne porabe pomnilnika.

**Kaj se zgodi, ko PHP-FPM doseže mejo?**  
Pri MemoryHigh jedro procese zavira in vrača pomnilnik. Pri MemoryMax se znotraj storitve aktivira OOM-killer in konča en proces, običajno velikega delavca. Ali se potem ustavi celotna storitev, ureja OOMPolicy=.

**Ali meja velja za vsak pool posebej?**  
Ne. Vsi pooli ene storitve FPM si delijo en cgroup in s tem skupno mejo. Ločene meje za vsako spletno stran zahtevajo ločene storitve FPM ali vsebnike.

**Kako mejo nastavim v Dockerju ali Kubernetesu?**  
Prek omejitve pomnilnika vsebnika, na primer z možnostjo --memory pri docker run ali resources.limits.memory v Kubernetesu. Oba prav tako uporabljata cgroups. Načelo ostaja enako: meja zunaj PHP, memory_limit v PHP zaklenjen.

## Viri

- [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: varnostna obvestila](https://www.php.net/)
- [Priročnik PHP: memory_limit](https://www.php.net/manual/en/ini.core.php)
