Laravel Octane helps when framework bootstrap is a meaningful share of your response time, and it does almost nothing when your requests are dominated by database queries, external API calls, or unindexed tables. That is the short answer. It is also the part most benchmark posts leave out.

You have seen the charts. Requests per second triple, the green bar towers over the grey one, and someone asks in standup why you are not running Octane yet. Those charts are usually honest. They are also usually measured against a route that renders a static view with no database work, which is nothing like the endpoint that is actually slow in your app.

This guide hands you the decision instead of the hype. You will get a clear model of what Octane changes in the request lifecycle, a way to tell in one afternoon whether your app fits the profile that benefits, the specific class of bugs that long-lived workers introduce, and what to fix instead when the numbers say Octane is not your problem.

Key TakeawaysLaravel Octane boots your application once and keeps it in memory, which removes framework bootstrap from every request. It does not make your queries, your API calls, or your own code any faster.The gain is proportional. If bootstrap is 40% of your response time you have a lot to win; if it is 5%, you are trading a real state-safety risk for a rounding error.Octane runs on Swoole, RoadRunner, or FrankenPHP. Swoole adds concurrent tasks, ticks, and an in-memory cache table, while FrankenPHP ships as a single binary with worker mode and automatic HTTPS.The real migration cost is discipline. Singletons that capture the request, static properties that grow, and packages that hold global state all become cross-request bugs.Profile a real endpoint with real data before you switch. The measurement takes an afternoon and it ends the argument.

What Laravel Octane actually changes

Laravel Octane boots your application once, keeps it in memory, and hands each incoming request to an already warm app. That is the whole idea. Everything else follows from it.

Under PHP-FPM, every request starts from nothing. PHP loads the autoloader, builds the container, registers and boots every service provider, parses config and routes, runs your code, sends the response, then throws the entire application away. That shared-nothing model is why PHP is famously hard to leak memory in, and it is also pure overhead paid on every single request.

Octane replaces the cycle with long-lived worker processes. Bootstrap happens once per worker, then the worker sits in a loop serving requests against the same container. Your controllers, jobs, and Blade views do not change. What changes is the lifetime of everything you put in memory.

Getting it running takes three commands.

composer require laravel/octane
php artisan octane:install
php artisan octane:start --server=frankenphp --workers=4 --max-requests=500

The three application servers behind Octane

  • Swoole is a PHP extension. It gives you the most features, including concurrent tasks, interval ticks, and in-memory tables that Octane exposes as a cache driver. The cost is a PECL extension on every machine that runs your app.

  • RoadRunner is a Go binary that manages PHP worker processes. Nothing to compile into PHP itself, and it behaves predictably inside containers.

  • FrankenPHP is a PHP application server built on Caddy, shipped as a single binary with worker mode, HTTP/2, HTTP/3, and automatic HTTPS. For most teams it is now the easiest of the three to deploy.

Octane changes how your app is served, not what your app does. If the app itself is still being built, you can generate a production-ready Laravel app from a prompt and pick the runtime later, once you have real endpoints and real data to measure.

Octane vs PHP-FPM and where your response time goes

The Octane vs PHP-FPM argument is really an argument about proportions. Split a request into four buckets and the answer falls out on its own.

  • Connection work. TLS, proxying, keep-alive. Octane does not touch this.

  • Framework bootstrap. Autoloader, container, service providers, config, routes. This is the only bucket Octane removes.

  • Your code. Queries, cache reads, outbound HTTP, serialization. Unchanged.

  • Response and teardown. Small, and mostly unchanged.

Suppose a reporting endpoint runs at 380 ms on the median, and a profiler attributes 25 ms of that to bootstrap. Octane can win you most of the 25 ms, roughly 6% of the total. The missing composite index on that same report is probably worth 200 ms. One of those two jobs is clearly first.

Run the same arithmetic on a thin JSON endpoint that reads one cached row and returns it. If the handler costs 8 ms and bootstrap costs 20 ms, your app is mostly framework, and Octane changes the shape of the whole tier. Same tool, opposite verdict.

Aspect

PHP-FPM

Laravel Octane

Process model

Request handled, then discarded

Long-lived workers reused across requests

Framework bootstrap

Paid on every request

Paid once per worker

Memory

Reclaimed at the end of each request

Held until the worker restarts

State leaks

Structurally almost impossible

The default failure mode

Deploys

New code serves the next request

Live only after a worker reload

Best fit

Mixed workloads, low to moderate traffic

Thin handlers, high request rates

When Laravel Octane helps

Laravel Octane performance gains are proportional, so the apps that win are the ones where bootstrap is a large slice of a small number. Four profiles come up again and again.

  • Thin, high-traffic API endpoints. Token auth, one indexed query, a JSON resource, done. Bootstrap can easily outweigh the handler.

  • Apps with a heavy boot. Dozens of packages, large config, long route files, and providers that do real work on every boot. Paying that once per worker is a genuine win.

  • Endpoints that fan out to several services. A Laravel Octane Swoole setup adds two things the other servers do not have, concurrent tasks and interval ticks.

  • Read-heavy routes with a hot dataset. Swoole tables give you a per-server cache measured in microseconds, with no network hop.

Concurrent tasks are the most underrated item on that list. When a dashboard needs three independent queries, or two outbound API calls that do not depend on each other, you can run them at the same time instead of in sequence.

use App\Models\Invoice;
use App\Models\Subscription;
use Laravel\Octane\Facades\Octane;

[$invoices, $subscriptions] = Octane::concurrently([
 fn () => Invoice::query()->pending()->get(),
 fn () => Subscription::query()->active()->get(),
]);

That call works on Swoole only, and it runs inside task workers, so treat it as a targeted tool rather than a default. It fits aggregation endpoints and the tenant-scoped reporting screens that multi-tenant SaaS apps tend to be full of.

When Laravel Octane does not help

Most Laravel apps are slow for reasons Octane cannot reach. Be honest about which list you are on.

  • Database-bound requests. An N+1 loop, a missing index, or an unbounded eager load will dominate any bootstrap saving. Laravel's eager loading documentation is a better afternoon than any runtime swap.

  • Low-traffic apps. If your peak is a few requests a second, you are adding a process supervisor, a reload step, and a new class of bug to save milliseconds nobody is waiting on.

  • Memory-hungry work. Imports, PDF generation, and image processing inflate worker memory that no longer gets handed back at the end of the request.

  • Queue-bound throughput. If the complaint is that jobs run late, Octane changes nothing. Queue workers are already long-lived processes. Add workers or fix the job.

  • Codebases with unaudited global state. Old packages, static registries, and singletons that hold request data all turn into intermittent bugs the day you switch.

A small agency team once put Octane in front of a client dashboard because a benchmark post looked convincing. The median barely moved, since nearly all of it was a report query joining four tables without a covering index. Two weeks later a support ticket arrived, a user seeing another tenant's company name in the header. The cause was a singleton that resolved the authenticated user in its constructor and then lived for the life of the worker. They rolled back, added the index, and got a far bigger improvement with none of the risk.

The state bugs Octane introduces and how to avoid them

Under PHP-FPM, leaking state between requests is close to impossible. Under Octane it is the default failure mode, and the bugs are the worst kind, intermittent, user-specific, and invisible in local testing where a single worker serves a single developer.

Singletons that capture the request

This is the one that bites everybody. A singleton resolves once, so whatever it read from the request or the authenticated user on that first call is what it keeps forever.

use App\Services\TenantContext;
use Illuminate\Contracts\Foundation\Application;

// Resolved once, then frozen for the life of the worker.
$this->app->singleton(TenantContext::class, function (Application $app) {
 return new TenantContext($app['request']->user()->tenant);
});

Bind it per request instead, or hand the object a closure it can call when it actually needs the value.

// Resolved per request, so the tenant is always the current one.
$this->app->bind(TenantContext::class, function (Application $app) {
 return new TenantContext(fn () => $app['request']->user()?->tenant);
});

Static properties that keep growing

A static array used as a per-request memo is harmless under PHP-FPM because the process dies with the response. Under Octane the same array becomes an unbounded cache that grows until the worker is recycled. Move it to the cache layer, or clear it explicitly when the request terminates.

Runtime mutation that outlives the request

Anything you change on a shared service persists for the rest of that worker's life. Config writes, locale changes, frozen test times, forced root URLs, and package state stored in statics all belong on the audit list. Octane sandboxes the container for each request, which catches part of this, but anything already resolved into a shared object is beyond its reach. If middleware sets it, something has to unset it.

Guardrails that cost you nothing

  • Recycle workers with --max-requests so a slow leak never becomes an outage.

  • Use the warm and flush arrays in config/octane.php to control which bindings survive a request.

  • Prefer bind() over singleton() for anything touching the request, the user, or the current tenant.

  • Run the full test suite plus a soak test against a single worker before you trust it.

  • Alert on resident memory growth per worker, not just on host totals.

Auditing an older codebase for this is tedious, and it is exactly the pass where AI refactoring tools for PHP and Laravel earn their keep.

New services are cheaper to keep stateless than to fix later. StackMivo's Laravel-native intelligence generates standard artifacts, models, migrations, FormRequests, Policies, and Pest tests, so there is less hand-rolled global state to audit in the first place.

How to test Laravel Octane on your app in one afternoon

  1. Pick two endpoints. Your highest-traffic route and your slowest p95 route. Nothing else matters yet.

  2. Record a baseline with real data. Use your APM or a timing middleware writing to a log. Capture p50 and p95 under realistic concurrency, not a single curl.

  3. Measure the bootstrap slice. Compare a route that returns a bare string against the real endpoint, or read the framework boot span in your tracing tool. That gap is your theoretical ceiling.

  4. Run the suite against Octane. Start one worker locally and run every test plus a manual pass. A single worker makes state leaks obvious fast.

  5. Soak it. Send a few thousand requests at that worker and watch memory. Flat is good. A staircase is a leak.

  6. Compare like for like. Same data, same concurrency, same machine, then decide with both numbers in front of you.

php artisan octane:start --server=frankenphp --workers=1 --max-requests=1000
php artisan octane:status
php artisan octane:reload

A team lead at a mid-size SaaS ran exactly this sequence before committing to anything. Bootstrap turned out to be roughly a fifth of the median on their token-authenticated API routes and a rounding error on the reporting screens, which were all query-bound. They enabled Octane on the API tier only, left the admin panel on PHP-FPM, and spent the rest of the sprint on indexes. Two runtimes, one pipeline, no argument in code review.

Read the official Laravel Octane documentation end to end before you start. The dependency injection caveats in particular are short, and they will save you a production incident.

Deploying Octane without breaking your pipeline

New code does not go live until the workers restart. That single fact breaks more Octane deployments than any state bug, so the reload belongs at the end of every deploy script.

php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan octane:reload

Run Octane under a process supervisor with a proxy in front, keep the reload as the final step, and make sure your rollback path restarts workers too. Queue workers follow the same rule and still need their own restart.

If you would rather not hand-build that pipeline, StackMivo's one-click deployment pushes to Laravel Cloud, Forge, Ploi, or a plain SSH server and runs migrations for you. When your next Laravel service is ready to be built rather than tuned, Get started free and keep every file it produces.

The verdict on Laravel Octane in 2026

Laravel Octane is a sharp tool with a narrow edge. It deletes framework bootstrap from every request, which is a real cost worth deleting, and it deletes nothing else. Apps with thin handlers and heavy boots gain a lot. Apps waiting on a database, a queue, or a third-party API gain close to nothing while taking on a new category of bug.

Four things to carry out of this guide. Measure your bootstrap slice before anyone argues about runtimes. Treat singletons, static properties, and request-scoped data as the actual migration work. Recycle workers and reload them on every deploy. Pick the server that matches your operational appetite, FrankenPHP for simplicity, Swoole for concurrency, RoadRunner for predictable containers.

If your profile says yes, Octane is one of the cheapest performance wins available to a Laravel team. If it says no, you just avoided a migration and learned precisely which query to fix instead. Either way, the afternoon paid for itself.