| name | octane-frankenphp-gotchas |
| description | Use when debugging unexpected behavior in Laravel Octane with FrankenPHP - stale data between requests, memory leaks, singleton contamination, static property state, or anything that "works once then breaks". |
Octane + FrankenPHP Gotchas
Laravel Octane keeps the application bootstrapped between requests. State that resets in PHP-FPM persists in Octane.
The Core Problem
PHP-FPM: fresh process per request → no state leakage.
Octane: same process handles N requests → any mutable shared state leaks.
Common Issues & Fixes
Singleton State Contamination
app()->singleton('my-service', fn() => new MyService());
app()->scoped('my-service', fn() => new MyService());
Static Property Pollution
class MyClass {
public static array $cache = [];
}
Service Provider State
If a ServiceProvider stores state in $this properties, it persists across requests. Use flushState() if provided, or restructure to be stateless.
Request-Specific Data in Singletons
class ReportService {
private User $user;
public function __construct() {
$this->user = auth()->user();
}
}
public function generate(): Report {
$user = auth()->user();
}
File Upload Issues
Octane may buffer request bodies differently. Always use $request->file(), not PHP's $_FILES.
Memory Leaks
Symptoms: memory grows with each request, worker restarts frequently.
- Avoid appending to static collections across requests
- Use
Octane::tick() for periodic cleanup if needed
- Check
memory_get_usage() before/after request in Telescope
Octane Config (config/octane.php)
'warm' => [
],
'flush' => [
],
Debugging Checklist
When "works first time, breaks on second request":
- Look for static properties modified during a request
- Look for singletons that capture request-specific data (auth, locale, tenant)
- Check
app()->scoped() vs app()->singleton()
- Enable Telescope — compare request #1 vs #2 memory/queries
- Test with
OCTANE_MAX_REQUESTS=1 to confirm FPM-style reset fixes it
FrankenPHP-Specific Notes
- Workers are true long-lived PHP processes (not Swoole coroutines)
- No fiber/coroutine isolation — one request at a time per worker
php_sapi_name() === 'frankenphp' for conditional code
- Xdebug works normally (unlike Swoole)