Laravel Octane: State Leakage &amp; Memory Management | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Laravel Octane: Worker Lifecycle, State Leakage, and Memory Management in Production

 Laravel Octane: Worker Lifecycle, State Leakage, and Memory Management in Production
=====================================================================================

 Octane keeps workers alive between requests, which means static state, singleton leaks, and unbounded memory growth can silently corrupt your app. Here is how to reason about and fix each class of problem.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 28 Jun 2026 · Updated 28 Jun 2026 · 4 min read

ShareCopy linkCopied

 ![Laravel Octane: Worker Lifecycle, State Leakage, and Memory Management in Production](https://cdn.msaied.com/309/1ba16118f9db7f27c39cd9f3d693df44.png) 

  On this page +1. [Why Octane Changes Everything About PHP State](#why-octane-changes-everything-about-php-state)
2. [Phase 1 — Boot (once per worker)](#phase-1-boot-once-per-worker)
3. [Phase 2 — Request handling (once per request)](#phase-2-request-handling-once-per-request)
4. [Phase 3 — Cleanup (once per request, after response)](#phase-3-cleanup-once-per-request-after-response)
5. [The Three Categories of State Leakage](#the-three-categories-of-state-leakage)
6. [1. Singleton Accumulation](#1-singleton-accumulation)
7. [2. Static Property Pollution](#2-static-property-pollution)
8. [3. Eloquent Model Event Listeners Stacking](#3-eloquent-model-event-listeners-stacking)
9. [Memory Management Strategies](#memory-management-strategies)
10. [Worker Restart Thresholds](#worker-restart-thresholds)
11. [Monitoring Memory Per Request](#monitoring-memory-per-request)
12. [Identifying Leaks with gc\_collect\_cycles()](#identifying-leaks-with-codegc-collect-cyclescode)
13. [Practical Checklist Before Deploying to Octane](#practical-checklist-before-deploying-to-octane)
14. [Key Takeaways](#key-takeaways)

 Why Octane Changes Everything About PHP State
---------------------------------------------

Traditional PHP-FPM boots the framework on every request and discards all memory when the request ends. Octane inverts this: a single worker process boots Laravel once and handles thousands of requests in sequence. The performance gain is real, but the contract changes completely — **anything you store in a static property, a resolved singleton, or a class-level variable now persists across requests unless you explicitly reset it**.

Understanding the three phases of an Octane worker is the foundation for writing safe code.

### Phase 1 — Boot (once per worker)

The application container is instantiated, all service providers are registered and booted, and every singleton bound in `register()` is resolved on first use. This is the only phase where you pay the full framework bootstrap cost.

### Phase 2 — Request handling (once per request)

Octane clones the base container into a request-scoped sandbox, dispatches the HTTP kernel, and returns the response. The sandbox clone is shallow — it shares the same singleton instances that were resolved during boot unless you tell Octane to flush them.

### Phase 3 — Cleanup (once per request, after response)

Octane fires `RequestHandled`, runs its own flush callbacks, and discards the shallow clone. Singletons that were resolved during boot are **not** discarded here.

---

The Three Categories of State Leakage
-------------------------------------

### 1. Singleton Accumulation

A service that appends to an internal array on every request will grow without bound:

```php
class AuditCollector
{
    private array $events = [];

    public function record(string $event): void
    {
        $this->events[] = $event; // leaks across requests
    }
}

```

Fix: register a flush callback in your service provider.

```php
public function boot(Application $app): void
{
    $app->make(AuditCollector::class); // resolve early so Octane sees it

    Octane::flush(function () use ($app) {
        $app->make(AuditCollector::class)->reset();
    });
}

```

Alternatively, bind the service as `scoped` instead of `singleton`. Scoped bindings are re-resolved on every request inside the sandbox:

```php
$this->app->scoped(AuditCollector::class);

```

### 2. Static Property Pollution

Static properties bypass the container entirely, so `scoped` bindings do not help:

```php
class FeatureFlags
{
    private static array $cache = [];

    public static function get(string $flag): bool
    {
        if (!isset(self::$cache[$flag])) {
            self::$cache[$flag] = DB::table('flags')->where('name', $flag)->value('enabled');
        }
        return (bool) self::$cache[$flag];
    }
}

```

This is fine for immutable config, but dangerous if flags can change mid-deployment. Register an Octane flush callback that calls `self::$cache = []`, or replace the static cache with a request-scoped service.

### 3. Eloquent Model Event Listeners Stacking

If you call `Model::creating(fn () => ...)` inside a request (e.g., in a controller or action), the closure is appended to the model's static dispatcher on every request. After 1 000 requests, 1 000 listeners fire for each `creating` event.

Always register model observers and event listeners in service providers during boot, never inside request handlers.

---

Memory Management Strategies
----------------------------

### Worker Restart Thresholds

Octane's `--max-requests` flag restarts a worker after N requests, reclaiming all memory:

```bash
php artisan octane:start --max-requests=500

```

This is a safety net, not a substitute for fixing leaks. A well-written Octane app should be stable at 10 000+ requests per worker.

### Monitoring Memory Per Request

Add a terminating middleware that logs memory growth:

```php
public function terminate(Request $request, Response $response): void
{
    $mb = round(memory_get_usage(true) / 1_048_576, 2);
    logger()->channel('octane')->debug('memory', ['mb' => $mb, 'path' => $request->path()]);
}

```

A flat line across requests means no leakage. A rising sawtooth means something is accumulating.

### Identifying Leaks with `gc_collect_cycles()`

Circular references between objects are not freed by PHP's reference-counting GC. Octane does not call `gc_collect_cycles()` automatically. Add it to your flush callback for memory-sensitive workers:

```php
Octane::flush(function () {
    gc_collect_cycles();
});

```

---

Practical Checklist Before Deploying to Octane
----------------------------------------------

- Audit every `singleton` binding: does it accumulate state? Switch to `scoped` or add a flush callback.
- Search for `static $` properties in domain code. Decide if they are truly immutable.
- Move all model observer and event listener registration to service providers.
- Set `--max-requests` as a backstop, not a crutch.
- Add per-request memory logging in staging before promoting to production.
- Test with `php artisan octane:start` locally and run your full test suite against the live worker.

---

Key Takeaways
-------------

- Octane workers are long-lived; PHP's request-scoped memory model no longer applies.
- Use `scoped` bindings for services that must be fresh each request.
- Static properties require explicit flush callbacks — the container cannot help you.
- Never register model listeners inside request handlers.
- Memory monitoring and `--max-requests` are complementary, not interchangeable.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [octane](https://www.msaied.com/public/articles?search=octane)
- [performance](https://www.msaied.com/public/articles?search=performance)
- [php](https://www.msaied.com/public/articles?search=php)

 Frequently asked questions 
---------------------------

  What is the difference between `singleton` and `scoped` in an Octane context?A `singleton` is resolved once per worker process and shared across all requests. A `scoped` binding is re-resolved for each request inside Octane's sandbox clone, giving you a fresh instance per request without the overhead of a full container re-boot.

   Does `--max-requests` fix state leakage?It mitigates the impact by periodically recycling workers, but it does not fix the underlying leak. A worker restarted every 500 requests can still serve corrupted state to requests 2 through 499. Proper flush callbacks and scoped bindings are the correct solution.

   Are third-party packages safe to use with Octane?Not automatically. Any package that stores state in static properties or appends to singleton collections can leak. Check the package's Octane compatibility notes, and if absent, audit its service provider and any static usage manually before deploying.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleAdvanced Authorization in Laravel: Gates, Policies, and Response-Based Access Control](https://www.msaied.com/public/articles/advanced-authorization-in-laravel-gates-policies-and-response-based-access-control-1) [Next articlePartial Indexes, Covering Indexes, and Query Plan Analysis in Laravel](https://www.msaied.com/public/articles/partial-indexes-covering-indexes-and-query-plan-analysis-in-laravel)  

   On this page
-------------

1. [Why Octane Changes Everything About PHP State](#why-octane-changes-everything-about-php-state)
2. [Phase 1 — Boot (once per worker)](#phase-1-boot-once-per-worker)
3. [Phase 2 — Request handling (once per request)](#phase-2-request-handling-once-per-request)
4. [Phase 3 — Cleanup (once per request, after response)](#phase-3-cleanup-once-per-request-after-response)
5. [The Three Categories of State Leakage](#the-three-categories-of-state-leakage)
6. [1. Singleton Accumulation](#1-singleton-accumulation)
7. [2. Static Property Pollution](#2-static-property-pollution)
8. [3. Eloquent Model Event Listeners Stacking](#3-eloquent-model-event-listeners-stacking)
9. [Memory Management Strategies](#memory-management-strategies)
10. [Worker Restart Thresholds](#worker-restart-thresholds)
11. [Monitoring Memory Per Request](#monitoring-memory-per-request)
12. [Identifying Leaks with gc\_collect\_cycles()](#identifying-leaks-with-codegc-collect-cyclescode)
13. [Practical Checklist Before Deploying to Octane](#practical-checklist-before-deploying-to-octane)
14. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
