[ GUIDE ]

How to monitor Laravel Octane in production

Octane workers persist across requests — which changes what you monitor and how. Memory drift, stale singletons, and worker health, with FrankenPHP/Swoole/RoadRunner specifics.

QUICK ANSWER

How do I monitor Laravel Octane?

Monitor three things PHP-FPM doesn't force you to: (1) per-worker memory growth across requests — look for upward drift that indicates a leak, (2) worker crash rate — frequent crashes signal Octane-incompatible code, (3) request latency distribution to confirm the worker boot savings are materializing. Laravel's nightwatch package is Octane-compatible; NightOwl records per-request memory, duration, and exceptions with worker PID context.

Updated · 2026-04-13

What makes Octane monitoring different

In PHP-FPM, every request starts in a fresh process. In Octane, a worker boots once and handles thousands of requests. Three implications:

  1. Memory accumulates. Leaks that were invisible in PHP-FPM (the process died) now grow until max_requests recycles the worker — or it crashes.
  2. Singletons carry state. A service bound as a singleton keeps its properties across every request the worker handles. Good for perf, dangerous if you stash per-request data there.
  3. Worker crashes cascade. A fatal error in a request kills the worker, affecting every in-flight request on that worker. Octane restarts it, but the blast radius is worker-sized, not request-sized.

Detecting memory leaks

Record memory at request start AND end, tagged by worker PID:

app/Http/Middleware/OctaneMemoryProbe.php

php
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class OctaneMemoryProbe
{
    public function handle(Request $request, Closure $next)
    {
        $startMb = memory_get_usage(true) / 1024 / 1024;
        $response = $next($request);
        $endMb = memory_get_usage(true) / 1024 / 1024;

        Log::channel('octane')->info('Request memory', [
            'pid' => getmypid(),
            'route' => $request->route()?->uri(),
            'start_mb' => round($startMb, 2),
            'end_mb' => round($endMb, 2),
            'delta_mb' => round($endMb - $startMb, 2),
        ]);

        return $response;
    }
}

Filter by PID. A healthy worker's delta hovers around zero; a leaky worker's end-of-request memory creeps upward over time. When you spot a leaking worker, the most recent requests before the growth spike usually hold the culprit.

Octane-safe patterns

Don't stash per-request data in singletons

php
// Bad — currentUser persists across requests on the same worker
$this->app->singleton(UserContext::class, function ($app) {
    return new UserContext($app['auth']->user());
});

// Good — bind fresh per request
$this->app->bind(UserContext::class, function ($app) {
    return new UserContext($app['auth']->user());
});

Use listeners for per-request reset

php
// app/Providers/OctaneServiceProvider.php
use Laravel\Octane\Events\RequestReceived;
use Laravel\Octane\Events\RequestTerminated;

Event::listen(function (RequestTerminated $event) {
    // Clear anything that shouldn't carry between requests
    app(MyStatefulThing::class)->reset();
});

Driver-specific notes

FrankenPHP

Single binary. Built-in HTTPS. The simplest operational story. Worker mode shares most Octane semantics. Supports HTTP/3 which matters for latency-sensitive APIs.

Swoole

Most performant under high concurrency. Adds coroutine support — Concurrently::run([...]) fans out I/O-bound work. Requires the swoole PHP extension; more complex operational story than FrankenPHP.

RoadRunner

Go-based worker manager. Stable, mature. Less performant than Swoole at very high concurrency but easier to debug. Laravel Vapor uses it under the hood.

What to alert on

  • Worker crashes — alert on crash rate > 0 sustained. Crashes usually mean Octane-incompatible code.
  • Memory growth — per-worker delta trending upward over 100+ requests.
  • Request latency regression — if p95 on Octane looks like PHP-FPM, something's wrong with your deploy.
  • max_requests restart rate spike — if workers recycle far more often than max_requests suggests, something is OOM-killing them.

THE EASY WAY

NightOwl runs on the same laravel/nightwatch package Octane apps already use

Octane support comes from the underlying laravel/nightwatch instrumentation, which is designed to work with FrankenPHP / Swoole / RoadRunner persistent workers. NightOwl consumes that data and shows per-request memory peak (peak_memory_usage), duration, exceptions, and queries — sort the Requests page by memory to spot leaky workers as they grow.

bash
composer require nightowl/agent
php artisan nightowl:install

From $5/month flat. Data in your PostgreSQL.

Frequently asked questions

What's different about monitoring Laravel Octane vs PHP-FPM?

Octane keeps workers alive across requests — instead of bootstrapping Laravel per request, a worker boots once and handles thousands. That changes three things: memory accumulates across requests (watch for leaks), singletons carry state between requests (watch for stale data), and worker crashes affect every request on that worker (watch for uncaught exceptions).

Which Octane driver should I use: Swoole, RoadRunner, or FrankenPHP?

FrankenPHP is the newest (shipped 2023) and simplest — single binary, no separate app server. Swoole is the most performant at high concurrency, with the largest Octane-specific feature surface (coroutines, concurrent tasks). RoadRunner is mature and stable, with Go-based worker management. For new projects in 2026, FrankenPHP is often the pragmatic pick; for maximum performance, Swoole; for Laravel Vapor, RoadRunner.

How do I detect memory leaks in Octane?

Compare memory_get_usage() at request start vs end within a single worker. A persistent upward drift over many requests indicates a leak. Log worker PID alongside memory and filter by PID to see per-worker growth. Common leak sources: static caches in service classes, event listeners not cleaning up, Eloquent models retained in a container-bound service.

What's the max_requests setting in Octane?

Octane recycles workers after processing a configurable number of requests (default varies by driver, typically 500-1000). The intent is safety net — even if you have a slow leak, periodic restarts bound its impact. Set it low enough that a leak doesn't crash before recycle, high enough that the bootstrap overhead doesn't dominate.

Should I use Octane in production?

Octane meaningfully reduces per-request latency (often 2-5x faster) and is stable in production. The tradeoffs are: (1) code must be Octane-safe — no relying on per-request state in singletons, no leaked globals, (2) deploy workflow must restart workers (octane:reload), (3) monitoring needs to account for persistent workers. Worth it for latency-sensitive apps; overkill for low-traffic admin panels.

How do I monitor Octane worker health?

Three signals: (1) worker crashes — Octane restarts crashed workers automatically, but frequent crashes signal an Octane-incompatible bug, (2) memory per worker over time — should be stable-ish, (3) request latency distribution — Octane workers should be substantially faster than PHP-FPM; if they're not, something's misconfigured. All three are visible in APMs that record per-request data.

Does Octane break Laravel monitoring tools?

Some — tools that assume fresh-per-request bootstrap can break. The Nightwatch package (used by NightOwl and Nightwatch Cloud) is Octane-compatible. Telescope needs the octane:telescope service binding. Generic APMs using request-start hooks may miss some spans if the hook doesn't fire on persistent workers. Check your tool's Octane docs before deploying.

What's the difference between Octane's concurrent tasks and regular queued jobs?

Octane's Concurrently facade (on Swoole) runs closures concurrently within a single request using coroutines — useful for parallelizing I/O-bound work inside one HTTP response. Queued jobs are for work that should happen outside the request entirely. Concurrent tasks are faster for in-request fan-out; queued jobs are for anything the user shouldn't wait on.

PRICING

Flat pricing. No event caps. No per-seat fees.

14-day free trial, no credit card. Your PostgreSQL, your data.

HOBBY

$5 /month

1 app · 14 days lookback · all Laravel events

TEAM

$15 /month

Up to 3 connected apps · unlimited environments · all Laravel events

AGENCY

$69 /month

Unlimited apps · unlimited agent instances · same flat rate at any traffic

Related