Summary
Laravel Octane keeps your application booted in memory and serves requests from long-running workers, instead of rebuilding the framework on every request the way PHP-FPM does. In 2026 you can run Octane on three servers: FrankenPHP, RoadRunner and Swoole (or Open Swoole). Each one makes a different trade-off between speed, simplicity, isolation and concurrency.
The short version: FrankenPHP is the easiest default for containers and new projects, RoadRunner is the cautious choice for large existing apps, Swoole pays off only when you actually use its coroutines and concurrent I/O, and plain PHP-FPM is still right for many apps whose slow parts are the database or third-party APIs. This guide explains how each option works, where it wins, the code changes Octane demands, and how to decide for your own product.
Why PHP-FPM leaves performance on the table
With classic PHP-FPM, every request starts from nothing. PHP loads Composer's autoloader, Laravel registers and boots service providers, reads configuration, builds the router and resolves middleware, and only then runs your controller. OPcache removes the cost of compiling PHP files, but it does not remove the cost of booting the framework. On a lean API endpoint, that boot can be a large share of total response time.
Octane changes the model. A worker boots Laravel once, keeps the application in memory and then handles many requests in a loop. Each request gets a fresh, cloned sandbox of the application state, but the expensive bootstrap work is done only once per worker. The result is usually lower latency and more requests per server for the same hardware, which matters when you pay for every extra instance on AWS or Google Cloud.
The trade-off is that your code now lives in a long-running process. State that used to disappear at the end of every request can now leak into the next one. That is the single most important thing to understand before you switch.
The three Octane servers at a glance
FrankenPHP
FrankenPHP is a modern PHP application server written in Go and built on the Caddy web server. The PHP interpreter is embedded in the same process, so there is no proxy hop between the web server and PHP. Because Caddy is built in, you get HTTP/2, HTTP/3, automatic HTTPS and features like early hints and Brotli or Zstandard compression without a separate Nginx layer. When you choose FrankenPHP during octane:install, Octane downloads the binary for you. It also ships as an official Docker image, which makes it a natural fit for Cloud Run, Kubernetes, ECS or Fargate.
RoadRunner
RoadRunner is also written in Go, but it runs as a separate server that manages a pool of ordinary PHP worker processes and talks to them over a fast RPC protocol. Workers are isolated from each other: if one crashes or gets into a bad state, RoadRunner restarts it and the others keep serving. It has been in production use since 2018 and has a mature plugin ecosystem (queues, gRPC, key-value storage, metrics). It is the conservative choice when stability matters more than squeezing out the last millisecond.
Swoole and Open Swoole
Swoole is a C extension installed through PECL. It replaces PHP's usual blocking execution model with an event loop and coroutines, closer to how Node.js works. When code waits on I/O, Swoole can switch to other work on the same worker. Through Octane, Swoole unlocks features the other servers do not have: Octane::concurrently() for running tasks in parallel, ticks and intervals, and the in-memory Octane cache and Swoole tables. The price is that your code and every package you use must be safe to run in that model.
Side-by-side comparison
| Criteria | PHP-FPM | FrankenPHP | RoadRunner | Swoole |
|---|---|---|---|---|
| Framework boot per request | Yes | No | No | No |
| Install | Standard everywhere | Single binary or Docker image | Go binary plus PHP workers | PECL extension |
| Web server needed in front | Nginx or Apache | No (Caddy built in) | Usually a proxy or load balancer | Usually a proxy or load balancer |
| Worker isolation | Per request | Threads in one process | Separate processes | Worker processes with coroutines |
| Concurrent I/O inside a request | No | No | No | Yes |
| Code changes needed | None | Octane-safe state | Octane-safe state | Octane-safe state plus coroutine-safe packages |
| Best fit | DB-bound or low-traffic apps | Containers, new projects, simple stacks | Large existing monoliths | Fan-out APIs, real-time workloads |
What benchmarks really tell you
If you search for Octane benchmarks in 2026 you will find results that contradict each other. One public test on PHP 8.5 put RoadRunner far ahead and Swoole level with PHP-FPM, while another open benchmark of Laravel Octane servers found FrankenPHP had the most stable tail latency and Swoole the highest peak throughput in several workloads. Both can be correct, because results depend heavily on the load tool, HTTP version, worker count, CPU limits and how much of each request is spent waiting on the database.
The practical lesson is simple: benchmark your own endpoints. Pick three or four real routes (a cached read, a database-heavy page, an authenticated API call and one that calls an external service), run them under realistic concurrency with a tool such as k6 or wrk, and compare p95 and p99 latency, throughput and memory per worker. That data is worth more than any headline number.
Getting started with Octane
Installing Octane takes a few minutes. Choosing the server is part of the install step:
composer require laravel/octane
# Pick one: frankenphp, roadrunner or swoole
php artisan octane:install --server=frankenphp
# Local development with automatic reload on file changes
php artisan octane:start --watch
# Production-style start: fixed workers, recycle workers to contain slow leaks
php artisan octane:start --server=frankenphp --host=0.0.0.0 --port=8000 \
--workers=4 --max-requests=500
Start with a worker count close to the number of CPU cores available to the container or VM, then tune it with load tests. More workers than cores rarely helps a CPU-bound app and often makes tail latency worse. The --max-requests option restarts each worker after a set number of requests, which is a cheap safety net against gradual memory growth.
When you deploy new code, workers must be reloaded, because they still hold the old code in memory:
php artisan octane:reload
On a VM, run Octane under a process manager such as Supervisor or systemd. In containers, make Octane the main process of the image and let the orchestrator handle restarts and health checks.
Making your code Octane-safe
Most of the work in an Octane migration is not the server, it is finding code that assumes a fresh process for every request. These are the patterns that cause real bugs.
Singletons that capture the request or config
A singleton resolved once at boot lives for the life of the worker. If it stores the current request, the authenticated user or a config value, every later request sees stale data.
// Risky under Octane: the first request is captured forever
$this->app->singleton(InvoiceService::class, function ($app) {
return new InvoiceService($app['request']);
});
// Safer: resolve the request when it is actually needed
$this->app->singleton(InvoiceService::class, function ($app) {
return new InvoiceService(fn () => $app['request']);
});
// Or use a scoped binding, which is reset for every request
$this->app->scoped(InvoiceService::class, function ($app) {
return new InvoiceService($app['request']);
});
Static properties and growing arrays
Static properties, static caches and arrays that you append to on every request will keep growing until the worker is recycled. Watch for helper classes that memoise results in a static array, and for event listeners that register more listeners each time they run.
class CurrencyRates
{
// This grows forever in a long-running worker
private static array $cache = [];
public static function rate(string $code): float
{
return self::$cache[$code] ??= self::fetch($code);
}
}
Move this kind of cache to Redis, Laravel's cache, or a scoped service that is rebuilt per request.
Third-party packages
Most popular Laravel packages work with Octane today, but older or less maintained ones may keep per-request state in singletons. Run your full test suite under Octane, then load-test with several different users at the same time and check that nobody ever sees another user's data. This one test catches most state-leak bugs before production.
Swoole-specific extras
If you choose Swoole, you can run independent operations in parallel inside one request:
use Laravel\Octane\Facades\Octane;
[$orders, $invoices, $tickets] = Octane::concurrently([
fn () => Order::where('account_id', $id)->latest()->take(10)->get(),
fn () => Invoice::where('account_id', $id)->unpaid()->get(),
fn () => Http::get("https://support.example.com/api/accounts/{$id}/tickets")->json(),
]);
This is where Swoole earns its reputation: a dashboard that calls three slow sources can finish in roughly the time of the slowest one, rather than the sum of all three. If your app does not need this, Swoole's extra complexity is hard to justify.
When to choose each option
Choose FrankenPHP when
- You deploy with Docker, Kubernetes, Cloud Run, ECS or Fargate and want one process per container.
- You are starting a new Laravel project or your team is new to Octane.
- You want HTTP/2, HTTP/3 and automatic HTTPS without maintaining Nginx.
Choose RoadRunner when
- You are moving a large, revenue-critical monolith and want process isolation while you find state bugs.
- You already use, or plan to use, RoadRunner plugins such as gRPC or its job queues.
- Your operations team prefers a long-established, well-documented runtime.
Choose Swoole when
- Requests fan out to several slow services and you will actually use
Octane::concurrently(). - You need very fast in-memory data shared across workers through Swoole tables or the Octane cache.
- Your team is comfortable auditing packages for coroutine safety.
Stay on PHP-FPM when
- Profiling shows most time is spent in SQL queries, external APIs or file I/O, not in framework boot.
- Traffic is low enough that one or two small servers cope easily.
- You rely on packages that are known to misbehave in long-running processes.
Octane is not a fix for a slow query. If an endpoint takes 400 ms and 350 ms of that is the database, fix indexes, N+1 queries and caching first. Our guide to Laravel REST API performance for high-usage applications covers those quick wins, and they usually come before Octane.
Octane and cloud cost
The business case for Octane is mostly about infrastructure cost and user experience. If each server handles more requests at lower latency, you can run fewer or smaller instances, or absorb traffic spikes without scaling out as aggressively. On autoscaling platforms, lower CPU per request can also mean fewer scale-out events. The exact saving depends entirely on your workload, so measure requests per instance before and after the change rather than relying on generic estimates. If cloud spend is a concern, our cloud cost optimisation service looks at runtime choices like this alongside database sizing and caching.
For container platforms, also read our comparison of Cloud Run vs GKE Autopilot vs App Engine and AWS Lambda vs Fargate vs EC2. A FrankenPHP image works well on both Cloud Run and Fargate.
A safe migration plan
- Profile first. Confirm that framework boot is a meaningful share of response time on your busiest routes.
- Audit state. Search service providers for singletons that touch the request, auth or config, and search the codebase for static caches.
- Run tests under Octane. Run the feature test suite against an Octane server in CI, not only against PHP-FPM.
- Load-test with multiple users. Check for data leaking between users, memory growth per worker and p99 latency.
- Roll out gradually. Send a small share of traffic to Octane instances behind the load balancer, watch errors and memory, then increase.
- Keep a rollback. Keep the PHP-FPM setup deployable until Octane has run cleanly through a few release cycles.
FAQ
Is Laravel Octane production-ready in 2026?
Yes. Octane is a first-party Laravel package with official support for FrankenPHP, RoadRunner and Swoole. The risk is not Octane itself but application code that keeps per-request state in long-lived objects.
Which Octane server is fastest?
It depends on the workload. Public 2026 benchmarks disagree: some favour RoadRunner, others show FrankenPHP with the most stable latency and Swoole with the highest peak throughput. Benchmark your own routes before deciding.
Do I still need Nginx with Octane?
With FrankenPHP you often do not, because Caddy is built in. With RoadRunner or Swoole, most teams keep Nginx or a cloud load balancer in front for TLS, static files and routing.
Will Octane make a slow, database-heavy app fast?
Usually not by much. Octane removes framework boot time. If most of the time is spent in queries or external APIs, fix those first with indexing, eager loading and caching.
Can I use Octane with Laravel queues and Horizon?
Yes. Queue workers are already long-running processes and are managed separately from Octane. Octane only changes how HTTP requests are served.
How long does an Octane migration take?
For a small, well-tested app it can be a few days. For a large monolith with many packages and custom singletons, plan for an audit, CI changes and a gradual rollout over several weeks.
Written by
Prakash Tank
Founder & Full-Stack Architect, DayDecoder
Prakash started as a certified Laravel developer and grew into a full-stack architect who owns a project end to end: requirements, architecture, development, deployment, and support. He designs event-driven systems with...
More about Prakash