Infra & Deploy

502 e 504 no Magento: dimensionar o pool do php-fpm sem chutar

Aumentar o pm.max_children no chute é a primeira coisa que todo mundo faz e a que mais derruba servidor. O número sai de uma medição de três segundos.

Por Roger Takemiya · Publicado em · 6 min de leitura

A loja começa a devolver 502 no horário de pico. Alguém abre o www.conf, dobra o pm.max_children, reinicia o php-fpm e o erro some por vinte minutos. Depois volta pior, agora com o servidor em swap e o MySQL lento junto.

Esse ciclo tem duas causas: ninguém olhou qual erro era, e ninguém mediu quanta memória um worker do Magento realmente gasta. Dá para resolver os dois em poucos minutos.

502 e 504 são problemas diferentes

Antes de mexer em qualquer arquivo, leia o log de erro do nginx. Ele é bem explícito sobre o que aconteceu.

tail -n 200 /var/log/nginx/error.log | grep upstream

Se a linha fala em connect() to unix:/run/php/php8.3-fpm.sock failed (11: Resource temporarily unavailable), o nginx nem conseguiu entregar a requisição: todos os workers estavam ocupados e a fila de espera encheu. Isso é 502 por falta de worker, e aí sim o pm.max_children entra na conversa.

Se a linha fala em recv() failed (104: Connection reset by peer) while reading response header from upstream, o worker existia e morreu no meio — normalmente estouro de memory_limit ou segfault. Aumentar o pool não conserta nada aqui; o remédio está no var/log/ do Magento e no log do PHP.

E se a linha fala em upstream timed out (110: Connection timed out), é 504: a requisição passou do fastcgi_read_timeout. Worker sobrando não resolve uma página que demora demais.

Do lado do php-fpm, existe um aviso que fecha o diagnóstico:

grep -c "reached pm.max_children" /var/log/php8.3-fpm.log

A mensagem é server reached pm.max_children setting, consider raising it. Se ela aparece dez vezes por dia no pico, o pool está apertado. Se não aparece nenhuma vez, o seu 502 é outra coisa e mexer no pool só vai gastar RAM à toa.

Quantos workers cabem no seu servidor

O pm.max_children é o teto de requisições PHP simultâneas. Cada uma delas é um processo com a memória dele. A conta é boba: memória disponível para PHP dividida pela memória média de um worker. O que quase ninguém faz é medir a segunda parte.

ps --no-headers -o rss -C php-fpm8.3 \
  | awk '{s+=$1; n++} END {print int(s/n/1024)" MB por worker em "n" processos"}'

Rode isso com a loja em uso, não às três da manhã. Em Magento o número costuma ficar bem acima do que a intuição diz, porque cada worker carrega o framework inteiro.

Do lado da memória disponível, tire do total o que MySQL, OpenSearch e Valkey já reservaram — e deixe folga para o sistema. Num servidor de 8 GB com 3 GB comprometidos, sobram uns 4,5 GB para o PHP com margem. Se o worker mediu 150 MB, o teto é 30, não 120.

Com o número na mão, o pool fica assim, em /etc/php/8.3/fpm/pool.d/www.conf:

pm = dynamic
pm.max_children = 30
pm.start_servers = 8
pm.min_spare_servers = 6
pm.max_spare_servers = 12
pm.max_requests = 500

O pm.max_requests merece atenção: o padrão é 0, que significa worker imortal. Qualquer vazamento de memória em módulo de terceiro se acumula até o fim dos tempos. Reciclar o processo a cada algumas centenas de requisições custa quase nada e evita aquele crescimento lento que ninguém explica.

Depois de mudar, valide antes de reiniciar:

php-fpm8.3 -t
systemctl reload php8.3-fpm

Os timeouts precisam conversar entre si

O nginx.conf.sample que vem no repositório do Magento define fastcgi_read_timeout 600s e fastcgi_connect_timeout 600s, além de fastcgi_buffers 16 16k e fastcgi_buffer_size 32k. Dez minutos é generoso de propósito: o mesmo arquivo é usado por rotas de admin que fazem trabalho pesado.

O problema é quando o lado do PHP não tem limite nenhum. O request_terminate_timeout nasce em 0: sem ele, uma requisição travada segura o worker para sempre e vai comendo o seu pool até chegar no 502. Eu deixo o php-fpm cortar antes do nginx desistir:

request_terminate_timeout = 300
request_slowlog_timeout = 10s
slowlog = /var/log/php8.3-fpm.slow.log

Com essa ordem, quem mata a requisição é o php-fpm, e o slowlog grava o backtrace de qual função estava rodando. Se o nginx corta primeiro, você fica com um 504 e nenhuma pista.

Vale lembrar que cron, consumers e bin/magento rodam pelo PHP de linha de comando e não ocupam o pool do FPM. Se o seu 502 aparece exatamente quando o catalog:images:resize está rodando, o problema é disputa de CPU e memória no servidor, não tamanho de pool.

Para enxergar a fila em tempo real, ligue o status do pool — ele nasce desligado:

pm.status_path = /status

E exponha só para o próprio servidor, num bloco à parte do nginx com allow 127.0.0.1; e deny all;. O campo que interessa é o listen queue: se ele vive acima de zero, tem requisição esperando worker. Se vive em zero e você continua vendo 502, pare de mexer no pool — o gargalo está em outro lugar, e o guia de performance cobre os suspeitos seguintes.

Perguntas rápidas

Uso pm = dynamic, static ou ondemand?

Em servidor dedicado à loja, static com o teto que a memória permite dá o tempo de resposta mais estável, porque não paga o custo de subir processo no pico. Dynamic é a escolha segura quando o servidor divide memória com outras coisas, e ondemand só faz sentido em ambiente de baixo tráfego ou de desenvolvimento.

Posso simplesmente aumentar o memory_limit do PHP para parar os 502?

Cuidado: memory_limit maior por worker significa menos workers cabendo na mesma RAM. Se você subir o limite sem baixar o max_children, o servidor entra em swap no pico e o site fica lento em vez de dar erro, que é mais difícil de diagnosticar.

Faz sentido ter um pool separado para o admin?

Faz, e é uma das mudanças que mais rendem em loja movimentada. O admin faz requisições longas de relatório e grid que seguram worker por muito tempo; com pool próprio, essa carga deixa de competir com o cliente que está tentando fechar o pedido.

Pra conferir na fonte

  1. PHP-FPM configuration — PHP Manual
  2. Module ngx_http_fastcgi_module — nginx
  3. nginx.conf.sample do Magento 2 — GitHub

php-fpm nginx 502

Precisa de um orçamento? Ficarei feliz em ajudar. Clique Aqui