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:
# "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.
# 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:
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; 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.
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.
- Tarkkailkaa kaikkien FPM-prosessien nykyistä muistintarvetta muutaman päivän ajan, myös kuormituspiikkien aikana.
- Selvittäkää workerin tyypillinen tarve ja kertokaa se palvelun kaikkien poolien arvolla
pm.max_children. - Varatkaa lisä master-prosessille, OPcachelle ja piikeille.
- Asettakaa
MemoryHigh=hieman mitatun huippuarvon yläpuolelle,MemoryMax=vähän sen yläpuolelle. - Hienosäätäkää arvot viikon kuluttua mittausten ja OOM-tapahtumien perusteella.
# 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.
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.
# 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.
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ää.
Mitä PHP:n sisällä tapahtuu muistin loppuessa, selittää memory_limit ja 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,MemoryMaxja sopivaOOMPolicy, lukitsemmememory_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 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ä.