All Articles
25 May 2026 12 min read 51 views
Laravel

Laravel Octane 2026 — 400% Performance Gain, Real Setup Guide (FrankenPHP, Swoole, RoadRunner)

Complete Laravel Octane setup guide for 2026 — FrankenPHP vs Swoole vs RoadRunner, worker count formula, production Nginx + Systemd config, memory gotchas, and zero-downtime deployment.

Tushar Modi.
Tushar Modi.
May 25, 2026 · Jaipur, India
12 min 51
Category Laravel
Published May 25, 2026
Read 12 min
Views 51
Updated Jun 6, 2026
Laravel Octane 2026 — 400% Performance Gain, Real Setup Guide (FrankenPHP, Swoole, RoadRunner)

Laravel Octane 2026 — 400% Performance, Real Setup Guide

I have been running Laravel applications on PHP-FPM for years. It works. It is predictable. Every developer on the team understands it. Debugging is straightforward. Deployments are boring in the good way.

Then I looked at what Octane actually does — not the marketing version, but the architectural reality — and understood why teams are reporting numbers that sound too good to be true.

A SaaS platform migrated from traditional Laravel deployment to Octane achieved a 400% improvement in request throughput while reducing server costs by 60%. Itpathsolutions

That is not a benchmark in ideal lab conditions. That is a production migration on a real application. This guide covers exactly what changed, how to set it up properly in 2026, and the gotchas that will bite you if you go in unprepared.

Why PHP-FPM Has a Hidden Performance Problem

To understand what Octane does, you need to understand what PHP-FPM does on every single request.

Traditional PHP-FPM boots the entire Laravel framework from scratch on every single request — loading configuration, registering service providers, resolving bindings — before handling the request itself. Octane boots the application once and keeps it in memory, serving subsequent requests from that warm state. GitHub

Here is what that cycle looks like on every request with PHP-FPM:

Request arrives
  → PHP process starts
  → Load composer autoloader
  → Boot Laravel application
  → Register all service providers
  → Resolve container bindings
  → Boot middleware stack
  → Handle the actual request
  → Send response
  → PHP process dies
  → Everything above repeats on next request

For a typical Laravel application, that bootstrap cycle takes 20-80ms before your code runs a single line of actual business logic. On a high-traffic API serving 1,000 requests per second, you are paying that bootstrap tax one thousand times per second.

Laravel Octane keeps your app booted in memory to reduce bootstrapping overhead, supports multiple high-performance drivers, and can deliver large throughput gains — but it changes the execution model, so you must manage state and memory carefully. Laravel

That last part is the part most tutorials skip. We will cover it properly.

The Three Drivers — Which One to Choose

Laravel Octane supercharges your application's performance by serving your application using high-powered application servers, including FrankenPHP, Open Swoole, Swoole, and RoadRunner. Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds. Boundev

In 2026, the practical choice is between three:

FrankenPHP — Best Starting Point for Most Teams

FrankenPHP is a PHP application server, written in Go, that supports modern web features like early hints, Brotli, and Zstandard compression. Boundev

Choose FrankenPHP if:

  • You are setting up Octane for the first time
  • You want the simplest production deployment
  • You need modern web features like HTTP/3 and early hints
  • Your team is not comfortable compiling PHP extensions

bash

composer require laravel/octane
php artisan octane:install --server=frankenphp
php artisan octane:start --server=frankenphp --port=8000

FrankenPHP ships as a single binary. No extension installation. No compilation. That simplicity is worth a lot in production.

RoadRunner — Balanced Choice for APIs

RoadRunner is easier to set up and has better Windows support. Swoole generally offers better raw performance — 10-15% faster — but has more complex installation. OneUptime

Choose RoadRunner if:

  • You want easier setup than Swoole with better performance than PHP-FPM
  • Your team develops on Windows
  • You want the Go-based plugin ecosystem
  • You need predictable resource usage

bash

composer require laravel/octane
php artisan octane:install --server=roadrunner
php artisan octane:start --server=roadrunner --port=8000

Swoole — Maximum Performance for High Traffic

Choose Swoole if:

  • You have genuinely high traffic (10k+ requests per minute)
  • You need concurrent tasks, coroutines, or async I/O
  • You are comfortable installing PHP extensions
  • Raw performance is the primary concern

bash

# Install Swoole extension first
pecl install swoole

# Add to php.ini
extension=swoole

# Then install Octane
composer require laravel/octane
php artisan octane:install --server=swoole
php artisan octane:start --server=swoole --port=8000 --workers=4

Complete Installation — Step by Step

Step 1 — Install Octane

bash

composer require laravel/octane
php artisan octane:install

The installer prompts you to choose your server. Select based on the guide above.

Step 2 — Configure octane.php

The installer creates config/octane.php. These are the settings that actually matter:

php

// config/octane.php
return [
    'server' => env('OCTANE_SERVER', 'frankenphp'),

    // Worker count — most impactful setting
    // Rule of thumb: (CPU cores * 2) for API apps
    // (CPU cores) for memory-intensive apps
    'workers' => env('OCTANE_WORKERS', 4),

    // Task workers for concurrent background tasks (Swoole only)
    'task_workers' => env('OCTANE_TASK_WORKERS', 2),

    // Restart worker after N requests to prevent memory leaks
    // Lower for memory-heavy apps, higher for light apps
    'max_requests' => env('OCTANE_MAX_REQUESTS', 500),

    // Max execution time per request in seconds
    'max_execution_time' => 30,

    // Listeners that flush state between requests
    'listeners' => [
        WorkerStarting::class => [
            EnsureUploadedFilesAreValid::class,
            EnsureRequestServerPortMatchesScheme::class,
        ],
        RequestReceived::class => [
            ...Octane::prepareApplicationForNextRequest(),
        ],
        RequestHandled::class => [
            FlushUploadedFiles::class,
        ],
        RequestTerminated::class => [
            FlushLocaleState::class,
            FlushQueuedCookies::class,
            FlushSessionState::class,
            FlushAuthenticationState::class,
            FlushCryptographyRequestState::class,
        ],
    ],

    // Services to warm on worker start
    'warm' => [
        ...Octane::defaultServicesToWarm(),
    ],
];

Step 3 — Environment Variables

env

OCTANE_SERVER=frankenphp
OCTANE_WORKERS=4
OCTANE_MAX_REQUESTS=500
APP_ENV=production
APP_DEBUG=false

Step 4 — Start and Verify

bash

# Development
php artisan octane:start --watch

# Production
php artisan octane:start --workers=4 --max-requests=500

# Check status
php artisan octane:status

# Reload workers without downtime
php artisan octane:reload

# Stop
php artisan octane:stop

The Worker Count Calculation

The most impactful configuration parameter is worker count. OneUptime

Getting this wrong is the single biggest cause of suboptimal Octane performance.

bash

# Check your CPU cores
nproc  # Linux
sysctl -n hw.ncpu  # macOS

General rules:

  • API-heavy apps (mostly I/O): CPU cores × 2
  • CPU-heavy apps (heavy computation): CPU cores
  • Mixed apps: CPU cores × 1.5, round up

env

# 4-core server, API-heavy
OCTANE_WORKERS=8

# 4-core server, CPU-heavy
OCTANE_WORKERS=4

# 4-core server, mixed
OCTANE_WORKERS=6

Monitor memory per worker and adjust. Each worker holds your entire application in memory.

Performance Numbers — What to Expect

Most applications see 5-20x performance improvements in requests per second and reduced latency. API applications typically see the highest gains — 15-20x — while view-heavy applications see 5-10x improvements. The exact improvement depends on your specific application architecture and workload patterns. OneUptime

Benchmark your own application before and after migration:

bash

# Install Apache Bench (comes with Apache tools)
# Before Octane — PHP-FPM
ab -n 1000 -c 50 http://yourdomain.com/api/v1/health

# After Octane
ab -n 1000 -c 50 http://yourdomain.com/api/v1/health

Or use autocannon for a more modern benchmark:

bash

npx autocannon -c 100 -d 10 http://localhost:8000/api/v1/health

Compare: requests per second, mean latency, p99 latency.

The Gotchas — What Nobody Warns You About

This section is the most important part of the guide. Octane changes the PHP execution model in ways that break assumptions every Laravel developer has built up over years.

Gotcha 1 — Memory Does Not Clear Between Requests

In PHP-FPM, every request gets a clean process. Memory is cleared automatically. In Octane, the same worker handles thousands of requests without dying.

What this means: Any state you accidentally store globally persists across requests.

php

// DANGEROUS in Octane — this accumulates across requests
class ReportService
{
    private static array $cache = [];

    public function generate(int $id): array
    {
        if (!isset(static::$cache[$id])) {
            static::$cache[$id] = $this->buildReport($id);
        }
        return static::$cache[$id];
    }
}

// After 500 requests, static::$cache is enormous
// Worker runs out of memory and dies

php

// SAFE — per-request cache via the container
class ReportService
{
    private array $cache = [];  // instance variable, not static

    public function generate(int $id): array
    {
        if (!isset($this->cache[$id])) {
            $this->cache[$id] = $this->buildReport($id);
        }
        return $this->cache[$id];
    }
}

// Register as scoped (new instance per request)
$this->app->scoped(ReportService::class);

Gotcha 2 — Singleton Leaks Between Requests

php

// DANGEROUS — singleton retains user state across requests
class AuthContext
{
    private ?User $user = null;

    public function setUser(User $user): void
    {
        $this->user = $user;
    }

    public function getUser(): ?User
    {
        return $this->user;  // Returns previous request's user!
    }
}

// Registered as singleton — same instance across all requests
app()->singleton(AuthContext::class);

php

// SAFE — scoped binding gives fresh instance per request
app()->scoped(AuthContext::class);

// Or register a flush callback in octane.php listeners
RequestTerminated::class => [
    function () {
        app(AuthContext::class)->flush();
    },
],

Gotcha 3 — Database Connection Pooling Behavior Changes

php

// config/database.php — add for Octane
'mysql' => [
    'driver'         => 'mysql',
    'host'           => env('DB_HOST'),
    // ...

    // Octane keeps connections alive between requests
    // Set pool size to match your worker count
    'pool' => [
        'min_connections' => 1,
        'max_connections' => env('OCTANE_WORKERS', 4),
        'connect_timeout' => 10.0,
        'wait_timeout'    => 3.0,
        'heartbeat'       => -1,
        'max_idle_time'   => 60.0,
    ],
],

Gotcha 4 — Third-Party Package Compatibility

Not every package was written with persistent memory in mind. Before migrating to production, audit your key packages:

bash

# Test for memory leaks with wrk
wrk -t4 -c100 -d30s http://localhost:8000/api/v1/your-endpoint

# Monitor memory during test
watch -n 1 'php artisan octane:status'

Known packages with Octane issues (check each package's current status):

  • Packages that store state in static properties
  • Packages that use register_shutdown_function()
  • Debugbar in non-development environments
  • Some older Spatie packages (most have been updated)

Gotcha 5 — File Uploads Need Special Handling

php

// Octane automatically handles this via FlushUploadedFiles listener
// But verify your upload handling is clean

// SAFE — Octane's default listeners clean this up
public function store(Request $request): JsonResponse
{
    $file = $request->file('avatar');  // Safe — cleaned after request
    $path = $file->store('avatars', 's3');
    return response()->json(['path' => $path]);
}

Production Deployment — Nginx + Systemd

Nginx Configuration

nginx

# /etc/nginx/sites-available/laravel-octane
server {
    listen 80;
    server_name yourdomain.com;

    # Redirect all HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

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

    # Proxy to Octane
    location / {
        proxy_pass         http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Upgrade           $http_upgrade;
        proxy_set_header   Connection        "upgrade";

        # Timeout settings
        proxy_connect_timeout 60s;
        proxy_send_timeout    60s;
        proxy_read_timeout    60s;
    }
}

Systemd Service

ini

# /etc/systemd/system/laravel-octane.service
[Unit]
Description=Laravel Octane Server
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/yourapp
ExecStart=/usr/bin/php artisan octane:start \
    --server=frankenphp \
    --host=127.0.0.1 \
    --port=8000 \
    --workers=4 \
    --max-requests=500
Restart=always
RestartSec=5

# Environment
Environment=APP_ENV=production
Environment=APP_DEBUG=false

# Logging
StandardOutput=append:/var/log/octane.log
StandardError=append:/var/log/octane-error.log

[Install]
WantedBy=multi-user.target

bash

# Enable and start
sudo systemctl enable laravel-octane
sudo systemctl start laravel-octane
sudo systemctl status laravel-octane

# Zero-downtime reload
sudo systemctl reload laravel-octane
# or
php artisan octane:reload

Zero-Downtime Deployment Script

bash

#!/bin/bash
# deploy.sh

set -e

echo "Pulling latest code..."
git pull origin main

echo "Installing dependencies..."
composer install --no-dev --optimize-autoloader

echo "Running migrations..."
php artisan migrate --force

echo "Clearing caches..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

echo "Reloading Octane workers (zero downtime)..."
php artisan octane:reload

echo "Deployment complete."

Concurrent Tasks with Swoole — Octane's Hidden Power

If you are running Swoole, Octane unlocks concurrent task execution — running multiple operations genuinely in parallel within a single request.

php

use Laravel\Octane\Facades\Octane;

public function dashboard(Request $request): JsonResponse
{
    // These three queries run CONCURRENTLY — not sequentially
    [$stats, $recentOrders, $topProducts] = Octane::concurrently([
        fn () => $this->analyticsService->getDashboardStats(),
        fn () => Order::with('customer')
                      ->latest()
                      ->limit(10)
                      ->get(),
        fn () => Product::withCount('orders')
                        ->orderByDesc('orders_count')
                        ->limit(5)
                        ->get(),
    ]);

    return response()->json([
        'stats'        => $stats,
        'orders'       => OrderResource::collection($recentOrders),
        'top_products' => ProductResource::collection($topProducts),
    ]);
}

Instead of three sequential queries (say 150ms each = 450ms total), all three run in parallel — total time is the slowest of the three, roughly 150ms. For data-heavy dashboards, this is the biggest single performance win Octane provides.

Is Octane Right for Your Application?

Octane is the right choice if:

  • Your application handles significant traffic (500+ requests per minute)
  • You have a Laravel API serving a mobile app or SPA
  • Response time is a user-visible concern
  • You are willing to audit for memory safety issues

Stick with PHP-FPM if:

  • Your application has low to moderate traffic
  • You rely on packages with known Octane compatibility issues
  • Your team is not familiar with long-running process patterns
  • Simplicity and debuggability are the priority

The honest middle ground: Run PHP-FPM in production and benchmark. If response times are acceptable and servers are not a bottleneck, Octane adds complexity for gains you may not need. If you are scaling horizontally by adding servers because a single server cannot handle the load — Octane is the better investment.

Production Checklist

Before enabling Octane:

  • Audit all static properties in your codebase
  • Check singleton bindings for request-scoped state
  • Verify all third-party packages are Octane-compatible
  • Test file upload handling
  • Set max_requests to prevent gradual memory growth

Configuration:

  • Worker count set to CPU cores × 2 for API apps
  • max_requests set (500 is a safe default, tune per app)
  • Database connection pool matches worker count
  • Redis connection pool configured if using Redis

Production deployment:

  • Nginx reverse proxy configured
  • Systemd service with Restart=always
  • Zero-downtime reload script ready
  • Memory monitoring in place
  • octane:status accessible for health checks

Wrapping Up

Laravel Octane is not a drop-in replacement for PHP-FPM that you enable and forget. It is an architectural change that requires understanding the new execution model — specifically that memory persists across requests and state must be managed deliberately.

But for applications with real traffic, the performance numbers are not exaggerated. API applications typically see 15-20x improvements in requests per second. That is the difference between needing four servers and needing one. OneUptime

Audit your codebase for memory safety issues, choose the right driver for your team and use case, set worker count correctly, and monitor memory in the first week. After that it is largely set-and-forget.

The 400% throughput claim is real. So is the work required to get there cleanly.

Tushar Modi — Full Stack Developer, Jaipur tusharmodi.in