Journal

FrankenPHP en conteneur Docker : mode worker et pièges à éviter

Journal du mercredi 26 août 2026, généré le 26 août à 21:48

Un serveur PHP qui remplace Nginx + PHP-FPM

FrankenPHP est un serveur d'application écrit en Go, basé sur Caddy, qui embarque directement l'interpréteur PHP. Il supprime le duo classique Nginx/PHP-FPM au profit d'un seul binaire capable de servir du HTTP/1, HTTP/2, HTTP/3, de gérer le TLS automatique et d'exécuter du PHP en mode worker pour éviter de rebootstraper le framework à chaque requête.

Construire l'image Docker

L'image officielle dunglas/frankenphp sert de base. Un Dockerfile minimal pour une application Symfony ou Laravel ressemble à ceci :

FROM dunglas/frankenphp:php8.3

COPY . /app
WORKDIR /app

RUN install-php-extensions \
    pdo_mysql opcache intl zip

RUN composer install --no-dev --optimize-autoloader

ENV FRANKENPHP_CONFIG="worker ./public/index.php"

Contrairement à une image PHP-FPM classique, il n'y a pas besoin d'un conteneur Nginx séparé ni de configuration de socket : le binaire écoute directement sur le port 80/443.

Configuration via le Caddyfile

Pour affiner le comportement (nombre de workers, watch en dev, compression), on peut fournir un Caddyfile personnalisé :

{
    frankenphp {
        num_threads 4
        worker {
            file ./public/index.php
            num 2
        }
    }
}

:80 {
    root * /app/public
    php_server
}

Mode worker : le vrai gain de performance

Sans mode worker, FrankenPHP se comporte comme PHP-FPM classique : chaque requête réinitialise le framework. Le mode worker garde le kernel de l'application (Symfony Kernel, Laravel bootstrap) chargé en mémoire entre les requêtes, ce qui peut diviser le temps de réponse par deux ou trois sur des applications lourdes en autoloading.

Le compromis : il faut s'assurer que le code applicatif ne conserve pas d'état entre les requêtes (variables statiques, connexions non fermées). Une fuite de mémoire dans un service qui vivrait normalement le temps d'une requête devient un problème cumulatif sur toute la durée de vie du worker.

Points d'attention en production

  • Redémarrage progressif : utilisez frankenphp reload ou un signal SIGHUP pour recharger les workers après un déploiement, sans interrompre les connexions en cours.
  • Watch en développement : l'option watch du Caddyfile redémarre automatiquement les workers quand un fichier PHP change, utile en local mais à désactiver en production.
  • Extensions natives : certaines extensions PHP historiquement pensées pour un modèle "une requête, un process" (comme xdebug) doivent être utilisées avec précaution en mode worker.
  • Compilation statique : FrankenPHP permet aussi de produire un binaire autonome embarquant l'application, une option intéressante pour distribuer un CLI ou un outil interne sans dépendance à un environnement PHP.