При росте посещаемости или внезапном наплыве поисковых роботов большинство серверов на базе LEMP (Linux, Nginx, MySQL, PHP-FPM) сталкиваются с одной и той же драмой: веб-сервер Nginx начинает выдавать посетителям ошибки 502 Bad Gateway или 504 Gateway Time-out, в логах появляется зловещее предупреждение server reached pm.max_children setting, consider raising it, а после необдуманного увеличения лимитов операционная система внезапно активирует OOM Killer и намертво убивает базу данных MySQL.
В этом руководстве мы разберём реальную математику работы менеджера процессов PHP-FPM, научимся с точностью до мегабайта рассчитывать значение pm.max_children под доступный объём оперативной памяти вашего VDS, сравним режимы static, dynamic и ondemand, а также внедрим проверенные конфигурации пулов без риска уронить боевой стек.
1. Почему дефолтный PHP-FPM роняет VDS: анатомия 502 ошибок и OOM
Менеджер процессов PHP-FPM (FastCGI Process Manager) обрабатывает каждый динамический HTTP-запрос в отдельном изолированном процессе-воркере (worker). В отличие от статических файлов, которые Nginx отдаёт мгновенно практически с нулевыми затратами процессора и памяти, выполнение PHP-скрипта (особенно в тяжелых CMS вроде WordPress, 1C-Битрикс или фреймворках Laravel) требует инициализации виртуальной машины Zend Engine, компиляции байткода, выполнения запросов к СУБД и выделения буферов памяти.
По умолчанию пакетные менеджеры Ubuntu и Debian поставляют файл конфигурации пула /etc/php/8.3/fpm/pool.d/www.conf со следующими усреднёнными параметрами:
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
В такой базовой конфигурации скрыты сразу две диаметрально противоположные ловушки:
- Ловушка №1 (Искусственное бутылочное горлышко): Лимит
pm.max_children = 5означает, что сервер физически способен одновременно выполнять только 5 параллельных PHP-запросов. Если на сайт одновременно зайдут 10–15 пользователей или робот начнёт сканирование каталога товаров, 6-й и последующие запросы встанут в очередь бэклога сокета. Когда таймаут ожидания ответа истечёт, Nginx оборвёт соединение с кодом504 Gateway Time-out, а в журнале PHP-FPM зафиксируется строка:[WARNING] [pool www] server reached pm.max_children setting (5), consider raising it. - Ловушка №2 (Слепое завышение и OOM Killer): Увидев эту ошибку, администраторы нередко ставят
pm.max_children = 50или100на бюджетном виртуальном сервере с 1–2 ГБ RAM. Как только происходит всплеск трафика, 50 воркеров PHP, каждый из которых потребляет от 50 до 120 МБ памяти, суммарно запрашивают 4–5 ГБ RAM. Память мгновенно заканчивается, сервер начинает уходить в глубокий своппинг, нагрузка LA взлетает до 40–80, и ядро Linux запускает механизм OOM Killer (Out Of Memory), аварийно завершая самый прожорливый процесс — как правило, демон СУБДmysqldилиmariadbd.
⚠️ Важное предупреждение: Swap не спасёт от неверного пула
Файл подкачки (Swap) на NVMe-диске предотвращает мгновенный крах ядра, но скорость работы оперативной памяти (20–40 ГБ/с) в сотни раз выше скорости даже самого быстрого дискового ввода-вывода. Если воркеры PHP-FPM начинают массово вытесняться в своп, время ответа сайта (TTFB) деградирует с 50 мс до 4–10 секунд, порождая лавинообразный рост очереди входящих соединений.
2. Режимы работы пула: static, dynamic или ondemand
Директива pm (process manager) определяет стратегию управления жизненным циклом рабочих процессов. PHP-FPM поддерживает три фундаментальных режима:
| Режим (pm) | Принцип работы | Плюсы | Минусы и риски | Где применять |
|---|---|---|---|---|
static |
Фиксированное число процессов (pm.max_children) запускается сразу при старте службы и постоянно удерживается в оперативной памяти. |
Максимальная производительность и минимальный TTFB: нет накладных расходов процессорного времени на постоянный fork() и завершение процессов. | Оперативная память занята постоянно, даже в часы полного штиля и отсутствия посетителей на сайте. | Выделенные VDS под один нагруженный сайт (Highload, интернет-магазины, порталы). |
dynamic |
Количество воркеров плавно масштабируется между min_spare_servers и max_children в зависимости от текущего потока входящих запросов. |
Разумный баланс утилизации памяти: при спаде нагрузки лишние воркеры выгружаются, освобождая RAM для дискового дистрибутивного кэша ОС. | При резком наплыве трафика форк десятков процессов создаёт паразитный спайк нагрузки на vCPU. Требует точной настройки 4 зависимых директив. | Универсальные сервера со смешанной нагрузкой и дневными колебаниями трафика. |
ondemand |
Воркеры вообще не висят в памяти. Процесс создаётся исключительно в момент поступления запроса и умирает через process_idle_timeout секунд простоя. |
Экстремальная экономия RAM: сервер с 10–20 низкопосещаемыми сайтами или dev-окружениями потребляет считанные мегабайты памяти. | Первый запрос после простоя испытывает микрозадержку (cold start latency 50–150 мс) на запуск процесса. Не подходит под регулярный трафик. | Shared-хостинг, панели управления с десятками тестовых сайтов, микро-VDS с 512 МБ–1 ГБ RAM. |
💡 Совет инженера: Режим static — золотой стандарт для Highload
Если на вашем VDS развёрнут один основной проект (или несколько сайтов с постоянной аудиторией), всегда переключайтесь на pm = static. Форк нового процесса в Linux требует выделения страниц виртуальной памяти, загрузки модулей и разбора opcache. Фиксированный пул избавляет процессор от мусорной работы и гарантирует стабильный TTFB.
3. Математическая формула расчёта: сколько памяти реально ест PHP
Для безошибочного расчёта предельного количества процессов необходимо вычислить два ключевых слагаемых: доступный бюджет оперативной памяти и средний реальный размер одного процесса PHP.
Шаг 1. Определение доступного бюджета памяти (RAM Available)
PHP-FPM не может единолично распоряжаться всей памятью машины. Часть ресурсов жизненно необходима операционной системе, кэшу ядра, веб-серверу Nginx и СУБД:
Память для PHP-FPM = Всего RAM − (Память ОС + Память СУБД/Redis + Резерв безопасности 15%)
Ориентировочные аппетиты смежных сервисов:
- Базовая ОС Linux (Ubuntu/Debian) + systemd + Nginx: ~250–350 МБ.
- MySQL / MariaDB с буфером InnoDB 256–512 МБ: ~400–700 МБ.
- Redis (кэширование сессий и объектов): ~100–250 МБ.
Шаг 2. Замер реального размера воркера PHP-FPM
Размер процесса кардинально зависит от стека. Чистый легковесный микроскрипт потребляет 20–30 МБ, типовой сайт на WordPress с 15–20 плагинами — около 55–80 МБ, а тяжелый интернет-магазин на 1C-Битрикс или Magento с генерацией PDF и обработкой изображений может легко забирать 120–180 МБ на один воркер.
Чтобы не гадать на кофейной гуще, выполните на работающем под нагрузкой сервере следующую команду:
# Средний размер одного процесса php-fpm в мегабайтах
ps --no-headers -o rss -C php-fpm8.3 | awk '{sum+=$1; count++} END {if (count>0) printf "%.2f MB\n", (sum/count)/1024; else print "Процессы не найдены"}'
Для оценки разброса минимального, среднего и максимального потребления памяти среди активных воркеров используйте расширенный пайплайн:
ps -C php-fpm8.3 -o pid=,rss= | awk '{
rss=$2/1024;
sum+=rss;
if (min=="" || rss<min) min=rss;
if (rss>max) max=rss;
count++
} END {
printf "Всего процессов: %d | Min: %.1f MB | Avg: %.1f MB | Max: %.1f MB\n", count, min, sum/count, max
}'
Шаг 3. Итоговая формула pm.max_children
Зная чистый бюджет памяти и средний размер воркера, рассчитываем заветное число:
pm.max_children = Доступная память для PHP (МБ) / Средний размер воркера (МБ)
Пример: На VDS с 2 ГБ RAM под управлением WordPress + MySQL + Nginx:
- Всего оперативной памяти: 2048 МБ.
- Резерв ОС и Nginx: 350 МБ.
- MySQL (InnoDB Buffer Pool 384 МБ + соединения): ~600 МБ.
- Остаток для PHP-FPM с запасом:
2048 − 350 − 600 ≈ 1100 МБ. - Средний вес воркера WordPress: 65 МБ.
- Итого:
1100 / 65 ≈ 16.9→ устанавливаем pm.max_children = 16.
4. Готовые пресеты пулов под VDS с 1, 2, 4 и 8 ГБ RAM
Ниже представлены проверенные конфигурационные пресеты файла /etc/php/8.3/fpm/pool.d/www.conf, рассчитанные для типового LEMP-сервера (где PHP и база данных MySQL/MariaDB работают на одном узле при среднем весе воркера 60–70 МБ).
| Объём RAM сервера | Бюджет под PHP | Режим (pm) | max_children | start_servers | min_spare | max_spare |
|---|---|---|---|---|---|---|
| 1 ГБ RAM (1 vCPU) | ~400–450 МБ | ondemand или dynamic |
6 |
2 |
1 |
3 |
| 2 ГБ RAM (2 vCPU) | ~1000–1100 МБ | dynamic / static |
16 |
4 |
3 |
8 |
| 4 ГБ RAM (2–4 vCPU) | ~2200–2400 МБ | static (рекомендуется) |
35 |
— (static) | — | — |
| 8 ГБ RAM (4 vCPU) | ~4500–5000 МБ | static (Highload) |
70 |
— (static) | — | — |
Пример готового блока конфигурации для VDS 2 ГБ RAM (dynamic)
; /etc/php/8.3/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 16
pm.start_servers = 4
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 1000
pm.process_idle_timeout = 10s
Пример готового блока конфигурации для VDS 4 ГБ RAM (static)
; /etc/php/8.3/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = static
pm.max_children = 35
pm.max_requests = 1500
5. Защита от утечек и зависаний: max_requests, slowlog и таймауты
Грамотный расчет pm.max_children решает проблему масштабирования, но не спасает от кривого кода плагинов и утечек памяти (memory leaks). Ниже приведены три критически важные директивы, которые обязан включить каждый сисадмин:
1. Автоматическая перезагрузка воркеров (pm.max_requests)
Даже в современном PHP 8.x сложные сторонние библиотеки со временем накапливают фрагментацию памяти или страдают от циклических ссылок, медленно раздувая воркер со 60 МБ до 250+ МБ. Директива pm.max_requests указывает воркеру корректно завершиться и переродиться после обработки фиксированного числа запросов:
; Перезапуск воркера каждые 1000 запросов
pm.max_requests = 1000
Перезапуск происходит бесшовно для пользователей, так как воркер умирает только после завершения текущего соединения.
2. Отлов зависших скриптов (request_terminate_timeout)
Если PHP-скрипт обратился к внешнему API платёжного шлюза, который перестал отвечать, или выполняет бесконечный цикл, воркер зависает намертво, занимая слот в pm.max_children. Если таких запросов наберётся десяток, весь пул парализуется. Задайте жесткий принудительный таймаут:
; Принудительное завершение скрипта при выполнении дольше 30 секунд
request_terminate_timeout = 30s
3. Медленный лог (slowlog): рентген узких мест
Вместо гадания, почему страница открывается по 5 секунд, включите встроенный профилировщик Slowlog. Он сохраняет стек вызовов функций для любых запросов, выполняющихся дольше заданного порога:
request_slowlog_timeout = 3s
slowlog = /var/log/php-fpm-slow.log
Заглянув в /var/log/php-fpm-slow.log, вы сразу увидите конкретный файл, функцию и номер строки (например, кривой хук плагина в functions.php или тяжёлый SQL-запрос), тормозящие проект.
6. Чек-лист применения настроек и мониторинг воркеров
Никогда не перезапускайте службу PHP-FPM вслепую на работающем сервере без валидации синтаксиса. Следуйте безопасному протоколу:
- Проверка синтаксиса конфигурации:
Если вывод гласитphp-fpm8.3 -tconfiguration file /etc/php/8.3/fpm/php-fpm.conf test is successful— синтаксис чист. - Бесшовная перезагрузка (Graceful Reload):
Командаsystemctl reload php8.3-fpmreload(в отличие отrestart) отправляет сигналUSR2мастер-процессу, позволяя активным воркерам дообслужить клиентские соединения без обрыва 502 ошибкой. - Проверка реального количества запущенных процессов:
ps aux | grep [p]hp-fpm | wc -l - Включение страницы статуса (php-fpm status):
Раскомментируйте директиву
pm.status_path = /statusвwww.confи настройте закрытый location в Nginx:
Теперь командаlocation ~ ^/(status|ping)$ { allow 127.0.0.1; deny all; fastcgi_pass unix:/run/php/php8.3-fpm.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }curl http://127.0.0.1/status?fullв реальном времени отобразит число активных воркеров, размер очереди (listen queue), пиковую нагрузку и длительность выполнения каждого запроса.
🎯 Готовые решения и подбор надёжной инфраструктуры
Для безошибочного создания конфигураций воспользуйтесь нашим генератором в pSEO-разделе: изучите готовые пресеты PHP-FPM для 2 ГБ RAM и PHP-FPM для 4 ГБ RAM. А если ваш проект перерос текущий тариф, разверните масштабируемый VDS на современных процессорах с быстрыми NVMe накопителями у проверенных облачных провайдеров.
Подобрать производительный VDS в Selectel →Рекомендуемые VDS-провайдеры
Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с
Timeweb Cloud
Ультрабыстрые VDS на процессорах до 5.0 GHz (AMD EPYC / Intel Core i9), NVMe накопители, поминутная тарификация, автоматические бэкапы и удобный REST API.
Selectel
Премиальная облачная инфраструктура корпоративного уровня с дата-центрами уровня Tier III в Москве и СПб. Отказоустойчивые дисковые массивы и гибкая настройка vCPU/RAM.
Beget
Легендарная стабильность, лучшая техподдержка 24/7, встроенная панель управления сервером и бесплатная установка любого стека (LEMP / LAMP) в 1 клик.