Dockerfile Laravel Propre - Guide Complet pour la Production

Étienne Lambert .

16 mars 2026

Comparaison de Dockerfile : les mauvaises pratiques (rouge) vs les bonnes pratiques (vert) pour un projet Laravel.

Un projet Laravel conteneurisé tient rarement sur une seule couche de configuration : il faut aligner les extensions PHP, les permissions, le cache, le mode d’exécution et la séparation entre développement et production. Dans cet article, je montre comment structurer un laravel dockerfile propre, quelles décisions font vraiment la différence, et comment éviter les images lourdes ou fragiles. L’objectif n’est pas de dockeriser pour cocher une case, mais d’obtenir un environnement reproductible que l’on peut maintenir sans douleur.

Les points qui comptent avant d’écrire la première ligne du build

  • Le Dockerfile construit l’image PHP, mais les services voisins restent à part dans Docker Compose ou l’orchestrateur.
  • Une base FPM légère suffit dans la majorité des cas, à condition d’installer seulement les extensions nécessaires.
  • Le multi-stage build réduit la taille finale et évite d’embarquer les outils de compilation dans l’image runtime.
  • Le développement local et la production ne doivent pas partager exactement les mêmes réglages.
  • Les secrets ne doivent jamais être gravés dans l’image, et les dossiers `storage` et `bootstrap/cache` doivent rester écriables.

Construire une base Laravel propre sans confondre image et environnement

Je vois souvent des équipes traiter le Dockerfile comme si c’était tout l’environnement. En pratique, il ne gère que l’image de l’application; les services voisins restent dans Compose ou dans l’orchestrateur. Pour un backend Laravel, cette séparation est saine : PHP-FPM dans un conteneur, Nginx dans un autre, la base de données ailleurs, et éventuellement Redis ou un worker pour les queues.
  • Le Dockerfile doit rester centré sur le runtime PHP.
  • Compose gère le lien entre l’application, la base et les services annexes.
  • Le développement local peut monter le code en volume, alors que la production doit rester immuable.
  • Les variables sensibles doivent venir de l’environnement, pas du build.

Cette découpe évite les conteneurs “fourre-tout” où chaque changement de service relance tout le reste. Une fois ce cadre posé, le choix de la base d’image et des extensions devient beaucoup plus simple.

Diagramme d'un environnement Docker pour Laravel : Nginx et PHP-FPM montés sur des volumes du HOST pour les données et les logs.

Choisir la bonne base et les bonnes extensions

Je pars généralement d’une image PHP-FPM légère, puis j’ajoute seulement les extensions réellement utilisées. Pour une API Laravel, je préfère garder une surface minimale et éviter d’empiler des paquets “au cas où”. Le bon choix dépend surtout du niveau de confort de l’équipe avec la compilation des dépendances système et du type de charge attendu.

Base Quand je la choisis Avantages Limites
php:8.4-fpm-bookworm-slim Par défaut pour la plupart des projets Laravel Bon compromis entre taille, compatibilité et facilité de maintenance Un peu plus volumineuse qu’une variante Alpine
php:8.4-fpm-alpine Quand chaque mégaoctet compte et que l’équipe maîtrise bien l’écosystème Alpine Image plus petite, démarrage souvent rapide Compilation et débogage parfois plus pénibles, surtout avec certaines extensions
dunglas/frankenphp Quand je veux un runtime moderne avec un serveur intégré, ou quand Octane fait partie du plan Stack plus compacte, bonne option pour certains déploiements Laravel Change le modèle d’exécution, donc demande plus de validation opérationnelle

Côté extensions, je garde une logique de besoin réel : pdo_mysql ou pdo_pgsql pour la base, intl pour l’internationalisation, zip pour les archives, opcache pour la performance, et redis si les queues ou le cache l’exigent. Si le projet manipule des montants ou des calculs précis, j’ajoute aussi bcmath. Le réflexe à éviter, c’est d’installer tout ce qui semble “utile un jour” : on alourdit le build sans améliorer le service rendu.

Avec ces choix verrouillés, le Dockerfile lui-même devient beaucoup plus simple à lire et à maintenir.

Un exemple de Dockerfile de production prêt à adapter

Voici la structure que je privilégie quand je veux une image Laravel sérieuse pour la production. Elle sépare les dépendances applicatives du runtime final, ce qui réduit la taille de l’image et limite les outils inutiles dans l’environnement d’exécution.
FROM php:8.4-fpm-bookworm AS base

WORKDIR /var/www/html

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        git \
        unzip \
        libicu-dev \
        libzip-dev \
        libpng-dev \
    && docker-php-ext-install \
        pdo_mysql \
        intl \
        zip \
        opcache \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

FROM base AS vendor

COPY composer.json composer.lock ./
RUN composer install \
    --no-interaction \
    --no-progress \
    --prefer-dist \
    --no-dev \
    --optimize-autoloader

FROM base AS app

COPY . .
COPY --from=vendor /var/www/html/vendor ./vendor

RUN chown -R www-data:www-data storage bootstrap/cache \
    && chmod -R ug+rwX storage bootstrap/cache

USER www-data

EXPOSE 9000
CMD ["php-fpm"]

La logique ici est volontairement simple. Je copie d’abord uniquement `composer.json` et `composer.lock` pour profiter du cache de build, puis je construis le répertoire `vendor` dans un stage séparé. Ensuite seulement, je copie le code applicatif complet. Ce détail change beaucoup de choses dès que les dépendances changent moins souvent que le code métier.

Je n’active pas systématiquement `config:cache` ou `route:cache` dans le build lui-même, parce que je préfère garder ce genre d’optimisation au moment du déploiement, quand les variables d’environnement sont réellement présentes et stables. C’est plus sûr, surtout quand plusieurs environnements coexistent.

Une fois ce socle en place, la vraie question devient l’adaptation au local, parce qu’on n’exécute pas la même image de la même manière selon l’environnement.

Adapter le même socle au développement local

En local, j’accepte un peu plus de souplesse : code monté en volume, Xdebug si nécessaire, et parfois des commandes interactives pour simplifier le diagnostic. En production, je veux l’inverse : image figée, pas de dépendances inutiles, pas de code modifié à la volée depuis l’hôte.

Aspect Développement Production
Code source Monté en volume pour voir les changements immédiatement Copié dans l’image pour garder un artefact immuable
Dépendances PHP Installées avec les besoins du débogage et parfois les paquets de dev Installées en mode `--no-dev`
Debug Xdebug, logs plus bavards, cycle de correction rapide Debug désactivé, journalisation maîtrisée
Cache Laravel Souvent régénéré ou laissé souple pour éviter les surprises Activé au déploiement quand l’environnement est stable
Modèle d’exécution FPM + Nginx, ou stack plus simple pour itérer vite FPM, Octane ou FrankenPHP selon les besoins réels

Si l’équipe travaille beaucoup sur une API, je préfère souvent un `docker-compose.override.yml` plutôt qu’un second Dockerfile. Le Dockerfile reste unique; l’override ajuste seulement les volumes, les ports, les variables et la commande de démarrage. C’est plus lisible, et surtout plus facile à faire évoluer quand la stack grossit.

Et même avec une bonne organisation, quelques erreurs reviennent sans cesse; c’est là qu’il faut être strict.

Les erreurs qui cassent les déploiements

Les incidents les plus agaçants ne viennent pas de Laravel lui-même, mais d’un Dockerfile trop permissif ou mal découpé. Je retrouve les mêmes pièges dans beaucoup de projets, et ils coûtent du temps au moment où l’on veut livrer vite.

Erreur Effet concret Correction que j’applique
Copier tout le projet avant `composer install` Le cache saute à chaque modification du code Je copie d’abord les fichiers de dépendances, puis le reste du projet
Graver des secrets dans l’image Risque de fuite et difficulté à refaire un build propre Je sors les secrets du Dockerfile et je passe par les variables d’environnement ou un gestionnaire de secrets
Laisser le conteneur en root Surface d’attaque plus large et problèmes de permissions plus fréquents Je bascule vers `www-data` dès que le runtime est prêt
Oublier `.dockerignore` Contexte de build gonflé par `vendor`, `node_modules`, `.git` ou les logs Je retire tout ce qui n’a rien à faire dans l’image
Mal gérer `storage` et `bootstrap/cache` Laravel ne peut plus écrire ses fichiers temporaires ou ses caches Je corrige les droits avant de lancer PHP-FPM
Vouloir mélanger build frontend et runtime backend sans séparation Image plus lourde, plus lente à construire, plus difficile à maintenir Je sors le build JS dans un stage dédié ou dans un pipeline séparé

Quand je nettoie un projet, j’ajoute presque toujours un `.dockerignore` minimal comme celui-ci :

.git
node_modules
vendor
storage/logs
.env

Le message est simple : le Dockerfile doit contenir ce qui sert à exécuter l’application, pas tout l’historique du dépôt. Une fois ces pièges éliminés, il reste les derniers arbitrages qui font la différence au quotidien.

Ce que je garde en tête avant de livrer l’image

Avant de pousser une image Laravel en production, je vérifie toujours quelques points très concrets. Ils ne sont pas spectaculaires, mais ils évitent la majorité des mauvaises surprises.

  • Je reconstruis l’image depuis zéro pour m’assurer que rien ne dépend d’un état local caché.
  • Je lance le conteneur avec seulement les variables nécessaires, sans `.env` embarqué dans l’image.
  • Je sépare les rôles si l’application doit aussi exécuter des queues, des tâches planifiées ou Octane.
  • Je surveille la taille finale et la vitesse de démarrage, parce qu’un conteneur trop lourd finit toujours par coûter plus cher.
  • Je garde un tag d’image immuable pour pouvoir reproduire exactement le même build si un rollback devient nécessaire.

Si je devais résumer ma méthode, je partirais d’un socle PHP-FPM minimal, je séparerais nettement le build des dépendances de l’exécution, et je laisserais Docker Compose gérer les services adjacents. C’est, à mon sens, la manière la plus fiable de faire tenir Laravel dans un conteneur sans transformer le fichier en usine à gaz.

Questions fréquentes

Séparer le Dockerfile (qui construit l'image PHP) des services voisins (gérés par Docker Compose ou l'orchestrateur) permet d'éviter les conteneurs "fourre-tout". Chaque service a son rôle, rendant l'ensemble plus maintenable, évolutif et réduisant les dépendances inutiles entre composants.
Pour la plupart des projets Laravel, une image comme `php:8.4-fpm-bookworm-slim` offre un bon compromis entre taille, compatibilité et facilité de maintenance. Les variantes Alpine sont plus petites mais peuvent rendre la compilation ou le débogage plus complexes.
Utilisez le multi-stage build pour séparer les étapes de compilation des dépendances (ex: `composer install`) de l'image finale de runtime. Cela permet de ne pas embarquer les outils de build et les dépendances de développement dans l'image de production, la rendant plus légère et sécurisée.
Évitez de copier tout le projet avant `composer install`, de graver des secrets dans l'image, de laisser le conteneur en `root`, d'oublier `.dockerignore`, de mal gérer les permissions de `storage` et `bootstrap/cache`, et de mélanger build frontend et backend.
En développement, privilégiez le montage du code source en volume pour des changements immédiats, installez les dépendances avec les outils de débogage (Xdebug) et utilisez un `docker-compose.override.yml` pour ajuster les volumes, ports et variables sans modifier le Dockerfile de base.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

laravel dockerfile dockerfile laravel production optimiser dockerfile laravel dockerfile laravel multi-stage build dockerfile laravel php-fpm
Autor Étienne Lambert
Étienne Lambert
Je m'appelle Étienne Lambert et j'ai 13 ans d'expérience dans le développement web, avec un accent particulier sur JavaScript, le backend, NoSQL et la sécurité. Mon parcours dans ce domaine a commencé par une curiosité insatiable pour la technologie et la manière dont elle façonne notre monde. J'aime partager mes connaissances et aider les lecteurs à naviguer dans les complexités du développement web, en rendant des sujets parfois ardus plus accessibles. Je m'efforce toujours de fournir des informations utiles, précises et à jour, en vérifiant mes sources et en comparant les différentes perspectives. J'écris sur des sujets variés qui vont des meilleures pratiques en matière de sécurité aux tendances émergentes dans le développement. Mon objectif est de simplifier des concepts techniques et d'organiser les connaissances de manière claire, afin que chacun puisse en tirer profit et se sentir confiant dans ses compétences en développement web.

Commentaires (0)

Ajouter un commentaire