Journal

FrankenPHP en production : un Dockerfile qui tient la route

Journal du jeudi 13 août 2026, généré le 23 août à 18:58

Ce que change FrankenPHP

FrankenPHP est un serveur d'application PHP écrit en Go, construit sur Caddy. Il embarque directement l'interpréteur PHP dans le binaire du serveur web : plus besoin de séparer Nginx et PHP-FPM, ni de gérer un socket Unix entre les deux. Un seul processus sert les requêtes, gère le HTTPS automatique (Let's Encrypt) et HTTP/3, et peut charger l'application en worker mode pour éviter de rebootstrapper le framework à chaque requête.

Dockerfile minimal

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

FROM dunglas/frankenphp:1-php8.3

RUN install-php-extensions \
    pdo_pgsql \
    opcache \
    zip

COPY . /app
WORKDIR /app

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

ENV SERVER_NAME=":80"
ENV FRANKENPHP_CONFIG="worker ./public/index.php"

EXPOSE 80

La variable SERVER_NAME pilote le comportement Caddy (domaine, port, HTTPS auto). Le FRANKENPHP_CONFIG active le mode worker : le script index.php est chargé une fois en mémoire, puis FrankenPHP lui délègue chaque requête via une boucle, à la manière de Swoole ou RoadRunner.

Le mode worker, avec précaution

En mode classique, chaque requête recharge le framework depuis zéro, comme avec PHP-FPM. En mode worker, l'état PHP (connexions DB, conteneur de services) reste résident entre les requêtes : gain de performance important, mais attention aux fuites d'état. Toute variable statique ou singleton mal réinitialisé entre deux requêtes peut fuiter d'un utilisateur à l'autre. Symfony et Laravel exposent des points d'entrée dédiés (frankenphp-worker.php, package laravel/octane avec le driver FrankenPHP) qui réinitialisent proprement le conteneur de services à chaque itération.

Docker Compose pour le développement

services:
  app:
    build: .
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/app
      - caddy_data:/data
    environment:
      SERVER_NAME: "localhost"

volumes:
  caddy_data:

Le volume caddy_data conserve les certificats TLS générés automatiquement entre les redémarrages du conteneur.

Points d'attention en production

  • Compiler les extensions PHP nécessaires au build via install-php-extensions, pas au runtime.
  • Activer opcache avec opcache.preload pointant vers l'autoload Composer pour cumuler les gains du worker mode.
  • Séparer les workers HTTP des workers de queue (Symfony Messenger, Laravel Queue) : ne pas faire tourner les jobs asynchrones dans le même worker que le serveur web.
  • Surveiller la mémoire : un worker qui vit longtemps peut accumuler des fuites invisibles en mode FPM classique.