PHP-FPM — менеджер процессов, который принимает запросы от веб-сервера и передаёт их рабочим процессам PHP. При неверных лимитах сайт либо упирается в очередь и отдаёт 502, либо съедает всю память сервера и вызывает OOM killer. Настройка сводится к трём вещам: понять, сколько памяти занимает один worker, выбрать режим пула и обеспечить наблюдаемость. Разберём это на примере PHP 8.4 в Debian-подобном дистрибутиве, включая Linux Mint.
Как устроен PHP-FPM
Мастер-процесс читает конфигурацию пула и управляет дочерними процессами. Каждый worker обслуживает один запрос за раз, поэтому число одновременных запросов ограничено числом процессов. Если все заняты, новые соединения ждут в listen-очереди сокета; когда очередь переполняется, клиент получает ошибку соединения, а Nginx — 502.
Пул описывается файлом в /etc/php/8.4/fpm/pool.d/. Для нескольких сайтов лучше завести отдельные пулы с разными пользователями и сокетами.
Режимы pm и когда какой выбирать
static— фиксированное число worker. Предсказуемая задержка, но память занята постоянно. Подходит для выделенного сервера с известной нагрузкой.dynamic— процессы добавляются и убираются в заданных границах. Компромисс для большинства сайтов.ondemand— worker запускается только при запросе. Экономит память на редких сайтах, но добавляет задержку на старте.
Для dynamic важны четыре параметра: pm.max_children, pm.start_servers, pm.min_spare_servers и pm.max_spare_servers. Значения должны быть согласованы: стартовое число входит в диапазон свободных, а минимум не превышает максимум.
Базовый конфиг пула
Ниже консервативный пример для сервера, где PHP обслуживает один сайт. Лимит в 40 процессов выбран с запасом и должен быть проверен расчётом ниже. Медленный лог почти не стоит ресурсов, но экономит часы при разборе инцидента.
[www]
user = www-data
group = www-data
listen = /run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 40
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500
request_terminate_timeout = 60s
request_slowlog_timeout = 5s
slowlog = /var/log/php8.4-fpm-slow.log
pm.status_path = /status
ping.path = /ping
access.log = /var/log/php8.4-fpm-access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
catch_workers_output = yes
decorate_workers_output = no
pm.max_requests перезапускает worker после указанного числа запросов. Это страховка от утечек памяти в старом коде, но слишком маленькое значение повышает нагрузку на CPU. request_terminate_timeout должен быть больше типичного времени ответа и согласован с таймаутами Nginx и прокси.
Сколько процессов нужно
Сначала измерьте реальный размер worker под нагрузкой. Команда ниже считает количество процессов и средний RSS в мегабайтах. Запускайте её в часы пик: в простое часть памяти ещё не занята.
ps --no-headers -o rss -C php8.4 | awk '{s+=$1; n++} END {print n, s/n/1024 " MB"}'
free -m
ss -xl | grep php8.4-fpm.sock
Формула простая: из общей памяти вычтите запас для ОС, базы данных и кэшей, остаток умножьте на 0,8 и разделите на средний RSS. Например, на сервере 8 ГБ, где 1,5 ГБ отдано системе и СУБД, а worker занимает 70 МБ, получается около 75 процессов. Но ставить максимум «под потолок» рискованно: пиковый расход может отличаться. Начните с 40–50 и следите за max children reached.
Nginx и таймауты
Nginx должен обращаться к тому же сокету, который слушает пул, и не обрывать запрос раньше, чем PHP-FPM. Если fastcgi_read_timeout меньше request_terminate_timeout, пользователь увидит 504, хотя PHP ещё работает. Для сокета важны права: Nginx и FPM должны иметь доступ к файлу.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
fastcgi_read_timeout 60s;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
}
Директива try_files $uri =404 из стандартного сниппета защищает от передачи несуществующих скриптов. Не увеличивайте буферы без причины: они повышают память на каждый запрос. Если приложение отдаёт большие ответы, лучше включить fastcgi_buffering on с записью во временный файл.
Status-страница и медленный лог
Включите pm.status_path = /status и ping.path = /ping в пуле, затем разрешите доступ только с localhost. Страница показывает очередь, число активных и простаивающих процессов, а также счётчик достижения лимита.
location = /status {
access_log off;
allow 127.0.0.1;
deny all;
include fastcgi_params;
fastcgi_param SCRIPT_NAME /status;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
В выводе значение listen queue больше нуля означает, что запросы ждут свободный worker. max children reached подтверждает, что лимит был достигнут хотя бы раз. slow requests показывает срабатывания медленного лога; по нему видно, какие скрипты и стек вызовов тормозят. Если очередь растёт, а память свободна, увеличивайте pm.max_children; если память на исходе, оптимизируйте код или выносите тяжёлые задачи в очередь.
OPcache и проверка конфигурации
OPcache хранит скомпилированный байт-код и снимает повторную компиляцию. В production отключите проверку timestamps и перезагружайте FPM после деплоя, иначе старый код останется в кэше. JIT включайте только после замеров: для веб-приложений выигрыш часто невелик.
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.jit=disable
opcache.jit_buffer_size=0
Перед перезагрузкой проверяйте синтаксис: php-fpm8.4 -tt разбирает конфигурацию и выводит итоговые значения. systemctl reload выполняет плавный перезапуск, restart — жёсткий, с обрывом соединений. Логи смотрите через journalctl -u php8.4-fpm, а не только в файлах пула.
Чек-лист
- Для каждого сайта — отдельный пул, пользователь и сокет.
pm.max_childrenрассчитан по фактическому RSS, а не «на глаз».pm.max_requestsограничивает последствия утечек памяти.- Таймауты FPM, Nginx и приложения согласованы.
- Status-страница доступна только с localhost.
- Медленный лог включён, порог соответствует SLA.
- OPcache настроен, деплой сопровождается reload.
- После изменений проверены 502, очередь сокета и потребление RAM.
Итог
Производительность PHP-FPM определяется не одной директивой, а балансом между памятью, числом worker и временем ответа. Измеряйте RSS, держите запас по RAM, включайте status-страницу и медленный лог, согласовывайте таймауты с Nginx. Такой подход позволяет переживать пики без 502 и без преждевременного расширения сервера.