pm.max_children — параметр PHP-FPM, от которого чаще всего зависит, упадёт сайт под нагрузкой в 502 Bad Gateway или выдержит её. Слишком низкое значение забивает сервер очередью запросов, слишком высокое — роняет его через нехватку памяти (OOM). Ниже — честная формула расчёта и то, чего формула не предсказывает без замера на реальной нагрузке.
Из чего складывается бюджет памяти для PHP-FPM
Прежде чем считать количество процессов, важно понять, сколько памяти на сервере физически свободно под PHP-FPM. Из общего объёма RAM вычитается то, что уже занято операционной системой, MySQL или другой СУБД, Redis и Nginx. Остаток и есть бюджет, который можно отдать воркерам PHP-FPM.
Например, на VPS с 8 GB RAM, если под систему и MySQL зарезервировано 3 GB, под PHP-FPM остаётся около 5 GB (5120 MB). Отдельный лимит памяти одного процесса PHP — тема статьи про memory_limit и upload_max_filesize, а здесь считается суммарный бюджет на весь пул воркеров.
Как измерить реальный размер одного процесса
Теоретические расчёты бесполезны без замера фактического потребления памяти воркерами. Правильный способ — посмотреть RSS (resident set size) уже работающих процессов php-fpm на сервере под обычной, а не пиковой нагрузкой.
Команда ps -o rss= -C php-fpm8.3 | awk '{sum+=$1} END {print sum/NR/1024" MB"}' выводит средний размер процесса в мегабайтах. Снимать значение стоит несколько раз в течение дня: после прогрева OPcache и под разной нагрузкой, потому что размер процесса растёт по мере подключения новых файлов и расширений.
Формула расчёта pm.max_children
В общем виде формула такая: pm.max_children равен объёму памяти, доступной PHP-FPM, делённому на средний размер одного процесса. Результат — верхняя граница, а не целевое значение: отдельные процессы раздуваются при обработке тяжёлых запросов, поэтому разумно оставить запас 20-30% от расчёта. Подробный разбор режимов pm и других параметров пула — в статье про пулы и воркеры PHP-FPM.
- Настройки opcache.memory_consumption и число закэшированных файлов
- Тяжёлые расширения — imagick, intl, gd — добавляют память каждому процессу
- Размер бутстрапа фреймворка: Laravel и Symfony грузят больше классов, чем голый PHP
- Драйвер хранения сессий — файловые сессии легче, чем сессии в памяти процесса
- Объём автозагрузки Composer — чем больше зависимостей, тем тяжелее стартовый код
Пример расчёта для нескольких конфигураций
Таблица ниже показывает только арифметику деления, а не результат замера на реальном сайте. Средний размер процесса и доступную память нужно взять со своего сервера, иначе числа будут бессмысленны.
| RAM для PHP-FPM | Средний размер процесса | pm.max_children (округлённо) |
|---|---|---|
| 1024 MB | 50 MB | 20 |
| 2048 MB | 60 MB | 34 |
| 4096 MB | 70 MB | 58 |
| 5120 MB | 60 MB | 85 |
Итоговые параметры пула
От pm.max_children зависят и остальные параметры режима dynamic: pm.start_servers обычно берут как 25-30% от max_children, pm.min_spare_servers — около половины от start_servers, а pm.max_spare_servers — в полтора раза больше start_servers. Как эти параметры передаются в Nginx через fastcgi_pass, описано в статье про связку Nginx и PHP-FPM.
pm = dynamic
pm.max_children = 70
pm.start_servers = 20
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.max_requests = 500Что происходит при неверном значении
Слишком низкое значение pm.max_children означает, что при всплеске трафика новые запросы встают в очередь к сокету PHP-FPM и ждут свободного процесса; если ожидание превышает таймаут Nginx, клиент получает 502 или 504. Это похоже на дефицит процессов, но причина в конфигурации, а не в коде.
Слишком высокое значение опаснее: если каждый процесс в пике весит 80-100 MB, а запущено их больше, чем позволяет физическая память, сервер уходит в swap или ядро убивает процессы через OOM killer. Восстановление после OOM занимает больше времени, чем короткая очередь запросов.
Частые вопросы
Можно ли поставить pm.max_children с большим запасом, на всякий случай?
Нет: лишние процессы не ускоряют обработку запросов, они просто занимают память, которая не используется большую часть времени, и повышают риск OOM в момент пикового трафика.
Как часто нужно пересчитывать значение?
Пересчёт стоит делать после изменения объёма RAM, после крупного обновления фреймворка или зависимостей и после заметного изменения трафика. Текущую загрузку пула удобно смотреть через pm.status_path.
Сайт всё равно отдаёт 502 после настройки pm.max_children, что проверить?
Сначала стоит исключить медленные запросы к базе данных и внешним API: если каждый запрос висит по 10-15 секунд, рост числа процессов только отложит проблему, а не решит её.