Веб-серверы и Nginx 9 мин чтения 2026-09-08

PHP-FPM на VDS: точный расчёт pm.max_children и тюнинг пулов под 1–8 ГБ RAM без 502 ошибок и OOM Killer

Пошаговый инженерный гид по расчёту pm.max_children в PHP-FPM. Формулы замера реального потребления памяти, сравнение режимов static, dynamic и ondemand, готовые пресеты www.conf под VDS с 1–8 ГБ RAM и защита от утечек памяти.

Инженерная редакция SysKit Проверено инженерами SysKit.ru

При росте посещаемости или внезапном наплыве поисковых роботов большинство серверов на базе 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:

  1. Всего оперативной памяти: 2048 МБ.
  2. Резерв ОС и Nginx: 350 МБ.
  3. MySQL (InnoDB Buffer Pool 384 МБ + соединения): ~600 МБ.
  4. Остаток для PHP-FPM с запасом: 2048 − 350 − 600 ≈ 1100 МБ.
  5. Средний вес воркера WordPress: 65 МБ.
  6. Итого: 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 вслепую на работающем сервере без валидации синтаксиса. Следуйте безопасному протоколу:

  1. Проверка синтаксиса конфигурации:
    php-fpm8.3 -t
    Если вывод гласит configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful — синтаксис чист.
  2. Бесшовная перезагрузка (Graceful Reload):
    systemctl reload php8.3-fpm
    Команда reload (в отличие от restart) отправляет сигнал USR2 мастер-процессу, позволяя активным воркерам дообслужить клиентские соединения без обрыва 502 ошибкой.
  3. Проверка реального количества запущенных процессов:
    ps aux | grep [p]hp-fpm | wc -l
  4. Включение страницы статуса (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.

Конфигурация
от 1 vCPU / 2 GB RAM / 30 GB NVMe
Enterprise & Highload
🔷

Selectel

Премиальная облачная инфраструктура корпоративного уровня с дата-центрами уровня Tier III в Москве и СПб. Отказоустойчивые дисковые массивы и гибкая настройка vCPU/RAM.

Конфигурация
от 2 vCPU / 4 GB RAM / 50 GB NVMe
Идеально для вебмастеров
🟠

Beget

Легендарная стабильность, лучшая техподдержка 24/7, встроенная панель управления сервером и бесплатная установка любого стека (LEMP / LAMP) в 1 клик.

Конфигурация
от 1 vCPU / 2 GB RAM / 35 GB NVMe

Вопросы и ответы (FAQ)

Популярные вопросы вебмастеров и сисадминов по данной теме

Что делать, если MySQL и PHP-FPM работают на одном маленьком VDS с 1–2 ГБ RAM?
В такой конфигурации главный приоритет — защита MySQL от OOM Killer. Жёстко ограничьте размер InnoDB Buffer Pool (например, 256–384 МБ в my.cnf), а для PHP-FPM установите pm.max_children не более 8–10 воркеров. Обязательно подключите swap-файл на 1–2 ГБ со значением vm.swappiness=10.
Почему режим ondemand не рекомендуют для сайтов с постоянной посещаемостью?
При режиме ondemand каждый процесс создаётся заново при поступлении запроса и уничтожается после простоя. На высокопосещаемом сайте это вызывает бесконечный цикл создания и уничтожения процессов (fork storm), перегружающий процессор и увеличивающий задержку TTFB на 50–150 мс.
Как понять, что сайт уперся в лимит pm.max_children прямо сейчас?
Проверьте системный журнал PHP-FPM командой journalctl -u php8.3-fpm -n 50 или файл /var/log/php8.3-fpm.log. Наличие строки «server reached pm.max_children setting» однозначно указывает на переполнение пула. Также проверьте параметр «listen queue» на странице статуса PHP-FPM.

Читайте также в блоге SysKit

Смежные руководства и полезные технические статьи