Laravel Octane en 2026 : Swoole, RoadRunner et Optimisation des Performances

Guide complet sur Laravel Octane 2.19 : choix entre FrankenPHP, Swoole et RoadRunner, prévention des fuites mémoire, et configuration pour la production.

Laravel Octane performance avec Swoole, RoadRunner et FrankenPHP en 2026

Laravel Octane transforme les performances des applications PHP en conservant le framework initialisé en mémoire entre les requêtes. La version 2.19 (août 2026) prend en charge trois serveurs prêts pour la production : FrankenPHP, Swoole, et RoadRunner. Chacun répond à des contraintes opérationnelles différentes, et choisir le mauvais coûte soit en performance, soit en stabilité.

Référence Rapide de Sélection de Serveur

FrankenPHP : configuration la plus simple, HTTP/3 natif, binaire unique. RoadRunner : binaire Go éprouvé, aucune extension PHP requise. Swoole : débit maximal avec concurrence par coroutines, nécessite la gestion d'une extension PECL.

Comment Octane Élimine le Coût d'Initialisation

PHP-FPM traditionnel crée un nouveau processus par requête. Laravel initialise son conteneur de services, charge la configuration, enregistre les providers et résout les middlewares à chaque appel HTTP. Octane inverse ce modèle : l'application démarre une seule fois par worker, puis la même instance traite des milliers de requêtes de façon séquentielle.

Le gain de performance provient de l'élimination du travail répétitif. Une application Laravel 12 typique passe entre 15 et 40ms à s'initialiser avant d'exécuter la logique métier. Avec Octane, ce coût tombe à zéro après la première requête. Les benchmarks sur du matériel identique montrent des améliorations de débit de 2x à 4x pour les endpoints API, avec un écart qui se creuse à mesure que la complexité de l'application augmente.

config/octane.phpphp
return [
    'server' => env('OCTANE_SERVER', 'frankenphp'),
    
    // Workers handle HTTP requests
    'workers' => env('OCTANE_WORKERS', 8),
    
    // Gracefully restart workers after N requests to prevent memory bloat
    'max_requests' => env('OCTANE_MAX_REQUESTS', 500),
    
    // Task workers for Swoole concurrent operations
    'task_workers' => env('OCTANE_TASK_WORKERS', 6),
    
    // Maximum execution time per request in seconds
    'max_execution_time' => 30,
];

Le paramètre max_requests agit comme une soupape de sécurité. Même du code bien écrit peut accumuler de la mémoire sur des centaines de requêtes. Fixer cette valeur à 500 force les workers à redémarrer avant que la pression mémoire ne devienne problématique.

FrankenPHP : Le Choix Par Défaut en 2026 pour les Nouveaux Projets

FrankenPHP se distribue sous forme de binaire unique construit sur Caddy et PHP. Il gère automatiquement les certificats HTTPS via Let's Encrypt, supporte HTTP/3 nativement, et ne nécessite aucune extension PHP à installer. L'installateur Laravel et les exemples de production utilisent désormais FrankenPHP par défaut.

bash
# Install Octane with FrankenPHP
composer require laravel/octane
php artisan octane:install --server=frankenphp

# Start the server
php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=8000

Pour les déploiements conteneurisés, FrankenPHP fournit des images Docker officielles :

dockerfile
# Dockerfile
FROM dunglas/frankenphp

RUN install-php-extensions \
    pcntl \
    pdo_pgsql \
    redis

COPY . /app

ENTRYPOINT ["php", "artisan", "octane:frankenphp"]

La configuration Docker Compose pour le développement active HTTPS et HTTP/3 :

yaml
# compose.yaml
services:
  frankenphp:
    build:
      context: .
    entrypoint: php artisan octane:frankenphp --workers=1 --max-requests=1
    ports:
      - "443:443"
      - "443:443/udp"
    volumes:
      - .:/app

FrankenPHP gère les redémarrages de workers de manière élégante et s'intègre directement au système de middlewares de Caddy pour les scénarios de routage avancés.

RoadRunner : Stabilité Go Sans Dépendances d'Extension

RoadRunner se compile en binaire Go qui gère les processus workers PHP. Il communique avec PHP via un protocole binaire, conservant la gestion des workers et le traitement HTTP en Go tout en exécutant le code applicatif en PHP. Cette architecture offre une isolation des processus : un worker PHP qui plante n'affecte pas le parent Go ni les workers voisins.

bash
# Install Octane with RoadRunner
composer require laravel/octane spiral/roadrunner-cli spiral/roadrunner-http

php artisan octane:install --server=roadrunner

# Download the RoadRunner binary
./vendor/bin/rr get-binary

# Start the server
php artisan octane:start --server=roadrunner --host=0.0.0.0 --port=8000

La configuration RoadRunner se trouve dans .rr.yaml à la racine du projet :

yaml
# .rr.yaml
version: "3"

server:
  command: "php artisan octane:start --server=roadrunner --host=0.0.0.0 --port=8000"
  relay: pipes

http:
  address: 0.0.0.0:8000
  middleware: ["headers", "gzip"]
  pool:
    num_workers: 8
    max_jobs: 500
    supervisor:
      max_worker_memory: 128

Le paramètre max_worker_memory (en Mo) termine les workers qui dépassent les limites de mémoire, offrant un filet de sécurité supplémentaire au-delà de max_requests. Pour les équipes avec des pipelines CI établis et une infrastructure PHP-FPM existante, RoadRunner offre le chemin de migration le plus fluide.

Swoole : Débit Maximal avec Concurrence par Coroutines

Swoole fonctionne comme une extension PHP qui remplace le cycle de vie standard des requêtes par un runtime événementiel capable de gérer des coroutines. Elle fournit des fonctionnalités indisponibles sur les autres serveurs : exécution concurrente de tâches, ticks et intervalles, et un cache en mémoire avec un débit de 2 millions d'opérations par seconde.

bash
# Install Swoole via PECL
pecl install swoole

# Add to php.ini
echo "extension=swoole.so" >> $(php --ini | grep "Loaded Configuration" | cut -d: -f2 | xargs)/php.ini

# Install Octane with Swoole
composer require laravel/octane
php artisan octane:install --server=swoole

# Start with task workers for concurrent operations
php artisan octane:start --server=swoole --workers=8 --task-workers=6

L'exécution concurrente de tâches de Swoole gère les opérations I/O parallèles au sein d'une seule requête :

app/Http/Controllers/DashboardController.phpphp
use App\Models\User;
use App\Models\Order;
use App\Models\Analytics;
use Laravel\Octane\Facades\Octane;

class DashboardController extends Controller
{
    public function index()
    {
        // Execute three queries concurrently instead of sequentially
        [$users, $orders, $analytics] = Octane::concurrently([
            fn () => User::active()->count(),
            fn () => Order::today()->sum('total'),
            fn () => Analytics::hourly()->get(),
        ]);

        return view('dashboard', compact('users', 'orders', 'analytics'));
    }
}

Sans Octane::concurrently(), ces trois requêtes s'exécutent séquentiellement : 50ms + 30ms + 40ms = 120ms au total. Avec l'exécution concurrente, le temps total égale la requête la plus lente : 50ms. Le flag --task-workers contrôle le nombre d'opérations concurrentes pouvant s'exécuter simultanément à travers tous les workers de requêtes.

Fonctionnalités Exclusives à Swoole

Les tâches concurrentes, ticks, intervalles, cache Octane et tables Swoole nécessitent l'extension Swoole. FrankenPHP et RoadRunner ne supportent pas ces fonctionnalités. Évaluez si votre application en a besoin avant de vous engager dans la complexité opérationnelle de Swoole.

Prévention des Fuites Mémoire : Écrire du Code Sans État

La performance d'Octane vient de l'état persistant, ce qui crée le principal défi : du code qui fonctionnait parfaitement sous PHP-FPM peut fuir de la mémoire de façon catastrophique sous Octane. L'instance de l'application survit entre les requêtes, donc les propriétés statiques, les singletons et l'état global s'accumulent.

Trois patterns causent la plupart des fuites mémoire :

Tableaux statiques qui croissent sans limite :

php
// WRONG: Memory leak - array grows with every request
class MetricsCollector
{
    public static array $data = [];

    public static function record(string $metric): void
    {
        self::$data[] = $metric; // Never cleared between requests
    }
}

// CORRECT: Use request-scoped storage or external systems
class MetricsCollector
{
    public function record(string $metric): void
    {
        Redis::lpush('metrics', $metric); // External storage
    }
}

Singletons contenant des données spécifiques à la requête :

php
// WRONG: First request's user leaks into subsequent requests
$this->app->singleton(UserContext::class, function ($app) {
    return new UserContext($app['request']->user());
});

// CORRECT: Use closures for deferred resolution
$this->app->singleton(UserContext::class, function ($app) {
    return new UserContext(fn () => $app['request']->user());
});

Injection du conteneur et de la requête dans les constructeurs :

php
// WRONG: Captures stale container/request
class PaymentService
{
    public function __construct(
        private Application $app,
        private Request $request
    ) {}
}

// CORRECT: Inject via method parameters or use helpers
class PaymentService
{
    public function processPayment(Request $request): void
    {
        $user = $request->user();
        $config = config('services.stripe.key'); // Global helper, always fresh
    }
}

Les helpers globaux app(), request(), et config() retournent toujours les valeurs courantes. Les utiliser au lieu de l'injection par constructeur évite la plupart des fuites liées aux singletons.

Patterns de Déploiement en Production

Les serveurs Octane nécessitent une supervision des processus pour redémarrer en cas de crash. Supervisor gère cela de manière fiable :

ini
; /etc/supervisor/conf.d/octane.conf
[program:octane]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan octane:start --server=frankenphp --host=127.0.0.1 --port=8000
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/octane.log
stopwaitsecs=3600

Pour les déploiements, les workers Octane doivent se recharger pour prendre en compte le nouveau code. La commande octane:reload gère cela avec élégance, attendant que les requêtes en cours se terminent avant de recycler les workers :

bash
# In deployment script (after git pull, composer install, etc.)
php artisan octane:reload

Nginx se place devant Octane pour servir les assets statiques et terminer SSL :

nginx
# /etc/nginx/sites-available/app.conf
upstream octane {
    server 127.0.0.1:8000;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name example.com;
    root /var/www/app/public;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        try_files $uri $uri/ @octane;
    }

    location @octane {
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        proxy_pass http://octane;
    }
}

La directive keepalive maintient des connexions persistantes entre Nginx et Octane, réduisant le coût des handshakes TCP pour les requêtes proxifiées.

Prêt à réussir tes entretiens Laravel ?

Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.

Arbre de Décision pour le Choix du Serveur

Choisir entre FrankenPHP, RoadRunner et Swoole dépend des contraintes opérationnelles et des exigences de l'application :

ExigenceServeur Recommandé
Configuration la plus simple, déploiement conteneuriséFrankenPHP
Support HTTP/3 natifFrankenPHP
Pas de dépendances d'extension PHPFrankenPHP ou RoadRunner
Isolation maximale des processusRoadRunner
Infrastructure PHP-FPM établieRoadRunner
I/O concurrent au sein des requêtesSwoole
Cache en mémoire (2M ops/sec)Swoole
Ticks, intervalles, tâches de fondSwoole
Tables Swoole pour état partagéSwoole

Pour les équipes démarrant de nouveaux projets en 2026, FrankenPHP offre le meilleur équilibre entre simplicité et capacité. La migration d'applications existantes fonctionne mieux avec RoadRunner grâce à son isolation des processus et son modèle de déploiement familier. Swoole convient aux APIs à haut débit qui bénéficient de la concurrence par coroutines et nécessitent des fonctionnalités spécifiques à Swoole.

Questions d'Entretien sur Laravel Octane

Les entretiens techniques couvrent de plus en plus Octane à mesure que les applications Laravel à fort trafic l'adoptent. Questions courantes et ce que recherchent les recruteurs :

"Comment Octane atteint-il ses gains de performance ?"

La réponse attendue : Octane démarre Laravel une fois par worker et réutilise cette instance entre les requêtes, éliminant le coût d'initialisation de 15-40ms à chaque requête. Les candidats doivent mentionner que les méthodes register et boot des service providers s'exécutent une fois par worker, pas par requête.

"Qu'est-ce qui cause les fuites mémoire dans les applications Octane ?"

Les bonnes réponses identifient trois sources : les tableaux statiques qui accumulent des données, les singletons capturant l'état spécifique à la requête, et l'injection par constructeur du conteneur ou des objets de requête. Les candidats doivent expliquer que la correction implique d'utiliser des closures pour la résolution différée ou de passer les données via les paramètres de méthode.

"Quand choisiriez-vous Swoole plutôt que RoadRunner ?"

Les avantages de Swoole sont la concurrence par coroutines via Octane::concurrently(), le cache Octane haute vitesse, et des fonctionnalités comme les ticks et intervalles. Le compromis est la complexité opérationnelle : Swoole nécessite la gestion d'une extension PECL, tandis que RoadRunner se distribue comme un binaire Go. Pour les applications CRUD sans besoins d'I/O concurrent, RoadRunner est plus simple à opérer. Pour plus de préparation aux entretiens Laravel, voir Questions d'Entretien Développeur PHP Laravel 2026.

"Comment gérez-vous les déploiements avec Octane ?"

La séquence de déploiement : arrêter d'accepter de nouvelles connexions, attendre que les requêtes en cours se terminent, recharger les workers avec le nouveau code. La commande php artisan octane:reload gère cela. Les candidats doivent mentionner Supervisor ou systemd pour la supervision des processus et Nginx ou Caddy comme reverse proxy.

Monitoring et Profilage des Applications Octane

Le monitoring de la mémoire devient critique sous Octane. Les workers persistent, donc la croissance mémoire indique des fuites plutôt qu'une surcharge normale de requête. Laravel Pulse et Telescope fonctionnent tous deux avec Octane et fournissent un profilage au niveau de la requête.

app/Providers/AppServiceProvider.phpphp
use Laravel\Octane\Events\RequestReceived;
use Laravel\Octane\Events\RequestTerminated;

public function boot(): void
{
    Event::listen(RequestReceived::class, function ($event) {
        $event->sandbox->instance('request.memory.start', memory_get_usage());
    });

    Event::listen(RequestTerminated::class, function ($event) {
        $start = $event->sandbox->make('request.memory.start');
        $delta = memory_get_usage() - $start;
        
        if ($delta > 1024 * 1024) { // More than 1MB growth
            Log::warning('High memory delta', [
                'path' => $event->request->path(),
                'delta_mb' => round($delta / 1024 / 1024, 2),
            ]);
        }
    });
}

Cet écouteur d'événement log les requêtes qui augmentent la mémoire de plus de 1Mo, aidant à identifier les endpoints qui nécessitent une optimisation. Pour le monitoring en production, des outils APM externes comme Datadog ou New Relic fournissent une visibilité au niveau des workers.

Intégration d'Octane avec les Queues et Horizon

Octane gère les requêtes HTTP ; Laravel Horizon gère les workers de queue. Les deux fonctionnent indépendamment et se complètent. Le traitement lourd appartient aux jobs en queue, gardant les workers Octane réactifs.

app/Http/Controllers/ReportController.phpphp
use App\Jobs\GenerateReport;

class ReportController extends Controller
{
    public function generate(Request $request)
    {
        // Dispatch to queue instead of blocking the Octane worker
        GenerateReport::dispatch($request->user(), $request->input('parameters'));

        return response()->json(['status' => 'processing']);
    }
}

Pour des patterns connexes sur le traitement en arrière-plan, voir Laravel Queues and Jobs: Asynchronous Architecture et Laravel Middleware Deep Dive.

Ce Qu'il Faut Retenir sur Laravel Octane en 2026

  • Octane 2.19 supporte trois serveurs : FrankenPHP (le plus simple), RoadRunner (le plus stable), et Swoole (le plus de fonctionnalités)
  • Les gains de performance viennent de l'élimination de l'initialisation par requête ; attendez une amélioration du débit de 2x à 4x
  • Les fuites mémoire surviennent parce que l'état des workers persiste ; évitez les tableaux statiques, les requêtes injectées par constructeur, et les singletons avec des données de requête
  • Utilisez --max-requests=500 pour forcer le recyclage des workers avant que la pression mémoire ne s'accumule
  • Les fonctionnalités exclusives à Swoole incluent Octane::concurrently(), ticks, intervalles, et le cache Octane
  • Déployez derrière Nginx ou Caddy avec Supervisor pour la gestion des processus
  • Profilez la croissance mémoire par requête ; des deltas supérieurs à 1Mo indiquent des fuites potentielles
  • Mettez le travail lourd en queue via Horizon ; gardez les workers Octane pour les réponses HTTP rapides

Passe à la pratique !

Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.

Défi du jour

Tu saurais repérer le bug en Laravel ?

Un vrai bout de code, un bug caché, une tentative par jour. Sans compte pour essayer.

Anthony Fillion-Maillet

Écrit par

Anthony Fillion-Maillet

Fondateur de SharpSkill

Développeur fullstack depuis plus de 10 ans. Il dirige SharpSkill et répond de tout ce qui y est publié.

Mis à jour le 11 septembre 2026

Tags

#laravel
#octane
#swoole
#roadrunner
#frankenphp
#performance
#php

Partager

Articles similaires