# Задаване на лимити на паметта със systemd и cgroups

> systemd MemoryMax за PHP-FPM: лимит на паметта чрез cgroup, както php.net препоръчва за PHP2Shell, с MemoryHigh, OOMPolicy и команди за проверка.

Източник: https://phpsos.de/bg/blog/speichergrenzen-systemd-cgroups/  
Актуалност: 25.10.2026  
Доставчик: PHP SOS, Спешна помощ за PHP и сигурност на сървъри. Незабавна помощ по WhatsApp: https://wa.me/491736494914?text=%D0%97%D0%B4%D1%80%D0%B0%D0%B2%D0%B5%D0%B9%D1%82%D0%B5%2C%20PHP%20SOS%2C%20%D0%B8%D0%BC%D0%B0%D0%BC%D0%B5%20%D0%BD%D1%83%D0%B6%D0%B4%D0%B0%20%D0%BE%D1%82%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%20%D1%81%20%D0%BD%D0%B0%D1%88%D0%B8%D1%8F%20PHP%20%D1%81%D1%8A%D1%80%D0%B2%D1%8A%D1%80.%20%D0%A4%D0%B8%D1%80%D0%BC%D0%B0%3A%20%E2%80%A6%20%D0%A3%D0%B5%D0%B1%D1%81%D0%B0%D0%B9%D1%82%3A%20%E2%80%A6

**Накратко:** За преходната мярка `memory_limit=-1` при PHP2Shell PHP-FPM се нуждае от лимит на паметта извън PHP. Под systemd го задавате с `MemoryHigh=` като спирачка и `MemoryMax=` като твърда горна граница в допълващ файл за услугата FPM. `ulimit`, `LimitAS=` или `LimitDATA=` според php.net са неподходящи за това. Проверете освен това `OOMPolicy=`, за да не прекрати един отделен worker цялата услуга.

## Препоръка, която първоначално учудва

Сред преходните мерки, които php.net публикува на 21 октомври 2026 г. за PHP2Shell, има една, която накара много администратори да се спрат: `memory_limit=-1`, тоест никакъв лимит на паметта в самия PHP. С години обратното се смяташе за добра практика.

Връзка с описанието на уязвимостта е очевидна. При CVE-2026-109618 състояние на недостиг на памет (Out-of-Memory) може да прекъсне PHP, докато данните на заявката са само частично инициализирани. Преходната мярка премества лимита на паметта извън PHP в операционната система. php.net я свързва изрично с две условия: стойността трябва да е заключена срещу презаписване, а лимитът трябва да идва чрез контейнер или cgroup, не чрез `RLIMIT_AS` или `RLIMIT_DATA`.

Който приложи само първата половина, има сървър без никакъв лимит на паметта. Тази статия показва втората половина. И заедно мярката остава преход: уязвимостта е отстранена едва с PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 или потвърден backport.

## cgroups, обяснени в две изречения

Control Groups, накратко cgroups, са функция на ядрото на Linux, която групира процеси и ограничава ресурсите им. systemd създава автоматично за всяка услуга собствена cgroup и всички процеси на услугата, при услугата FPM тоест главният процес и всички worker процеси, принадлежат към нея.

Опциите за паметта на systemd задават стойности на контролера на паметта на cgroup v2. Дали Вашата система ползва cgroup v2, показва кратка команда:

```bash
# "cgroup2fs" означава: cgroup v2 е активна
stat -fc %T /sys/fs/cgroup/
```

Актуалните версии на разпространените дистрибуции ползват cgroup v2 по подразбиране. Ако командата покаже нещо друго, изяснете това преди прехода, защото документацията на systemd описва старите опции за v1 като остарели.

## Задаване на systemd MemoryMax за PHP-FPM

Лимитът се записва в допълващ файл, т.нар. drop-in. Така файлът на unit от пакета остава непроменен, а актуализация не презаписва Вашата настройка. Имената на услугите се различават според дистрибуцията; примерът ползва Debian и Ubuntu с PHP 8.3.

```ini
# Отваря редактор за drop-in: sudo systemctl edit php8.3-fpm
# Резултат: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
```

Опциите накратко, по документацията на systemd:

| Опция | Ефект | Указание за PHP-FPM |
|---|---|---|
| `MemoryHigh=` | граница на задушаване: процесите се забавят силно, паметта се възвръща | същинската управляваща стойност, малко под `MemoryMax` |
| `MemoryMax=` | абсолютна граница: ако не може да се удържи, OOM killer се активира в услугата | последна линия на защита |
| `MemorySwapMax=` | граница за разменяне | `0` предотвратява услугата да се измъкне в swap |
| `OOMPolicy=` | поведение след OOM събитие: `continue`, `stop` или `kill` | `continue` оставя останалите worker процеси да работят |
| `LimitAS=`, `LimitDATA=` | rlimits за процес | според php.net неподходящи за тази мярка |

`OOMPolicy=` заслужава особено внимание. При `stop` или `kill` systemd след OOM събитие прекратява цялата услуга и всички сайтове на тази услуга FPM отпадат. Коя стойност важи без собствено указание, определя системната настройка по подразбиране `DefaultOOMPolicy=`. Затова проверете действащата стойност, вместо да я предполагате.

След това рестартирайте услугата и заключете `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
; Файл на пул, напр. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
```

Как работи заключването, описва [php_admin_value: заключване на настройки срещу презаписване](https://phpsos.de/bg/blog/php-admin-value-sperren/).

## Намиране на правилната стойност

Твърде тесен лимит създава прекъсвания в нормална работа, твърде щедър почти не защитава. Изходната точка е действителното потребление.

1. Наблюдавайте текущата нужда от памет на всички процеси на FPM в продължение на няколко дни, и при пикове на натоварването.
2. Установете типичната нужда на един worker и я умножете по `pm.max_children` на всички пулове на услугата.
3. Планирайте надбавка за главния процес, OPcache и пикове.
4. Задайте `MemoryHigh=` малко над измерения пик, `MemoryMax=` с известно разстояние над него.
5. След седмица коригирайте стойностите според измерванията и OOM събитията.

```bash
# Памет на всички процеси на PHP-FPM в MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Текущ преглед на всички cgroups, подредени по памет
systemd-cgtop -m
```

Стойността на `memory_limit` преди прехода трябва да запишете. След актуализацията можете да се върнете към фиксиран лимит в PHP; как да го изберете смислено, е описано в [Правилна настройка на memory_limit](https://phpsos.de/bg/blog/memory-limit-richtig-setzen/).

## Наблюдение: кога лимитът се задейства

Лимит на паметта, чието задействане никой не забелязва, е тих източник на грешки. Ядрото брои OOM събитията за всяка cgroup, а systemd и протоколът на ядрото ги отбелязват.

```bash
# OOM брояч на cgroup на услугата (път при cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Съобщения на OOM killer в протокола на ядрото
journalctl -k --since "-1 day" | grep -i "out of memory"
```

Ако броячът `oom_kill` нараства, ядрото прекратява worker процеси. Това може да сочи твърде тесен лимит, грешно приложение или необичайни заявки. В сегашното положение си струва такива събития да се съпоставят с протоколите за достъп в същото време.

> **Важно:**
> Лимитът на паметта е преходна мярка и не замества актуализацията на PHP. Планирайте актуализацията успоредно и след нея рестартирайте всички PHP процеси. Всички преходни мерки накратко са в [Незабавни мерки, когато още не можете да пачнете](https://phpsos.de/bg/php2shell/sofortmassnahmen-ohne-patch/).

Какво се случва вътре в PHP при недостиг на памет, обяснява [memory_limit и Out-of-Memory](https://phpsos.de/bg/php/out-of-memory/). При Apache с mod_php същата техника важи за услугата Apache, но тогава PHP споделя лимита със самия уеб сървър.

## Извод: не забравяйте втората половина

`memory_limit=-1` е приемлива преходна мярка само заедно с външен лимит. systemd го прави възможен с няколко реда: `MemoryHigh` като спирачка, `MemoryMax` като твърда горна граница, `MemorySwapMax` срещу измъкване в swap и съзнателно избрана `OOMPolicy`. Измерете преди това, наблюдавайте след това и имайте предвид, че всички пулове на една услуга споделят лимита.

Преди всичко обаче: планирайте края на мярката. След актуализацията до коригирана версия на PHP се връщате към фиксиран `memory_limit`. Лимита на cgroup можете да запазите като допълнителна защита срещу излизащи извън контрол процеси.

## Как помага PHP SOS конкретно?

- **Измерване:** отчитаме нуждата от памет на Вашите PHP услуги и извеждаме от нея подходящи лимити.
- **Прилагане:** задаваме drop-in файлове с `MemoryHigh`, `MemoryMax` и подходяща `OOMPolicy`, заключваме `memory_limit` в пула и рестартираме услугите контролирано.
- **Наблюдение:** включваме OOM събитията в мониторинга и коригираме стойностите.
- **Към пача:** чрез [PHP пач услугата](https://phpsos.de/bg/leistungen/php-patch-service/) инсталираме актуализацията и след това връщаме преходните стойности по ред.


## Често задавани въпроси

**Защо memory_limit трябва да е -1? Не е ли опасно?**  
Само по себе си би било. php.net препоръчва memory_limit=-1 като преходна мярка изрично заедно с лимит на паметта чрез контейнер или cgroup. Така ограничението се премества от PHP в операционната система. Без този външен лимит не бива да задавате стойността.

**Мога ли вместо това да използвам ulimit или LimitAS=?**  
Не. php.net изрично съветва против RLIMIT_AS и RLIMIT_DATA, а точно тези лимити задават ulimit -v, LimitAS= и LimitDATA=. Документацията на systemd освен това сочи, че LimitAS= ограничава адресното пространство, не потреблението на памет.

**Какво се случва, когато PHP-FPM достигне лимита?**  
При MemoryHigh ядрото забавя процесите и възвръща памет. При MemoryMax в рамките на услугата се активира OOM killer и прекратява един процес, обикновено голям worker. Дали след това спира цялата услуга, определя OOMPolicy=.

**Важи ли лимитът за всеки пул поотделно?**  
Не. Всички пулове на една услуга FPM споделят една cgroup и следователно общ лимит. Отделни лимити за всеки сайт изискват отделни услуги FPM или контейнери.

**Как да задам лимита в Docker или Kubernetes?**  
Чрез ограничението на паметта на контейнера, например опцията --memory при docker run или resources.limits.memory в Kubernetes. И двете също използват cgroups. Принципът остава същият: лимит извън PHP, memory_limit в PHP заключен.

## Източници

- [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: Съобщения за сигурност](https://www.php.net/)
- [Ръководство на PHP: memory_limit](https://www.php.net/manual/en/ini.core.php)
