FrankenPHP Symfony: 3× Performance Boost Guide

FrankenPHP Symfony: 3× Performance Boost Guide

Sam Rollin
Sam Rollin
January 24, 2026
Last updated : February 15, 2026
January 24, 2026

PHP-FPM has served us well for years, but it carries a fundamental limitation: every request triggers a full framework bootstrap. FrankenPHP takes a different approach by keeping your Symfony application warm in memory, eliminating that repeated startup cost. The results? Published benchmarks show 3× throughput improvements and latency drops from 45ms to 8ms.

This guide covers what FrankenPHP worker mode is, how to set it up with Symfony, what the benchmarks actually show (and what they don't), and the operational realities you need to prepare for. This comprehensive look at FrankenPHP Symfony integration will help you understand the PHP-FPM vs FrankenPHP tradeoffs.

Prerequisites

Before getting started, make sure you have:

  • PHP 8.2 or higher
  • Symfony 7.2 (Symfony 7.4 recommended for native support)
  • Docker (recommended) or a compatible hosting environment
  • Basic familiarity with Symfony's runtime component

What Is FrankenPHP and Why Does It Matter?

FrankenPHP embeds PHP directly into a Go-based server built on Caddy. Created by Kévin Dunglas (the person behind API Platform), it replaces the traditional Nginx PHP-FPM setup with a single application server. This Caddy PHP server approach simplifies deployment significantly.

As of May 2025, FrankenPHP is officially supported by the PHP Foundation, with the codebase hosted under the PHP organization. It has over 8,000 GitHub stars and more than 100 contributors.

The key feature we're focusing on is worker mode. Here's the difference:

Traditional PHP-FPM:

  • Spawns new PHP processes per request
  • Framework bootstraps on every request
  • Database and cache connections re-establish each time

FrankenPHP Worker Mode:

  • Maintains persistent workers handling multiple requests
  • Boots Symfony once and keeps the kernel warm
  • Database and Redis connections stay open between requests

This architectural shift produces four specific gains:

  • No bootstrap overhead per request
  • Persistent database and cache connections (PHP persistent connections)
  • OPcache bytecode stays warm in memory
  • Fewer file system operations

Understanding the Benchmarks

Before implementing anything, let's look at what the FrankenPHP benchmarks actually show.

Primary Benchmark Data (AWS t3.medium, January 2026)

Test conditions: Symfony 7.4 JSON API endpoint, k6 load testing

Nginx PHP-FPM: ~1,240 RPS throughput, ~45ms p95 Latency

FrankenPHP Worker Mode: ~3,850 RPS throughput, ~8ms p95 Latency

Difference: 3.1× higher throughput, 5.6× faster latency

These numbers come from a single-author published benchmark. They're plausible given the elimination of bootstrapping overhead, but your results will vary based on your actual workload.

Why Performance Claims Vary So Much

You'll see wildly different numbers across sources:

  • Published API benchmarks: 3× throughput, 5-6× latency improvement
  • Raspberry Pi 5 community tests: 12-19× improvement
  • Conference presentations: "15× faster"

These aren't necessarily contradictory. The improvement depends heavily on how much of your request time is spent on framework bootstrap versus actual work like database queries or external API calls. CPU-bound endpoints with minimal framework overhead will see smaller gains than endpoints that are bootstrap-heavy.

Our experience shows that applications with heavy Doctrine bootstrap performance issues or complex service containers see the most dramatic improvements. Simple endpoints that are already well-cached show more modest gains.

Implementation Steps

This section covers the Symfony 7.4 FrankenPHP setup process step by step.

Step 1: Choose Your Symfony Version Strategy

Symfony 7.4 : Native FrankenPHP support is built in. No additional packages required.

Symfony 7.2-7.3: You'll need the runtime package:

composer require runtime/frankenphp-symfony

The Symfony runtime component handles the integration automatically.

Step 2: Set Up Your Environment Configuration

Add this to your environment variables:

APP_RUNTIME=Runtime\FrankenPhpSymfony\Runtime

For Symfony 7.4 , this step may be unnecessary depending on your setup, as the framework handles detection automatically.

Step 3: Configure FrankenPHP Worker Mode

Your Caddyfile (or equivalent configuration) needs to point to your Symfony front controller:

{
    frankenphp
}

localhost {
    root * /app/public
    php_server {
        worker /app/public/index.php
    }
}

Step 4: Run FrankenPHP

For local development:

frankenphp php-server --worker public/index.php

With FrankenPHP Docker:

docker run -v $PWD:/app -p 80:80 dunglas/frankenphp

Step 5: Configure Worker Restart Limits

The runtime exposes a frankenphp_loop_max setting that controls how many requests each worker handles before restarting:

FRANKENPHP_LOOP_MAX=500

The default is 500 requests. Set to -1 to never restart workers. This setting exists to help manage memory leaks in long-running processes.

Common Mistakes to Avoid

Understanding PHP memory leak worker mode issues is essential for successful deployment.

1. Forgetting That Workers Are Long-Lived

The biggest mindset shift: you're no longer in a share-nothing per-request model. Static variables and service state persist between requests.

Problem code:

class RequestCounter
{
    private static int $count = 0;
    
    public function increment(): int
    {
        return   self::$count;
    }
}

This counter will accumulate across requests, which is probably not what you want.

2. Ignoring Memory Leaks

What's a minor annoyance in PHP-FPM becomes critical in worker mode. A service that leaks 1MB per request will eventually crash your worker.

We've found that the most common leak sources are:

  • Event listeners that accumulate data
  • Services caching request-specific data
  • Doctrine entities held in memory

Use Symfony's kernel.reset tag for services that need cleanup between requests:

services:
    App\Service\RequestScopedService:
        tags:
            - { name: 'kernel.reset', method: 'reset' }

The kernel.reset Symfony mechanism is crucial for proper Symfony performance optimization in worker mode.

3. Expecting Hot Reload in Development

Because the application stays in memory, code changes won't take effect immediately. You'll need a file watcher to restart workers during development:

frankenphp php-server --worker public/index.php --watch

Check the FrankenPHP documentation for the exact watch configuration that fits your setup.

4. Not Testing for State Leakage

Before deploying worker mode to production, run sequential requests through your endpoints and verify that request A doesn't affect request B's results.

Testing and Verification

Verify Worker Mode Is Active

Check your response headers or logs. FrankenPHP adds specific headers indicating worker mode is active.

Load Test Before and After

Run comparative load tests with a tool like k6 or wrk:

# Baseline with PHP-FPM
k6 run --vus 50 --duration 30s load-test.js

# After switching to FrankenPHP
k6 run --vus 50 --duration 30s load-test.js

Compare throughput and p95 latency. You should see improvements, though the magnitude depends on your specific endpoints.

Monitor Memory Usage

Watch your worker memory consumption over time:

docker stats

If memory climbs steadily, you have a leak to track down or need to lower your frankenphp_loop_max value.

Check for State Leakage

Create a test that sends multiple sequential requests and verifies isolation:

public function testRequestIsolation(): void
{
    // First request sets a value
    $this->client->request('POST', '/session/set', ['value' => 'first']);
    
    // New client session
    $newClient = static::createClient();
    
    // Should not see the previous value
    $newClient->request('GET', '/session/get');
    $this->assertNotEquals('first', $newClient->getResponse()->getContent());
}

When FrankenPHP Makes Sense (and When It Doesn't)

Good candidates:

  • API endpoints with significant framework bootstrap overhead
  • Applications where infrastructure costs are a concern
  • Projects already comfortable with containerized deployments

Less ideal:

  • Applications that are already heavily tuned with OPcache preloading (gains will be smaller)
  • Legacy code with static state assumptions that would require significant refactoring
  • Teams without experience managing long-running processes

Conclusion

FrankenPHP worker mode offers real performance gains for Symfony applications by eliminating per-request framework bootstrapping. Published benchmarks show 3× throughput improvements and 5-6× latency reductions, though your specific results depend on your workload characteristics.

The tradeoff is operational complexity. You're moving from PHP's traditional share-nothing model to persistent processes, which means paying more attention to memory management and state isolation.

Our approach involves starting with non-critical endpoints in staging, measuring the actual gains for your specific use case, and gradually expanding once you've confirmed your services handle the worker lifecycle correctly.

Implementing FrankenPHP requires careful attention to service configuration, memory management, and deployment infrastructure. If you're evaluating whether worker mode fits your Symfony application, or you've run into issues with memory leaks and state management during migration, we can help you assess your codebase and plan an implementation approach that accounts for your specific architecture.

Share this article