# Muistirajojen asettaminen systemd:llä ja cgroupeilla

> systemd MemoryMax PHP-FPM:lle: muistiraja cgroupilla php.netin PHP2Shell-suosituksen mukaan, MemoryHigh-, OOMPolicy- ja tarkistuskomentojen kanssa.

Lähde: https://phpsos.de/fi/blog/speichergrenzen-systemd-cgroups/  
Tilanne: 25.10.2026  
Palveluntarjoaja: PHP SOS, PHP-hätäapu ja palvelimen tietoturva. Pikaapu WhatsAppilla: https://wa.me/491736494914?text=Hei%20PHP%20SOS%2C%20tarvitsemme%20apua%20PHP-palvelimemme%20kanssa.%20Yritys%3A%20%E2%80%A6%20Verkkosivusto%3A%20%E2%80%A6

**Lyhyesti:** PHP2Shellin siirtymäajan toimenpide `memory_limit=-1` vaatii PHP-FPM:lle muistirajan PHP:n ulkopuolelle. systemd:n alla asetatte sen arvolla `MemoryHigh=` jarruna ja arvolla `MemoryMax=` kovana ylärajana FPM-palvelun täydennystiedostossa. `ulimit`, `LimitAS=` tai `LimitDATA=` ovat tähän php.net:n mukaan sopimattomia. Tarkistakaa lisäksi `OOMPolicy=`, jotta yksittäinen worker ei lopeta koko palvelua.

## Suositus, joka aluksi hämmästyttää

Siirtymäajan toimenpiteiden joukossa, jotka php.net julkaisi PHP2Shellistä 21. lokakuuta 2026, on yksi, joka sai monet ylläpitäjät pysähtymään: `memory_limit=-1`, eli ei muistirajaa PHP:ssä itsessään. Vuosia päinvastaista pidettiin hyvänä käytäntönä.

Yhteys aukon kuvaukseen on ilmeinen. CVE-2026-109618:ssa muistin loppuminen voi keskeyttää PHP:n, kun pyynnön tiedot on alustettu vain osittain. Siirtymäajan toimenpide siirtää muistirajan PHP:stä käyttöjärjestelmään. php.net sitoo sen nimenomaisesti kahteen ehtoon: arvon on oltava lukittu ylikirjoitukselta, ja rajan on tultava kontin tai cgroupin kautta, ei `RLIMIT_AS`- tai `RLIMIT_DATA`-rajojen kautta.

Joka toteuttaa vain ensimmäisen puolikkaan, saa palvelimen ilman minkäänlaista muistirajaa. Tämä kirjoitus näyttää toisen puolikkaan. Myös yhdessä toimenpide on siirtymä: aukko on korjattu vasta versiolla PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 tai vahvistetulla backportilla.

## cgroupit kahdessa lauseessa

Control Groups, lyhyesti cgroupit, ovat Linux-ytimen toiminto, joka ryhmittelee prosesseja ja rajaa niiden resursseja. systemd luo jokaiselle palvelulle automaattisesti oman cgroupin, ja kaikki palvelun prosessit, FPM-palvelussa master ja kaikki workerit, kuuluvat siihen.

systemd:n muistivalinnat asettavat cgroup v2 -muistiohjaimen arvoja. Käyttääkö järjestelmänne cgroup v2:ta, näyttää lyhyt komento:

```bash
# "cgroup2fs" tarkoittaa: cgroup v2 on aktiivinen
stat -fc %T /sys/fs/cgroup/
```

Yleisten jakeluiden nykyiset versiot käyttävät oletuksena cgroup v2:ta. Jos komento näyttää jotain muuta, selvittäkää se ennen muutosta, sillä systemd-dokumentaatio kuvaa vanhat v1-valinnat vanhentuneiksi.

## systemd MemoryMax PHP-FPM:lle

Raja tulee täydennystiedostoon, niin sanottuun drop-iniin. Näin paketin unit-tiedosto pysyy muuttumattomana, eikä päivitys ylikirjoita asetustanne. Palvelunimet vaihtelevat jakelun mukaan; esimerkki käyttää Debiania ja Ubuntua PHP 8.3:lla.

```ini
# Avaa muokkaimen drop-iniä varten: sudo systemctl edit php8.3-fpm
# Tulos: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
```

Valinnat yleiskuvana systemd:n dokumentaation mukaan:

| Valinta | Vaikutus | Huomio PHP-FPM:lle |
|---|---|---|
| `MemoryHigh=` | kuristusraja: prosesseja hidastetaan voimakkaasti, muistia otetaan takaisin | varsinainen ohjausarvo, hieman `MemoryMax`-arvon alapuolella |
| `MemoryMax=` | ehdoton raja: jos sitä ei voi pitää, OOM-killer aktivoituu palvelussa | viimeinen puolustuslinja |
| `MemorySwapMax=` | vaihtoalueen raja | `0` estää palvelun väistämästä sen sijaan swapiin |
| `OOMPolicy=` | käyttäytyminen OOM-tapahtuman jälkeen: `continue`, `stop` tai `kill` | `continue` antaa muiden workerien jatkaa |
| `LimitAS=`, `LimitDATA=` | rlimitit prosessia kohden | php.net:n mukaan tähän toimenpiteeseen sopimattomia |

`OOMPolicy=` ansaitsee erityishuomion. Arvoilla `stop` tai `kill` systemd lopettaa OOM-tapahtuman jälkeen koko palvelun, ja kaikki tämän FPM-palvelun verkkosivustot kaatuvat. Mikä arvo on voimassa ilman omaa määritystä, määrää järjestelmän oletus `DefaultOOMPolicy=`. Tarkistakaa voimassa oleva arvo siksi sen sijaan, että olettaisitte sen.

Sen jälkeen käynnistäkää palvelu uudelleen ja lukitkaa poolissa `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
; Pool-tiedosto, esim. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
```

Miten lukitseminen toimii, kuvaa [php_admin_value: asetusten lukitseminen ylikirjoitukselta](https://phpsos.de/fi/blog/php-admin-value-sperren/).

## Oikean arvon löytäminen

Liian niukka raja aiheuttaa keskeytyksiä normaalikäytössä, liian antelias ei suojaa juuri lainkaan. Lähtökohta on todellinen kulutus.

1. Tarkkailkaa kaikkien FPM-prosessien nykyistä muistintarvetta muutaman päivän ajan, myös kuormituspiikkien aikana.
2. Selvittäkää workerin tyypillinen tarve ja kertokaa se palvelun kaikkien poolien arvolla `pm.max_children`.
3. Varatkaa lisä master-prosessille, OPcachelle ja piikeille.
4. Asettakaa `MemoryHigh=` hieman mitatun huippuarvon yläpuolelle, `MemoryMax=` vähän sen yläpuolelle.
5. Hienosäätäkää arvot viikon kuluttua mittausten ja OOM-tapahtumien perusteella.

```bash
# Kaikkien PHP-FPM-prosessien muisti megatavuina (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Kaikkien cgroupien jatkuva yleiskatsaus muistin mukaan lajiteltuna
systemd-cgtop -m
```

`memory_limit`-arvo kannattaa kirjata muistiin ennen muutosta. Päivityksen jälkeen voitte palata kiinteään rajaan PHP:ssä; miten sen valitsette järkevästi, kerrotaan kohdassa [memory_limit-asetuksen määrittäminen oikein](https://phpsos.de/fi/blog/memory-limit-richtig-setzen/).

## Valvonta: milloin raja laukeaa

Muistiraja, jonka laukeamista kukaan ei huomaa, on hiljainen virhelähde. Ydin laskee OOM-tapahtumat cgroupia kohden, ja systemd sekä ytimen loki kirjaavat ne.

```bash
# Palvelun cgroupin OOM-laskuri (polku cgroup v2:lla)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# OOM-killerin ilmoitukset ytimen lokissa
journalctl -k --since "-1 day" | grep -i "out of memory"
```

Jos laskuri `oom_kill` kasvaa, ydin lopettaa workereita. Se voi viitata liian niukkaan rajaan, virheelliseen sovellukseen tai epätavallisiin pyyntöihin. Nykytilanteessa tällaiset tapahtumat kannattaa verrata samanaikaisiin käyttölokeihin.

> **Tärkeää:**
> Muistiraja on siirtymäajan toimenpide eikä korvaa PHP-päivitystä. Suunnitelkaa päivitys rinnakkain ja käynnistäkää sen jälkeen kaikki PHP-prosessit uudelleen. Kaikki siirtymäajan toimenpiteet yleiskatsauksena ovat kohdassa [Pikatoimet, jos ette voi vielä päivittää](https://phpsos.de/fi/php2shell/sofortmassnahmen-ohne-patch/).

Mitä PHP:n sisällä tapahtuu muistin loppuessa, selittää [memory_limit ja Out-of-Memory](https://phpsos.de/fi/php/out-of-memory/). Apachella mod_php:n kanssa sama tekniikka pätee Apache-palveluun, mutta PHP jakaa tällöin rajan itse verkkopalvelimen kanssa.

## Yhteenveto: älkää unohtako toista puolikasta

`memory_limit=-1` on puolustettava siirtymäajan toimenpide vain yhdessä ulkoisen rajan kanssa. systemd mahdollistaa tämän rajan muutamalla rivillä: `MemoryHigh` jarruna, `MemoryMax` kovana ylärajana, `MemorySwapMax` swapiin väistämistä vastaan ja tietoisesti valittu `OOMPolicy`. Mitatkaa ennen, tarkkailkaa jälkeen ja pitäkää mielessä, että palvelun kaikki poolit jakavat rajan.

Ennen kaikkea: suunnitelkaa toimenpiteen loppu. Päivityksen jälkeen korjattuun PHP-versioon palaatte kiinteään `memory_limit`-arvoon. Cgroup-rajan voitte säilyttää lisävarmistuksena karkaavia prosesseja vastaan.

## Miten PHP SOS auttaa konkreettisesti?

- **Mittaus:** Kartoitamme PHP-palveluidenne muistintarpeen ja johdamme siitä sopivat rajat.
- **Toteutus:** Asetamme drop-init arvoilla `MemoryHigh`, `MemoryMax` ja sopiva `OOMPolicy`, lukitsemme `memory_limit`-arvon poolissa ja käynnistämme palvelut hallitusti uudelleen.
- **Valvonta:** Liitämme OOM-tapahtumat valvontaan ja hienosäädämme arvoja.
- **Päivitykseen ohjaus:** [PHP-päivityspalvelun](https://phpsos.de/fi/leistungen/php-patch-service/) kautta asennamme päivityksen ja purkamme siirtymäarvot sen jälkeen järjestelmällisesti.


## Usein kysytyt kysymykset

**Miksi memory_limit pitäisi asettaa arvoon -1? Eikö se ole vaarallista?**  
Yksinään se olisi. php.net suosittelee memory_limit=-1-asetusta siirtymäajan toimenpiteenä nimenomaisesti yhdessä kontin tai cgroupin kautta asetetun muistirajan kanssa. Rajaus siirtyy siten PHP:stä käyttöjärjestelmään. Ilman tätä ulkoista rajaa arvoa ei pitäisi asettaa.

**Voinko käyttää sen sijaan ulimitiä tai LimitAS=-asetusta?**  
Et. php.net neuvoo nimenomaisesti olemaan käyttämättä RLIMIT_AS- ja RLIMIT_DATA-rajoja, ja juuri nämä rajat asettavat ulimit -v, LimitAS= ja LimitDATA=. systemd-dokumentaatio huomauttaa lisäksi, että LimitAS= rajaa osoiteavaruutta, ei muistinkäyttöä.

**Mitä tapahtuu, kun PHP-FPM saavuttaa rajan?**  
MemoryHigh-asetuksella ydin hidastaa prosesseja ja ottaa muistia takaisin. MemoryMax-asetuksella OOM-killer aktivoituu palvelun sisällä ja lopettaa prosessin, yleensä suuren workerin. Pysähtyykö sen jälkeen koko palvelu, määrää OOMPolicy=.

**Koskeeko raja jokaista poolia erikseen?**  
Ei. Kaikki FPM-palvelun poolit jakavat yhden cgroupin ja siten yhteisen rajan. Sivustokohtaiset erilliset rajat vaativat erilliset FPM-palvelut tai kontit.

**Miten asetan rajan Dockerissa tai Kubernetesissa?**  
Kontin muistirajoituksen kautta, esimerkiksi valinnalla --memory komennossa docker run tai resources.limits.memory Kubernetesissa. Molemmat käyttävät myös cgroupeja. Periaate pysyy samana: raja PHP:n ulkopuolella, memory_limit lukittuna PHP:ssä.

## Lähteet

- [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: tietoturvailmoitukset](https://www.php.net/)
- [PHP-käsikirja: memory_limit](https://www.php.net/manual/en/ini.core.php)
