Skip to content
Concept

Octane·FrankenPHP Runtime

Why Nexia runs Laravel through Octane and FrankenPHP, and what the long-lived worker model changes for Core and App code.

Octane and FrankenPHP Runtime

Keep tenant and actor objects inside the current call; do not capture them in static properties or process-wide singletons. If a local PHP change is not visible, run task app:reload and retry. The details below explain the worker lifecycle; Tenant and context covers context restoration.

Persistent PHP workers

Nexia runs the Laravel host with Laravel Octane and FrankenPHP. FrankenPHP is the selected application server. Octane is the Laravel integration layer that boots the application into workers, hands requests and tasks to those workers, and prepares the framework between operations.

This differs from a process model that boots a fresh PHP application for every HTTP request. A worker can serve many requests before it is recycled. Framework services and warmed code can therefore be reused, while request-specific state must be cleared or replaced deliberately.

Runtime responsibilities

LayerRuntime responsibility
FrankenPHPAccept HTTP traffic and run persistent PHP workers
Laravel OctaneBoot Laravel, dispatch operations, and invoke preparation or cleanup listeners
CoreEstablish and release tenant, actor, authorization, and contribution runtime state
App SDKExpose request-safe contracts without leaking Core implementations
AppKeep domain code scoped to the current call and avoid process-global request state

Code reload and worker restart

The development watch list includes Core, configuration, routes, the local App SDK source folder, and App package source folders. That makes local changes normally restart the development worker. If the running web worker still serves the previous code, run task app:reload and then reload the browser. This is a deterministic local fallback that restarts only the Octane app container and waits until it is healthy.

A production deployment must likewise restart workers after code or cached configuration changes; a persistent worker does not discover a new release merely because files changed on disk.

HTTP and queue context

Octane does not define Nexia tenancy. It provides lifecycle hooks; Core owns the responsibility for establishing and safely clearing the tenant and authenticated user for each request.

Finally, queue workers are separate long-lived processes. They share the same need to restore and release context, but octane:frankenphp does not supervise the Redis/Horizon queue runtime by itself. HTTP and queue operations belong to the same deployment model while retaining separate worker lifecycles.

Two tenants in one worker

Consider two consecutive requests handled by one worker. The first request is for tenant A and opens a Workshop record. The next request is for tenant B. The framework prepares the application for the second request, tenant middleware identifies B, and Core resolves B's actor and authorization state. The Workshop App queries through the current tenant connection and returns only B's record.

If the App had stored tenant A's model in a static property, Octane could keep that object alive and make the second request unsafe. The correct implementation stores durable facts in tenant persistence, passes identifiers through explicit contracts, and resolves request-bound objects only after the current tenant and actor exist. Recycling a worker after a maximum number of requests is a defensive operational control, not the fix for a context leak.

Source of truth: docs/developers/content/en/core-runtime/backend-runtime.md