Laravel Octane keeps the application loaded in memory between requests via Swoole, RoadRunner or FrankenPHP, avoiding a fresh container boot and service provider pass on every request. It is not a universal speedup but a tool for one specific bottleneck — the cost of framework bootstrap — and it adds a new class of bugs, covered below.
When Octane helps, and when it is not needed
The gain shows up where framework bootstrap is a noticeable share of request time: short API endpoints with simple business logic and a high request rate. Where the response is dominated by database queries, external APIs or heavy template rendering, bootstrap is already a small share of total time, and Octane changes almost nothing.
Octane needs CPU cores that stay occupied by resident workers, unlike a PHP-FPM pool that can run in dynamic or ondemand mode. A baseline Laravel setup on a VPS with Nginx is covered in the article on Laravel on a VPS — Octane replaces the FPM socket in that scheme with its own server, which Nginx proxies separately.
Choosing a driver: Swoole or RoadRunner
Swoole is a PHP extension that has to be built or installed via PECL; it provides coroutines and a built-in HTTP server. RoadRunner is a separate Go binary that talks to PHP workers over a socket and needs no extension compile, so it is easier to install on a managed VDS. The difference between them and Swoole's coroutine queues is covered in the article on Swoole and RoadRunner.
Octane gains less from OPcache's JIT than people expect: classes are compiled once at worker startup in both modes, FPM and Octane alike, so plain OPcache matters more than JIT. The setup is covered in the article on OPcache and JIT.
How many workers to run
A starting point is to set the worker count to the number of available CPU cores minus one core reserved for the OS and other services: on a 4-core VDS that is 3 workers. Unlike pm.max_children, you cannot simply scale the process count with RAM here — each worker occupies a core for the whole request, instead of waiting on I/O as cheaply as an FPM process.
- synchronous HTTP calls without coroutines block the worker for the whole wait
- heavy synchronous image processing or regular expressions occupy the core entirely
- a blocking session or cache driver slows down every request in the worker
- accumulated memory leaks in static properties force more frequent worker restarts
- a database connection limit per worker caps parallelism
Table: an FPM request versus an Octane worker
The table shows the difference in request lifecycle, not performance numbers — those must be measured under your own load.
| Parameter | PHP-FPM (classic) | Laravel Octane |
|---|---|---|
| Framework boot | on every request | once, at worker startup |
| State reset between requests | full, a new process | manual, via service providers |
| Static properties and singletons | do not accumulate | can accumulate between requests |
| Worker restart | after pm.max_requests | after Octane's max-requests or octane:reload |
Configuration and worker restarts
Octane starts with one command that sets an explicit worker count and a per-worker request limit — by analogy with pm.max_requests this is a hygienic restart that bounds accumulated memory leaks, not a sign of something broken.
php artisan octane:start --server=swoole --host=0.0.0.0 --port=8000 --workers=3 --task-workers=2 --max-requests=500Common mistakes when moving to Octane
The most dangerous mistake is storing request-specific data in a static class property or in a container singleton binding: in FPM this is safe because the process lives for one request, but in Octane that value stays in the worker's memory and can leak into the response of the next, unrelated user.
The second common mistake is exposing Octane's port directly to the internet. Octane's own HTTP server does not replace Nginx: the reverse proxy from the article on Laravel on a VPS is still needed for TLS, request rate limiting and static files.
Frequently asked questions
Do you need to remove PHP-FPM once Octane is set up?
No: FPM usually stays for cron jobs, artisan commands and queues that do not go through Octane's HTTP loop — Octane only replaces web request handling.
How much memory does Octane need compared to FPM?
Each worker keeps the loaded framework in memory for its whole life, so baseline consumption is higher than an idle FPM process; the exact number should be measured the same way, by RSS, as for a regular pool.
Can Octane be enabled without changing any code?
Usually not: the code needs an audit for static state and services that assume the process lives for one request rather than the worker's entire lifetime.