FrankenPHP: el servidor de PHP que quiere jubilar a Nginx + PHP-FPM
Si trabajas con PHP, seguro que tu stack de producción se parece a esto: un contenedor Docker con Nginx (o Apache) delante y PHP-FPM detrás gestionando los procesos. Llevas años así, funciona, y tampoco te has planteado mucho cambiarlo y es normal.
Pero hay un juguete relativamente nuevo que está ganando terreno y que merece una mijina de atención: FrankenPHP. Un servidor de aplicaciones PHP escrito en Go que mete en un solo binario lo que antes necesitabas en dos servicios separados. Y que además trae un modo worker que puede multiplicar el rendimiento de tu aplicación Symfony o Laravel por 2, 3 o hasta 4 veces.
¿Es para tanto? ¿Debería cambiar todo lo que tengo montado? Vamos a verlo con una cervecita.

¿Qué es FrankenPHP?
FrankenPHP es un servidor de aplicaciones PHP construido sobre 💡 Caddy Servidor web escrito en Go, conocido por su configuración automática de HTTPS con Let's Encrypt, soporte HTTP/3 nativo y su simplicidad de uso. Más info → . Lo creó Kévin Dunglas Kévin Dunglas Desarrollador francés, creador de API Platform y uno de los principales contribuidores del ecosistema Symfony. Ahora lidera el desarrollo de FrankenPHP. , el mismo que creó API Platform, y a día de hoy está bajo la organización oficial de PHP en GitHub y respaldado por la PHP Foundation.
Lo que hace es combinar servidor web + ejecución de PHP en un solo proceso, es decir, te olvidas de configurar Nginx por un lado y PHP-FPM por otro, un solo binario y palante.
Nginx recibe la petición HTTP → la reenvía a PHP-FPM via socket → PHP-FPM arranca un proceso PHP → ejecuta tu código → devuelve la respuesta → el proceso muere (o vuelve al pool).
FrankenPHP recibe la petición HTTP → ejecuta tu código PHP directamente dentro del mismo proceso → devuelve la respuesta. Un solo servicio, sin intermediarios.
Y ojo, que esto NO sustituye a Docker
Esto es lo primero que hay que dejar claro porque genera confusión, FrankenPHP no reemplaza a Docker, va dentro de Docker. Lo que reemplaza es la necesidad de tener Nginx + PHP-FPM como servicios separados dentro de tu contenedor.
De hecho, encaja de lujo con Docker porque al ser un solo binario, tus imágenes quedan más limpias y simples, un solo proceso y menos parafernalia.
Los dos modos: clásico y worker
FrankenPHP tiene dos formas de funcionar, y la diferencia entre las dos es grande.
Modo clásico
Funciona básicamente igual que PHP-FPM. Cada petición arranca la ejecución de PHP desde cero: carga autoloader, levanta el framework, procesa la petición y se acabó. Es un replacement directo, cambias tu Nginx + PHP-FPM por FrankenPHP y todo sigue funcionando igual.
¿Qué tal es el rendimiento? Pues prácticamente el mismo que PHP-FPM, no vas a notar diferencia significativa. La ventaja aquí es operativa porque hay un solo servicio en lugar de dos.
Modo worker
En modo worker la cosa se pone más curiosa porque tu aplicación PHP arranca una sola vez y se queda en memoria. Las peticiones siguientes se saltan todo el bootstrap (autoloader, contenedor de dependencias, configuración…) y van directas a la lógica de negocio.
Es como la diferencia entre encender el coche cada vez que quieres ir a algún sitio y tener el coche ya arrancado y en punto muerto esperando. El ahorro es brutal.
<?php
// worker.php — ejemplo simplificado
require __DIR__ . '/vendor/autoload.php';
// El bootstrap se ejecuta UNA SOLA VEZ
$app = buildApplication();
// Este bucle se repite por cada petición
while ($request = \frankenphp_handle_request()) {
// Tu lógica de negocio
$response = $app->handle($request);
// Envía la respuesta
echo $response;
}
Con Symfony y Laravel ya hay integraciones oficiales que hacen esto transparente, no tienes que tocar tu código de aplicación.
¿Cuánto más rápido es realmente?
Aquí toca ser honesto y no vender humo, porque depende mucho del modo que uses.
Modo clásico vs PHP-FPM
Estos modos son casi iguales, benchmarks independientes muestran que ambos andan por los 20.000-21.000 peticiones por segundo en escenarios comparables y la diferencia es despreciable. Si alguien te dice que FrankenPHP en modo clásico es 3.5x más rápido, te está confundiendo con los números del modo worker.
Modo worker vs PHP-FPM
Aquí sí hay algo de diferencia notable:
| Métrica | PHP-FPM | ⚡ FrankenPHP Worker |
|---|---|---|
| Peticiones/segundo | ~4.000 | ~15.000 |
| Latencia media | ~30ms | ~15ms |
| Bootstrap por petición | Sí, cada vez | Solo la primera vez |
Esos números son de aplicaciones reales con frameworks pesados tipo Symfony o Laravel. Cuanto más gordo sea el bootstrap de tu app (más servicios, más configuración, más dependencias), más notas la diferencia.
La trampa: datasets grandes
Pero ojito que en pruebas con Eloquent cargando modelos con 15+ relaciones, FrankenPHP worker fue un 20% más lento que PHP-FPM. ¿Por qué? Porque el garbage collector de un proceso de larga duración gestiona la memoria de forma diferente, y con objetos muy pesados eso puede jugar en contra.
Vamos que no todo es velocidad bruta, hay que probar con tu caso de uso real.
Lo que trae de serie que lo peta
Aparte de la velocidad en modo worker, FrankenPHP viene con cosas que normalmente necesitas configurar aparte:
Certificados de Let's Encrypt generados y renovados automáticamente. Sin certbot, sin crons, sin dramas.
Soporte nativo del protocolo más moderno. Tu app sirve sobre HTTP/3 sin configurar nada extra.
El único SAPI de PHP que soporta Early Hints de forma nativa. Puede mejorar tiempos de carga un ~30%.
Actualizaciones en tiempo real tipo WebSocket, pero sobre HTTP. Listo para usar, sin montar un servidor aparte.
Lo de los 💡 Early Hints Respuesta HTTP 103 que permite al servidor enviar indicaciones al navegador sobre recursos que va a necesitar (CSS, JS, fuentes) antes de que la respuesta principal esté lista. El navegador empieza a descargarlos en paralelo. Más info → es especialmente interesante. FrankenPHP es literalmente el único servidor de PHP que lo soporta de forma nativa. Si tu app tiene assets pesados, puedes ganar un buen pellizco de rendimiento percibido sin tocar nada de tu código.
¿Cuándo usarlo y cuándo no?
- Proyectos nuevos con Symfony o Laravel donde puedes aprovechar el modo worker desde el principio
- APIs con mucho tráfico donde la latencia importa
- Cuando quieres simplificar tu stack Docker (un proceso en vez de dos)
- Si necesitas Early Hints, HTTP/3 o Mercure sin montar servicios extra
- Proyectos legacy con mucho estado global que no son compatibles con el modo worker
- Si tu hosting no te deja elegir el servidor (shared hosting, por ejemplo)
- Apps donde el cuello de botella es la base de datos, no el bootstrap — ahí da igual el servidor
- Si no tienes tiempo de probar y validar — PHP-FPM es aburrido pero funciona siempre
Cómo probarlo en 5 minutos
Si quieres darle rápidito sin montar nada desde cero:
# Tirar del contenedor oficial
docker pull dunglas/frankenphp:latest
# Arrancar con tu proyecto PHP montado
docker run -v $PWD:/app/public -p 80:80 -p 443:443 dunglas/frankenphp
Y para un docker-compose.yml más serio con tu proyecto Symfony:
services:
php:
image: dunglas/frankenphp
volumes:
- ./:/app
ports:
- "80:80"
- "443:443"
environment:
SERVER_NAME: localhost
Eso te levanta FrankenPHP en modo clásico, sirviendo tu proyecto tal cual. Si quieres probar el modo worker con Symfony, instalas el runtime:
composer require runtime/frankenphp-symfony
Y la integración se encarga del resto, sin tocar tu código de aplicación.
Casos reales donde FrankenPHP marca la diferencia
Para que no quede todo en teoría, aquí van ejemplos concretos donde el modo worker te da un salto de rendimiento real:
-
API REST con Symfony que recibe +500 req/s — El bootstrap de Symfony es pesado (contenedor de dependencias, configuración, event listeners…). Con worker mode te saltas todo eso en cada petición. Pasa de ~4.000 a ~15.000 req/s fácilmente.
-
Panel de administración Laravel con muchas rutas — Laravel carga todas las rutas en cada petición. Con 200+ rutas registradas, ese parsing se come un buen trozo de tiempo. En modo worker las rutas se cargan una vez y ya están en memoria.
-
Webhook receiver que procesa eventos de Stripe/PayPal — Recibes ráfagas de muchos webhooks a la vez cuando hay picos de compras. La latencia baja de ~30ms a ~15ms y tu endpoint no se atraganta.
-
Microservicio de autenticación (JWT) — Validar tokens, consultar permisos, devolver respuesta. Es una operación ligera pero que se repite miles de veces. El overhead del bootstrap pesa más que la propia lógica, así que eliminarlo con worker mode es un win enorme.
-
App con Server-Sent Events (SSE) usando Mercure — Si necesitas notificaciones en tiempo real (chat, dashboards, alertas), FrankenPHP trae Mercure integrado. Con PHP-FPM tendrías que montar un servidor Mercure aparte y gestionar otro servicio más.
¿Producción, desarrollo o ambos?
Ambos, pero con matices. En desarrollo lo puedes usar tal cual como reemplazo de tu Nginx + PHP-FPM local, simplifica el setup y arrancas más rápido. En producción funciona perfectamente, de hecho está pensado para ello, con HTTPS automático, HTTP/3 y todo lo que hemos visto.
Eso sí, en producción con el modo worker hay que tener cuidado con los memory leaks. Como el proceso no muere entre peticiones, si tu código tiene fugas de memoria (variables estáticas que acumulan datos, caches en memoria que crecen sin control…), con el tiempo el proceso se hincha. La recomendación es monitorizar el consumo de memoria y configurar un límite de peticiones por worker para que se reinicie periódicamente.
¿Es el futuro o una moda?
FrankenPHP no es una moda. Tiene detrás a Kévin Dunglas (un pavo con un recorrrido brutal en el ecosistema PHP), está bajo la organización oficial de PHP en GitHub, y la PHP Foundation lo respalda oficialmente desde 2025, que no es poca cosa.
Pero tampoco creo que Nginx + PHP-FPM vaya a desaparecer mañana. Lleva décadas funcionando, todo el mundo lo conoce, y es la opción por defecto en la inmensa mayoría de hostings y tutoriales, esa inercia no se cambia en dos días.
Lo que sí creo es que para proyectos nuevos, especialmente APIs y aplicaciones Symfony/Laravel, FrankenPHP es una opción muy seria que merece la pena evaluar. El modo worker es una ventaja real, no humo. Y la simplificación operativa de tener un solo binario en vez de dos servicios es un alivio.
¿Debería sustituir todo lo que tengo montado ahora mismo? No, Lo que ya funciona con PHP-FPM, que siga funcionando. Pero la próxima vez que montes algo nuevo, dale una oportunidad.
EA, pues eso. Nos vemos en los bares chavales! 🍻
Pon a prueba lo aprendido
1. ¿Qué reemplaza FrankenPHP?
2. ¿Qué ventaja principal tiene el modo worker frente al modo clásico?
3. ¿Cuánta diferencia de rendimiento hay entre FrankenPHP en modo clásico y PHP-FPM?
4. ¿Qué característica tiene FrankenPHP que ningún otro SAPI de PHP ofrece de forma nativa?