Laravel Octane nel 2026: quando ha senso per una PMI e come evitarne i tranelli in produzione

Laravel Octane nel 2026: quando ha senso per una PMI e come evitarne i tranelli in produzione

Octane non è il tasto turbo che il marketing vorrebbe farti credere. È un cambio di modello d'esecuzione che, sulla codebase sbagliata, può degradare la stabilità invece di migliorarla. L'ho imparato un anno fa su un e-commerce B2B di una PMI lombarda: Laravel 10, ~120 richieste al secondo medie, picchi a 600 RPS durante le campagne email. Dopo una migrazione "veloce" a Octane su Swoole, in 48 ore due worker su quattro avevano superato 1.2 GB di RSS e Supervisor li riavviava ogni dieci minuti. Il colpevole era un singleton custom registrato in un service provider che accumulava entry in un array statico per "ottimizzare" le lookup. Su PHP-FPM era invisibile: ogni request azzerava tutto. Su Octane era una bomba a orologeria. Quel weekend l'ho passato a fare l'audit dei singleton, non a guardare grafici Grafana di RPS. La lezione è semplice: prima di pensare a "quanto guadagno", devi sapere se la tua codebase è pronta a vivere in memoria.

Cosa cambia davvero quando passi da PHP-FPM a Octane?

PHP-FPM è un modello "shared-nothing": ad ogni richiesta HTTP nasce un processo PHP, viene incluso public/index.php, viene fatto il bootstrap del framework Laravel (registrazione di tutti i service provider, costruzione del container, parsing delle route), si processa la richiesta, si distrugge tutto. Il framework Laravel boota tipicamente in 10-30ms a seconda del numero di service provider, della cache di config e della complessità di bootstrap/app.php. Su un server con 50 RPS quello è invisibile; a 500 RPS sono 5-15 secondi al secondo bruciati solo in bootstrap, sommati su tutti i worker. È esattamente il sovraccarico che opcode cache e composer dump-autoload --optimize provano a mitigare ma non possono eliminare, perché il modello è quello.

Octane spezza questo pattern. Boot l'applicazione una sola volta all'avvio del worker, la mantiene in memoria, e serve N richieste consecutive senza ricominciare da capo. La documentazione ufficiale di Laravel Octane lo descrive come "supercharge your application's performance" - la realtà tecnica è che salti il bootstrap e tieni warm le connessioni esterne (DB, Redis, queue). Il guadagno non è uniforme: su un endpoint che fa una query semplice e ritorna JSON, puoi misurare 3-5x. Su un endpoint che spende il 90% del tempo in operazioni I/O esterne (chiamate API, query lente), il guadagno percentuale è marginale. Ed è qui che molti benchmark sintetici ingannano: se misuri "hello world" guadagni 10x, ma il tuo gestionale non è hello world.

Octane sposta il problema. Su PHP-FPM la latenza viene dal bootstrap. Su Octane viene dalla qualità del codice che hai scritto, perché non hai più la rete di sicurezza del "tutto si azzera ogni request".

I tre driver Octane 2.x: FrankenPHP, Swoole, RoadRunner

Octane 2.x supporta tre application server. Tre filosofie diverse, tre profili di rischio operativo diversi.

FrankenPHP è il newcomer (2023, divenuto driver Octane ufficiale nel 2024) ed è scritto in Go. Lo sviluppo è guidato da Kévin Dunglas, lo stesso autore di API Platform. Si distingue per un setup minimo: un singolo binario Go che embedda PHP, parla HTTP/2, HTTP/3, supporta early hints, Brotli, Zstandard. Niente Nginx davanti se non vuoi, niente Supervisor obbligatorio (puoi usare il worker mode integrato), niente estensione PHP da compilare. Per chi parte da zero su un VPS Hetzner o simili, è oggi il setup più rapido da portare in produzione. Lato performance, sui benchmark che ho visto è competitivo con Swoole su workload realistici e supera RoadRunner sul throughput puro grazie all'integrazione PHP nativa.

Swoole è il veterano: estensione PHP scritta in C++, esiste dal 2012 ed è il driver storico di Octane. È il più veloce in assoluto sui workload CPU-bound grazie al supporto coroutine e all'event loop interno, ma porta con sé due cose: la dipendenza da un'estensione PHP compilata (pecl install swoole o pacchetto distro), e una superficie di compatibilità più stretta con alcuni package Laravel che non sono "swoole-aware". Va anche detto che Swoole forka in OpenSwoole nel 2022 dopo un dissenso interno: oggi convivono entrambi e Octane li supporta, ma la documentazione spesso non distingue chiaramente - questo a volte crea confusione su quale installare.

RoadRunner è scritto in Go (come FrankenPHP) ma usa un'architettura diversa: un server Go che spawna worker PHP separati e comunica via pipe binarie. Niente estensione PHP, ottima compatibilità con package legacy, ma in genere il throughput puro è inferiore a Swoole e FrankenPHP. È la scelta più conservativa: stessa filosofia "long-running" ma con un blast radius più contenuto se qualcosa va male, perché ogni worker PHP è un processo isolato.

Il mio consiglio operativo per una PMI nel 2026: parti da FrankenPHP se stai facendo greenfield o non hai vincoli di compatibilità. Parti da Swoole se hai già operatività su questo stack e vuoi spremere ogni millisecondo. RoadRunner resta interessante se hai package PHP esoterici o restrizioni che ti vietano di compilare estensioni in produzione.

Il vero costo nascosto: memory leak, singleton e static state

Il pattern d'errore più comune su Octane non è la performance: è la stabilità. Quando l'applicazione vive in memoria fra una request e l'altra, qualunque cosa scritta in uno scope persistente diventa un potenziale leak. Vediamo i tre casi che ho incontrato concretamente.

Caso 1 - array statici "ottimizzazione" della cache. Codice tipico in un service provider:

class LookupService
{
    private static array $cache = [];

    public function find(int $id): ?Model
    {
        if (! isset(self::$cache[$id])) {
            self::$cache[$id] = Model::find($id);
        }
        return self::$cache[$id];
    }
}

Su PHP-FPM funziona ed è anche un'ottimizzazione legittima per evitare lookup ripetute nella stessa request. Su Octane è un disastro: l'array $cache non viene mai svuotato, cresce di richiesta in richiesta finché non saturi la RAM del worker. Soluzione: usare il container di Laravel con scoped() (binding scoped a request, introdotto in Laravel 8 e maturo in Octane), oppure svuotare manualmente nello state reset hook di Octane.

Caso 2 - singleton custom che catturano $app. Issue GitHub laravel/octane#887 documenta un memory leak storico nel ViewServiceProvider: ogni risoluzione del Blade compiler aggiungeva una callback all'array terminatingCallbacks[] del container originale, che cresceva indefinitamente. Lezione: se in un singleton inietti $app direttamente, potresti tenere un riferimento al container del primo request anche dopo che Laravel ha "reset" lo stato per le successive. Il pattern corretto è iniettare una closure che risolva il container al momento dell'uso, non il container stesso.

Caso 3 - connessioni persistenti DB che non rispettano il config. Anche con 'options' => [PDO::ATTR_PERSISTENT => false] esplicito, in alcune configurazioni Octane mantiene la connessione MySQL aperta fra le request del worker. Generalmente è quello che vuoi (è parte del guadagno di performance), ma se hai logica che si aspetta il classico pattern "connessione fresca" per ogni request - per esempio dopo un DB::beginTransaction() non terminato in un edge case - puoi avere errori MySQL 1305 SAVEPOINT does not exist apparentemente inspiegabili. La soluzione è auditare ogni punto in cui il codice assume una connessione "pulita".

La difesa di linea in tutti questi casi è il flag --max-requests di octane:start. Default ragionevole: 500. Significa "dopo 500 richieste, ricicla il worker, rilascia tutta la sua RAM, ricomincia". È esattamente la rete di sicurezza che ti protegge dai memory leak che ti sei perso nell'audit. Su workload con leak noti puoi abbassare a 100-250; su codebase pulite puoi salire a 1000-2000. Lo trovi documentato nelle Octane best practices community e nelle issue GitHub del progetto.

Il setup di produzione "difensivo" che uso come baseline:

php artisan octane:start \
    --server=frankenphp \
    --host=127.0.0.1 \
    --port=8000 \
    --workers=auto \
    --max-requests=500

--workers=auto mappa il numero di worker al numero di core fisici. Su un VPS Hetzner CCX23 (4 vCPU dedicate) parto da 4 worker e monitoro RAM/RPS prima di salire. Senza dati di profiling, aggiungere worker è cargo cult.

Quando Octane ha senso per una PMI nel 2026 e quando invece NO?

La risposta breve: solo se hai un problema di throughput o latenza che PHP-FPM non riesce più a coprire dopo che hai sistemato le query, il caching e il piano di hosting. Octane non aggiusta query Eloquent N+1 e non ti compra IOPS sul disco. Lo dico spesso ai clienti che mi chiedono Octane come "primo intervento": prima fammi vedere lo slow query log e l'opcache stat, poi parliamo.

Octane ha senso quando, in ordine:

  1. PHP-FPM è già stato ottimizzato. OPcache acceso e dimensionato (opcache.memory_consumption=256, opcache.max_accelerated_files=20000, opcache.validate_timestamps=0 in produzione con deploy che fa cachetool opcache:reset), pm = dynamic o pm = ondemand calibrato sul carico, slow log attivo. Vedi anche la mia guida all'ottimizzazione performance PHP su server dedicato.
  2. Le query Eloquent sono già state ripulite. Eager loading dichiarativo, indici sui filtri ricorrenti, niente N+1 nei loop. Se non hai ancora applicato le tecniche descritte nel mio articolo su come ottimizzare le query Eloquent in gestionali ed e-commerce Laravel, Octane mascherà solo i sintomi senza curarli.
  3. Il caching applicativo è in place. Per i pattern di cache dichiarativa basati su Redis, la mia guida al caching Redis su Laravel 12 copre i casi tipici di una PMI.
  4. L'osservabilità è già abilitata. Senza un sistema che ti dice in tempo reale RAM/CPU dei worker e tail latency, debuggare un memory leak in produzione è doloroso. La transizione al sistema di Health Routing /up di Laravel 12 e un setup di monitoraggio IT proattivo sono pre-requisiti, non opzionali.

Octane NON ha senso quando: l'app serve <50 RPS medi, la latenza non è un problema percepito, la codebase ha tanti package legacy con singleton non auditati, il team non ha capacità di intervenire rapidamente sui worker se qualcosa va male di notte. In questi casi, PHP-FPM ben configurato è la scelta tecnicamente onesta, e l'ho consigliato a clienti che si aspettavano il contrario.

Roadmap pratica di adozione: come passarci senza rischiare il business

L'errore più frequente nelle migrazioni Octane è il "big bang": disattivi PHP-FPM, attivi Octane, pushi in produzione. Mai. La transizione corretta è graduale e reversibile.

  1. Audit della codebase. Cerca static su proprietà di classe, cerca public function register() nei service provider che bindino con singleton(), cerca array statici usati come cache. Il pacchetto Larastan con la regola OctaneCompatibilityRule aiuta in fase di sviluppo. Documentazione d'oro: la pagina "Octane caveats" della doc ufficiale.
  2. Staging identico a produzione. Setup Octane su un'istanza staging che riceva traffico replicato (tcpreplay o un load balancer che mirror il 5% del traffico produzione) per almeno una settimana. È in questa fase che emergono i memory leak che l'audit statico non vede.
  3. Deploy in produzione "side-by-side". Nginx davanti, upstream con weight 90/10: 90% del traffico va a PHP-FPM, 10% a Octane. Monitora 24-48 ore. Aumenta gradualmente: 50/50, poi 90/10 a favore di Octane, infine 100% Octane. Tieni PHP-FPM pronto al rollback con un nginx -s reload di emergenza.
  4. Tuning post-cutover. Misura RAM/RSS dei worker dopo 24h. Se cresce linearmente, hai un leak - abbassa --max-requests come palliativo e cerca la causa root. Se è stabile, puoi alzare --max-requests per ridurre l'overhead di ricreazione worker.

In particolare il punto 3 è quello che salva il business: avere un fallback a un click distanza ti permette di sperimentare senza la paura di rompere tutto. Il mio approccio è simile a quello che uso per gestire i crash di PHP-FPM su VPS unmanaged - la differenza la fa avere sempre un piano B operativo.

Octane è uno strumento potente ma esigente. Per una PMI che ha già fatto i compiti su query, caching e infrastruttura, un cutover ben fatto può davvero fare la differenza fra un Black Friday in pace e una notte insonne. Per una PMI che cerca un "tasto turbo" senza prima aver capito perché l'applicazione è lenta, è invece un'ottima ricetta per spostare il problema da PHP-FPM a un memory leak in produzione. Se stai valutando Octane per il tuo gestionale o e-commerce Laravel e vuoi evitare il giro lungo per scoprire da solo i tranelli, scopri il mio approccio professionale alla performance Laravel - lavoro con stack Laravel da prima che Octane esistesse e ho gestito sia le migrazioni andate bene sia quelle finite a tre del mattino. Se vuoi una valutazione concreta sul tuo caso, contattami per una consulenza: in due settimane di audit ti consegno un piano operativo che ti dice se Octane ha senso per la tua situazione, quale driver scegliere e quali interventi sulla codebase fare prima del cutover.

Ultima modifica: