---
title: "PHP-FPM che crasha sotto carico su VPS: come ho diagnosticato un OOM killer silenzioso su un portale Laravel con 200 utenti concorrenti"
description: "PHP-FPM crasha sotto carico su VPS con Laravel: OOM killer, pm.max_children sbagliato, memory leak. Diagnosi, tuning e auto-restart in produzione."
canonical: "https://www.mauriziofonte.it/blog/post/ripristino-crash-php-fpm-laravel-vps.html"
author: "Maurizio Fonte"
date: "2025-07-03T07:27:15+02:00"
modified: "2026-04-14T17:48:55+02:00"
section: "Incident Response"
tags: ["Incident Response", "VPS & Hosting"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Incident Response](https://www.mauriziofonte.it/blog/cat/incident_response.html) [VPS & Hosting](https://www.mauriziofonte.it/blog/cat/vps.html)

# [PHP-FPM che crasha sotto carico su VPS: come ho diagnosticato un OOM killer silenzioso su un portale Laravel con 200 utenti concorrenti](https://www.mauriziofonte.it/blog/post/ripristino-crash-php-fpm-laravel-vps.html)

Giovedì 3 Luglio 2025, alle 07:27 ![PHP-FPM che crasha sotto carico su VPS: come ho diagnosticato un OOM killer silenzioso su un portale Laravel con 200 utenti concorrenti](https://www.mauriziofonte.it/image/001960200-ripristino-crash-php-fpm-laravel-vps.jpg)

A luglio 2025, il responsabile operativo di un portale B2B per la distribuzione di ricambi auto in provincia di Modena mi ha segnalato un problema ricorrente: ogni giorno, tra le 10:00 e le 11:00 del mattino, l'applicazione diventava irraggiungibile per 3-5 minuti. Gli utenti vedevano una pagina bianca con "502 Bad Gateway" di Nginx, poi tutto tornava normale. Il portale - un'applicazione Laravel 10 su VPS OVH Value (4 vCPU, 8 GB RAM, 80 GB SSD NVMe) - gestiva circa 200 utenti concorrenti nell'ora di punta, quando i magazzinieri di tutta Italia inserivano gli ordini della giornata. Il problema andava avanti da tre settimane, e il precedente intervento dell'hosting provider era stato "provi ad aumentare la RAM" - un consiglio da 25 euro al mese che non avrebbe risolto nulla.

Il 502 Bad Gateway di Nginx, in uno stack LEMP, significa quasi sempre una cosa sola: Nginx ha provato a passare la richiesta a PHP-FPM e PHP-FPM non era disponibile - o perché era morto, o perché tutti i worker erano occupati. La domanda era: perché moriva esattamente alla stessa ora ogni giorno?

## Cosa succede davvero quando PHP-FPM va in crash su un VPS e Nginx restituisce 502?

Nginx e PHP-FPM comunicano tramite un socket Unix (o TCP). Nginx riceve la richiesta HTTP, la passa a PHP-FPM che la processa e restituisce la risposta. Se PHP-FPM non è in ascolto sul socket - perché è crashato o perché tutti i worker sono occupati e la coda è piena - Nginx non ha nessuno a cui passare la richiesta e restituisce 502. Il primo passo diagnostico è sempre capire quale delle due situazioni è:

```bash
# PHP-FPM è vivo?
systemctl is-active php8.2-fpm

# Se è attivo, quanti worker sono occupati?
# (richiede pm.status_path configurato in pool.d/www.conf)
curl -s http://127.0.0.1/fpm-status | grep -E "active|idle|total"

# Se è morto, perché?
journalctl -u php8.2-fpm --since "today" --no-pager | tail -30
```

Sul VPS modenese, `systemctl is-active` restituiva `inactive (dead)` durante le finestre di downtime. PHP-FPM non era sovraccarico - era proprio morto. Il log di journalctl mostrava la firma inconfondibile dell'OOM killer del kernel Linux:

```text
Jul 03 10:17:42 vps kernel: Out of memory: Killed process 18423 (php-fpm8.2)
Jul 03 10:17:42 vps kernel: php-fpm8.2 invoked oom-killer: [...] total-vm:6842368kB
Jul 03 10:17:42 vps systemd[1]: php8.2-fpm.service: A process has been killed by the OOM killer.
```

L'OOM killer è il meccanismo del kernel Linux che interviene quando la RAM fisica più lo swap sono esauriti: sceglie il processo che consuma più memoria e lo termina. Su un VPS con 8 GB di RAM dove PHP-FPM era il processo più grosso, era la vittima naturale. Ma il kernel non distingue tra "processo essenziale per il business" e "processo sacrificabile" - uccide il più grosso, punto.

Stai cercando un **Consulente Informatico** esperto per diagnosticare e risolvere crash PHP-FPM sulla tua infrastruttura Laravel? Nel mio [profilo professionale](https://www.mauriziofonte.it/pages/about) trovi l'esperienza concreta su tuning PHP-FPM, ottimizzazione memoria e gestione di VPS Hetzner, OVH, Contabo e Digital Ocean per PMI.

## La causa: pm.max_children e l'aritmetica che non torna

La configurazione del pool PHP-FPM nel file `/etc/php/8.2/fpm/pool.d/www.conf` conteneva questa riga:

```ini
pm = dynamic
pm.max_children = 200
```

Duecento worker PHP-FPM. Su un server con 8 GB di RAM. Il calcolo è quello che insegno a ogni team che gestisce un VPS con PHP: [la formula per pm.max_children](https://perlod.com/tutorials/php-fpm-max-children/) è `RAM disponibile per PHP / memoria media per worker`. Per misurare la memoria media per worker:

```bash
ps --no-headers -o "rss,cmd" -C php-fpm8.2 | awk '{ sum+=$1; n++ } END { printf "%d MB (su %d worker)\n", sum/n/1024, n }'
```

Sul VPS modenese, ogni worker consumava in media 82 MB di RSS. Con 8 GB di RAM totali, di cui circa 1,5 GB occupati da MySQL, Nginx, Redis e il sistema operativo, restavano circa 6,5 GB per PHP-FPM. 6.500 MB / 82 MB = **79 worker**, non 200. Con 200 worker a pieno regime (200 × 82 MB = 16,4 GB), PHP-FPM richiedeva il doppio della RAM disponibile. Ogni giorno tra le 10 e le 11, quando i 200 utenti concorrenti generavano abbastanza richieste da attivare tutti i worker, la RAM si esauriva e l'OOM killer interveniva.

La correzione:

```ini
; /etc/php/8.2/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 70          ; 6500 MB / 82 MB = 79, arrotondato giù con margine 12%
pm.start_servers = 17         ; 25% di max_children
pm.min_spare_servers = 17     ; 25% di max_children
pm.max_spare_servers = 52     ; 75% di max_children
pm.max_requests = 500         ; recicla ogni worker dopo 500 richieste (previene memory leak)
```

Il parametro `pm.max_requests = 500` è spesso dimenticato ed è fondamentale: forza il riciclo di ogni worker dopo 500 richieste. Se un'estensione PHP o un pezzo di codice applicativo ha un memory leak - anche piccolo, anche pochi KB per richiesta - senza `pm.max_requests` il worker cresce indefinitamente fino a consumare tutta la RAM assegnata. Con il riciclo, il leak viene resettato ad ogni ciclo di 500 richieste.

## Diagnosticare il memory leak: quando pm.max_requests non basta

Sul VPS modenese, dopo il fix del `pm.max_children`, il problema immediato era risolto. Ma durante il monitoring successivo ho notato un pattern: la memoria media per worker cresceva da 82 MB al restart a 110 MB dopo 400 richieste. Un memory leak di circa 70 KB per richiesta - piccolo, ma cumulativo. Con `pm.max_requests = 500`, il worker veniva riciclato prima di diventare critico. Ma il leak restava, e in futuro con più traffico poteva diventare un problema anche con il max_children corretto.

Per trovare il colpevole, ho abilitato la status page di PHP-FPM - una funzionalità che la maggior parte delle installazioni di produzione non attiva e che è la fonte più preziosa di informazioni sul comportamento dei worker:

```ini
; /etc/php/8.2/fpm/pool.d/www.conf
pm.status_path = /fpm-status
pm.status_listen = 127.0.0.1:9001
```

```bash
# Status completo con dettaglio per-worker
curl -s "http://127.0.0.1:9001/fpm-status?full" | grep -E "pid|requests|duration|memory"
```

L'output mostra per ogni worker: PID, numero di richieste servite, durata dell'ultima richiesta, e - il dato cruciale - la memoria consumata. Confrontando la memoria tra worker appena spawnati e worker con 400+ richieste, ho isolato il pattern: i worker che processavano la rotta `/api/catalog/search` crescevano di 200 KB per richiesta, gli altri di 10-20 KB. La causa era un'istanza di Eloquent Collection che veniva accumulata in una proprietà statica di un service class - un anti-pattern comune in Laravel dove un service registrato come singleton nel container accumula stato tra le richieste. Il fix: aggiungere un metodo `flush()` e chiamarlo nel middleware `Terminate`. Dopo il fix, il leak si è ridotto a 5 KB per richiesta - fisiologico per PHP.

Per chi gestisce applicazioni Laravel con problemi di performance che vanno oltre PHP-FPM - query lente, indici mancanti, buffer pool MySQL sottodimensionato - ho descritto il metodo di diagnostica completo nel mio articolo sul [refactoring di database MySQL su Laravel](https://www.mauriziofonte.it/blog/post/rifacimento-strategico-database-mysql-laravel.html), dove un report che impiegava 47 minuti è stato portato a 11 secondi con quattro giorni di lavoro.

## Auto-restart: la rete di sicurezza che manca a ogni installazione di default

L'aspetto più grave dell'incidente non era il crash in sé - era che PHP-FPM non si riavviava automaticamente dopo l'OOM kill. Il service systemd di default per PHP-FPM non include una direttiva `Restart=`, il che significa che se il processo viene ucciso dall'OOM killer, resta morto fino al riavvio manuale. Su un VPS senza monitoring, questo può significare ore di downtime - esattamente quello che era successo al cliente modenese per tre settimane. PHP-FPM moriva alle 10:17, il titolare chiamava l'helpdesk OVH alle 10:20, l'helpdesk verificava che il VPS fosse "online" (lo era - era PHP-FPM a essere morto, non il server), il titolare restartava manualmente PHP-FPM dopo 20 minuti di ticket. Ogni giorno.

La fix è un override di systemd:

```bash
systemctl edit php8.2-fpm
```

```ini
# Contenuto dell'override
[Service]
Restart=always
RestartSec=5
OOMScoreAdjust=-200
```

`Restart=always` fa ripartire PHP-FPM dopo qualunque tipo di terminazione. `RestartSec=5` aspetta 5 secondi prima del restart (per evitare loop di crash). `OOMScoreAdjust=-200` abbassa la priorità di PHP-FPM nella classifica dell'OOM killer - non lo rende immune, ma lo rende meno probabile come vittima rispetto a processi meno critici. La stessa configurazione di [emergency restart è documentata nella sezione global di PHP-FPM](https://www.php.net/manual/en/install.fpm.configuration.php) con i parametri `emergency_restart_threshold` e `emergency_restart_interval`:

```ini
; /etc/php/8.2/fpm/php-fpm.conf - sezione global
emergency_restart_threshold = 5
emergency_restart_interval = 1m
```

Questo dice a PHP-FPM: se 5 worker child crashano (segfault o errore fatale) in 1 minuto, riavvia l'intero master process. È la rete di sicurezza interna di FPM, complementare al `Restart=always` di systemd che copre il caso in cui è il master stesso a morire.

## Prevenzione: monitoring della memoria e alert prima del crash

Il tuning è il fix. Il monitoring è la prevenzione. Senza monitoring, il prossimo problema di memoria - che arriverà, perché il traffico cresce e il codice cambia - verrà scoperto di nuovo dal titolare che vede il 502.

Lo stack che ho installato sul VPS modenese:

```bash
# Alert Prometheus per memoria PHP-FPM sopra il 70% della RAM disponibile
# (da aggiungere a /etc/prometheus/rules/php-fpm.yml)
```

```yaml
groups:
  - name: php-fpm
    rules:
      - alert: PHPFPMMemoryHigh
        expr: sum(process_resident_memory_bytes{job="php-fpm"}) / 1024 / 1024 > 5600
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "PHP-FPM usa più di 5.6 GB di RAM da 5 minuti"
          runbook: "Verificare pm.max_children, pm.max_requests, memory leak"

      - alert: PHPFPMDown
        expr: up{job="php-fpm"} == 0
        for: 30s
        labels:
          severity: critical
        annotations:
          summary: "PHP-FPM non risponde"
          runbook: "systemctl status php8.2-fpm, journalctl -u php8.2-fpm, dmesg | grep oom"
```

L'alert `PHPFPMMemoryHigh` scatta quando PHP-FPM usa più del 70% della RAM disponibile per PHP (5,6 GB su 8 GB totali con 6,5 GB disponibili) - abbastanza presto da intervenire prima che l'OOM killer si attivi, abbastanza tardi da non generare falsi positivi nell'ora di punta normale. Ho descritto lo stack completo di monitoring Prometheus + Grafana + Alertmanager nel mio articolo sul [monitoring proattivo per Laravel su VPS unmanaged](https://www.mauriziofonte.it/blog/post/vps-monitoring-laravel-prevenzione-downtime.html) - includendo le regole per disco, CPU, code Laravel, job falliti e certificati SSL.

## Cosa ho misurato dopo il fix

Dopo il tuning di `pm.max_children`, la risoluzione del memory leak, l'aggiunta dell'auto-restart e l'attivazione del monitoring, ho monitorato il VPS per 7 giorni. I risultati:

- **Zero crash PHP-FPM** in 7 giorni (prima: uno al giorno, stesso orario).
- **RAM peak nell'ora di punta**: 6,1 GB su 8 GB - 76% di utilizzo, con margine sufficiente per assorbire picchi anomali senza coinvolgere l'OOM killer.
- **Tempo di risposta mediano**: da 1.800 ms (con worker in coda per saturazione) a 340 ms (con worker adeguatamente dimensionati).
- **p99 response time**: 1.200 ms, accettabile per un portale B2B con query di catalogo complesse.
- **Memory leak post-fix**: 5 KB per richiesta, fisiologico - con `pm.max_requests = 500`, ogni worker viene riciclato dopo aver accumulato al massimo 2,5 MB in più della baseline. Nessun impatto operativo.

Il costo totale dell'intervento: mezza giornata di lavoro. Il costo dell'alternativa proposta dal provider (upgrade RAM da 8 a 16 GB): 25 euro al mese, 300 euro l'anno - che avrebbe mascherato il problema senza risolverlo, perché con 200 worker a 82 MB ciascuno anche 16 GB non sarebbero bastati (200 × 82 = 16.400 MB, praticamente tutta la RAM disponibile).

Per il quadro completo di ottimizzazione dello stack PHP - non solo FPM ma anche OPcache, JIT, e caching applicativo - ho descritto il metodo nel mio articolo sulla [riduzione di un checkout da 4,2 secondi a 280 millisecondi su Hetzner senza upgrade hardware](https://www.mauriziofonte.it/blog/post/ottimizzazione-performance-php-server-hetzner-ovh-digital-ocean.html). E per chi sta valutando il passaggio da PHP-FPM a un runtime persistente come Swoole o RoadRunner, la decisione va presa con dati alla mano - ne ho parlato nella guida su [Laravel Octane nel 2026 e quando ha senso per una PMI](https://www.mauriziofonte.it/blog/post/laravel-octane-prestazioni-pmi-swoole-roadrunner-php-fpm-scalabilita.html).

Se la tua applicazione Laravel mostra 502 intermittenti - soprattutto se compaiono sempre nelle fasce di traffico più alto - prima di comprare RAM o upgradare il VPS, controlla `dmesg | grep -i "out of memory"` e `ps --no-headers -o rss -C php-fpmX.Y | awk '{sum+=$1} END {print sum/1024 " MB"}'`. Se la somma della RAM dei worker supera la RAM disponibile, il problema è nel `pm.max_children`, non nell'hardware. E se il tuo PHP-FPM non ha `Restart=always` nel service systemd - cosa che puoi verificare con `systemctl cat php8.2-fpm | grep Restart` - aggiungilo oggi. Ci vogliono 30 secondi e possono fare la differenza tra un downtime di 5 secondi (auto-restart) e un downtime di 5 ore (nessuno se ne accorge fino alla mattina dopo). [Contattami](https://www.mauriziofonte.it/pages/contattami) se hai bisogno di un tuning PHP-FPM: in mezza giornata diagnostico il pattern di consumo, calcolo il `max_children` corretto per il tuo workload e configuro auto-restart e monitoring per evitare downtime futuri.

Dall'articolo al lavoro vero

Questo problema di sicurezza, sul tuo sistema

Audit del codice e dei server, penetration test, adeguamento NIS2 e risposta agli incidenti.

[Vedi come lavoro sulla sicurezza](https://www.mauriziofonte.it/servizi/consulenza-cybersecurity-nis2.html) Ultima modifica: Martedì 14 Aprile 2026, alle 17:48 [« Articolo precedenteBackup Laravel su VPS che falliscono da tre mesi senza che nessuno se ne accorga: diagnosi e strategia di ripristino3 Luglio 2025](https://www.mauriziofonte.it/blog/post/backup-fallito-strategia-urgente-laravel-vps.html)[Articolo successivo »MySQL esposto su un VPS Hetzner con root senza password: il CIS benchmark che applico nelle prime due ore di hardening4 Luglio 2025](https://www.mauriziofonte.it/blog/post/hardening-urgente-mysql-laravel-vps.html)

### Articoli correlati a PHP-FPM che crasha sotto carico su VPS: come ho diagnosticato un OOM killer silenzioso su un portale Laravel con 200 utenti concorrenti

- [Ottimizzare PHP-FPM per carichi elevati: pool, worker e tuning avanzato](https://www.mauriziofonte.it/blog/post/ottimizzare-php-fpm-carichi-elevati-pool-worker-tuning.html)

  Durante un Black Friday, l'applicazione e commerce di un cliente ha saturato i worker PHP FPM in 90 secondi: 502 Bad Gateway per tutti. Il problema · 29 Dicembre 2025

  ![Ottimizzare PHP-FPM per carichi elevati: pool, worker e tuning avanzato](https://www.mauriziofonte.it/image/002620070-ottimizzare-php-fpm-carichi-elevati-pool-worker-tuning.jpg)
- [Errori PHP critici su VPS gestiti senza supporto tecnico: guida operativa per il ripristino](https://www.mauriziofonte.it/blog/post/errori-php-critici-vps-gestiti-senza-supporto.html)

  Un e commerce Laravel su Hetzner fermo da 3 ore: schermata bianca, nessun log visibile, PHP FPM che si riavvia in loop. La causa era un segfault · 10 Luglio 2025

  ![Errori PHP critici su VPS gestiti senza supporto tecnico: guida operativa per il ripristino](https://www.mauriziofonte.it/image/002060070-errori-php-critici-vps-gestiti-senza-supporto.jpg)
- [Configurare swap file per risolvere saturazioni RAM su VPS gestite senza supporto tecnico: guida pratica per Debian e Ubuntu](https://www.mauriziofonte.it/blog/post/configurare-swap-file-vps-debian-ubuntu.html)

  Un VPS con 4 GB di RAM e zero swap: MySQL killato dall'OOM killer ogni notte durante l'import listini, con perdita di transazioni InnoDB. Come · 18 Luglio 2025

  ![Configurare swap file per risolvere saturazioni RAM su VPS gestite senza supporto tecnico: guida pratica per Debian e Ubuntu](https://www.mauriziofonte.it/image/002130070-configurare-swap-file-vps-debian-ubuntu.jpg)
- [Monitoraggio VPS con Prometheus e Grafana: setup minimale per applicazioni PHP](https://www.mauriziofonte.it/blog/post/prometheus-grafana-monitoraggio-vps-applicazioni-php.html)

  La metà dei downtime che ho gestito nell'ultimo anno erano prevedibili: disco pieno, memoria esaurita, connessioni MySQL saturate. Tutti i segnali · 26 Settembre 2025

  ![Monitoraggio VPS con Prometheus e Grafana: setup minimale per applicazioni PHP](https://www.mauriziofonte.it/image/002740070-prometheus-grafana-monitoraggio-vps-applicazioni-php.jpg)
- [PHP e memory management: come evitare i memory leak nelle applicazioni di lunga esecuzione](https://www.mauriziofonte.it/blog/post/php-memory-management-memory-leak-applicazioni-lunga-esecuzione.html)

  I worker Laravel Horizon di un cliente consumavano 500MB di RAM dopo 6 ore di esecuzione e venivano killati automaticamente. Il memory leak non · 10 Marzo 2026

  ![PHP e memory management: come evitare i memory leak nelle applicazioni di lunga esecuzione](https://www.mauriziofonte.it/image/002710070-php-memory-management-memory-leak-applicazioni-lunga-esecuzione.jpg)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "PHP-FPM che crasha sotto carico su VPS: come ho diagnosticato un OOM killer silenzioso su un portale Laravel con 200 utenti concorrenti", mauriziofonte.it, 2025. https://www.mauriziofonte.it/blog/post/ripristino-crash-php-fpm-laravel-vps.html

Pubblicato: 2025-07-03 · Aggiornato: 2026-04-14.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
