Laravel Octane in 2026: Swoole, RoadRunner and Performance Optimization
Master Laravel Octane 2.19 with FrankenPHP, Swoole and RoadRunner. Learn server selection, memory leak prevention, concurrent tasks, and production deployment patterns for high-performance PHP applications.

Laravel Octane transforms PHP application performance by keeping the framework bootstrapped in memory across requests. Version 2.19 (August 2026) supports three production-ready servers: FrankenPHP, Swoole, and RoadRunner. Each serves different operational requirements, and selecting the wrong one costs either performance or stability.
FrankenPHP: simplest setup, HTTP/3 native, single binary. RoadRunner: battle-tested Go binary, no PHP extensions required. Swoole: maximum throughput with coroutine concurrency, requires PECL extension management.
How Octane Eliminates Bootstrap Overhead
Traditional PHP-FPM spawns a new process per request. Laravel boots its service container, loads configuration, registers providers, and resolves middleware on every single HTTP call. Octane inverts this model: the application boots once per worker, then the same instance handles thousands of requests sequentially.
The performance gain comes from removing repeated work. A typical Laravel 12 application spends 15 to 40ms bootstrapping before executing business logic. With Octane, that cost drops to zero after the first request. Benchmarks on identical hardware show 2x to 4x throughput improvements for API endpoints, with the gap widening as application complexity increases.
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,
];The max_requests setting acts as a safety valve. Even well-written code can accumulate memory over hundreds of requests. Setting this to 500 forces workers to restart before memory pressure becomes problematic.
FrankenPHP: The 2026 Default for New Projects
FrankenPHP ships as a single binary built on Caddy and PHP. It handles HTTPS certificates automatically through Let's Encrypt, supports HTTP/3 out of the box, and requires no PHP extensions to install. Laravel's installer and production examples now default to FrankenPHP.
# 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=8000For containerized deployments, FrankenPHP provides official Docker images:
# Dockerfile
FROM dunglas/frankenphp
RUN install-php-extensions \
pcntl \
pdo_pgsql \
redis
COPY . /app
ENTRYPOINT ["php", "artisan", "octane:frankenphp"]The Docker Compose configuration for development enables HTTPS and HTTP/3:
# compose.yaml
services:
frankenphp:
build:
context: .
entrypoint: php artisan octane:frankenphp --workers=1 --max-requests=1
ports:
- "443:443"
- "443:443/udp"
volumes:
- .:/appFrankenPHP handles worker restarts gracefully and integrates directly with Caddy's middleware system for advanced routing scenarios.
RoadRunner: Go-Based Stability Without Extension Dependencies
RoadRunner compiles to a Go binary that manages PHP worker processes. It communicates with PHP over a binary protocol, keeping worker management and HTTP handling in Go while executing application code in PHP. This architecture provides process isolation: a crashing PHP worker does not affect the Go parent or sibling workers.
# 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=8000RoadRunner configuration lives in .rr.yaml at the project root:
# .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: 128The max_worker_memory setting (in MB) terminates workers that exceed memory limits, providing an additional safety net beyond max_requests. For teams with established CI pipelines and existing PHP-FPM infrastructure, RoadRunner offers the smoothest migration path.
Swoole: Maximum Throughput with Coroutine Concurrency
Swoole operates as a PHP extension that replaces the standard request lifecycle with an event-driven, coroutine-capable runtime. It provides features unavailable in other servers: concurrent task execution, ticks and intervals, and an in-memory cache with 2 million operations per second throughput.
# 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=6Swoole's concurrent task execution handles parallel I/O operations within a single request:
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'));
}
}Without Octane::concurrently(), these three queries execute sequentially: 50ms + 30ms + 40ms = 120ms total. With concurrent execution, the total time equals the slowest query: 50ms. The --task-workers flag controls how many concurrent operations can run simultaneously across all request workers.
Concurrent tasks, ticks, intervals, Octane cache, and Swoole tables require the Swoole extension. FrankenPHP and RoadRunner do not support these features. Evaluate whether your application needs them before committing to Swoole's operational complexity.
Memory Leak Prevention: Writing Stateless Code
Octane's performance comes from persistent state, which creates the primary challenge: code that worked fine under PHP-FPM can leak memory catastrophically under Octane. The application instance survives across requests, so static properties, singletons, and global state accumulate.
Three patterns cause most memory leaks:
Static arrays that grow unbounded:
// 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 holding request-specific data:
// 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());
});Container and request injection in constructors:
// 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
}
}The global helpers app(), request(), and config() always return current values. Using them instead of constructor injection sidesteps most singleton-related leaks.
Production Deployment Patterns
Octane servers need process supervision to restart on crashes. Supervisor handles this reliably:
; /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=3600For deployments, Octane workers must reload to pick up new code. The octane:reload command handles this gracefully, waiting for in-flight requests to complete before recycling workers:
# In deployment script (after git pull, composer install, etc.)
php artisan octane:reloadNginx sits in front of Octane to serve static assets and terminate SSL:
# /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;
}
}The keepalive directive maintains persistent connections between Nginx and Octane, reducing TCP handshake overhead for proxied requests.
Ready to ace your Laravel interviews?
Practice with our interactive simulators, flashcards, and technical tests.
Server Selection Decision Tree
Choosing between FrankenPHP, RoadRunner, and Swoole depends on operational constraints and application requirements:
| Requirement | Recommended Server |
|---|---|
| Simplest setup, containerized deployment | FrankenPHP |
| HTTP/3 support out of the box | FrankenPHP |
| No PHP extension dependencies | FrankenPHP or RoadRunner |
| Maximum process isolation | RoadRunner |
| Established PHP-FPM infrastructure | RoadRunner |
| Concurrent I/O within requests | Swoole |
| In-memory caching (2M ops/sec) | Swoole |
| Ticks, intervals, background tasks | Swoole |
| Swoole tables for shared state | Swoole |
For teams starting new projects in 2026, FrankenPHP provides the best balance of simplicity and capability. Migrating existing applications works best with RoadRunner due to its process isolation and familiar deployment model. Swoole suits high-throughput APIs that benefit from coroutine concurrency and require Swoole-specific features.
Interview Questions on Laravel Octane
Technical interviews increasingly cover Octane as high-traffic Laravel applications adopt it. Common questions and what interviewers look for:
"How does Octane achieve its performance gains?"
The expected answer: Octane boots Laravel once per worker and reuses that instance across requests, eliminating the 15-40ms bootstrap cost on each request. Candidates should mention that service providers' register and boot methods run once per worker, not per request.
"What causes memory leaks in Octane applications?"
Strong answers identify three sources: static arrays that accumulate data, singletons capturing request-specific state, and constructor injection of the container or request objects. Candidates should explain that the fix involves using closures for deferred resolution or passing data through method parameters.
"When would you choose Swoole over RoadRunner?"
Swoole's advantages are coroutine concurrency via Octane::concurrently(), the high-speed Octane cache, and features like ticks and intervals. The tradeoff is operational complexity: Swoole requires managing a PECL extension, while RoadRunner ships as a Go binary. For CRUD applications without concurrent I/O needs, RoadRunner is simpler to operate. For more Laravel interview preparation, see PHP Laravel Developer Interview Questions 2026.
"How do you handle deployments with Octane?"
The deployment sequence: stop accepting new connections, wait for in-flight requests to complete, reload workers with new code. The php artisan octane:reload command handles this. Candidates should mention Supervisor or systemd for process supervision and Nginx or Caddy as a reverse proxy.
Monitoring and Profiling Octane Applications
Memory monitoring becomes critical under Octane. Workers persist, so memory growth indicates leaks rather than normal request overhead. Laravel Pulse and Telescope both work with Octane and provide request-level profiling.
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),
]);
}
});
}This event listener logs requests that increase memory by more than 1MB, helping identify endpoints that need optimization. For production monitoring, external APM tools like Datadog or New Relic provide worker-level visibility.
Integrating Octane with Queues and Horizon
Octane handles HTTP requests; Laravel Horizon manages queue workers. The two operate independently and complement each other. Heavy processing belongs in queued jobs, keeping Octane workers responsive.
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']);
}
}For related patterns on background processing, see Laravel Queues and Jobs: Asynchronous Architecture and Laravel Middleware Deep Dive.
What to Remember About Laravel Octane in 2026
- Octane 2.19 supports three servers: FrankenPHP (simplest), RoadRunner (most stable), and Swoole (most features)
- Performance gains come from eliminating per-request bootstrap; expect 2x to 4x throughput improvement
- Memory leaks occur because worker state persists; avoid static arrays, constructor-injected requests, and singletons with request data
- Use
--max-requests=500to force worker recycling before memory pressure builds - Swoole-only features include
Octane::concurrently(), ticks, intervals, and the Octane cache - Deploy behind Nginx or Caddy with Supervisor for process management
- Profile memory growth per request; deltas over 1MB indicate potential leaks
- Queue heavy work through Horizon; keep Octane workers handling fast HTTP responses
Start practicing!
Test your knowledge with our interview simulators and technical tests.
Can you spot the bug in Laravel?
One real snippet, one hidden bug, one attempt a day. No account needed to try.

Written by
Anthony Fillion-MailletFounder of SharpSkill
Full-stack developer for over 10 years. Runs SharpSkill and answers for everything published here.
Updated on September 11, 2026
Tags
Share
Related articles

PHP Laravel Developer Interview Questions 2026: Complete Preparation Guide
Master Laravel interview questions for 2026. From Eloquent ORM and Service Container to queues, testing, and Laravel 13 features, prepare for technical interviews with real-world examples.

Laravel Solutions: Advanced Patterns, Debugging and Interview Questions 2026
Master Laravel solutions with advanced architectural patterns, debugging techniques, and interview-ready knowledge. Covers service classes, repository pattern, debugging with Telescope, and common interview questions for Laravel developers.

Laravel 12 in 2026: New Features, Starter Kits and Interview Questions
Laravel 12 brings redesigned starter kits with React 19, Vue 3, Livewire 4, and WorkOS AuthKit. A complete guide covering new features, upgrade path, and key interview questions for 2026.