← Voltar aos artigos

php-fpm · servidor · performance · memória · produção

'server reached pm.max_children setting' no PHP-FPM: o que isso significa e como calcular o número certo

Publicado em 30/07/2026 · 10 min de leitura

O aviso [pool www] server reached pm.max_children setting (10), consider raising it significa uma coisa só: em algum momento todos os seus workers estavam ocupados ao mesmo tempo, e as requisições que chegaram depois tiveram de esperar numa fila. Ele é um sintoma, não um diagnóstico. E o conselho embutido na própria mensagem — "consider raising it" — é a parte que mais causa estrago, porque o número certo não sai de chute: sai de uma conta entre a RAM disponível e a memória que cada worker realmente consome. Subir o pm.max_children sem fazer essa conta troca um problema de fila por um de swap, e aí o servidor fica mais lento do que estava. Este artigo mostra o mecanismo, como medir, a fórmula — e como perceber quando o problema não é o número de workers.

O mecanismo: um worker atende uma requisição por vez

O PHP-FPM tem um processo master (que não executa código PHP: ele só gerencia) e vários processos workers, que são quem de fato roda o seu script. A regra que explica tudo: um worker atende uma requisição por vez, do começo ao fim. O PHP-FPM não é assíncrono — enquanto o worker espera uma query do banco voltar, ele está ocupado, parado, sem poder atender mais ninguém.

O pm.max_children é o teto absoluto de workers que aquele pool pode ter. Não importa o modo (static, dynamic ou ondemand): nenhum deles cria worker além desse número. Quando os max_children estão todos ocupados e chega mais uma conexão, ela não é recusada de imediato — ela fica esperando na fila do socket (o listen queue). É exatamente nesse instante que o master registra o aviso no log.

Ou seja, a mensagem não diz "está tudo quebrado". Ela diz: "a demanda simultânea encostou no teto que você configurou". As consequências vêm em escada, e é isso que o leitor sente como "o site ficou lento".

1. Tráfego normal — sobra worker

Chegam 3 requisições, você tem 10 workers. Todas são atendidas na hora; ninguém espera.

workers ocupados: 3 / 10      fila: 0

2. Pico — o teto é atingido

Chegam 10 simultâneas. Todos os workers estão trabalhando. A 11ª conexão não tem para quem ir.

workers ocupados: 10 / 10     fila: 1
[pool www] server reached pm.max_children setting (10), consider raising it

3. A fila cresce e vira latência

Enquanto a fila não esvazia, cada nova requisição espera o tempo de fila + o tempo de processamento. O usuário percebe como "o site está lento", sem erro nenhum na tela.

workers ocupados: 10 / 10     fila: 25

4. Se a fila não drena: erro visível

Duas saídas possíveis, e o erro que aparece diz qual foi: se a espera passar do fastcgi_read_timeout do nginx, vira 504 Gateway Timeout; se a fila do socket encher até o limite do backlog, novas conexões passam a ser recusadas e vira 502 Bad Gateway.

504 = esperou demais na fila     502 = a fila estourou

5. A causa real pode não ser o teto

Se cada requisição demora 3s por causa de uma query sem índice, 10 workers dão conta de ~3 requisições por segundo. Dobrar para 20 workers dobra o consumo de RAM e continua empilhando fila — porque o gargalo é o tempo de cada request, não a quantidade de atendentes.

vazão ≈ workers ÷ tempo por requisição

Diagnóstico: ligue a status page antes de mexer

Mudar configuração sem medir é chute com passo extra. O FPM traz uma página de status embutida que responde exatamente as perguntas que importam aqui. Habilite no pool e recarregue:

; no pool (ex.: /etc/php/8.3/fpm/pool.d/www.conf)
pm.status_path = /status

No nginx, exponha o caminho restrito ao seu IP ou à rede interna (não deixe público) e recarregue o FPM com systemctl reload php8.3-fpmreload é graceful, não derruba requisições em andamento; restart derruba.

Medindo a memória real por worker

Este é o número que a maioria chuta — e é o único que a fórmula precisa. Meça o RSS (memória residente) dos workers em produção, sob carga real:

ps --no-headers -o rss,args -C php-fpm | grep 'pool ' \
  | awk '{s+=$1; n++} END {printf "%d workers | média %.0f MB\n", n, s/n/1024}'

O grep 'pool ' exclui o processo master (que não é worker). Rode algumas vezes em horários diferentes e use a média — e olhe também o maior valor, porque worker que processa upload ou relatório pesado destoa. Um app Laravel/WordPress típico costuma ficar entre 30 MB e 80 MB por worker, mas use o seu número, não o do tutorial.

A fórmula (com a margem que ninguém lembra)

pm.max_children = (RAM total − RAM do sistema − RAM dos outros serviços) ÷ memória média por worker

O que costuma faltar na conta é o meio do numerador: o sistema operacional e, principalmente, o banco de dados. Um exemplo concreto numa VPS de 2 GB rodando MySQL na mesma máquina:

RAM total ................. 2048 MB
− sistema operacional ..... −400 MB
− MySQL ................... −512 MB
= disponível para o PHP ... 1136 MB

worker de 40 MB → 1136 / 40 = 28 workers
worker de 60 MB → 1136 / 60 = 18 workers
worker de 80 MB → 1136 / 80 = 14 workers

Repare no impacto: a mesma máquina comporta 28 ou 14 workers dependendo do peso do seu app. É por isso que não existe um valor "recomendado" universal — e por que copiar o pm.max_children de um blog é uma aposta.

evite
; "reached max_children? então dobra"
pm.max_children = 50
; sem medir memória por worker, sem descontar SO e banco.
; Em pico real: 50 × 60 MB = 3000 MB numa VPS de 2 GB → swap.
prefira
; medido: 60 MB por worker | 2 GB − 400 (SO) − 512 (MySQL) = 1136 MB
pm.max_children = 18
pm = dynamic
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
; depois do reload: acompanhar max listen queue e max children reached

Quando mais workers não resolvem nada

Esta é a parte que separa o ajuste certo do paliativo. A vazão do seu pool é aproximadamente workers ÷ tempo médio por requisição. Com 10 workers e 100 ms por requisição, você atende ~100 req/s. Com os mesmos 10 workers e 2 s por requisição, você atende ~5 req/s — e vai encostar no teto com um tráfego ridículo.

Nesse segundo caso, aumentar max_children só faz mais workers ficarem parados esperando a mesma coisa (o banco, uma API externa sem timeout, um file_get_contents remoto). Você gasta RAM para não ganhar vazão. O caminho é reduzir o tempo por requisição: índice na query, cache, timeout nas chamadas externas, ou mover o trabalho pesado para uma fila assíncrona. Para descobrir onde o tempo está indo, ligue o slowlog do FPM (request_slowlog_timeout + slowlog): ele grava o backtrace do script travado, apontando função e linha.

Sua status page mostra max children reached: 340 e slow requests: 315. O que isso indica?

Ver resposta

Resposta certa: Requisições lentas segurando os workers — a causa está no código/banco

Os dois números andando juntos são a assinatura da lentidão: quase todo request que encostou no teto também estourou o request_slowlog_timeout. Os workers não estão faltando — estão parados esperando algo demorado. Aumentar o teto só multiplica processos ociosos consumindo RAM. Abra o slowlog e ataque o que ele apontar; o max children reached cai sozinho depois.

E se a memória sobe com o tempo? Se cada worker vai engordando ao longo das horas (vazamento em alguma extensão ou no seu código), configure pm.max_requests = 500: depois de 500 requisições o worker é encerrado e recriado limpo. Custa quase nada (recriar processo é barato perto do estrago de um worker inchado) e é uma proteção padrão em produção.

Resumo de decisão

Sintoma na status page Causa provável O que fazer
max children reached alto, slow requests ≈ 0 Falta de worker de verdade Recalcular pela RAM e aumentar com a conta
max children reached alto e slow requests alto Requisições lentas Slowlog → índice/cache/timeout; não aumentar o teto
max listen queue alto, mas só em picos curtos Rajada pontual Aumentar um pouco a reserva (min_spare_servers)
Memória dos workers cresce com o tempo Vazamento pm.max_requests = 500
Aumentou e o servidor piorou Swap Reduzir para o valor calculado, medir de novo

Em uma frase: o aviso é um convite a medir, não a aumentar. Meça a memória por worker, faça a conta descontando o sistema e o banco, aplique com reload e volte na status page para conferir se o número parou de subir.

no manualConfiguration

A página de configuração do FPM lista todas as diretivas de pool — pm, pm.max_children, pm.max_requests, request_slowlog_timeout e companhia — com o significado exato de cada uma. É a referência para ajustar com segurança.

estudar na árvore →
no manualStatus Page

A status page é o instrumento de medição do FPM: entenda o que cada campo (listen queue, max listen queue, max children reached, slow requests) realmente conta antes de tomar qualquer decisão de capacidade.

estudar na árvore →

Leve o estudo além do artigo

O manual inteiro do PHP como uma jornada: progresso por tópico, anotações e leitura embutida.