Calcular o pm.max_children do PHP-FPM em vez de adivinhar
A mensagem server reached pm.max_children não quer dizer que deva duplicar o valor. Meça, faça as contas, escolha o modo de funcionamento e comprove depois que o valor assenta mesmo.
A dada altura, esta linha aparece no log do PHP-FPM, e já traz o conselho incluído:
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
O conselho não está errado, apenas incompleto. pm.max_children é o único parâmetro do PHP-FPM em que um valor demasiado alto é mais perigoso do que um valor demasiado baixo. Demasiado baixo custa tempo de espera e, no pior dos casos, um 502. Demasiado alto custa a memória RAM do servidor inteiro, e a partir daí é o kernel que escolhe sozinho o processo que termina. A experiência mostra que não escolhe o PHP, escolhe a base de dados.
Este guia mostra o caminho que vai de adivinhar a calcular: medir a memória que um processo de trabalho consome de facto, determinar o orçamento, escolher o modo de funcionamento e, no fim, comprovar que o valor está certo.
Todas as indicações se referem ao Debian 13 (trixie), ao Debian 12 (bookworm), ao Ubuntu 24.04 LTS e ao Ubuntu 22.04 LTS. Os comandos estão escritos para serem executados como root; como utilizador normal, anteponha sudo. Substitua em toda a parte o número de versão do PHP pelo do seu sistema.
| Sistema | PHP | Serviço | Ficheiro do pool | Log do FPM |
| Debian 13 (trixie) | 8.4 | php8.4-fpm | /etc/php/8.4/fpm/pool.d/www.conf | /var/log/php8.4-fpm.log |
| Debian 12 (bookworm) | 8.2 | php8.2-fpm | /etc/php/8.2/fpm/pool.d/www.conf | /var/log/php8.2-fpm.log |
| Ubuntu 24.04 LTS | 8.3 | php8.3-fpm | /etc/php/8.3/fpm/pool.d/www.conf | /var/log/php8.3-fpm.log |
| Ubuntu 22.04 LTS | 8.1 | php8.1-fpm | /etc/php/8.1/fpm/pool.d/www.conf | /var/log/php8.1-fpm.log |
Que versões estão instaladas, mostra-o um olhar ao diretório. Em servidores que passaram por uma atualização de distribuição, são muitas vezes duas:
ls /etc/php/
ls /etc/php/*/fpm/pool.d/
O que o pm.max_children limita realmente
O valor não limita visitantes nem ligações, limita o número de pedidos PHP que estão a ser executados no mesmo instante. Um pedido de 80 milissegundos ocupa um processo de trabalho durante 80 milissegundos e liberta-o a seguir. Daqui decorre quase tudo o resto:
- Muito tráfego precisa de poucos processos, desde que os scripts sejam rápidos. 40 pedidos por segundo com 80 milissegundos cada dão pouco mais de três pedidos em simultâneo, não 40.
- Um único ponto lento deita a conta abaixo. Uma chamada a uma interface de programação alheia sem um timeout próprio mantém um processo ocupado durante cinco segundos, apesar de esse processo não estar a fazer nada. Cinco chamadas destas por segundo prendem 25 processos de forma permanente.
- Ligações em espera não ocupam processo nenhum. Uma ligação keep-alive ao nginx custa um descritor de ficheiro, mas não custa um processo de trabalho.
Quando todos os processos estão ocupados, os novos pedidos ficam à espera na fila de aceitação do socket. O tamanho dessa fila está no ficheiro do pool, em listen.backlog, com 511 de origem, e o kernel limita-a ainda através de net.core.somaxconn, que nos quatro sistemas está em 4096:
sysctl net.core.somaxconn
Enquanto a fila chegar, o visitante nota apenas tempos de carregamento mais longos. Quando também ela enche, o kernel rejeita a ligação, o nginx regista 11: Resource temporarily unavailable e ao visitante chega um 502. Para distinguir das restantes causas: resolver o nginx 502 Bad Gateway.
Um pormenor que causa muita confusão: o aviso surge uma vez por fase de saturação, não por cada pedido afetado. O FPM coloca internamente uma marca e só a apaga quando volta a haver um processo livre. Uma única linha pode representar uma hora de ponta inteira. Quem conta linhas subestima o problema. O valor honesto é o contador max children reached na página de estado, que é consultada mais abaixo.
O caminho de volta, antes de mudar seja o que for
Há duas coisas que podem correr mal aqui, e ambas atingem o serviço em produção.
Primeiro: o FPM deixa de arrancar. Um systemctl reload envia ao processo principal o sinal USR2, e este reinicia-se a si próprio com a nova configuração. Se ela for inválida, a inicialização é interrompida e o processo principal termina. De uma instância a funcionar passa a haver uma instância morta. Por isso, sem exceção, verifique primeiro e recarregue depois:
php-fpm8.2 -t
Em caso de sucesso, a saída termina com test is successful.
Segundo: o valor está demasiado alto. Nesse caso o servidor não fica logo em baixo, fica apenas no pico de carga seguinte, e de forma tão completa que até um início de sessão por SSH bloqueia ou nem chega a estabelecer-se. Para isso precisa de um segundo caminho até ao servidor.
Nos servidores root KVM e nos servidores dedicados da KernelHost, esse caminho é a consola VNC na área de cliente. Está ligada à camada de virtualização ou à própria máquina, e continua acessível mesmo quando o serviço SSH já não responde por falta de memória. Inicie sessão por aí antes, nem que seja uma única vez. Uma via de salvamento que só se experimenta pela primeira vez durante a emergência não é via nenhuma.
Faça além disso uma cópia do ficheiro do pool, com data e hora no nome, para que uma segunda tentativa não sobrescreva a primeira cópia:
mkdir -p /root/backups
cp -a /etc/php/8.2/fpm/pool.d/www.conf /root/backups/www.conf.$(date +%F-%H%M)
ls -l /root/backups/
O caminho de volta tem então três linhas:
cp -a /root/backups/www.conf.2026-09-03-1015 /etc/php/8.2/fpm/pool.d/www.conf
php-fpm8.2 -t
systemctl reload php8.2-fpm
Uma proteção para o caso de se enganar nas contas
Um limite de memória na unidade systemd faz com que um erro de avaliação atinja um processo de trabalho do PHP e não a base de dados: o kernel passa a terminar processos dentro do control group do FPM, em vez de procurar o maior processo de todo o sistema.
mkdir -p /etc/systemd/system/php8.2-fpm.service.d
cat > /etc/systemd/system/php8.2-fpm.service.d/memoria.conf <<'EOF'
[Service]
MemoryHigh=1500M
MemoryMax=2G
EOF
systemctl daemon-reload
systemctl restart php8.2-fpm
Verifique se ficou mesmo aplicado:
systemctl show php8.2-fpm -p MemoryHigh -p MemoryMax
MemoryHigh trava e liberta memória, MemoryMax é o limite duro. Os quatro sistemas usam a hierarquia unificada de control groups, portanto os valores atuam de imediato. Isto é uma proteção, não um substituto da conta: quando o limite é atingido, o visitante afetado continua a ver um erro, só que não cai o servidor inteiro. Sobre como guardar corretamente estes ficheiros de complemento: criar um serviço systemd.
E a terceira regra, que não precisa de comando nenhum: uma alteração de cada vez. Quem mexe ao mesmo tempo no modo de funcionamento, no pm.max_children e nos valores spare fica sem saber, depois, o que é que fez efeito.
Levantamento: qual é o pool que vale
Cada pool tem o seu próprio pm.max_children, e para a memória o que conta é a soma de todos os pools:
grep -n "^pm" /etc/php/*/fpm/pool.d/*.conf
No estado de origem, os quatro sistemas trazem exatamente o mesmo:
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
Estes não são valores recomendados, são marcadores de posição com os quais o PHP arranca mesmo numa máquina de testes muito pequena. A isso acresce um limite máximo global, válido para todos os pools em conjunto:
grep -n "^process.max" /etc/php/*/fpm/php-fpm.conf
Se a saída ficar vazia, a linha está comentada e vale o valor predefinido 0, ou seja, nenhum limite global. Em servidores com muitos pools, é precisamente esta linha que impede que a soma rebente com a memória assim que vários pools ficam sob carga ao mesmo tempo.
No fim, o que vale não é o ficheiro, é aquilo que o FPM faz dele. A opção -tt imprime a configuração completamente resolvida, incluindo todos os valores predefinidos que não constam de ficheiro nenhum:
php-fpm8.2 -tt 2>&1 | grep -E "^\[|pm |pm\.|listen ="
Esta saída serve mais tarde também de prova de que uma alteração ficou mesmo aplicada.
Medir a memória que um processo de trabalho consome
É aqui que a maioria dos guias se torna imprecisa. Há três números que são confundidos com regularidade:
memory_limit, 128M de origem no modo FPM: um limite máximo por pedido, não um consumo. Um script que precisa de 12 MB continua a precisar apenas de 12 MB com 512M.- RSS: tudo o que está neste momento em memória RAM, incluindo o OPcache partilhado e as bibliotecas partilhadas. Quem soma o RSS de 20 processos conta o mesmo OPcache vinte vezes.
- PSS: as páginas partilhadas são divididas pelo número dos seus utilizadores. É o único dos três números que faz sentido somar.
Para a conta precisa do PSS. O kernel entrega-o já agregado em /proc/<pid>/smaps_rollup, legível como root:
pgrep -f "php-fpm: pool www" | while read -r p; do
awk '/^Pss:/ {print $2}' "/proc/$p/smaps_rollup"
done | awk '{s+=$1; n++} END {
if (!n) { print "nenhum processo de trabalho encontrado"; exit }
printf "Processos: %d Soma: %.0f MiB Média: %.1f MiB\n", n, s/1024, s/1024/n
}'
Para comparar, o mesmo pool visto através do RSS:
ps -eo pid,rss,args --sort=-rss | grep '[p]hp-fpm' | head
Conforme o tamanho do OPcache, a soma dos RSS fica bastante mais alta. Quem faz as contas com ela acaba com um pm.max_children demasiado pequeno e compra tempo de espera que não era preciso.
Há duas condições que decidem se a medição vale alguma coisa. Meça sob carga real, não logo a seguir a um reinício: um processo acabado de criar é barato, porque no início limita-se a partilhar as páginas de memória do processo principal, e só fica caro com os primeiros pedidos. E não se fique apenas pela média: se a média estiver nos 60 MiB e o maior processo nos 190 MiB, não faça as contas com 60.
A segunda fonte: o log de acessos do FPM
Se lho pedir, o FPM regista por cada pedido o consumo de pico e o tempo de execução. As duas linhas estão comentadas no ficheiro do pool:
access.log = /var/log/php8.2-fpm.access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
A grafia %{mili}d está correta assim mesmo, vem sem alterações do ficheiro que acompanha o pacote e devolve o tempo de execução em milissegundos, enquanto %{kilo}M dá o consumo de pico em kilobytes. A seguir verifique, recarregue e veja se o ficheiro é criado:
php-fpm8.2 -t
systemctl reload php8.2-fpm
ls -l /var/log/php8.2-fpm.access.log
Deixe o log a correr um dia inteiro, para que as horas de ponta fiquem lá dentro. Depois avalie o consumo de pico em quantis:
awk '{print $(NF-1)+0}' /var/log/php8.2-fpm.access.log | sort -n | awk '{v[NR]=$1} END {
if (!NR) exit
p=int(NR*0.95); if (p<1) p=1
printf "Pedidos: %d Mediana: %d kB p95: %d kB Máximo: %d kB\n", NR, v[int((NR+1)/2)], v[p], v[NR]
}'
A mesma avaliação para o tempo de execução, de que vai precisar já a seguir para a segunda conta, vai buscar a coluna anterior:
awk '{print $(NF-2)+0}' /var/log/php8.2-fpm.access.log | sort -n | awk '{v[NR]=$1} END {
if (!NR) exit
p=int(NR*0.95); if (p<1) p=1
printf "Mediana: %.0f ms p95: %.0f ms Máximo: %.0f ms\n", v[int((NR+1)/2)], v[p], v[NR]
}'
Duas ressalvas. Primeira: as posições dos campos dependem da linha de formato mostrada acima, porque essa linha termina com o tempo de execução, a memória e a percentagem de CPU. Quem alterar o access.format tem de as ajustar. Segunda: o %{kilo}M é a contabilidade de memória do próprio PHP e, por isso, um limite inferior: falta ali o código do programa, as extensões e a memória que a biblioteca C não devolve logo a seguir a um pedido. Para a fórmula vale portanto o PSS. O log de acessos serve para encontrar os scripts fora da norma, aqueles que fazem um processo crescer de forma duradoura.
Volte a desligar o log a seguir, ou configure-lhe uma rotação. A regra que vem com o pacote abrange apenas o log de erros, e um disco cheio produz sintomas que já não têm nada que ver com o PHP-FPM: disco cheio no Linux, encontrar e libertar espaço.
A conta
Há dois números: um limite máximo que vem da memória RAM e uma necessidade que vem da carga. O valor certo é o mais pequeno dos dois.
Limite máximo a partir da memória RAM
pm.max_children = orçamento para PHP / PSS por processo de trabalho
O orçamento não é a memória RAM toda:
free -m
A coluna available já tem em conta a cache que pode ser libertada, mas não inclui a memória que os seus processos de trabalho estão a ocupar nesse momento. O orçamento é, portanto, o available mais a soma de PSS medida, menos uma reserva. Para a reserva, cerca de 20% da memória total tem dado bons resultados, com um mínimo de 512 MB. É ela que absorve aquilo que não aparece em medição nenhuma: um buffer pool da base de dados que cresce, um backup, uma atualização de pacotes, uma importação em massa lançada na pior altura.
| RAM total | Ocupado de forma permanente | Reserva | Orçamento para PHP | PSS por processo | pm.max_children |
| 4 GB | 1,5 GB | 0,8 GB | 1,7 GB | 60 MB | 29 |
| 8 GB | 3,0 GB | 1,6 GB | 3,4 GB | 80 MB | 43 |
| 16 GB | 6,0 GB | 3,2 GB | 6,8 GB | 110 MB | 63 |
Arredonde sempre para baixo. Um processo a mais não rende nada, um processo a mais do que o servidor suporta pode custar tudo.
Necessidade a partir da carga
processos necessários = pedidos por segundo × tempo médio de execução em segundos
Os pedidos por segundo vêm da página de estado do FPM: accepted conn a dividir por start since dá a média desde o último arranque. O tempo de execução vem da avaliação acima, e é o valor p95, não a mediana, senão estará a planear para a tarde calma.
Exemplo: 40 pedidos por segundo no pico, tempo p95 de 300 milissegundos, dá 12 pedidos em simultâneo e, com uma margem para picos curtos, uns 20 a 25. Se o limite máximo estiver nos 43, escreva o valor da necessidade e deixe a memória restante onde ela rende mais, ou seja, na cache da base de dados.
Se a necessidade ficar acima do limite máximo, mesmo assim não a escreva. Nesse caso, ou a memória RAM é pequena de mais, ou os scripts são lentos de mais, ou passam pelo PHP pedidos que podiam ser servidos como ficheiro estático ou a partir de uma cache. As três causas têm solução, um valor demasiado alto não tem: limita-se a adiar o momento da falha.
dynamic, ondemand ou static
O modo de funcionamento define quando é que os processos nascem e desaparecem. O limite máximo pm.max_children aplica-se nos três casos.
| Modo de funcionamento | Processos no arranque | Comportamento | Memória | Adequado a |
| dynamic | pm.start_servers | mantém entre min_spare e max_spare processos parados à disposição e cria mais, até ao max_children, quando é preciso | oscila com a carga | ao caso normal: um a poucos pools, com carga variável |
| ondemand | nenhum | só cria um processo quando chega um pedido e volta a terminá-lo passado o pm.process_idle_timeout | a mais baixa em repouso | a muitos pools num servidor, a sites com pouco tráfego |
| static | pm.max_children | exatamente esse número, de forma permanente, sem criar nem terminar processos durante o serviço | constante, sempre no máximo | a um pool em hardware reservado para isso, com carga uniforme |
dynamic é a predefinição e é a escolha certa para o caso normal. Custa algum tempo de CPU na criação dos processos e exige que os quatro valores encaixem uns nos outros.
ondemand poupa memória de forma sensível em repouso, quando há uma dúzia de pools de sites pouco visitados na mesma máquina. O preço é o primeiro pedido depois de uma fase de sossego, que tem de esperar pela criação do processo. O pm.process_idle_timeout controla quanto tempo sobrevive um processo parado (dez segundos de origem) e atua exclusivamente neste modo de funcionamento. Repare ainda: aqui o aviso de saturação diz max_children sem o prefixo pm.. Quem procurar pela formulação habitual não encontra nada.
static é honesto: aquilo que escrever fica ocupado de imediato e de forma permanente, e em troca deixa de haver surpresas sob carga. Faz sentido quando o PHP é o principal consumidor da máquina. Se o PHP partilhar a memória RAM com uma base de dados, o static tira a esta a possibilidade de manter mais cache durante algum tempo.
Independentemente do modo de funcionamento, vale a pena olhar para o pm.max_requests, 0 de origem e portanto sem limite. Um valor como 500 substitui cada processo de trabalho ao fim de 500 pedidos. Contra um consumo de memória que cresce devagar por causa de uma extensão mal feita, isto é eficaz e barato, porque o OPcache continua partilhado e não é reconstruído. Mas não ponha o valor a 20, senão o FPM passa o tempo a criar processos.
start_servers e os valores spare
Estes três valores só atuam com pm = dynamic e decidem com que rapidez o FPM reage a um pico de carga:
pm.min_spare_servers: é o número mínimo de processos parados que o FPM mantém à disposição, a almofada para os picos. Baixo de mais significa que cada pico começa por esperar que os processos sejam criados.pm.max_spare_servers: é o número máximo de processos parados que o FPM tolera. Sem este limite, o FPM ficaria com todos os processos depois de um pico e, com eles, com a memória que ocupam.pm.start_servers: é o número de processos que existem imediatamente a seguir ao arranque.
No arranque, o FPM verifica quatro condições e recusa o serviço se uma delas for violada: os dois valores spare têm de ser maiores do que zero, nenhum deles pode ser maior do que pm.max_children, o max_spare não pode ser menor do que o min_spare e o start_servers tem de ficar entre os dois. Se o pm.start_servers faltar por completo, o FPM calcula-o sozinho e regista:
NOTICE: [pool www] pm.start_servers is not set. It's been set to 3.
A fórmula para isso é min_spare + (max_spare - min_spare) / 2, ou seja, o meio entre os dois valores spare. Como ponto de partida, na prática funciona:
pm.max_children = 40
pm.start_servers = 10
pm.min_spare_servers = 6
pm.max_spare_servers = 16
Fácil de esquecer: também os processos parados ocupam memória. O orçamento tem de aguentar o pm.max_children, mas o consumo do dia a dia corresponde mais ou menos ao max_spare mais os processos ativos. Um max_spare alto mantém a memória ocupada também às três da manhã.
A relação com o swap
É tentador incluir o swap na conta. Não o faça. Um processo de trabalho cujos dados estão no disco responde ordens de grandeza mais devagar e fica, por isso, ocupado mais tempo. O número de pedidos em simultâneo sobe, o FPM cria mais processos e estes empurram ainda mais memória para fora. É esta realimentação que explica por que motivo um servidor sobrelotado não vai piorando aos poucos, cai em poucos minutos.
O que o swap faz mesmo assim é servir de almofada para o caso de erro. Sem ele, uma sobrelotação acaba de forma abrupta, com o kernel a terminar um processo. Com ele, fica primeiro com um servidor lento e, com isso, com uma janela de tempo para intervir. A regra é: swap sim, mas o pm.max_children calcula-se exclusivamente contra a memória RAM real, nunca contra a soma de memória RAM e swap.
Se já está a paginar para o swap, vê-o em dois sítios. O free -m mostra a ocupação, mas não diz nada sobre a atividade, porque uma página que foi para o swap uma vez e nunca mais foi precisa é inofensiva. Elucidativas são as colunas si e so, ou seja, a entrada e a saída de páginas por segundo:
free -m
vmstat 1 5
Valores permanentemente acima de zero significam que o servidor está a trabalhar contra o disco. Se o seu kernel trouxer o indicador de pressão de memória, ele é ainda mais direto, porque não diz quanto foi para o swap, diz quanto tempo os processos tiveram de esperar por causa disso:
cat /proc/pressure/memory
Se o ficheiro não existir, a função está desligada no kernel e fica-se pelo vmstat. Como criar swap, registá-lo de forma permanente e acertar o vm.swappiness, está no artigo criar swap e evitar falhas por falta de memória.
Valor demasiado alto: o OOM killer em vez de uma fila de espera
Suponha que escreve 200 para que o aviso desapareça de uma vez. Em repouso não acontece nada, a página carrega, tudo parece resolvido. Na avalanche seguinte, o FPM cria mesmo até 200 processos, cada um cresce até ao seu tamanho real com os primeiros pedidos, a memória livre desce, o kernel começa por deitar fora a cache de ficheiros (o que torna a base de dados mais lenta e faz as consultas demorar mais), depois começa a paginar para o swap e depois entra o OOM killer.
Este escolhe a vítima pelo consumo de memória, e o maior processo isolado num servidor web não é um processo de trabalho do PHP com 80 MB, é a base de dados com o seu buffer pool:
dmesg -T | grep -iE "out of memory|oom-kill"
journalctl -k --since "24 hours ago" | grep -i "out of memory"
As duas linhas que interessam têm este aspeto:
php-fpm8.2 invoked oom-killer: gfp_mask=0x1100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Out of memory: Killed process 1234 (mariadbd) total-vm:2891234kB, anon-rss:1783456kB,
file-rss:0kB, shmem-rss:0kB, UID:107 pgtables:4096kB oom_score_adj:0
Aqui, quem desencadeia e quem é atingido são dois processos diferentes, e é precisamente isso que torna o diagnóstico desagradável: na caixa de correio está um aviso sobre uma base de dados que caiu, e ninguém se lembra da configuração do PHP de anteontem. Conforme a base de dados, entre parênteses aparece mariadbd ou mysqld.
Se acabar mesmo por atingir um processo de trabalho, o próprio FPM comunica-o e, com o limite de memória definido, fica ainda registado no journal:
WARNING: [pool www] child 1234 exited on signal 9 (SIGKILL) after 3612.472183 seconds from start
php8.2-fpm.service: A process of this unit has been killed by the OOM killer.
A diferença entre estes dois sintomas é o verdadeiro ponto deste artigo. Um pm.max_children demasiado baixo produz uma fila de espera: mensurável, documentada no log, atribuível a um valor concreto, e o servidor continua a responder. Um valor demasiado alto produz um processo terminado num sítio que não escolheu, num serviço que, conforme os casos, não volta por si próprio. Tempo de espera é um estado de funcionamento, um evento OOM é um incidente.
Erros frequentes e soluções
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it: num dado momento, todos os processos de trabalho estavam ocupados. A linha aparece uma vez por fase de saturação, ou seja, uma única linha pode significar uma hora inteira de saturação. Só aumente o valor depois de ter medido o PSS e determinado o orçamento.
WARNING: [pool www] server reached max_children setting (5), consider raising it: a mesma situação com pm = ondemand, na formulação sem o prefixo pm..
ALERT: [pool www] pm.min_spare_servers and pm.max_spare_servers cannot be greater than pm.max_children, seguido de ERROR: failed to post process the configuration e de ERROR: FPM initialization failed: é o caso mais frequente quando se baixa o pm.max_children. Quem passa de 50 para 8 e deixa ficar pm.max_spare_servers = 20 tem a seguir um serviço que já não arranca. Os quatro valores mudam-se em conjunto.
ALERT: [pool www] pm.start_servers must not be less than pm.min_spare_servers and not greater than pm.max_spare_servers: o pm.start_servers está fora do intervalo. Corrija o valor ou remova a linha, e nesse caso o FPM calcula-o sozinho.
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes): isto é o memory_limit e não tem nada que ver com o pm.max_children. Um único script pediu mais do que o PHP permite por pedido. Um pm.max_children mais alto não muda nada nisso e, ao contrário, um memory_limit mais baixo não reduz a memória de que um processo de trabalho precisa, apenas interrompe os scripts mais cedo. Para o planeamento vale mesmo assim: o memory_limit é o teto daquilo que um processo pode pedir.
connect() to unix:/run/php/php8.2-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream no log de erros do nginx: todos os processos ocupados e, ainda por cima, a fila de aceitação cheia. Um listen.backlog maior apenas adia o problema, a causa está no número de processos ou no tempo de execução dos scripts.
WARNING: [pool www] child 1234 exited on signal 9 (SIGKILL) after 3612.472183 seconds from start: o processo foi terminado de fora à força, quase sempre pelo OOM killer. Aqui o valor está demasiado alto, não demasiado baixo. Confirme no log do kernel.
"Aumentei o valor e não muda nada." Quase sempre foi editado o ficheiro errado: uma segunda versão do PHP em /etc/php/, um segundo pool ou uma cópia dentro de pool.d/ que nem sequer é lida. O que conta é qual o socket que o nginx contacta e qual o pool que está à escuta nele:
grep -Rn "fastcgi_pass" /etc/nginx/
grep -n "^listen *=" /etc/php/*/fpm/pool.d/*.conf
php-fpm8.2 -tt 2>&1 | grep "pm.max_children"
"Depois do reload o site desapareceu." Um reload reinicia o FPM com a nova configuração e, se esta for inválida, o processo principal termina. Reponha a cópia de /root/backups e habitue-se a correr php-fpm8.2 -t antes de cada reload.
Como saber que o valor está certo
"O aviso desapareceu" não é prova nenhuma. Com o valor 500 também desaparece, até ao primeiro evento OOM. Sólidas são cinco verificações.
Primeira, a página de estado do FPM. Escreva pm.status_path = /status no ficheiro do pool, verifique e recarregue, e consulte o socket diretamente, passando totalmente ao lado do nginx. Assim a página não fica acessível a partir da internet:
apt-get install -y libfcgi-bin
SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET \
cgi-fcgi -bind -connect /run/php/php8.2-fpm.sock
Quatro linhas da saída contêm a resposta:
| Campo | Significado | Valor pretendido |
| max children reached | quantas vezes o limite máximo foi atingido desde o arranque | 0 |
| max listen queue | a fila de espera mais longa observada no socket | 0 |
| max active processes | o maior número de processos ativos em simultâneo | bastante abaixo do pm.max_children |
| slow requests | pedidos acima do request_slowlog_timeout | 0, de preferência |
Isto só passa a ser elucidativo ao fim de uma semana completa, com todas as horas de ponta lá dentro. Se depois disso o max active processes estiver em 12 enquanto o pm.max_children está em 43, tem folga e pode usar a reserva noutro sítio.
Segunda, a memória sob carga, não de noite, mas no pico. O available tem de ficar bastante acima de zero e as colunas si e so devem estar a zero:
free -m
vmstat 1 5
Terceira: nenhum evento OOM. Esta verificação é a mais importante, porque exclui o pior dos sintomas. Uma saída vazia é o resultado desejado:
journalctl -k --since "7 days ago" | grep -i "out of memory"
Quarta, a soma de todos os pools. Num servidor com vários sites não conta o valor isolado, conta a soma, multiplicada pelo PSS por processo. Essa soma tem de caber no orçamento, mesmo que todos os pools fiquem sob carga ao mesmo tempo:
php-fpm8.2 -tt 2>&1 | grep "pm.max_children"
Quinta: aguenta um reinício. É o ponto que mais vezes se salta. Um valor que está num ficheiro que nem sequer é lido só dá nas vistas no reinício seguinte, e esse raramente acontece no momento em que se está a olhar:
systemctl is-enabled php8.2-fpm
systemctl restart php8.2-fpm
php-fpm8.2 -tt 2>&1 | grep "pm.max_children"
Reinicie a seguir o servidor uma vez de forma controlada, enquanto ainda está a acompanhar. Nos servidores root KVM e nos servidores dedicados da KernelHost, esse reinício é despoletado na área de cliente e o arranque acompanha-se pela consola VNC, mesmo que o serviço web ainda não responda. Os servidores estão no datacenter maincubes em Frankfurt am Main.
Lista de verificação rápida
- Cópia do ficheiro do pool para
/root/backups, e acesso pela consola VNC experimentado uma vez. - Medir o PSS por processo de trabalho sob carga real, não a seguir a um reinício, e não fazer as contas com RSS.
- Determinar o orçamento:
availablemais a soma de PSS em curso, menos uma reserva definida de forma consciente. - Calcular o limite máximo, contrapor a necessidade a partir dos pedidos por segundo vezes o tempo p95, ficar com o valor mais pequeno e arredondar para baixo.
- Escolher o modo de funcionamento e ajustar os valores spare em conjunto com o
pm.max_children. php-fpm8.2 -t, depoissystemctl reload, depoisphp-fpm8.2 -ttcomo prova de que o valor está carregado.- Uma semana depois, verificar o
max children reached, omax listen queuee o log do kernel.
Perguntas frequentes
Como calculo corretamente o pm.max_children?
Porque é que um valor demasiado alto é mais perigoso do que um demasiado baixo?
Porque é que devo fazer as contas com PSS e não com RSS?
Quando uso dynamic, quando ondemand e quando static?
Posso incluir o swap na conta?
Aumentei o pm.max_children e não muda nada. A que se deve?
Depois de baixar o pm.max_children, o PHP-FPM já não arranca. O que aconteceu?
O memory_limit tem alguma coisa que ver com o pm.max_children?
2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

