Laravel

FrankenPHP con Laravel: qué cambió con la PHP Foundation y cómo usarlo con Octane

Autorangel cruz
Publicado
Lectura11 min de lectura
FrankenPHP con Laravel: qué cambió con la PHP Foundation y cómo usarlo con Octane

FrankenPHP es un servidor de aplicaciones PHP escrito en Go sobre Caddy, apoyado oficialmente por la PHP Foundation desde mayo de 2025, y es la forma más directa de correr Laravel en modo worker con Octane. El modo worker arranca la aplicación una vez y la mantiene en memoria entre peticiones. El costo menos comentado está en las actualizaciones: el binario que instala Octane trae su propio PHP, así que el parche de php2shell de octubre no te llega con apt upgrade.

Escribo esto a partir de un artículo de ScriptWalker que presenta la incorporación de FrankenPHP a la PHP Foundation como la gran noticia de PHP en 2026. La idea de fondo es buena: para un equipo con Laravel, FrankenPHP es una tercera vía entre el PHP-FPM de siempre y reescribir la app para Swoole o RoadRunner. Pero el anuncio es de mayo de 2025, y las cifras de rendimiento que da (de 3 a 5 veces más peticiones por segundo, latencias de 60 a 90 ms) no traen fuente. Abajo va lo que sí dicen la Foundation, la documentación de FrankenPHP y la de Octane.

Qué es FrankenPHP

FrankenPHP es un servidor que lleva PHP adentro. En vez de tener Nginx por un lado y PHP-FPM por otro, un solo proceso de Caddy recibe la petición HTTP y la ejecuta en un hilo de PHP. Lo creó Kévin Dunglas con el patrocinio de Les-Tilleuls.coop, el repositorio está escrito en Go, tiene licencia MIT y más de 11.000 estrellas en GitHub.

Tiene dos modos:

  • Modo clásico. Es el que corre sin configuración extra. Según su documentación, funciona como "a drop-in replacement for PHP-FPM or Apache with mod_php": sirve archivos PHP y cada petición arranca la aplicación desde cero, igual que con FPM.
  • Modo worker. La aplicación arranca una vez y queda en memoria, y cada petición nueva la atiende esa instancia ya cargada. De ahí sale la ganancia de rendimiento, y también todos los cuidados que vienen más abajo.

Como corre sobre Caddy, trae compresión Brotli y Zstandard, HTTPS automático con HTTP/2 y HTTP/3, y un hub de Mercure para tiempo real.

Qué significa que FrankenPHP esté en la PHP Foundation

El 15 de mayo de 2025, la PHP Foundation anunció que FrankenPHP "is now officially supported". El 8 de junio, a 30 años del primer release de PHP, el repositorio pasó a la organización php de GitHub.

Lo que el apoyo no cambia lo dijo Jakub Zelenka, en nombre de la Foundation, en la lista de internals:

  • La gobernanza sigue igual. Kévin Dunglas, Robert Landers y Alexander Stecher siguen a cargo de los releases y las revisiones de código.
  • Administrativamente, FrankenPHP queda en "essentially the same administrative bucket as PECL extensions".
  • No hay planes de integrarlo en php-src. Si algún día se discute, va a pasar por una RFC.

Y el anuncio aclara que PHP-FPM y los otros SAPI "will continue to be fully supported solutions". Lo que FrankenPHP gana es respaldo: la Foundation contribuye a su desarrollo y el código vive junto al de PHP. ScriptWalker lo pone en el mismo nivel que Composer, PHPStan y Xdebug; la Foundation lo compara con las extensiones de PECL, que es bastante menos épico.

Cómo usar FrankenPHP con Laravel

Hay tres formas, de menos a más compromiso. Todas están en la guía de Laravel de FrankenPHP.

1. Modo clásico, sin tocar tu código

Con Docker, montas el proyecto en /app de la imagen oficial:

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

Con el binario, un Caddyfile en la raíz del proyecto alcanza:

{
	frankenphp
}
 
localhost {
	root public/
	encode zstd br gzip
	php_server {
		try_files {path} index.php
	}
}

Y después frankenphp run. Así FrankenPHP reemplaza a Nginx más PHP-FPM, pero Laravel sigue arrancando en cada petición.

2. Modo worker con Octane

Es lo que la mayoría busca cuando habla de FrankenPHP y Laravel. Octane soporta FrankenPHP, Swoole, Open Swoole y RoadRunner, y con FrankenPHP descarga el binario por ti:

composer require laravel/octane
php artisan octane:install --server=frankenphp
php artisan octane:frankenphp

Las opciones que más vas a tocar, según la documentación de FrankenPHP: --workers (por defecto auto), --max-requests (500 por defecto), --https para certificados automáticos con HTTP/2 y HTTP/3, y --watch para recargar en desarrollo. En producción, Octane recomienda dejarlo corriendo con Supervisor y ejecutar php artisan octane:reload después de cada deploy, porque el código vive en memoria hasta que reinicias los workers.

3. La app como binario único

FrankenPHP puede empaquetar la aplicación entera en un ejecutable: PHP, Caddy y tu código de Laravel en un solo archivo. La guía usa la imagen dunglas/frankenphp:static-builder-gnu y build-static.sh. Un detalle que la guía remarca: cada versión nueva se extrae en un directorio temporal distinto, así que storage/ tiene que vivir fuera, con la variable LARAVEL_STORAGE_PATH.

Qué cambia en tu código con el modo worker

En modo worker, lo que en PHP-FPM moría al terminar la petición ahora sobrevive. La documentación de FrankenPHP lista lo que persiste entre peticiones: variables static, propiedades estáticas de clases, variables globales, cachés en memoria y los cambios hechos con ini_set(), date_default_timezone_set(), set_error_handler() y parecidos. También advierte que $_ENV no se resetea entre peticiones, así que nada de guardar ahí datos de un usuario.

Octane resetea el estado del framework, pero no el que crea tu aplicación. Su documentación señala dos trampas típicas:

  • Los métodos register y boot de los service providers corren una sola vez, cuando arranca el worker.
  • Un singleton que recibe el contenedor o el Request en el constructor se queda con esa instancia para siempre. Con el request, la petición siguiente ve los headers, el input y el query string de la anterior.

La salida que propone Octane es pasar un closure que resuelva la instancia actual:

$this->app->singleton(Service::class, function () {
    return new Service(fn () => Container::getInstance());
});

Para las fugas de memoria de código que nunca pensó en procesos largos, Octane reinicia cada worker después de 500 peticiones. Lo bajas con --max-requests=250 si la memoria crece más rápido de lo que te gusta.

Antes de cambiar, revisa también las extensiones. FrankenPHP necesita PHP compilado como thread-safe, y la lista de problemas conocidos marca imap, newrelic y pcov como no compatibles. Para cobertura de tests, la recomendación es correr PHPUnit o Pest con la CLI de PHP, fuera de FrankenPHP.

FrankenPHP y php2shell: el PHP viene dentro del binario

Después de las dos RCE de PHP de octubre, esto es lo primero que revisaría en una app con Octane. La documentación de Octane lo dice sin vueltas: "The FrankenPHP static binary includes its own copy of PHP, so updating your system's PHP installation will not update the PHP version used by Octane."

Si actualizaste el PHP del sistema a 8.4.27 y Octane sigue usando un binario de FrankenPHP viejo, tu aplicación sigue expuesta. Los pasos que da Octane:

./frankenphp version                              # ver el PHP que trae
rm frankenphp
php artisan octane:install --server=frankenphp    # descarga el último release

Y después reinicias Octane. Si instalaste FrankenPHP en el sistema, lo actualizas por donde lo instalaste.

Las versiones que importan:

  • FrankenPHP 1.13.2, del 21 de octubre, es el release de seguridad de php2shell. Las imágenes de Docker se construyen con PHP 8.2.35, 8.3.36, 8.4.27 y 8.5.12, y los binarios estáticos solo con PHP 8.5.12. Si usas el binario que descarga Octane, tu app corre en PHP 8.5 aunque el servidor tenga 8.4.
  • FrankenPHP 1.13.4, del 26 de octubre, corrige siete vulnerabilidades propias. La más grave afecta solo a 1.13.2 y 1.13.3: en modo worker, un cliente que manda un header poco común y corta la conexión antes de que se atienda tira abajo todo el proceso. Si actualizaste por php2shell, vuelve a actualizar.
  • En Octane, la versión mínima de FrankenPHP pasó a 1.13.2 el 22 de octubre en la rama 2.x. El último tag, v2.22.0, todavía exige 1.13.0, así que con un binario 1.13.0 o 1.13.1 Octane no te avisa: revísalo tú con ./frankenphp version.

El mismo cambio de lógica vale para el binario único de la sección anterior: si distribuyes tu app como ejecutable, el parche llega cuando lo vuelves a compilar y desplegar.

El límite duro de memoria tira todo el servidor

El parche de php2shell cambió cómo PHP maneja el out-of-memory: superar memory_limit ya no corta el script en el acto, y si la asignación pasa un límite duro (por defecto, el doble de memory_limit), PHP termina el proceso. FrankenPHP lo documenta como problema conocido, con la diferencia que importa:

PHP-FPM only loses one child process. FrankenPHP serves all requests from threads of a single process, so every in-flight request is lost and the server stops until it is restarted.

Con FPM, un array_merge() gigante mata un worker. Con FrankenPHP, mata el servidor y todas las peticiones en curso. La recomendación es correrlo bajo algo que lo reinicie: una restart policy de Docker o Restart= de systemd, que los paquetes deb y rpm ya traen configurado.

¿Vale la pena cambiar PHP-FPM por FrankenPHP?

Depende del modo, y en el modo clásico la respuesta corta es que ganas comodidad más que velocidad: un solo proceso, HTTPS automático, HTTP/3, menos configuración que Nginx más FPM. Laravel arranca igual en cada petición.

El modo worker es donde está el rendimiento, y también donde está el costo. Te conviene si:

  • Tu aplicación tiene un arranque pesado (muchos service providers, configuración grande) y eso pesa en cada petición.
  • Puedes auditar tus singletons y los paquetes que usas en busca de estado entre peticiones.
  • Tienes un proceso para actualizar el binario de FrankenPHP con la misma disciplina que el PHP del sistema.

Si alguna de las tres te falta, PHP-FPM sigue siendo una opción perfectamente soportada, como dijo la propia Foundation. Y si te decides, empieza por medir: corre la misma ruta con FPM y con Octane sobre tu aplicación real antes de creerle a una cifra de "3 a 5 veces más rápido" que nadie firmó.

Si estás preparando el deploy de una app de Laravel desde cero, lo cubrí en Aprende Laravel: deploy.

Preguntas frecuentes

¿Qué es FrankenPHP?

Es un servidor de aplicaciones PHP escrito en Go sobre el servidor web Caddy. Ejecuta PHP dentro del mismo proceso que atiende HTTP, en modo clásico (como PHP-FPM) o en modo worker (la app queda en memoria entre peticiones). Lo creó Kévin Dunglas y lo apoya la PHP Foundation desde mayo de 2025.

¿FrankenPHP es parte oficial de PHP?

Su código vive en la organización php de GitHub desde junio de 2025 y la PHP Foundation contribuye a su desarrollo, pero no está integrado en php-src y conserva a sus mantenedores. La Foundation lo ubica en la misma categoría administrativa que las extensiones de PECL.

¿FrankenPHP reemplaza a PHP-FPM?

No. Según la PHP Foundation, PHP-FPM sigue siendo una opción con soporte completo. FrankenPHP en modo clásico puede ocupar su lugar, pero es una alternativa.

¿Cómo uso FrankenPHP con Laravel Octane?

Instala Octane con composer require laravel/octane, ejecuta php artisan octane:install --server=frankenphp para descargar el binario y arranca con php artisan octane:frankenphp. La guía de FrankenPHP lista las opciones.

¿Qué es el modo worker de FrankenPHP?

Es el modo en que la aplicación arranca una vez y queda en memoria, y cada petición la atiende esa instancia ya cargada. Es más rápido porque se salta el arranque, pero las variables estáticas, los singletons y los cambios de configuración sobreviven entre peticiones.

¿Actualizar PHP en el servidor actualiza el PHP de FrankenPHP?

No si usas el binario estático, que es el que descarga Octane. Trae su propia copia de PHP: hay que borrar el binario y volver a ejecutar php artisan octane:install --server=frankenphp. Con las imágenes de Docker, hay que reconstruir sobre la etiqueta nueva.

¿Qué versión de FrankenPHP corrige php2shell?

La 1.13.2, del 21 de octubre de 2026. Conviene ir directo a la 1.13.4, que corrige un crash de todo el proceso en modo worker introducido en la 1.13.2.

Fuentes

¿Tienes un proyecto en mente?

Trabajo con Laravel, WordPress, SEO técnico y servidores MCP. El primer paso es una llamada de descubrimiento, sin costo ni compromiso, donde me cuentas qué necesitas y te digo con honestidad si puedo ayudarte.

Hablemos