Ce que change FrankenPHP par rapport à PHP-FPM
FrankenPHP est un serveur d'application écrit en Go, construit sur Caddy, qui embarque directement l'interpréteur PHP via cgo. Il remplace le duo nginx + PHP-FPM par un seul binaire capable de servir du HTTP/1, HTTP/2, HTTP/3, du TLS automatique et des fichiers statiques, sans configuration réseau supplémentaire entre un serveur web et un pool de processus PHP.
L'intérêt principal en conteneur est double : une image plus simple (un seul processus à superviser) et un mode worker qui garde l'application PHP (Symfony, Laravel...) chargée en mémoire entre les requêtes, évitant de rebootstraper le framework à chaque appel.
Une image Docker minimale
L'équipe FrankenPHP publie des images officielles basées sur des variantes Alpine ou Debian. Un Dockerfile simple pour une app PHP classique :
FROM dunglas/frankenphp:php8.3-alpine
COPY . /app
WORKDIR /app
RUN install-php-extensions pdo_mysql opcache
ENV SERVER_NAME=":80"
Le binaire frankenphp lit un Caddyfile pour sa configuration. Par défaut, l'image en fournit un minimal, mais on peut le surcharger pour activer le mode worker :
{
frankenphp
}
:80 {
root * /app/public
php_server {
worker /app/public/index.php
}
}
Build multi-stage pour la prod
Comme pour toute image PHP, on sépare la construction des dépendances (Composer, assets) de l'image finale :
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist
FROM dunglas/frankenphp:php8.3
COPY --from=vendor /app/vendor /app/vendor
COPY . /app
WORKDIR /app
Pièges du mode worker
- Le code applicatif reste chargé entre les requêtes : toute variable statique ou singleton mal réinitialisé peut fuir d'une requête à l'autre. Symfony et Laravel proposent des adaptateurs (
frankenphp-symfony,laravel-octane) qui gèrent ce reset entre chaque itération. - Les fuites mémoire progressives ne sont plus corrigées automatiquement par la fin de processus PHP-FPM classique ; il faut surveiller la consommation ou configurer
max_requestspour recycler périodiquement les workers. - Les extensions PHP chargées dynamiquement doivent être compatibles ZTS (Zend Thread Safety), requis par le modèle de FrankenPHP.
Développement local
Pour itérer sans reconstruire l'image, on monte le code en volume et on désactive le mode worker (rechargement à chaque requête, comme PHP-FPM classique) :
docker run -v $PWD:/app -p 80:80 dunglas/frankenphp
On réserve le mode worker aux environnements de test de charge et à la production, où le gain de performance justifie la rigueur supplémentaire sur la gestion d'état.