Introduction
Bienvenue sur mon blog où Symfony et tout son écosystème sont à l’honneur !
Comme beaucoup d’entre vous, je découvre peu à peu l’univers de l’Ops. Aujourd’hui, je vous propose de plonger dans l’installation d’un serveur web moderne avec FrankenPHP. Et vous allez voir, FrankenPHP met littéralement une claque monumentale à Apache !
Grâce à sa puissance, sa simplicité d’utilisation et son intégration parfaite avec Docker, FrankenPHP permet de déployer rapidement des environnements sur mesure. Que ce soit pour faire tourner différentes versions de PHP, installer un serveur de mails local comme Mailtrap, ou lancer un projet Symfony complet, les possibilités sont immenses.
Dans ce guide, nous allons voir ensemble :
- Pourquoi choisir Docker pour vos projets web
- Qu’est-ce que FrankenPHP et pourquoi il révolutionne le serveur PHP traditionnel
Préparez-vous à franchir un cap dans la modernisation de vos applications Symfony ! 🚀
Docker : pourquoi se compliquer la vie ? 🐳
Nous y voilà : Docker, l’outil que tout le monde semble utiliser aujourd’hui.
Soyons honnêtes : à mes yeux, Docker est avant tout un outil de gestion d’environnements. Plus DevOps que purement développeur, en réalité.
Mais dans un monde où les environnements applicatifs deviennent de plus en plus complexes, il est désormais indispensable pour tout développeur expérimenté de maîtriser Docker. Pourquoi ? Simplement parce que Docker permet de :
- Travailler dans un environnement identique à celui de la production
- Créer facilement de la redondance pour tester la charge et la montée en capacité
- Isoler chaque projet avec ses propres dépendances, sans conflits
Bien sûr, Docker n’est pas parfait.
L’inconvénient majeur, c’est la consommation de ressources : faire tourner une VM qui, elle-même, fait tourner plusieurs conteneurs peut devenir lourd sur certaines machines, surtout quand les projets se multiplient.
Malgré tout, les avantages surpassent largement les inconvénients, surtout lorsqu’on veut un serveur PHP moderne, léger et modulable avec des solutions comme FrankenPHP.
FrankenPHP : pourquoi c’est devenu indispensable 🚀
Apache, soyons honnêtes, n’est plus tout jeune… et c’est un doux euphémisme.
Pendant que l’écosystème JavaScript évoluait à une vitesse folle, le monde PHP avait besoin d’un sérieux coup de jeune. Heureusement, Kevin Dunglas est arrivé avec FrankenPHP, un serveur web nouvelle génération qui bouscule toutes nos habitudes !
Et ce n’est pas juste une mise à jour : FrankenPHP apporte de vraies innovations :
- Support natif des Early Hints pour optimiser le temps de chargement des ressources critiques
- Mode Worker pour faire tourner PHP en mode multi-processus ultra-performant
- Serveur web Caddy intégré, ultra-rapide et moderne
- Intégration native de Mercure pour le temps réel et les notifications push
Le gain de performance est spectaculaire :
Une même page Symfony se charge jusqu’à 4 fois plus vite qu’avec une stack classique Apache+PHP-FPM, tout en consommant beaucoup moins de mémoire.
En un mot : magique.

Le Dockerfile : la base de tout 🛠
Quand on a une architecture simple, pas besoin de se compliquer la vie : tout commence par un Dockerfile.
Ce fichier sert à donner des instructions précises à Docker :
- Quelle image de base utiliser (récupérée automatiquement depuis le Docker Hub, le dépôt officiel des images Docker)
- Comment copier les fichiers du projet
- Quelles commandes exécuter pour configurer l’environnement
Le Docker Hub, pour faire simple, c’est la bibliothèque centrale où sont stockées toutes les images Docker prêtes à l’emploi.
La documentation officielle de FrankenPHP recommande de démarrer avec un Dockerfile minimaliste.
L’idée est simple : partir léger, et faire évoluer le Dockerfile au fur et à mesure des besoins spécifiques du projet.
C’est une approche qui évite de tomber dans une configuration inutilement complexe dès le départ.
Les commandes Docker : gérer vos conteneurs efficacement ⚙️
Comme tout système, Docker vient avec ses propres commandes personnalisées.
Et la magie, c’est qu’en plus de créer et de gérer des conteneurs, Docker permet de les relancer à chaud sans devoir tout reconstruire.
Lorsque vous utilisez une image existante depuis le Docker Hub, Docker ne va pas la télécharger à chaque fois.
Au lieu de cela, il va réutiliser l’image locale et la mapper directement au nouveau conteneur créé pour votre projet.
👉 Un énorme gain de temps et d’espace disque !
# 1. Vérifier que Docker fonctionne
docker --version
# 2. Construire l'image (remplace 'mon-app' par le nom que tu veux)
docker build -t mon-app .
# 3. Lister tes images pour vérifier qu'elle existe
docker images
# 4. Démarrer un conteneur à partir de ton image
docker run -p 8080:80 mon-app
# 5. Ouvrir un navigateur à http://localhost:8080 pour tester
# 6. Voir les conteneurs qui tournent
docker ps
# 7. Arrêter ton conteneur (remplace CONTAINER_ID par l'ID réel)
docker stop CONTAINER_ID
# 8. Voir les logs de ton conteneur
docker logs CONTAINER_ID
# 9. Entrer dans ton conteneur (pour le debugging)
docker exec -it CONTAINER_ID sh
# 10. Nettoyer (supprimer conteneurs arrêtés, images orphelines)
docker system prune
Attention au cache !
Docker utilise un système de cache intelligent pour accélérer les déploiements.
Cependant, il peut parfois devenir un problème : si vous mettez à jour votre image ou vos fichiers sources, Docker risque de réutiliser une ancienne version par erreur.
Dans ce cas, pensez à reconstruire proprement votre conteneur avec :
docker compose build --no-cache
Cela indique à Docker de tout recompiler sans utiliser d’anciennes couches en cache.
Vous pouvez également cibler un conteneur précis si vous avez plusieurs services dans votre docker-compose.yml
.
Le squelette Symfony + FrankenPHP 🚀
Pour un projet Symfony avec FrankenPHP, voici un Dockerfile plus avancé que le minimum vital.
Il contient déjà :
- Les principales extensions PHP nécessaires aux projets modernes (MySQL, GD, Intl, Zip, Opcache…)
- Le mode Worker activé pour profiter pleinement des performances de FrankenPHP
⚡ Important : pour que Symfony fonctionne correctement en mode Worker, n’oubliez pas d’installer le package adapté avec Composer :
composer require runtime/frankenphp-symfony
FROM dunglas/frankenphp:latest
WORKDIR /app
RUN install-php-extensions \
pdo_mysql \
gd \
intl \
zip \
opcache
# Copier Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Copier l'application
COPY . .
RUN chmod +x bin/console
# FrankenPHP avec Symfony
ENV FRANKENPHP_CONFIG="worker /app/public/index.php"
ENV APP_RUNTIME="Runtime\\FrankenPhpSymfony\\Runtime"
ENV SERVER_NAME=localhost
EXPOSE 80 443
Avec ce Dockerfile, votre projet Symfony est prêt à tourner en mode Worker, bénéficiant ainsi d’un temps de réponse ultra-rapide et d’une consommation mémoire drastiquement réduite par rapport à une stack PHP classique.
Docker Compose : construire une architecture solide 🏗️
En 2025, les besoins en architecture de projets web sont de plus en plus variés, et c’est parfaitement normal.
Pour ce guide, on vise l’excellence : créer un environnement de test ultra-complet pour Symfony et FrankenPHP.
Voici ce que nous allons intégrer à notre stack Docker :
Et bientôt : un cache Redis, car on vise le top.
Une base de données PostgreSQL
Un outil d’administration de base avec PGAdmin
Un serveur de mails local avec Mailcatcher
Un stockage S3-compatible via MinIO
Mercure Hub pour le temps réel ( mis à jour bientôt).
Voici notre docker-compose.yml
complet
services:
php:
build:
context: .
dockerfile: docker/Dockerfile
volumes:
- .:/app
- ./docker/php.ini:/usr/local/etc/php/conf.d/app.ini
- ./docker/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
- ./docker/certs:/etc/caddy/certs
ports:
- "8080:80"
- "443:443"
depends_on:
- database
- redis
environment:
- APP_ENV=dev
- APP_DEBUG=1
- SERVER_NAME=localhost:443
- DATABASE_URL=postgresql://app:password@database:5432/app
- MERCURE_URL=http://php/.well-known/mercure
- MERCURE_PUBLIC_URL=http://localhost/.well-known/mercure
- MERCURE_JWT_SECRET=!ChangeThisMercureHubJWTSecretKey!
- REDIS_URL=redis://redis:6379
database:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: app
POSTGRES_DB: app
volumes:
- database_data:/var/lib/postgresql/data
ports:
- "5432:5432"
pgadmin:
image: dpage/pgadmin4
environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin
ports:
- "5050:80"
depends_on:
- database
redis:
image: redis:alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
minio:
image: minio/minio
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
command: server /data --console-address ":9001"
mailer:
image: schickling/mailcatcher
ports:
- "1025:1025"
- "1080:1080"
###> symfony/mercure-bundle ###
mercure:
image: dunglas/mercure
restart: unless-stopped
environment:
# Uncomment the following line to disable HTTPS,
#SERVER_NAME: ':80'
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
# Set the URL of your Symfony project (without trailing slash!) as value of the cors_origins directive
MERCURE_EXTRA_DIRECTIVES: |
cors_origins http://127.0.0.1:8000
# Comment the following line to disable the development mode
command: /usr/bin/caddy run --config /etc/caddy/dev.Caddyfile
healthcheck:
test: ["CMD", "curl", "-f", "https://localhost/healthz"]
timeout: 5s
retries: 5
start_period: 60s
volumes:
- mercure_data:/data
- mercure_config:/config
###< symfony/mercure-bundle ###
volumes:
database_data:
redis_data:
minio_data:
caddy_data:
caddy_config:
###> symfony/mercure-bundle ###
mercure_data:
mercure_config:
###< symfony/mercure-bundle ###
Points forts de cette architecture
- Séparation claire entre les services
- Volumes dédiés pour persister les données
- Services critiques comme PostgreSQL, Redis et MinIO prêts à l’emploi
- Mercure opérationnel pour le temps réel
- Extensibilité future (ajout possible de RabbitMQ, ElasticSearch, etc.)
Tout lancer en 2 minutes chrono ! ⏱️
Maintenant que tout est en place, il ne vous reste plus qu’à lancer votre environnement Docker en quelques commandes ultra-simples :
docker compose down
docker compose build php
docker compose up -d
Explication rapide :
docker compose up -d
: pour démarrer tous les services en mode « détaché » (en arrière-plan)
docker compose down
: pour tout arrêter proprement si un conteneur tourne déjà
docker compose build php
: pour reconstruire uniquement le service PHP avec le dernier Dockerfile
Résultat 🎉
Si tout est bien configuré (vous pouvez facilement vérifier dans Docker Desktop),
👉 tous vos conteneurs seront démarrés sans erreur.
Il ne reste plus qu’à ouvrir votre navigateur et accéder à :
https://localhost
Et là, Bienvenue dans un environnement Symfony moderne, rapide et ultra-puissant, propulsé par FrankenPHP ! ⚡



Conclusion : FrankenPHP + Docker, l’avenir de Symfony ? 🚀
Comme nous venons de le voir, FrankenPHP est aujourd’hui un véritable « must-have » pour tout développeur PHP moderne.
Avec cette petite introduction à Docker, vous disposez déjà des bases solides pour comprendre, construire et déployer un projet Symfony performant en quelques minutes.
Grâce à cette architecture :
- Vous pouvez faire tourner FrankenPHP rapidement, sans prise de tête
- Vous bénéficiez d’un gain de performances énorme face à un setup Apache traditionnel
- Votre environnement est scalable, future-proof, et prêt à évoluer avec vos projets
Et ce n’est que le début : la stack est totalement modulaire (Redis, Mercure, MinIO…), et pourra encore s’enrichir facilement. 🔥
Concrètement, nous venons de créer un environnement Dockerisé complet, plug & play, prêt pour le développement comme pour la production. Magique, non ?
Disclaimer :
On entend souvent dire que « PHP est mort »…
Après avoir testé FrankenPHP et Docker ensemble, on peut clairement affirmer que PHP a encore énormément d’avenir !
Cet article t’a plu ?
J’essaie autant que possible de rendre ces articles simple et accessible, donc reste pas loin pour suivre la suite.
Si tu veux aller plus loin => la doc de frankenPHP
Laisser un commentaire