FrankenPHP vs PHP-FPM (Part 2): Symfony Under Real Load and Why Workers Win
Extracto
Real-world Symfony benchmark: 30+ packages, high load, and FrankenPHP acting like a long-running beast.
Resumen
Resumen Principal
Este análisis detalla una evaluación exhaustiva de rendimiento entre FrankenPHP y PHP-FPM en el manejo de un proyecto Symfony moderno, trascendiendo las pruebas básicas de "Hello World". El objetivo principal fue comparar la eficiencia de los mecanismos del servidor HTTP al procesar solicitudes PHP a través de Symfony, centrándose en el manejo de peticiones, el tiempo de inicio (bootstrap) del framework, la autocarga de PHP vía Composer, y la eficiencia del worker pool. Los resultados demuestran una ventaja significativa para FrankenPHP, que consistentemente superó a PHP-FPM en diversas métricas de carga y latencia. Un factor clave en esta disparidad es la capacidad del modo worker de FrankenPHP para eliminar el costo de arranque en frío que experimentan las aplicaciones Symfony en las configuraciones tradicionales de PHP-FPM, donde cada solicitud reinicia el framework.
Elementos Clave
- Entorno de Prueba Controlado: Los benchmarks se ejecutaron en una instancia dedicada de AWS EC2 (6 vCPUs, 4 GB RAM) para garantizar un entorno limpio y justo. Todas las pruebas se realizaron directamente dentro de la instancia, eliminando la latencia de red y la interferencia del hardware local, asegurando que los resultados reflejaran el rendimiento inherente de cada pila tecnológica.
- Metodología de Benchmarking Integral: Se utilizaron tres herramientas distintas para una evaluación robusta: wrk para medir el rendimiento bruto (solicitudes por segundo) y los límites de carga; wrk2 para simular un flujo de tráfico consistente a una tasa fija y revelar el rendimiento bajo presión controlada; y k6 para emular el uso en el mundo real con múltiples usuarios virtuales y medir la latencia percentil y las tasas de error.
- Configuraciones de Prueba en Symfony: Se inició con la configuración de Symfony más básica posible ("Symfony Skeleton") – un controlador simple que retorna una Response – para aislar el comportamiento del servidor, minimizando la sobrecarga de la aplicación. Se evaluaron cuatro configuraciones: FrankenPHP, FrankenPHP (Optimizado), PHP-FPM, y PHP-FPM (Optimizado), ajustando parámetros como los límites de memoria y el conteo de workers.
- Rendimiento Superior de FrankenPHP Bajo Carga: En las pruebas de rendimiento bruto con
wrk
, FrankenPHP sirvió aproximadamente un 50% más de solicitudes por segundo que PHP-FPM. Conwrk2
bajo presión (2000 RPS), la latencia promedio de PHP-FPM se disparó a casi 1 segundo completo, mientras que FrankenPHP se mantuvo estable en alrededor de 5 ms. A 5000 RPS, PHP-FPM mostró fallas y picos de latencia masivos.
Análisis e Implicaciones
La clara ventaja de rendimiento de FrankenPHP, especialmente su modo worker, representa una solución eficaz para el desafío del arranque en frío en aplicaciones PHP modernas basadas en frameworks. Esto implica una mejora significativa en la capacidad de respuesta y escalabilidad de las aplicaciones, lo que puede traducirse en una mejor experiencia de usuario y menores costos operativos bajo cargas elev
Contenido
In the first part of this series, we ran a series of raw performance benchmarks between FrankenPHP and PHP-FPM using a minimal PHP script. The goal was to isolate and compare the performance of the underlying HTTP server mechanisms — not PHP itself, nor any application logic.
This time, we’re taking it further.
We’re swapping out the simple echo "Hello World"
script with something real — a modern Symfony project, including routing, services, and, eventually, a large set of dependencies and bundles.
Press enter or click to view image in full size
What Are We Actually Testing?
Let’s be clear — this is still not a full-stack benchmark. We are testing the one thing that matters in this context:
How efficiently can a server handle modern PHP requests, specifically through Symfony, when run via Nginx + PHP-FPM versus FrankenPHP?
What are we testing:
- HTTP Request Handling
- Symfony Bootstrap Time
- PHP Autoloading via Composer
- Static Controller Response
- Worker Pool Efficiency
What we are NOT testing:
- MySQL queries
- Redis or external services
- Business logic performance
- Authentication, validation
- Templating or rendering speed
Why Symfony?
Although I ran similar tests using Laravel, the results followed the same pattern — FrankenPHP consistently outperformed PHP-FPM. However, this article isn’t meant to compare frameworks, so I chose Symfony purely arbitrarily. The point is to evaluate how different server stacks handle modern PHP applications, not how the frameworks themselves behave.
Laravel fans, don’t worry — the numbers were just as satisfying. I simply didn’t want this article to turn into a Symfony vs Laravel debate.
Test Environment
To ensure a fair and clean environment:
- The tests were executed on a dedicated AWS EC2 instance
- Specs: 6 vCPUs and 4 GB RAM
- All benchmarks were run directly from within the instance, avoiding network latency or local hardware interference
Tools Used for Benchmarking
To ensure our results are both fair and meaningful, we used three different tools:
- wrk — To measure raw throughput (requests per second) and determine how much load a setup can handle at full throttle.
- wrk2 — To simulate a consistent stream of traffic at a fixed request rate. This reveals how each setup performs under controlled pressure.
- k6 — To mimic real-world usage with multiple virtual users making parallel requests, including latency percentiles and error rates.
Each tool served a unique purpose:
- wrk was our stress test — hammering the server to see its upper limits, like pushing it to failure at the gym.
- wrk2 gave us controlled load — helping us see when performance starts to drop, long before things catch fire.
- k6 simulated reality — what it’s like when real users hit your app all day long, refresh like maniacs, or open 5 tabs at once.
All tests were repeated multiple times to reduce noise and build a reliable baseline for comparison.
In the optimized setups, I fine-tuned worker pools, memory limits, and thread counts to better align with available system resources.
FrankenPHP optimized —> WORKER MODE
Minimal Symfony Benchmark (Symfony Skeleton)
To understand the raw performance differences between FrankenPHP and PHP-FPM, we started with the most basic Symfony setup possible — a single controller that returns a classic Response. This “Hello, world” scenario eliminates most application overhead and isolates the web server’s behavior.
We tested four configurations:
- FrankenPHP
- FrankenPHP (Optimized) — with worker mode count and memory limit tuned
- PHP-FPM
- PHP-FPM (Optimized) — with
pm = static
, adjusted process limits, and opcache tuning
Before diving into the numbers, it’s worth noting one key performance bottleneck in traditional PHP-FPM setups: in Symfony apps, every single request reboots the entire framework — from autoloading to container building. This cold-start overhead can easily add tens or even hundreds of milliseconds per request. FrankenPHP’s worker mode removes that cost entirely.
Each configuration was tested using three tools — wrk
, wrk2
, and k6
— covering raw throughput, fixed-rate latency, and real-world concurrency.
wrk — Raw Throughput
Under raw load, FrankenPHP already pulls ahead. Even in this minimal setup, it serves ~50% more requests per second than PHP-FPM. The optimized FrankenPHP setup breaks 2,700 RPS, while stock PHP-FPM lags behind.
Press enter or click to view image in full size
wrk2 — Latency Under Pressure
Here we pushed both servers to fixed request rates: 1000, 2000, and even 5000 RPS. FrankenPHP remained mostly stable in both latency and request count, while PHP-FPM began to show signs of struggle.
Notably:
- At 2000 RPS, PHP-FPM’s average latency shot up to nearly 1 full second, while FrankenPHP stayed around 5 ms.
- At 5000 RPS, PHP-FPM missed requests and suffered huge spikes in response time.
Press enter or click to view image in full size
k6 — Simulating Real Users
The k6
tool gave us a more real-world perspective.
At 100 and 300 concurrent users:
- All setups handled the load well.
- FrankenPHP stayed slightly ahead in latency.
- No errors observed.
Press enter or click to view image in full size
At 500 users:
- FrankenPHP was still stable (~150 ms).
- Regular PHP-FPM began returning errors in over 68% of requests unless heavily optimized.
At 1000 and 5000 users:
- PHP-FPM collapsed entirely unless fully tuned — and even then, error rates climbed fast.
- FrankenPHP handled 5,000 users at once with higher latency, but zero errors.
Press enter or click to view image in full size
Heavy Symfony Application Benchmark
After the minimal setup, we moved to a more realistic Symfony application — one with 20–30 common packages included. This simulates a typical modern PHP project with authentication, serialization, routing, config, and service layers in place.
Added packages include:
twig
, doctrine/orm
, lexik/jwt-authentication-bundle
, symfony/serializer
, api-platform/core
, symfony/security-bundle
, and others commonly found in real-world apps.
The core difference?
The autoloader now has real work to do, and Symfony’s service container has to boot many more components. This is where FrankenPHP’s architecture starts to shine.
wrk — Raw Throughput
Even with a large codebase, FrankenPHP pushed over 2,700 RPS, compared to ~850 RPS on PHP-FPM. That’s 3x better in some configurations.
This isn’t just about request rate — it’s about startup cost. Since FrankenPHP keeps workers alive between requests, the large autoloader is no longer a bottleneck after the first boot. PHP-FPM, on the other hand, starts from zero each time.
Press enter or click to view image in full size
wrk2 — Latency Under Pressure
At fixed rates of 1000, 2000, and 5000 RPS:
- FrankenPHP optimized handled 5000 RPS with ~2s latency and zero errors.
- PHP-FPM optimized collapsed at 5000 RPS with >1s latency and 1%+ error rate.
The performance cliff here is dramatic — FrankenPHP buys you time and stability under traffic surges.
k6 — Real-World Simulation
This is where it gets real.
With 1000 and 5000 virtual users accessing the real Symfony app:
- PHP-FPM struggled to survive. Even with opcache, optimized workers, and tuned pools — it either crashed or served massive error rates.
- FrankenPHP served traffic at a high latency (2–4 seconds), but never failed.
That’s the difference between a slow website and a down website.
Press enter or click to view image in full size
Conclusions
FrankenPHP didn’t just win the benchmark — it changed the rules of engagement.
The Autoloader Advantage
This test confirmed a key insight:
The heavier your application is, the more FrankenPHP helps you.
Symfony’s bootstrap process can take tens or hundreds of milliseconds when cold. With PHP-FPM, this happens every single request. With FrankenPHP, it happens once — then reused via long-lived workers.
In other words:
- FrankenPHP scales with your codebase.
- PHP-FPM scales against it.
FrankenPHP Worker Mode ≈ Node.js with Express Model
In the traditional PHP-FPM setup:
- Every request starts a brand-new PHP process.
- It reloads the entire autoloader and bootstraps the application every single time.
- That’s expensive — especially for large projects with many dependencies.
In FrankenPHP’s worker mode:
- The server preloads the application once — including the autoloader, configuration, and service container.
- Every incoming request is handled by the same long-running process, without re-bootstrapping.
It behaves like a long-lived server process — just like Node.js Express or a Go HTTP server.
In Node.js with Express:
- You launch the server with
node index.js
, and every request is handled by that already-running process with all modules loaded.
In FrankenPHP (with worker mode):
- A similar model is used — a Go+PHP web server starts once, and the embedded PHP interpreter stays alive in memory, avoiding the per-request overhead.
FrankenPHP Worker = long-running PHP app, just like Express runs your app once and handles requests without restarting anything.
TLDR
FrankenPHP is not just a faster alternative — it redefines how PHP applications are executed.
Thanks to its worker mode, your app runs like a long-living server, not a script. This eliminates redundant autoloader initialization and framework bootstrapping, making performance more consistent and scalable.
As seen in our benchmarks, the gap grows with complexity: the more packages, bundles, and services you add, the more FrankenPHP pulls ahead. It’s not a marginal gain — it’s architectural.
Whether you’re running a small API or a large Symfony or Laravel monolith, FrankenPHP offers a modern execution model that finally lets PHP behave like a first-class citizen in high-performance environments — much closer to Node.js or Go in terms of request handling.
If you’re building serious PHP apps and care about performance, FrankenPHP deserves your attention.
If you want to inspect, reproduce, or extend these benchmarks — Docker configs, scripts, and code are all in my GitHub repo.
UPDATE: PART 3 is HERE
Fuente: Medium