FrankenPHP Worker Mode: Safe State in Laravel Octane | 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 State, Shared Services, and Safe Bootstrapping        On this page       1. [  Why Worker Mode Changes Everything ](#why-worker-mode-changes-everything)
2. [  The Lifecycle You Must Internalize ](#the-lifecycle-you-must-internalize)
3. [  Identifying Dangerous Singletons ](#identifying-dangerous-singletons)
4. [  Registering Octane Flush Callbacks ](#registering-octane-flush-callbacks)
5. [  Static Properties: The Hidden Landmine ](#static-properties-the-hidden-landmine)
6. [  Safe Bootstrapping Pattern ](#safe-bootstrapping-pattern)
7. [  Key Takeaways ](#key-takeaways)

  ![Laravel Octane + FrankenPHP: Persistent State, Shared Services, and Safe Bootstrapping](https://cdn.msaied.com/224/cc0aa09965b63e7311e93282849ada05.png)

  #laravel   #octane   #frankenphp   #performance   #architecture  

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

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

       Table of contents

1. [  01   Why Worker Mode Changes Everything  ](#why-worker-mode-changes-everything)
2. [  02   The Lifecycle You Must Internalize  ](#the-lifecycle-you-must-internalize)
3. [  03   Identifying Dangerous Singletons  ](#identifying-dangerous-singletons)
4. [  04   Registering Octane Flush Callbacks  ](#registering-octane-flush-callbacks)
5. [  05   Static Properties: The Hidden Landmine  ](#static-properties-the-hidden-landmine)
6. [  06   Safe Bootstrapping Pattern  ](#safe-bootstrapping-pattern)
7. [  07   Key Takeaways  ](#key-takeaways)

 Why Worker Mode Changes Everything
----------------------------------

FrankenPHP's worker mode keeps your Laravel application bootstrapped in memory across thousands of requests. The PHP process never dies between requests — the container, service providers, and any resolved singletons all persist. That is the source of the throughput gain, and the source of every subtle bug you will spend a weekend debugging.

This article focuses on the practical discipline required to write application code that is safe under these conditions.

---

The Lifecycle You Must Internalize
----------------------------------

Under a traditional PHP-FPM setup, every request gets a fresh process. Under FrankenPHP worker mode (or Laravel Octane with any driver), the lifecycle looks like this:

1. **Bootstrap** — runs once when the worker starts.
2. **Request loop** — `$kernel->handle()` runs for each request on the same container instance.
3. **Flush** — Octane fires `octane:request-handled` and resets registered services.

Octane's `RequestHandled` event triggers its own cleanup, but only for services it knows about. Anything you bind yourself is your responsibility.

---

Identifying Dangerous Singletons
--------------------------------

A singleton is dangerous in worker mode when it holds **request-scoped state** — authenticated user, request headers, per-request configuration, or accumulated data.

```php
// DANGEROUS: holds the resolved user across requests
app()->singleton(CurrentUser::class, function ($app) {
    return new CurrentUser(
        $app['auth']->user() // resolved at bind time, not request time
    );
});

```

The fix is to make the resolution lazy and request-aware:

```php
// SAFE: resolves fresh on every call
app()->bind(CurrentUser::class, function ($app) {
    return new CurrentUser($app['auth']->user());
});

```

If you genuinely need a singleton for performance (e.g., a compiled rule set), separate the **immutable config** from the **mutable state**:

```php
app()->singleton(PricingEngine::class, function ($app) {
    // Rules loaded once from cache — safe, immutable
    return new PricingEngine(PricingRules::fromCache());
});

```

---

Registering Octane Flush Callbacks
----------------------------------

Octane exposes `Octane::flush()` for registering cleanup callbacks that run after every request:

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

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Octane::flush([
            CurrentUser::class,
            TenantContext::class,
        ]);

        // Or a closure for finer control
        Octane::afterRequest(function () {
            app(MetricsCollector::class)->flush();
        });
    }
}

```

The array form calls `app()->forgetInstance()` on each class. The closure form lets you do custom teardown — flushing buffers, resetting static properties, or clearing in-memory queues.

---

Static Properties: The Hidden Landmine
--------------------------------------

Octane's flush mechanism cannot reset PHP static properties. If you have:

```php
class QueryLogger
{
    private static array $log = [];

    public static function record(string $sql): void
    {
        self::$log[] = $sql;
    }
}

```

`$log` grows unbounded across every request in the worker. The fix is to register a reset in your `afterRequest` hook:

```php
Octane::afterRequest(function () {
    QueryLogger::reset(); // clears the static array
});

```

Alternatively, move the state into a container-bound service that Octane can flush.

---

Safe Bootstrapping Pattern
--------------------------

For services that are expensive to construct but safe to share (database connection pools, compiled Twig environments, ML model handles), use a two-phase pattern:

```php
class InferenceServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Singleton: model weights loaded once
        $this->app->singleton(EmbeddingModel::class, fn() =>
            EmbeddingModel::loadFromDisk(config('ai.model_path'))
        );
    }

    public function boot(): void
    {
        // Per-request context wraps the singleton safely
        $this->app->bind(EmbeddingContext::class, fn($app) =>
            new EmbeddingContext($app->make(EmbeddingModel::class))
        );
    }
}

```

The heavy model is a singleton. The context — which may hold per-request token counts or user preferences — is bound (not singleton), so it is reconstructed fresh each request.

---

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

- **`bind` vs `singleton`** is now a correctness decision, not just a performance one.
- Use `Octane::flush([...])` and `Octane::afterRequest(...)` to register explicit teardown for every stateful service.
- Static class properties bypass Octane's flush — reset them manually or eliminate them.
- Separate immutable configuration (safe to share) from mutable request context (must be rebound).
- Test worker-mode safety by running your test suite with `OCTANE_TESTING=true` and asserting no state leaks between simulated requests.

 Found this useful?

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

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

  3 questions  

     Q01  Does FrankenPHP worker mode require Laravel Octane?        Yes. FrankenPHP's worker mode integrates with Laravel through the Octane package, which manages the request loop, lifecycle events, and flush callbacks. Without Octane, you would need to implement the worker loop and teardown yourself. 

      Q02  How do I test for state leaks between requests in worker mode?        Octane ships with a `withOctane()` test helper and a `OctaneTestCase` base. You can simulate multiple sequential requests in a single test and assert that state from request one does not bleed into request two. Also audit any `static` properties and container singletons that hold mutable data. 

      Q03  Is it safe to use Laravel's Auth facade in a singleton resolved at boot time?        No. The Auth facade resolves the guard lazily, but if you resolve `auth()-&gt;user()` inside a singleton's constructor at boot, you capture a null or stale user for the lifetime of the worker. Always resolve the authenticated user inside a `bind` (per-request) service or directly within the controller/action. 

  Continue reading

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

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

 [ ![Cursor Pagination, Lazy Collections, and Chunked Iteration at Scale in Laravel](https://cdn.msaied.com/226/0fcac108846a1b5039ff28cfc7c9281d.png) laravel eloquent performance 

### Cursor Pagination, Lazy Collections, and Chunked Iteration at Scale in Laravel

Offset pagination breaks under large datasets. Learn when to reach for cursor pagination, lazy collections, an...

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

 17 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/cursor-pagination-lazy-collections-and-chunked-iteration-at-scale-in-laravel) [ ![Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues](https://cdn.msaied.com/225/fc3ad6c9188459b1f2fb165912fca5b3.png) laravel queues jobs 

### Job Batching, Chaining, and Rate-Limited Middleware in Laravel Queues

Go beyond basic dispatching: learn how Laravel's Bus::batch(), job chains, and rate-limited middleware compose...

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

 17 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/job-batching-chaining-and-rate-limited-middleware-in-laravel-queues-1) [ ![Laravel Contextual HTTP Clients: Per-Service Config, Retries, and Middleware Stacks](https://cdn.msaied.com/223/2706e56a293bb3b59f39b52956efac09.png) laravel http-client service-container 

### Laravel Contextual HTTP Clients: Per-Service Config, Retries, and Middleware Stacks

Stop scattering HTTP client config across service classes. Learn how to build named, pre-configured HTTP clien...

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

 17 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-contextual-http-clients-per-service-config-retries-and-middleware-stacks) 

   [  ![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)
