Laravel Octane is exciting because it changes the usual PHP rhythm.
Traditional PHP is like a hotel room cleaned after every guest. Request comes in, framework boots, response goes out, memory disappears. Nice and predictable.
Laravel Octane keeps your application in memory using servers like Swoole, Open Swoole, RoadRunner, or FrankenPHP. That can make requests faster because Laravel doesn't need to fully boot from scratch every time. But it also means your app starts behaving more like a long-running process.
And that changes the rules.
Why Octane Feels Fast
The classic Laravel request lifecycle does a lot of work. It loads configuration, resolves services, boots providers, prepares the container, and handles the request.
That's fine for many apps. PHP's request-per-process model is simple and safe.
Octane changes the model by booting the application once and reusing it for many requests. Imagine cooking in a restaurant where the kitchen stays open instead of being rebuilt for every order. Faster? Absolutely. But now you must keep the kitchen clean between customers.
What Octane Can Improve
- Framework boot overhead — Reusing the application reduces repeated startup work.
- High request throughput — Long-running workers can serve more requests under the right workload.
- Latency-sensitive endpoints — Small API responses may benefit when boot cost matters.
- Concurrent task support — Octane can help with some concurrent execution patterns depending on the server.
- Production efficiency — Fewer repeated boot cycles can reduce waste.
But Octane is not a magic "make slow code fast" button. If your endpoint is slow because of database queries, missing indexes, external APIs, or huge payloads, Octane won't fix the real bottleneck.
Putting Octane on bad queries is like installing racing tires on a car with no engine oil.
The Big Mental Shift: State Can Leak
In traditional PHP, request state usually dies at the end of the request. With Octane, workers stay alive.
That means static properties, singletons, cached service state, and global variables can accidentally carry data from one request to another.
This example is dangerous:
class RequestTracker
{
public static array $visitedUrls = [];
public function track(string $url): void
{
self::$visitedUrls[] = $url;
}
}
Under a normal request lifecycle, this might look harmless. Under Octane, that static array can keep growing across requests handled by the same worker.
The fix is not "never use memory." The fix is understanding what memory is request-specific and what memory is safe to share.
State That Deserves Suspicion
- Static arrays — They can grow forever if you append request data.
- Mutable singletons — A shared service can accidentally store user-specific information.
- Cached authenticated users — Never keep request identity in long-lived service state.
- Large temporary payloads — Memory may not be released how you expect.
- Third-party SDK clients — Some clients keep internal state and need careful handling.
Octane turns your app from a disposable coffee cup into a reusable bottle. Better performance, but now you must wash it.
Shared State Is Not Always Bad
Some state is safe and useful to keep around.
Configuration, route definitions, compiled containers, and reusable read-only metadata can benefit from persistence. Problems appear when shared state becomes user-specific or request-specific.
This service is safer because it holds immutable configuration:
class TaxCalculator
{
public function __construct(
private readonly string $defaultRegion
) {}
public function estimate(int $amountCents): int
{
return match ($this->defaultRegion) {
'US' => (int) round($amountCents * 0.08),
default => 0,
};
}
}
The service does not collect request data. It does not remember users. It behaves the same across requests.
The rule is simple: shared read-only state is usually fine; shared mutable request state is a loaded trap.
Database Connections Live Longer Too
This catches teams more often than the static-property trap.
In a classic PHP-FPM setup, each request opens its own database connection (or pulls from a connection pool that exists outside PHP). Octane keeps the worker's PDO instance alive across requests. That has two consequences worth knowing about.
First, transaction state can leak. If a request begins a transaction and crashes before commit/rollback, the next request inheriting that connection can find itself inside an open transaction. Always make sure you commit or roll back. Wrap risky paths in try/finally.
Second, stale connections happen. MySQL's wait_timeout will close a connection that has been idle too long. The next request reuses the dead handle and fails with "MySQL server has gone away." The fix is configuration, not code:
'mysql' => [
// ...
'options' => [
PDO::ATTR_PERSISTENT => false,
],
'sticky' => true,
],
'octane' => [
// reset connections between requests if you've seen stale-connection errors
'flush' => [
'database',
],
],
Octane lets you flush certain services between requests. Database connections are a common candidate when your traffic pattern has long quiet periods. The trade-off is minor — you lose a tiny bit of the speedup — but you gain predictability.
A useful habit: explicitly close connections in register_shutdown_function if your app has long-running batch routes, and watch for "MySQL has gone away" in production logs as a signal that flush settings need tuning.
Octane Does Not Replace Queues, Cache, Or Indexes
A common mistake is treating Octane as a performance cure-all.
It isn't.
If an endpoint makes 40 queries, Octane may reduce framework overhead, but it still makes 40 queries. If your API waits two seconds for a payment provider, Octane does not make the provider faster. If your response serializes a huge collection, you still pay the serialization cost.
Before reaching for Octane, check:
- Database queries — Are you eager loading correctly?
- Indexes — Are filters and sorts supported by indexes?
- External services — Can slow work move to jobs?
- Caching — Are expensive repeated calculations cached safely?
- Payload size — Are you returning too much data?
Octane is like upgrading the highway. It helps traffic move, but it won't fix a broken truck blocking the lane.
Concurrency Primitives And Tick Timers
Octane gives you a few tools that don't exist in classic PHP.
Octane::concurrently() runs callbacks in parallel inside the same request. It's useful when a single endpoint needs to fan out to several independent calls — like fetching three different API summaries to compose one response.
use Laravel\Octane\Facades\Octane;
public function show()
{
[$users, $orders, $revenue] = Octane::concurrently([
fn () => $this->stats->users(),
fn () => $this->stats->orders(),
fn () => $this->stats->revenue(),
]);
return [
'users' => $users,
'orders' => $orders,
'revenue' => $revenue,
];
}
Three sequential calls of 200ms each become one parallel call of about 200ms. That's real wall-clock time saved, especially for dashboards.
Octane::tick() runs a callback at a regular interval inside the worker — useful for prewarming caches, refreshing in-memory data, or pinging health checks without spawning a separate process.
These primitives are powerful. They're also easy to abuse. A tick() that hits the database every five seconds can quietly become your slowest endpoint. Use them with the same care as any other long-running concern.
Deployment And Worker Management Matter
Long-running workers need operational care.
You need to restart workers after deployments, monitor memory, handle code reloads correctly, and think about graceful restarts. A bug that leaks memory may not appear in one request, but after 10,000 requests it becomes obvious.
Production Checklist
- Restart workers on deploy — Old workers can keep old code in memory.
php artisan octane:reloadis your friend. - Monitor memory usage — Watch for slow growth over time and set a
--max-requestsceiling. - Avoid request-specific singletons — Resolve request data fresh.
- Test under load — Some issues only appear after repeated requests.
- Keep queues separate — Octane serves HTTP; queues still handle background work.
This is where Octane feels less like classic PHP and more like Node, Go, or Java services. You're responsible for process behavior now.
When Octane Is Worth It
Octane is worth considering when:
- Your app has high request volume — Boot overhead becomes meaningful at scale.
- Your endpoints are already optimized — You've fixed obvious query and cache issues.
- You can monitor production properly — Memory and worker health are visible.
- Your team understands shared state — Everyone knows the new rules.
- Latency matters — API speed is a product concern, not just vanity.
It may not be worth it when your app is small, mostly admin CRUD, bottlenecked by the database, or maintained by a team not ready for long-running PHP behavior.
Final Tips
The first time you debug a long-running PHP worker, it feels weird. You're used to PHP forgetting everything after each request. Then Octane says, "Actually, I remember." That can be powerful and slightly terrifying.
Going forward, treat Octane as an architectural choice, not a composer package you install casually on Friday afternoon. Measure first, understand state, then decide.
When PHP starts feeling long-running, performance gets better — but discipline matters more. Good luck tuning it 🚀




