Octane + FrankenPHP: Safe Singletons in Laravel | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Octane + FrankenPHP: Persistent Services, Shared State, and Safe Singletons        On this page       1. [  The Problem Nobody Talks About Until Production ](#the-problem-nobody-talks-about-until-production)
2. [  Why Singletons Become Dangerous ](#why-singletons-become-dangerous)
3. [  The Octane Flush Hook ](#the-octane-flush-hook)
4. [  Designing Octane-Safe Services From the Start ](#designing-octane-safe-services-from-the-start)
5. [  Prefer Immutable Value Objects ](#prefer-immutable-value-objects)
6. [  Inject Request-Scoped Data via Method Arguments ](#inject-request-scoped-data-via-method-arguments)
7. [  Use scoped() for Request-Lifetime Bindings ](#use-codescopedcode-for-request-lifetime-bindings)
8. [  Auditing Existing Code ](#auditing-existing-code)
9. [  Takeaways ](#takeaways)

  ![Laravel Octane + FrankenPHP: Persistent Services, Shared State, and Safe Singletons](https://cdn.msaied.com/612/2bd17daafd0c48a0aa824a6d744fb403.png)

  #laravel   #octane   #frankenphp   #performance   #architecture  

 Laravel Octane + FrankenPHP: Persistent Services, Shared State, and Safe Singletons 
=====================================================================================

     31 Aug 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

  9 sections  

1. [  01   The Problem Nobody Talks About Until Production  ](#the-problem-nobody-talks-about-until-production)
2. [  02   Why Singletons Become Dangerous  ](#why-singletons-become-dangerous)
3. [  03   The Octane Flush Hook  ](#the-octane-flush-hook)
4. [  04   Designing Octane-Safe Services From the Start  ](#designing-octane-safe-services-from-the-start)
5. [  05   Prefer Immutable Value Objects  ](#prefer-immutable-value-objects)
6. [  06   Inject Request-Scoped Data via Method Arguments  ](#inject-request-scoped-data-via-method-arguments)
7. [  07   Use scoped() for Request-Lifetime Bindings  ](#use-codescopedcode-for-request-lifetime-bindings)
8. [  08   Auditing Existing Code  ](#auditing-existing-code)
9. [  09   Takeaways  ](#takeaways)

       The Problem Nobody Talks About Until Production
-----------------------------------------------

When you move a Laravel application from PHP-FPM to Octane (whether backed by Swoole, RoadRunner, or FrankenPHP), the container is **bootstrapped once** and then reused across every request handled by that worker. That single fact invalidates a large class of assumptions most Laravel code silently makes.

This article focuses on the practical patterns you need to write services that behave correctly under persistent workers — and how to audit existing code before you flip the switch.

---

Why Singletons Become Dangerous
-------------------------------

Under PHP-FPM every request gets a fresh process. A singleton bound in a service provider is instantiated once per request, so stale state is impossible — the process dies.

Under Octane a worker process handles thousands of requests. A singleton is instantiated **once for the lifetime of the worker**. Any mutable state it accumulates is visible to every subsequent request.

```php
// Dangerous under Octane
class CartService
{
    private array $items = [];

    public function add(int $productId): void
    {
        $this->items[] = $productId; // leaks across requests!
    }
}

```

Request 1 adds item 42. Request 2 from a completely different user now sees item 42 in `$this->items`.

---

The Octane Flush Hook
---------------------

Octane ships with a `RequestHandled` event and a dedicated `OctaneServiceProvider` hook for resetting state between requests.

```php
use Laravel\Octane\Facades\Octane;

class CartServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Octane::tick('flush-cart', function () {
            $this->app->forgetInstance(CartService::class);
        })->everySeconds(0); // runs between every request
    }
}

```

A cleaner approach is to listen to the `RequestHandled` event directly:

```php
use Laravel\Octane\Events\RequestHandled;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->app['events']->listen(RequestHandled::class, function () {
            $this->app->forgetInstance(CartService::class);
        });
    }
}

```

Now `CartService` is re-instantiated fresh on the next request, eliminating the leak.

---

Designing Octane-Safe Services From the Start
---------------------------------------------

### Prefer Immutable Value Objects

If a service only holds configuration (injected at construction) and never accumulates mutable state, it is safe as a singleton.

```php
final class CurrencyFormatter
{
    public function __construct(
        private readonly string $locale,
        private readonly string $currency,
    ) {}

    public function format(int $cents): string
    {
        return \NumberFormatter::create($this->locale, \NumberFormatter::CURRENCY)
            ->formatCurrency($cents / 100, $this->currency);
    }
}

```

No mutable fields — safe to keep alive for the worker's entire lifetime.

### Inject Request-Scoped Data via Method Arguments

Avoid storing per-request data on the service. Pass it as method arguments instead.

```php
// Bad: stores request context on the singleton
class AuditLogger
{
    private ?User $actor = null;

    public function setActor(User $user): void { $this->actor = $user; }
    public function log(string $event): void { /* uses $this->actor */ }
}

// Good: actor is a method parameter
class AuditLogger
{
    public function log(User $actor, string $event): void { /* stateless */ }
}

```

### Use `scoped()` for Request-Lifetime Bindings

Laravel's `scoped()` binding (introduced for Octane compatibility) registers a singleton that Octane automatically flushes between requests:

```php
$this->app->scoped(CartService::class, fn () => new CartService());

```

This is the idiomatic solution — prefer it over manual `forgetInstance` calls.

---

Auditing Existing Code
----------------------

Before enabling Octane on an existing app, grep for these patterns:

- `$this->app->singleton(...)` — check if the resolved class holds mutable instance state.
- Static properties on any class (`private static array $cache = []`) — these persist across requests **and** across workers sharing the same process.
- Facades that resolve to singletons with state (e.g., a custom `Auth` driver that caches the resolved user).

```bash
grep -rn 'static \$' app/
grep -rn 'singleton(' app/Providers/

```

For each hit, ask: *does this class accumulate state that must be per-request?* If yes, switch to `scoped()` or add a flush listener.

---

Takeaways
---------

- Under Octane/FrankenPHP, `singleton()` bindings live for the worker's lifetime — mutable state leaks across requests.
- Use `scoped()` for any service that holds per-request state; Octane flushes scoped bindings automatically.
- Immutable services (pure config, no mutable fields) are safe as true singletons and benefit from zero re-instantiation cost.
- Audit static properties carefully — they survive even `forgetInstance` calls.
- Listen to `RequestHandled` for custom teardown logic that `scoped()` cannot cover.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-octane-frankenphp-persistent-services-shared-state-and-safe-singletons&text=Laravel+Octane+%2B+FrankenPHP%3A+Persistent+Services%2C+Shared+State%2C+and+Safe+Singletons) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-octane-frankenphp-persistent-services-shared-state-and-safe-singletons) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  What is the difference between `singleton()` and `scoped()` in Laravel Octane?        `singleton()` creates one instance for the entire worker process lifetime. `scoped()` also creates one instance per worker, but Octane automatically flushes and re-instantiates it between requests, making it safe for services that hold per-request state. 

      Q02  Do static class properties get reset between Octane requests?        No. Static properties persist for the entire worker process lifetime and are not affected by `forgetInstance` or `scoped()` bindings. You must reset them manually in a `RequestHandled` listener or redesign the code to avoid static mutable state. 

      Q03  Is FrankenPHP's worker mode identical to Swoole for these purposes?        The container lifecycle is the same — one bootstrap per worker, persistent across requests. The flush hooks and `scoped()` binding behavior work identically. The main differences are in the underlying HTTP server implementation, not in how Laravel manages service lifetimes. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Filament v3 Custom Table Columns: Rendering Complex UI Without Hacks](https://cdn.msaied.com/611/05db05ad084cfdd9a407ff7707dcfaf7.png) filament laravel livewire 

### Filament v3 Custom Table Columns: Rendering Complex UI Without Hacks

Learn how to build fully custom Filament v3 table columns with Blade views, state callbacks, and Alpine.js — w...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 30 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/filament-v3-custom-table-columns-rendering-complex-ui-without-hacks) [ ![CQRS Without Event Sourcing: Practical Read/Write Model Separation in Laravel](https://cdn.msaied.com/610/ed0ccf7bb832d7b82f057cb14f506e65.png) laravel cqrs architecture 

### CQRS Without Event Sourcing: Practical Read/Write Model Separation in Laravel

You don't need event sourcing to benefit from CQRS. This article shows how to split read and write models in a...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 30 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/cqrs-without-event-sourcing-practical-readwrite-model-separation-in-laravel) [ ![Laravel AI SDK: Tool-Calling Agents and Conversation Persistence](https://cdn.msaied.com/609/675722df30f32b9b1d547c9b86dce00b.png) laravel ai agents 

### Laravel AI SDK: Tool-Calling Agents and Conversation Persistence

Build reliable tool-calling AI agents in Laravel using the Prism package, with typed tool definitions, convers...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 30 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-ai-sdk-tool-calling-agents-and-conversation-persistence-3) 

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

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
