
Octane makes Laravel fast by removing the most expensive thing a PHP request does: booting the framework. Under classic PHP-FPM, every request starts from zero — load the autoloader, read the cached config, register every service provider, resolve the container — and throws all of it away when the response is sent. Octane boots the application once, holds it in memory, and feeds it request after request from long-lived workers.
The win is that fixed boot cost, typically tens of milliseconds per request, multiplied across every request you serve. For an API with lean endpoints, that can mean two to three times the throughput on the same VPS. Be realistic about the other side: if an endpoint spends 300 ms in database queries, Octane does not make the queries faster — it shaves the boot overhead and leaves the rest of the profile exactly where it was.
Octane needs PHP 8.1 or newer, a Laravel app already running correctly in production, and root access to install a server runtime and a process manager. Exhaust the ordinary wins first — config, route, and view caching, OPcache, Redis for sessions and cache — because Octane amplifies a well-tuned app rather than rescuing an untuned one.
FrankenPHP is the right default in 2026: it is the option Laravel's own installer suggests first, it ships as a single binary with no PHP extension to compile, and it speaks HTTP/2 and HTTP/3 natively because it is built on Caddy. Choose Swoole instead when you specifically want its extras — concurrent tasks via Octane::concurrently, the Swoole table for shared memory, and interval tickers — and accept that it requires installing and maintaining the swoole PECL extension. RoadRunner sits between them: a Go binary, solid and mature, with fewer Laravel-specific extras than Swoole.
The honest advice: unless you already know you need a Swoole-only feature, start with FrankenPHP. Octane's API is identical across all three, so switching later is a config change, not a rewrite.
Two commands get Octane into the project, and one starts it serving:
octane:install downloads the FrankenPHP binary and writes config/octane.php. Bind to 127.0.0.1, not 0.0.0.0 — Nginx will be the public face. Worker count defaults to one per CPU core, which is the right starting point; raise --workers only after watching real traffic. The --watch flag that reloads workers on file changes is for development only; in production it burns CPU watching a filesystem that never changes between deploys.
Octane replaces PHP-FPM, not your web server. Nginx still terminates TLS, serves static assets directly from public/, and proxies everything else to the Octane port:
The try_files line lets Nginx serve CSS, JS, and images itself — no reason to wake a PHP worker for a static file — and hands everything else to Octane. The X-Forwarded headers matter: trust them in Laravel's TrustProxies middleware or every generated URL will be http and every visitor IP will read 127.0.0.1. This proxy-to-a-port pattern is the same one CloudStick supports as a proxy web application type — nginx-cs handles the vhost, TLS, and headers, and forwards to whatever port your app listens on — so Octane fits a CloudStick server the same way a Node app does, with Supervisord Management keeping the process alive from the dashboard.
octane:start runs in the foreground and dies with your SSH session, so production needs a process manager. A Supervisor program is the standard answer:
Deploys change one habit: because workers hold the old code in memory, every deploy must end with php artisan octane:reload, which gracefully cycles the workers onto the new release — the Octane equivalent of reloading PHP-FPM. Forget it and the site keeps serving last week's code no matter what is on disk.
The application surviving between requests is Octane's superpower and its one real trap. Under PHP-FPM, sloppy state is erased every request for free. Under Octane, a static array that grows on every request is a memory leak, and request data cached in a singleton is a data leak — user A's context served to user B. The classic mistake is injecting the request into a singleton's constructor: the first request gets frozen in and every later request sees it.
The rules that keep you safe: resolve the request inside method calls rather than constructors, avoid unbounded static caches, and read Octane's dependency-injection warnings in the docs before converting an older codebase. As a backstop, config/octane.php lets you cap a worker's lifetime with max_requests so each worker is recycled after, say, 500 requests — leaks get garbage-collected by process death before they can matter.
From here: benchmark a single endpoint before and after with a tool like wrk so you know your actual gain, keep monitoring worker memory for the first week (supervisorctl status and your server dashboard's memory graph tell the story), and only then decide whether to tune worker counts. Octane on a modest VPS, fronted by Nginx and supervised properly, is one of the highest-leverage performance upgrades a Laravel app can get — the setup above is all of it.

