Laravel Octane is not a magic speed switch. It is a different runtime model. The official Laravel 13 documentation describes Octane as serving the application with high-powered servers such as FrankenPHP, Open Swoole, Swoole, and RoadRunner. The app is booted once, kept in memory, and reused for many requests.
The Apr 11, 2026 crawl date is a useful timestamp for this guide. The core advice is still the same: Octane can make Laravel very fast, but only if the code is safe for long-lived workers.
composer require laravel/octane
php artisan octane:install
php artisan octane:startFrankenPHP is a strong modern default because it is easy to deploy and supports a production-friendly HTTP stack. RoadRunner is also solid. Swoole and Open Swoole unlock features such as concurrent tasks, ticks, intervals, and Swoole tables, but they require extension-level setup and more operational care.
Pick the server based on your deployment shape, not on benchmark screenshots. FrankenPHP is usually the easiest starting point for teams that want a modern PHP application server with a smaller operations burden. RoadRunner is a good fit when the team already likes a Go binary in front of PHP workers and wants predictable worker management. Swoole and Open Swoole are attractive when you need coroutine features, task workers, ticks, intervals, and in-memory tables.
FrankenPHP: easiest default for many Laravel teams and clean for container deployment.
RoadRunner: strong worker process model and simple binary-based operations.
Swoole or Open Swoole: deepest feature set, but the team must be comfortable managing PHP extensions and long-lived async behavior.
Local development: use the same driver as staging when possible so worker-state bugs appear before deployment.
In a normal PHP request lifecycle, Laravel boots for each request. Service providers run, configuration loads, routes are prepared, and the container is built. Octane keeps that booted application in memory, then feeds many requests through workers.
That means Octane mostly removes repeated framework boot cost. It does not make a bad query efficient, it does not remove a remote API call, and it does not save a page that renders 400 relationship lookups in a Blade loop. Fix the normal bottlenecks first, then Octane can make the healthy app feel much quicker.
The first Octane rule is to avoid storing request-specific state in long-lived objects. Anything tied to the current request, authenticated user, tenant, locale, or mutable config should be resolved inside the method handling the request, not captured in a singleton constructor.
final class ReportService
{
public function build(Request $request): Report
{
// Use the current request here.
}
}The dangerous version is subtle. A singleton that accepts Request, User, TenantContext, or a mutable settings object in its constructor might work perfectly under PHP-FPM, then leak state between requests under Octane. Prefer method arguments, scoped services, or resolving the current state inside the request method.
final class BadTenantReporter
{
public function __construct(private TenantContext $tenant) {}
public function report(): array
{
return ['tenant' => $this->tenant->id()];
}
}That example is small, but the bug is real: the object can live beyond the request that created it. Under Octane, shared services should either be stateless or explicitly reset between requests.
Laravel Octane restarts workers after a configured number of requests to help limit memory leaks. You can tune this with --max-requests, and you should monitor memory during local and staging tests before assuming production is safe.
php artisan octane:start --workers=4 --max-requests=250Worker count should be chosen with CPU, memory, request profile, and queue pressure in mind. More workers can increase concurrency, but they also multiply memory usage. Start conservatively, run a realistic load test, and watch memory after hundreds or thousands of requests rather than after one happy-path page load.
When using a server that supports them, Octane can run concurrent tasks and recurring ticks. These are useful for independent work such as fetching two slow API endpoints at once, refreshing a small local cache, or warming data that every request needs. They are not a replacement for queues. If the work must survive process restarts, needs retries, or should be audited, put it on a real queue.
use Laravel\Octane\Facades\Octane;
[$github, $stripe] = Octane::concurrently([
fn () => app(GitHubClient::class)->status(),
fn () => app(BillingClient::class)->status(),
]);Octane includes fast in-memory storage features for specific drivers. Treat them as process-local acceleration, not as the source of truth. Redis, the database, and durable external systems still own data that must be consistent across deploys, workers, and machines. In-memory state is excellent for small hot data that can be rebuilt safely.
Login, logout, subscriptions, and tenant switching across repeated requests.
Any feature that reads auth(), request(), config(), or locale data.
Queues, broadcasts, and scheduled tasks that share services with HTTP code.
File uploads and temporary files.
Memory usage after hundreds or thousands of requests.
Reload behavior during deployments.
I also add one explicit Octane smoke test to staging: sign in as one user, hit tenant-specific pages, sign out, sign in as a different user, and repeat the same pages. If anything from the first account appears in the second account, stop the deployment and inspect long-lived services before looking at CSS, caching, or browser state.
Octane is excellent for apps with high request volume, expensive boot time, or a lot of repeated framework work. It is not the first fix for slow SQL, missing indexes, remote API bottlenecks, or N+1 queries. Fix those first, then let Octane amplify a healthy app.