FrankenPHP Worker Mode: Safe State in Laravel Octane | 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 + FrankenPHP: Persistent State, Shared Services, and Safe Bootstrapping

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

 Running Laravel under FrankenPHP's worker mode unlocks real throughput gains, but persistent state between requests is a silent killer. Learn exactly which services to reset, how to write safe singletons, and how to structure bootstrapping for long-lived workers.

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

ShareCopy linkCopied

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

  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)

 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.

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

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

  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.

   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.

   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.

   ![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 articleLaravel Contextual HTTP Clients: Per-Service Config, Retries, and Middleware Stacks](https://www.msaied.com/public/articles/laravel-contextual-http-clients-per-service-config-retries-and-middleware-stacks) [Next articleJob Batching, Chaining, and Rate-Limited Middleware in Laravel Queues](https://www.msaied.com/public/articles/job-batching-chaining-and-rate-limited-middleware-in-laravel-queues-1)  

   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)

 ###  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)
