# Ustawianie limitów pamięci za pomocą systemd i cgroups

> systemd MemoryMax dla PHP-FPM: limit pamięci przez cgroup zgodnie z zaleceniem php.net dla PHP2Shell, z MemoryHigh, OOMPolicy i poleceniami kontrolnymi.

Źródło: https://phpsos.de/pl/blog/speichergrenzen-systemd-cgroups/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**Krótko:** Dla środka przejściowego PHP2Shell `memory_limit=-1` PHP-FPM potrzebuje limitu pamięci poza PHP. W systemd ustawiają go Państwo za pomocą `MemoryHigh=` jako hamulca i `MemoryMax=` jako twardej górnej granicy w pliku uzupełniającym dla usługi FPM. `ulimit`, `LimitAS=` lub `LimitDATA=` są według php.net do tego nieodpowiednie. Proszę ponadto sprawdzić `OOMPolicy=`, aby pojedynczy proces roboczy nie zakończył całej usługi.

## Zalecenie, które początkowo dziwi

Wśród środków przejściowych, które php.net opublikował 21 października 2026 w sprawie PHP2Shell, jest jeden, który zaskoczył wielu administratorów: `memory_limit=-1`, czyli brak limitu pamięci w samym PHP. Przez lata za dobrą praktykę uchodziło coś odwrotnego.

Związek z opisem luki nasuwa się sam. W CVE-2026-109618 stan braku pamięci (Out-of-Memory) może przerwać PHP w chwili, gdy dane żądania są zainicjowane tylko częściowo. Środek przejściowy przenosi limit pamięci z PHP do systemu operacyjnego. php.net wiąże go wyraźnie z dwoma warunkami: wartość musi być zablokowana przed nadpisaniem, a limit musi pochodzić z kontenera lub cgroup, a nie z `RLIMIT_AS` czy `RLIMIT_DATA`.

Kto wdroży tylko pierwszą połowę, ma serwer bez jakiegokolwiek limitu pamięci. Ten artykuł pokazuje drugą połowę. Także razem środek pozostaje rozwiązaniem przejściowym: luka jest usunięta dopiero z PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 lub potwierdzonym backportem.

## cgroups w dwóch zdaniach

Control Groups, w skrócie cgroups, to funkcja jądra Linuksa, która grupuje procesy i ogranicza ich zasoby. systemd zakłada dla każdej usługi automatycznie własną cgroup i należą do niej wszystkie procesy usługi, w przypadku usługi FPM proces główny i wszystkie procesy robocze.

Opcje pamięci w systemd ustawiają wartości kontrolera pamięci cgroup v2. To, czy Państwa system używa cgroup v2, pokazuje krótkie polecenie:

```bash
# "cgroup2fs" oznacza: cgroup v2 jest aktywna
stat -fc %T /sys/fs/cgroup/
```

Aktualne wersje popularnych dystrybucji domyślnie używają cgroup v2. Jeśli polecenie pokazuje coś innego, proszę to wyjaśnić przed przestawieniem, ponieważ dokumentacja systemd opisuje stare opcje v1 jako przestarzałe.

## Ustawianie systemd MemoryMax dla PHP-FPM

Limit trafia do pliku uzupełniającego, tak zwanego drop-in. Dzięki temu plik jednostki z pakietu pozostaje niezmieniony, a aktualizacja nie nadpisze Państwa ustawienia. Nazwy usług różnią się zależnie od dystrybucji; przykład używa Debiana i Ubuntu z PHP 8.3.

```ini
# Otwiera edytor dla drop-in: sudo systemctl edit php8.3-fpm
# Wynik: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
```

Opcje w skrócie, według dokumentacji systemd:

| Opcja | Działanie | Uwaga dla PHP-FPM |
|---|---|---|
| `MemoryHigh=` | granica dławienia: procesy są mocno hamowane, pamięć jest odzyskiwana | właściwa wielkość sterująca, nieco poniżej `MemoryMax` |
| `MemoryMax=` | granica bezwzględna: jeśli nie da się jej utrzymać, w usłudze uaktywnia się OOM-killer | ostatnia linia obrony |
| `MemorySwapMax=` | limit przenoszenia na dysk | `0` zapobiega ucieczce usługi do swapu |
| `OOMPolicy=` | zachowanie po zdarzeniu OOM: `continue`, `stop` lub `kill` | `continue` pozwala pozostałym procesom roboczym działać dalej |
| `LimitAS=`, `LimitDATA=` | rlimity na proces | według php.net nieodpowiednie dla tego środka |

`OOMPolicy=` zasługuje na szczególną uwagę. Przy `stop` lub `kill` systemd po zdarzeniu OOM kończy całą usługę i wszystkie strony tej usługi FPM przestają działać. To, jaka wartość obowiązuje bez własnego wpisu, określa domyślne ustawienie systemu `DefaultOOMPolicy=`. Dlatego proszę sprawdzić obowiązującą wartość, zamiast ją zakładać.

Następnie zrestartować usługę i zablokować `memory_limit` w puli:

```bash
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
```

```ini
; Plik puli, np. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
```

Jak działa blokowanie, opisuje artykuł [php_admin_value: blokowanie ustawień przed nadpisaniem](https://phpsos.de/pl/blog/php-admin-value-sperren/).

## Znalezienie właściwej wartości

Zbyt ciasny limit powoduje przerwania w normalnej pracy, zbyt hojny prawie nie chroni. Punktem wyjścia jest rzeczywiste zużycie.

1. Obserwować bieżące zapotrzebowanie na pamięć wszystkich procesów FPM przez kilka dni, także w szczytach obciążenia.
2. Ustalić typowe zapotrzebowanie jednego procesu roboczego i pomnożyć przez `pm.max_children` wszystkich pul usługi.
3. Uwzględnić zapas na proces główny, OPcache i szczyty.
4. `MemoryHigh=` ustawić tuż powyżej zmierzonej wartości szczytowej, `MemoryMax=` z pewnym odstępem powyżej.
5. Po tygodniu skorygować wartości na podstawie pomiarów i zdarzeń OOM.

```bash
# Pamięć wszystkich procesów PHP-FPM w MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Bieżący przegląd wszystkich cgroups posortowany według pamięci
systemd-cgtop -m
```

Wartość `memory_limit` sprzed przestawienia warto zanotować. Po aktualizacji można wrócić do stałego limitu w PHP; jak go sensownie dobrać, opisuje artykuł [Prawidłowe ustawienie memory_limit](https://phpsos.de/pl/blog/memory-limit-richtig-setzen/).

## Monitorowanie: kiedy limit zadziała

Limit pamięci, którego zadziałania nikt nie zauważa, jest cichym źródłem błędów. Jądro zlicza zdarzenia OOM dla każdej cgroup, a systemd i dziennik jądra je odnotowują.

```bash
# Licznik OOM cgroup usługi (ścieżka przy cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Komunikaty OOM-killera w dzienniku jądra
journalctl -k --since "-1 day" | grep -i "out of memory"
```

Jeśli licznik `oom_kill` rośnie, jądro kończy procesy robocze. Może to wskazywać na zbyt ciasny limit, na wadliwą aplikację albo na nietypowe żądania. W obecnej sytuacji warto porównywać takie zdarzenia z logami dostępu z tego samego czasu.

> **Ważne:**
> Limit pamięci jest środkiem przejściowym i nie zastępuje aktualizacji PHP. Proszę planować aktualizację równolegle, a po niej zrestartować wszystkie procesy PHP. Przegląd wszystkich środków przejściowych znajduje się na stronie [Środki doraźne, gdy nie mogą Państwo jeszcze załatać](https://phpsos.de/pl/php2shell/sofortmassnahmen-ohne-patch/).

Co dzieje się wewnątrz PHP przy braku pamięci, wyjaśnia artykuł [memory_limit i Out-of-Memory](https://phpsos.de/pl/php/out-of-memory/). W Apache z mod_php ta sama technika dotyczy usługi Apache, przy czym PHP dzieli wtedy limit z samym serwerem WWW.

## Wniosek: nie zapomnieć o drugiej połowie

`memory_limit=-1` jest uzasadnionym środkiem przejściowym tylko razem z limitem zewnętrznym. systemd umożliwia ten limit kilkoma wierszami: `MemoryHigh` jako hamulec, `MemoryMax` jako twarda górna granica, `MemorySwapMax` przeciw ucieczce do swapu i świadomie wybrane `OOMPolicy`. Proszę mierzyć wcześniej, obserwować potem i pamiętać, że wszystkie pule jednej usługi dzielą limit.

Przede wszystkim jednak: proszę planować koniec środka. Po aktualizacji do naprawionej wersji PHP wracają Państwo do stałego `memory_limit`. Limit cgroup mogą Państwo zachować jako dodatkowe zabezpieczenie przed wymykającymi się spod kontroli procesami.

## Jak konkretnie pomaga PHP SOS?

- **Pomiar:** ustalamy zapotrzebowanie na pamięć Państwa usług PHP i wyprowadzamy z niego odpowiednie limity.
- **Wdrożenie:** ustawiamy drop-iny z `MemoryHigh`, `MemoryMax` i odpowiednim `OOMPolicy`, blokujemy `memory_limit` w puli i kontrolowanie restartujemy usługi.
- **Obserwacja:** zdarzenia OOM włączamy do monitoringu i korygujemy wartości.
- **Prowadzenie do poprawki:** za pośrednictwem [usługi poprawek PHP](https://phpsos.de/pl/leistungen/php-patch-service/) wgrywamy aktualizację i potem w uporządkowany sposób wycofujemy wartości przejściowe.


## Częste pytania

**Dlaczego memory_limit ma być ustawiony na -1? Czy to nie jest niebezpieczne?**  
Samo w sobie byłoby. php.net zaleca memory_limit=-1 jako środek przejściowy wyraźnie razem z limitem pamięci przez kontener lub cgroup. Ograniczenie przenosi się w ten sposób z PHP do systemu operacyjnego. Bez tego zewnętrznego limitu nie należy ustawiać tej wartości.

**Czy mogę zamiast tego użyć ulimit lub LimitAS=?**  
Nie. php.net wyraźnie odradza RLIMIT_AS i RLIMIT_DATA, a właśnie te limity ustawiają ulimit -v, LimitAS= i LimitDATA=. Dokumentacja systemd zwraca ponadto uwagę, że LimitAS= ogranicza przestrzeń adresową, a nie zużycie pamięci.

**Co się dzieje, gdy PHP-FPM osiąga limit?**  
Przy MemoryHigh jądro hamuje procesy i odzyskuje pamięć. Przy MemoryMax w obrębie usługi uaktywnia się OOM-killer i kończy jeden proces, zwykle duży proces roboczy. To, czy potem zatrzyma się cała usługa, reguluje OOMPolicy=.

**Czy limit obowiązuje dla każdej puli osobno?**  
Nie. Wszystkie pule jednej usługi FPM dzielą jedną cgroup, a więc wspólny limit. Osobne limity dla każdej strony wymagają osobnych usług FPM lub kontenerów.

**Jak ustawić limit w Docker lub Kubernetes?**  
Przez ograniczenie pamięci kontenera, na przykład opcję --memory w docker run lub resources.limits.memory w Kubernetes. Oba również korzystają z cgroups. Zasada pozostaje ta sama: limit poza PHP, memory_limit w PHP zablokowany.

## Źródła

- [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: komunikaty bezpieczeństwa](https://www.php.net/)
- [Podręcznik PHP: memory_limit](https://www.php.net/manual/en/ini.core.php)
