Laravel Octane: Achieving Sub-50ms Response Times

Master Laravel Octane with Swoole, RoadRunner, and FrankenPHP. Learn memory leak prevention, production deployment patterns, and benchmarks for high-performance apps.

Richard Joseph Porter
18 min read
laraveloctaneperformanceswooleroadrunnerphpoptimization

Traditional PHP applications using PHP-FPM bootstrap the entire framework on every single request. That shared-nothing architecture is reliable but repeats expensive work---autoloading, service provider registration, configuration loading---for each incoming request. Laravel Octane fundamentally changes this model by booting your application once, keeping it in memory, and feeding subsequent requests to already-warmed workers.

The result? Response times that drop from 200-300ms to under 50ms for typical API endpoints, and throughput improvements of 5-20x depending on your application type. After optimizing several high-traffic Laravel applications with Octane over the past year, I have compiled the patterns, pitfalls, and production configurations that make the difference between a successful Octane deployment and a memory-leaking disaster.

What Is Laravel Octane and Why Does It Matter

Laravel Octane supercharges your application's performance by serving it through high-powered application servers: FrankenPHP, Swoole, Open Swoole, or RoadRunner. Unlike PHP-FPM, which spawns a fresh PHP process for each request, Octane boots your Laravel application once and keeps it resident in memory.

The Traditional PHP-FPM Model

Request 1: Boot PHP → Load Composer autoloader → Register service providers → Build service container → Route request → Execute controller → Return response → Terminate

Request 2: Boot PHP → Load Composer autoloader → Register service providers → Build service container → Route request → Execute controller → Return response → Terminate

(Repeat for every request)

The Octane Model

Server Start: Boot PHP → Load Composer autoloader → Register service providers → Build service container → Keep in memory

Request 1: Route request → Execute controller → Return response → Reset request state
Request 2: Route request → Execute controller → Return response → Reset request state
Request 3: Route request → Execute controller → Return response → Reset request state

(Framework stays warm, only request-specific work happens per request)

This architectural shift eliminates repeated bootstrap overhead, yielding dramatically lower latency and higher requests-per-second capacity. For API-heavy applications, I have seen improvements of 15-20x in throughput. View-heavy applications typically see 5-10x improvements.

Swoole vs RoadRunner vs FrankenPHP: Choosing Your Server

Laravel Octane supports multiple application servers, each with distinct characteristics. Your choice depends on your infrastructure, operational requirements, and performance needs.

FrankenPHP: The New Performance Leader

FrankenPHP is a modern PHP application server written in Go, built on top of Caddy. Recent benchmarks (July 2025) show FrankenPHP handling nearly 5x more requests per second than PHP-FPM, outpacing both RoadRunner and Swoole variants.

Laravel's official benchmark on MacBook M1 Pro:

Server Single Request Concurrency 8 (5 seconds)
FrankenPHP 0.88 ms 1.59 ms
RoadRunner 2.61 ms 4.00 ms
Swoole 4.94 ms 5.39 ms

Advantages:

  • No PHP extension required (zero-config installation)
  • Automatic HTTPS with Let's Encrypt via Caddy
  • Modern features: early hints, Brotli/Zstandard compression
  • Excellent Docker integration

Disadvantages:

  • Fewer advanced features than Swoole
  • Newer ecosystem with less community documentation

Swoole: The Feature-Rich Option

Swoole is a PHP extension that adds coroutines, async I/O, and advanced features to PHP. It requires compilation into your PHP installation.

Advantages:

  • Concurrent task execution with Octane::concurrently()
  • Interval ticks for scheduled in-memory operations
  • High-performance in-memory cache (2+ million ops/second)
  • Swoole Tables for shared data across workers
  • Mature ecosystem with extensive documentation

Disadvantages:

  • Requires PHP extension installation (adds deployment complexity)
  • Does not work with Xdebug (debugging limitations)
  • Limited APM support (Datadog, New Relic have known issues)

RoadRunner: The Enterprise Choice

RoadRunner is a Go-based application server that replaces PHP-FPM without requiring a PHP extension.

Advantages:

  • No PHP extension required (simple installation)
  • Full Xdebug support for debugging
  • Excellent APM compatibility (Datadog, New Relic work out-of-box)
  • Battle-tested in enterprise environments
  • Process management built-in

Disadvantages:

  • Fewer advanced features than Swoole
  • Slightly lower raw performance than FrankenPHP

Decision Matrix

Requirement Best Choice
Maximum performance, simple setup FrankenPHP
Concurrent tasks, in-memory caching Swoole
Debugging with Xdebug RoadRunner
APM/Monitoring critical RoadRunner
Docker/Kubernetes deployment FrankenPHP or RoadRunner
Legacy infrastructure constraints RoadRunner

Installation and Configuration

Installing Laravel Octane

# Install Octane
composer require laravel/octane

# Run the installation wizard
php artisan octane:install

The installer prompts you to choose your server. For production deployments, I recommend starting with FrankenPHP for its simplicity and performance, unless you need Swoole-specific features.

FrankenPHP Setup

FrankenPHP is automatically downloaded by Octane when first needed:

php artisan octane:start --server=frankenphp

For Docker deployments:

FROM dunglas/frankenphp

RUN install-php-extensions \
    pcntl \
    pdo_mysql \
    redis \
    opcache

COPY . /app

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

Swoole Setup

Swoole requires the PHP extension:

# Using PECL
pecl install swoole

# Enable the extension
echo "extension=swoole.so" >> /etc/php/8.3/cli/conf.d/swoole.ini

# Verify installation
php -m | grep swoole

For Docker, use a Swoole-enabled base image:

FROM phpswoole/swoole:php8.3

# Your application setup
COPY . /var/www/html
WORKDIR /var/www/html

CMD ["php", "artisan", "octane:start", "--server=swoole", "--host=0.0.0.0", "--port=8000"]

RoadRunner Setup

# Install RoadRunner packages
composer require spiral/roadrunner-cli spiral/roadrunner-http

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

# Start the server
php artisan octane:start --server=roadrunner

Configuration Options

Configure Octane in config/octane.php:

<?php

return [
    // Default server (frankenphp, swoole, roadrunner)
    'server' => env('OCTANE_SERVER', 'frankenphp'),

    // HTTPS mode
    'https' => env('OCTANE_HTTPS', false),

    // Maximum request execution time
    'max_execution_time' => 30,

    // Swoole-specific configuration
    'swoole' => [
        'options' => [
            // Worker count: start with 2x CPU cores
            'worker_num' => env('OCTANE_WORKERS', max(4, swoole_cpu_num() * 2)),

            // Task workers for concurrent operations
            'task_worker_num' => env('OCTANE_TASK_WORKERS', swoole_cpu_num()),

            // Restart workers after N requests (memory leak protection)
            'max_request' => env('OCTANE_MAX_REQUESTS', 5000),

            // Maximum wait time for worker restart
            'max_wait_time' => 60,

            // Logging
            'log_file' => storage_path('logs/swoole_http.log'),
            'log_level' => SWOOLE_LOG_WARNING,

            // Large request handling
            'package_max_length' => 10 * 1024 * 1024, // 10MB
        ],
    ],

    // Octane cache configuration (Swoole only)
    'cache' => [
        'driver' => 'octane',
        'max_entries' => 100000,
    ],

    // Files to watch during development
    'watch' => [
        'app',
        'bootstrap',
        'config',
        'database',
        'public/**/*.php',
        'resources/**/*.php',
        'routes',
        'composer.lock',
        '.env',
    ],

    // Listeners to flush between requests
    'listeners' => [
        // ...
    ],

    // Warm services on boot
    'warm' => [
        // ...
    ],

    // Flush services between requests
    'flush' => [
        // ...
    ],
];

Performance Benchmarks: Before and After Octane

To demonstrate Octane's impact, I benchmarked a typical Laravel API application with these characteristics:

  • Laravel 12 with 45 registered service providers
  • 12 Eloquent models with relationships
  • Redis caching layer
  • JWT authentication via Laravel Sanctum
  • PostgreSQL database

Test Environment

  • Server: AWS c6i.xlarge (4 vCPUs, 8GB RAM)
  • Database: RDS PostgreSQL db.r6g.large
  • Redis: ElastiCache r6g.large
  • Load Testing: k6 with 100 concurrent virtual users

Results: Simple API Endpoint (JSON response, single query)

Configuration Requests/sec Avg Response P99 Response Memory/Worker
PHP-FPM (pm=dynamic, 20 children) 1,240 78ms 156ms 45MB
Octane + RoadRunner (8 workers) 8,450 11ms 28ms 62MB
Octane + Swoole (8 workers) 9,120 10ms 24ms 68MB
Octane + FrankenPHP (8 workers) 11,340 8ms 19ms 58MB

Results: Complex API Endpoint (5 queries, eager loading, transformations)

Configuration Requests/sec Avg Response P99 Response
PHP-FPM 380 245ms 520ms
Octane + RoadRunner 2,840 34ms 72ms
Octane + Swoole 3,150 30ms 65ms
Octane + FrankenPHP 3,890 24ms 52ms

Key Observations

  1. FrankenPHP consistently outperforms other servers in raw throughput
  2. Response time improvements are most dramatic for bootstrap-heavy applications
  3. Memory per worker is higher with Octane (expected: application stays resident)
  4. P99 latency drops significantly, improving user experience consistency
  5. Database query time becomes the bottleneck once bootstrap overhead is eliminated

Memory Leak Prevention and Debugging

The most critical aspect of Octane deployment is understanding that your application now runs as a long-lived process. Traditional PHP patterns that work fine with PHP-FPM can cause catastrophic memory leaks under Octane. This is particularly important when migrating legacy Laravel applications to Octane, as older codebases often contain patterns incompatible with long-running processes.

Understanding the Problem

With PHP-FPM, each request gets a fresh PHP process. Any memory allocated is freed when the process terminates. With Octane, workers persist across thousands of requests, so accumulated state never gets cleaned up automatically.

<?php

// MEMORY LEAK: Static array grows indefinitely
class AnalyticsService
{
    private static array $events = [];

    public function track(string $event, array $data): void
    {
        // Each request adds to this array - it never gets cleared!
        self::$events[] = [
            'event' => $event,
            'data' => $data,
            'timestamp' => now(),
        ];
    }
}

The Golden Rules for Octane Compatibility

Rule 1: Never inject the container or request into singleton constructors

<?php

// BAD: Container injection in singleton
$this->app->singleton(PaymentService::class, function (Application $app) {
    return new PaymentService($app); // Stale container reference!
});

// GOOD: Use resolver closure
$this->app->singleton(PaymentService::class, function () {
    return new PaymentService(fn () => Container::getInstance());
});

// GOOD: Use global helper
$this->app->singleton(PaymentService::class, function () {
    return new PaymentService();
});

// In the service
class PaymentService
{
    public function process(Request $request): void
    {
        // Get current request using helper
        $user = request()->user();
        // or
        $user = app('request')->user();
    }
}

Rule 2: Avoid static properties that accumulate data

<?php

// BAD: Static cache that grows forever
class ProductRepository
{
    private static array $cache = [];

    public function find(int $id): Product
    {
        if (!isset(self::$cache[$id])) {
            self::$cache[$id] = Product::find($id); // Grows indefinitely!
        }
        return self::$cache[$id];
    }
}

// GOOD: Use Laravel's cache with TTL
class ProductRepository
{
    public function find(int $id): Product
    {
        return Cache::remember("product:{$id}", 300, function () use ($id) {
            return Product::find($id);
        });
    }
}

Rule 3: Reset state in service providers when needed

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Laravel\Octane\Events\RequestReceived;
use Laravel\Octane\Events\RequestTerminated;

class OctaneServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Reset custom services between requests
        $this->app['events']->listen(RequestReceived::class, function () {
            // Clear any request-specific state
            app(AnalyticsService::class)->reset();
        });

        $this->app['events']->listen(RequestTerminated::class, function () {
            // Cleanup after request
            app()->forgetInstance(TemporaryService::class);
        });
    }
}

Detecting Memory Leaks

Monitor memory usage during development:

# Watch memory usage in real-time
watch -n 1 'php artisan octane:status'

# Profile memory during load testing
php artisan octane:start --max-requests=100
# Run your load tests, then check if memory stabilized

Create a memory monitoring middleware:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class MemoryMonitorMiddleware
{
    public function handle(Request $request, Closure $next): mixed
    {
        $startMemory = memory_get_usage(true);

        $response = $next($request);

        $endMemory = memory_get_usage(true);
        $memoryUsed = ($endMemory - $startMemory) / 1024 / 1024;

        if ($memoryUsed > 10) { // Log if request used more than 10MB
            Log::warning('High memory usage detected', [
                'path' => $request->path(),
                'memory_mb' => round($memoryUsed, 2),
                'total_memory_mb' => round($endMemory / 1024 / 1024, 2),
            ]);
        }

        return $response;
    }
}

Use Larastan for Static Analysis

The Larastan PHPStan extension includes Octane compatibility rules. For additional code quality assurance, consider AI-assisted code review to identify potential memory leak patterns before they reach production.

composer require --dev larastan/larastan

# Add to phpstan.neon
includes:
    - vendor/larastan/larastan/extension.neon

parameters:
    level: 6
    paths:
        - app/

# Run analysis
./vendor/bin/phpstan analyse

The Max-Requests Safety Net

Always configure max_requests to gracefully restart workers:

# Command line
php artisan octane:start --max-requests=5000

# Or in config/octane.php for Swoole
'swoole' => [
    'options' => [
        'max_request' => 5000,
    ],
],

Start with 1,000-5,000 requests per worker and adjust based on memory profiling. For memory-intensive applications, values as low as 500 may be necessary.

Handling Stateful Services Properly

Certain Laravel services maintain state that must be handled carefully under Octane.

Authentication State

Laravel automatically resets authentication state between requests. However, custom authentication guards may need manual handling:

<?php

// If you have a custom guard that caches the user
class CustomGuard implements Guard
{
    private ?User $user = null;

    public function user(): ?User
    {
        return $this->user ??= $this->resolveUser();
    }

    // Add a reset method
    public function reset(): void
    {
        $this->user = null;
    }
}

// Register for Octane reset
$this->app['events']->listen(RequestReceived::class, function () {
    auth()->guard('custom')->reset();
});

Database Connections

Octane handles database connection pooling, but long-running queries can cause issues:

<?php

// config/database.php - set connection timeouts
'mysql' => [
    'driver' => 'mysql',
    // ... other config
    'options' => [
        PDO::ATTR_TIMEOUT => 30,
        PDO::ATTR_PERSISTENT => false, // Important for Octane
    ],
],

Session Handling

Use database or Redis sessions, never file sessions with Octane:

<?php

// config/session.php
return [
    'driver' => env('SESSION_DRIVER', 'redis'), // or 'database'

    // For Redis
    'connection' => env('SESSION_CONNECTION', 'session'),
];

Third-Party Package Compatibility

Some packages assume fresh boot per request. Test thoroughly and implement resets:

<?php

// Example: Resetting a package that caches state
$this->app['events']->listen(RequestTerminated::class, function () {
    // Reset Spatie's Permission cache
    app()->make(\Spatie\Permission\PermissionRegistrar::class)->forgetCachedPermissions();
});

Swoole-Specific Features

If you choose Swoole, leverage its unique capabilities for additional performance gains.

Concurrent Task Execution

Run multiple operations in parallel:

<?php

use Laravel\Octane\Facades\Octane;

class DashboardController extends Controller
{
    public function index(): JsonResponse
    {
        // Execute three queries concurrently
        [$users, $orders, $analytics] = Octane::concurrently([
            fn () => User::with('subscription')->paginate(10),
            fn () => Order::where('status', 'pending')->count(),
            fn () => Analytics::getDashboardMetrics(),
        ]);

        return response()->json([
            'users' => $users,
            'pending_orders' => $orders,
            'analytics' => $analytics,
        ]);
    }
}

Constraints:

  • Maximum 1,024 tasks per concurrently call
  • Tasks run in separate worker processes
  • Configure task workers: --task-workers=6

High-Performance In-Memory Cache

The Octane cache driver provides extreme performance (2+ million ops/second):

<?php

use Illuminate\Support\Facades\Cache;

// Store with TTL
Cache::store('octane')->put('rate_limit:user:123', 1, 60);

// Increment atomically
Cache::store('octane')->increment('rate_limit:user:123');

// Auto-refreshing cache intervals
Cache::store('octane')->interval('exchange_rates', function () {
    return Http::get('https://api.exchangerate.io/latest')->json();
}, seconds: 300);

Swoole Tables for Shared State

For data that must be shared across all workers:

<?php

// config/octane.php
'tables' => [
    'rate_limits:10000' => [
        'count' => 'int',
        'reset_at' => 'int',
    ],
],

// Usage
use Laravel\Octane\Facades\Octane;

$table = Octane::table('rate_limits');

// Set rate limit data
$table->set('user:123', [
    'count' => 1,
    'reset_at' => time() + 60,
]);

// Check and increment
$data = $table->get('user:123');
if ($data && $data['count'] < 100) {
    $table->set('user:123', [
        'count' => $data['count'] + 1,
        'reset_at' => $data['reset_at'],
    ]);
}

Ticks for Background Operations

Execute functions at regular intervals:

<?php

// In a service provider's boot() method
use Laravel\Octane\Facades\Octane;

Octane::tick('metrics-reporter', function () {
    $metrics = [
        'memory' => memory_get_usage(true),
        'connections' => DB::connection()->getPdo()->query('SHOW STATUS LIKE "Threads_connected"')->fetchColumn(1),
    ];

    Cache::put('server_metrics', $metrics, 60);
})
->seconds(30)
->immediate(); // Run once immediately on boot

Production Deployment Patterns

Nginx Reverse Proxy Configuration

Always deploy Octane behind a reverse proxy for static file serving and SSL termination:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

upstream octane {
    server 127.0.0.1:8000;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    root /var/www/example.com/public;

    # Serve static files directly
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }

    # Proxy dynamic requests to Octane
    location / {
        proxy_http_version 1.1;
        proxy_set_header Host $http_host;
        proxy_set_header Scheme $scheme;
        proxy_set_header SERVER_PORT $server_port;
        proxy_set_header REMOTE_ADDR $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 $connection_upgrade;

        proxy_pass http://octane;

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Health check endpoint
    location /up {
        proxy_pass http://octane;
        proxy_set_header Host $http_host;
    }
}

Supervisor Configuration

Use Supervisor to manage Octane processes:

[program:octane]
process_name=%(program_name)s
command=php /var/www/example.com/artisan octane:start --server=frankenphp --host=127.0.0.1 --port=8000 --workers=8 --max-requests=5000
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/octane.log
stopwaitsecs=3600

Docker Compose Production Setup

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    command: php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=8000 --workers=8 --max-requests=5000
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/up"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          memory: 1G
        reservations:
          memory: 512M
    networks:
      - app-network

  horizon:
    build:
      context: .
      dockerfile: Dockerfile
    command: php artisan horizon
    restart: unless-stopped
    depends_on:
      - app
    networks:
      - app-network

  scheduler:
    build:
      context: .
      dockerfile: Dockerfile
    command: php artisan schedule:work
    restart: unless-stopped
    depends_on:
      - app
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: laravel-octane
spec:
  replicas: 3
  selector:
    matchLabels:
      app: laravel-octane
  template:
    metadata:
      labels:
        app: laravel-octane
    spec:
      containers:
        - name: octane
          image: your-registry/laravel-app:latest
          command: ["php", "artisan", "octane:start", "--server=frankenphp", "--host=0.0.0.0", "--port=8000", "--workers=4", "--max-requests=5000"]
          ports:
            - containerPort: 8000
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "1000m"
          livenessProbe:
            httpGet:
              path: /up
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /up
              port: 8000
            initialDelaySeconds: 5
            periodSeconds: 5
          env:
            - name: OCTANE_SERVER
              value: "frankenphp"

Zero-Downtime Deployments

After deploying new code, reload Octane workers:

# Graceful reload (recommended)
php artisan octane:reload

# Or if using Supervisor
supervisorctl signal SIGUSR1 octane

# For Kubernetes, use rolling updates
kubectl rollout restart deployment/laravel-octane

When to Use Octane vs Traditional PHP-FPM

Octane is not always the right choice. Consider these factors. For comprehensive guidance on API architecture, see our Laravel API development best practices guide.

Use Octane When

  • API-heavy applications: Maximum benefit from eliminated bootstrap overhead
  • High-traffic applications: When you need to serve thousands of requests per second
  • Real-time features: WebSockets, long-polling, server-sent events
  • Microservices: Low latency is critical for service-to-service communication
  • Cost optimization: Serve more traffic with fewer servers. Combined with AWS cost optimization strategies, Octane can reduce both response times and infrastructure expenses.

Stick with PHP-FPM When

  • Legacy codebases: Applications with extensive static state or incompatible patterns
  • Heavy third-party packages: Some packages are not Octane-compatible
  • Limited DevOps resources: Octane requires more operational knowledge
  • Infrequent traffic: The complexity is not justified for low-traffic sites
  • Debugging requirements: If you need Xdebug constantly (use RoadRunner if you must have Octane)

Hybrid Approach

Consider running Octane for API routes and PHP-FPM for web routes:

# API routes to Octane
location /api {
    proxy_pass http://127.0.0.1:8000;
}

# Web routes to PHP-FPM
location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    include fastcgi_params;
}

Monitoring and Observability

Health Check Endpoint

Laravel provides a built-in /up route for health checks:

<?php

// routes/web.php or api.php - customize if needed
Route::get('/health', function () {
    return response()->json([
        'status' => 'healthy',
        'timestamp' => now()->toISOString(),
        'octane' => [
            'server' => config('octane.server'),
            'workers' => config('octane.swoole.options.worker_num', 'auto'),
        ],
        'memory' => [
            'used_mb' => round(memory_get_usage(true) / 1024 / 1024, 2),
            'peak_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2),
        ],
    ]);
});

Laravel Pulse Integration

Laravel Pulse provides real-time application monitoring:

composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate

Custom Metrics with Prometheus

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Prometheus\CollectorRegistry;

class PrometheusMiddleware
{
    public function handle(Request $request, Closure $next): mixed
    {
        $start = microtime(true);

        $response = $next($request);

        $duration = microtime(true) - $start;

        $registry = app(CollectorRegistry::class);
        $histogram = $registry->getOrRegisterHistogram(
            'app',
            'http_request_duration_seconds',
            'Request duration in seconds',
            ['method', 'route', 'status']
        );

        $histogram->observe($duration, [
            $request->method(),
            $request->route()?->getName() ?? 'unknown',
            $response->getStatusCode(),
        ]);

        return $response;
    }
}

Key Takeaways

Laravel Octane transforms PHP application performance by eliminating per-request bootstrap overhead. After deploying Octane across multiple production applications, these principles guide successful implementations:

  • Choose your server wisely: FrankenPHP for maximum performance, Swoole for advanced features, RoadRunner for enterprise compatibility
  • Eliminate stateful patterns: No static properties that accumulate, no container injection in singletons
  • Configure max-requests: Always set a limit to prevent runaway memory leaks
  • Monitor memory continuously: Memory leaks are silent killers in long-running processes
  • Use appropriate tooling: Larastan for static analysis, Pulse for monitoring
  • Deploy behind a reverse proxy: Nginx or Caddy for static files and SSL termination
  • Plan for graceful reloads: Zero-downtime deployments require proper worker management

The performance gains are substantial: 5-20x throughput improvements and sub-50ms response times are achievable for most applications. However, Octane requires a shift in development mindset from request-scoped to process-scoped thinking.

Start with a non-production environment, run thorough load tests, monitor memory patterns, and gradually roll out to production. The investment in understanding Octane pays dividends in infrastructure costs, user experience, and application scalability.


Ready to supercharge your Laravel application with Octane? I specialize in Laravel performance optimization with over 14 years of PHP experience building high-traffic applications. My Performance Optimization service includes Octane implementation, memory leak auditing, and production deployment configuration. Schedule a free consultation to discuss your performance goals.


Related Reading:

External Resources:

Richard Joseph Porter - Professional headshot

Richard Joseph Porter

Full-stack developer with expertise in modern web technologies. Passionate about building scalable applications and sharing knowledge through technical writing.

Need Help Upgrading Your Laravel App?

I specialize in modernizing legacy Laravel applications with zero downtime. Get a free codebase audit and upgrade roadmap.