⚠️ Traduction non officielle - Cette documentation est une traduction communautaire non officielle de Docker.

Configuration de production Laravel avec Docker Compose

Ce guide montre comment mettre en place un environnement Laravel prêt pour la production en utilisant Docker et Docker Compose. Cette configuration est conçue pour des déploiements d'applications Laravel rationalisés, évolutifs et sécurisés.

Note

Pour expérimenter une configuration prête à l'emploi, téléchargez le dépôt Exemples Docker Laravel. Il contient des configurations pré-configurées pour le développement et la production.

Structure du projet

mon-app-laravel/
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── docker/
│   ├── common/
│   │   └── php-fpm/
│   │       └── Dockerfile
│   ├── development/
│   ├── production/
│   │   ├── php-fpm/
│   │   │   └── entrypoint.sh
│   │   └── nginx
│   │       ├── Dockerfile
│   │       └── nginx.conf
├── compose.dev.yaml
├── compose.prod.yaml
├── .dockerignore
├── .env
├── vendor/
├── ...

Cette disposition représente un projet Laravel typique, avec les configurations Docker stockées dans un répertoire docker unifié. Vous trouverez deux fichiers Compose — compose.dev.yaml (pour le développement) et compose.prod.yaml (pour la production) — pour garder vos environnements séparés et gérables.

Créer un Dockerfile for PHP-FPM (production)

Pour la production, le Dockerfile php-fpm crée une image optimisée avec uniquement les extensions PHP et les bibliothèques dont votre application a besoin. Comme le montre l'exemple GitHub, un seul Dockerfile avec des constructions multi-étapes maintient la cohérence et réduit la duplication entre le développement et la production. L'extrait suivant ne montre que les étapes liées à la production :

# Étape 1 : Environnement de construction et dépendances Composer
FROM php:8.4-fpm AS builder

# Installer les dépendances système et les extensions PHP pour Laravel avec prise en charge de MySQL/PostgreSQL.
# Les dépendances de cette étape ne sont requises que pour la construction de l'image finale.
# Node.js et la construction des ressources sont gérés dans l'étape Nginx, pas ici.
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    unzip \
    libpq-dev \
    libonig-dev \
    libssl-dev \
    libxml2-dev \
    libcurl4-openssl-dev \
    libicu-dev \
    libzip-dev \
    && docker-php-ext-install -j$(nproc) \
    pdo_mysql \
    pdo_pgsql \
    pgsql \
    opcache \
    intl \
    zip \
    bcmath \
    soap \
    && pecl install redis \
    && docker-php-ext-enable redis \
    && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Définir le répertoire de travail à l'intérieur du conteneur
WORKDIR /var/www

# Copier l'ensemble du code de l'application Laravel dans le conteneur
# -----------------------------------------------------------
# Dans Laravel, `composer install` peut déclencher des scripts
# nécessitant un accès au code de l'application.
# Par exemple, l'événement `post-autoload-dump` peut exécuter
# des commandes Artisan comme `php artisan package:discover`. Si le
# code de l'application (y compris le fichier `artisan`) n'est pas
# présent, ces commandes échoueront, entraînant des erreurs de construction.
#
# En copiant l'ensemble du code de l'application avant d'exécuter
# `composer install`, nous nous assurons que tous les fichiers nécessaires sont
# disponibles, permettant à ces scripts de s'exécuter avec succès.
# Dans d'autres cas, il serait possible de copier d'abord les fichiers composer
# pour tirer parti du mécanisme de mise en cache des couches de Docker.
# -----------------------------------------------------------
COPY . /var/www

# Installer Composer et les dépendances
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
    && composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist

# Étape 2 : Environnement de production
FROM php:8.4-fpm

# Installer uniquement les bibliothèques d'exécution nécessaires en production
# libfcgi-bin et procps sont requis pour le script php-fpm-healthcheck
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq-dev \
    libicu-dev \
    libzip-dev \
    libfcgi-bin \
    procps \
    && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Télécharger et installer le script de vérification de santé de php-fpm
RUN curl -o /usr/local/bin/php-fpm-healthcheck \
    https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck \
    && chmod +x /usr/local/bin/php-fpm-healthcheck

# Copier le script d'initialisation
COPY ./docker/php-fpm/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# Copier la structure de stockage initiale
COPY ./storage /var/www/storage-init

# Copier les extensions PHP et les bibliothèques de l'étape de construction
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/

# Utiliser la configuration PHP de production recommandée
# -----------------------------------------------------------
# PHP fournit des configurations de développement et de production.
# Ici, nous remplaçons le php.ini par défaut par la version
# de production pour appliquer des paramètres optimisés pour les performances
# et la sécurité dans un environnement live.
# -----------------------------------------------------------
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Activer la page de statut de PHP-FPM en modifiant zz-docker.conf avec sed
RUN sed -i '/\[www\]/a pm.status_path = /status' /usr/local/etc/php-fpm.d/zz-docker.conf
# Mettre à jour le variables_order pour inclure E (pour ENV)
#RUN sed -i 's/variables_order = "GPCS"/variables_order = "EGPCS"/' "$PHP_INI_DIR/php.ini"

# Copier le code de l'application et les dépendances de l'étape de construction
COPY --from=builder /var/www /var/www

# Définir le répertoire de travail
WORKDIR /var/www

# Assurer les permissions correctes
RUN chown -R www-data:www-data /var/www

# Passer à l'utilisateur non privilégié pour exécuter l'application
USER www-data

# Changer la commande par défaut pour exécuter le script d'entrée
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

# Exposer le port 9000 et démarrer le serveur php-fpm
EXPOSE 9000
CMD ["php-fpm"]

Créer un Dockerfile for PHP-CLI (production)

Pour la production, vous avez souvent besoin d'un conteneur séparé pour exécuter les commandes Artisan, les migrations et d'autres tâches CLI. Dans la plupart des cas, vous pouvez exécuter ces commandes en réutilisant le conteneur PHP-FPM existant :

$ docker compose -f compose.prod.yaml exec php-fpm php artisan route:list

Si vous avez besoin d'un conteneur CLI séparé avec des extensions différentes ou une séparation stricte des préoccupations, envisagez un Dockerfile php-cli :

# Étape 1 : Environnement de construction et dépendances Composer
FROM php:8.4-cli AS builder

# Installer les dépendances système et les extensions PHP requises pour Laravel + support MySQL/PostgreSQL
# Certaines dépendances ne sont requises pour les extensions PHP que dans l'étape de construction
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    unzip \
    libpq-dev \
    libonig-dev \
    libssl-dev \
    libxml2-dev \
    libcurl4-openssl-dev \
    libicu-dev \
    libzip-dev \
    && docker-php-ext-install -j$(nproc) \
    pdo_mysql \
    pdo_pgsql \
    pgsql \
    opcache \
    intl \
    zip \
    bcmath \
    soap \
    && pecl install redis \
    && docker-php-ext-enable redis \
    && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Définir le répertoire de travail à l'intérieur du conteneur
WORKDIR /var/www

# Copier l'ensemble du code de l'application Laravel dans le conteneur
COPY . /var/www

# Installer Composer et les dépendances
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
    && composer install --no-dev --optimize-autoloader --no-interaction --no-progress --prefer-dist

# Étape 2 : Environnement de production
FROM php:8.4-cli

# Installer les bibliothèques clientes requises pour les extensions php en cours d'exécution
RUN apt-get update && apt-get install -y --no-install-recommends \
    libpq-dev \
    libicu-dev \
    libzip-dev \
    && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Copier les extensions PHP et les bibliothèques de l'étape de construction
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/

# Utiliser la configuration de production par défaut pour les arguments d'exécution de PHP
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

# Copier le code de l'application et les dépendances de l'étape de construction
COPY --from=builder /var/www /var/www

# Définir le répertoire de travail
WORKDIR /var/www

# Assurer les permissions correctes
RUN chown -R www-data:www-data /var/www

# Passer à l'utilisateur non privilégié pour exécuter l'application
USER www-data

# Commande par défaut : Fournir un shell bash pour permettre l'exécution de n'importe quelle commande
CMD ["bash"]

Ce Dockerfile est similaire au Dockerfile PHP-FPM, mais il utilise l'image php:8.4-cli comme image de base et configure le conteneur pour l'exécution de commandes CLI.

Créer un Dockerfile pour Nginx (production)

Nginx sert de serveur web pour l'application Laravel. Vous pouvez inclure des ressources statiques directement dans le conteneur. Voici un exemple de Dockerfile possible pour Nginx :

# docker/nginx/Dockerfile
# Étape 1 : Construction des ressources
FROM debian AS builder

# Installer Node.js et outils de construction
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl \
    nodejs \
    npm \
    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Définir le répertoire de travail
WORKDIR /var/www

# Copier le code de l'application Laravel
COPY . /var/www

# Installer les dépendances Node.js et construire les ressources
RUN npm install && npm run build

# Étape 2 : Image de production Nginx
FROM nginx:alpine

# Copier la configuration Nginx personnalisée
# -----------------------------------------------------------
# Remplacer la configuration Nginx par défaut par notre personnalisée
# qui est optimisé pour servir une application Laravel.
# -----------------------------------------------------------
COPY ./docker/nginx/nginx.conf /etc/nginx/nginx.conf

# Copier les ressources publiques d'application Laravel de l'étape de construction
# -----------------------------------------------------------
# Nous n'avons besoin que du répertoire 'public' de notre application Laravel.
# -----------------------------------------------------------
COPY --from=builder /var/www/public /var/www/public

# Définir le répertoire de travail sur le répertoire public
WORKDIR /var/www/public

# Exposer le port 80 et démarrer Nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Ce Dockerfile utilise une construction multi-étape pour séparer le processus de construction des ressources de l'image finale. La première étape installe Node.js et construit les ressources, tandis que la seconde étape configure l'image Nginx avec la configuration optimisée et les ressources construites.

Créer une configuration Docker Compose pour la production

Pour assembler tous les services, créez un fichier compose.prod.yaml qui définit les services, volumes et réseaux pour l'environnement de production. Voici un exemple de configuration :

services:
  web:
    build:
      context: .
      dockerfile: ./docker/production/nginx/Dockerfile
    restart: unless-stopped # Automatiquement redémarrer à moins que le service ne soit explicitement arrêté
    volumes:
      # Monter le volume 'laravel-storage' vers '/var/www/storage' à l'intérieur du conteneur.
      # -----------------------------------------------------------
      # Ce volume stocke des données persistantes comme les fichiers téléchargés et le cache.
      # L'option ':ro' le monte en lecture seule dans le service 'web' car Nginx n'a besoin que de lire ces fichiers.
      # Le service 'php-fpm' monte le même volume sans ':ro' pour permettre les opérations d'écriture.
      # -----------------------------------------------------------
      - laravel-storage-production:/var/www/storage:ro
    networks:
      - laravel-production
    ports:
      # Mapper le port 80 à l'intérieur du conteneur au port spécifié par 'NGINX_PORT' sur la machine hôte.
      # -----------------------------------------------------------
      # Cela permet d'accéder à l'extérieur au serveur web Nginx exécuté à l'intérieur du conteneur.
      # Par exemple, si 'NGINX_PORT' est défini sur '8080', accéder à 'http://localhost:8080' atteindra l'application.
      # -----------------------------------------------------------
      - "${NGINX_PORT:-80}:80"
    depends_on:
      php-fpm:
        condition: service_healthy # Attendre la vérification de santé de php-fpm

  php-fpm:
    # Pour le service php-fpm, nous allons créer une image personnalisée pour installer les extensions PHP nécessaires et configurer correctement les permissions.
    build:
      context: .
      dockerfile: ./docker/common/php-fpm/Dockerfile
      target: production # Utiliser la 'production' étape dans le Dockerfile
    restart: unless-stopped
    volumes:
      - laravel-storage-production:/var/www/storage # Monter le volume de stockage
    env_file:
      - .env
    networks:
      - laravel-production
    healthcheck:
      test: ["CMD-SHELL", "php-fpm-healthcheck || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 3
    # L'attribut 'depends_on' avec 'condition: service_healthy' garantit que
    # ce service ne démarrera pas avant que le service 'postgres' passe son test de santé.
    # Cela empêche l'application de tenter de se connecter à la base de données avant qu'elle ne soit prête.
    depends_on:
      postgres:
        condition: service_healthy

  # Le service 'php-cli' fournit une interface de ligne de commande pour exécuter des commandes Artisan et d'autres tâches CLI.
  # -----------------------------------------------------------
  # Cela est utile pour exécuter des migrations, des seeders ou tout script personnalisé.
  # Il partage le même codebase et l'environnement que le service 'php-fpm'.
  # -----------------------------------------------------------
  php-cli:
    build:
      context: .
      dockerfile: ./docker/php-cli/Dockerfile
    tty: true # Active un terminal interactif
    stdin_open: true # Garde l'entrée standard ouverte pour 'docker exec'
    env_file:
      - .env
    networks:
      - laravel

  postgres:
    image: postgres:16
    restart: unless-stopped
    user: postgres
    ports:
      - "${POSTGRES_PORT}:5432"
    environment:
      - POSTGRES_DB=${POSTGRES_DATABASE}
      - POSTGRES_USER=${POSTGRES_USERNAME}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres-data-production:/var/lib/postgresql/data
    networks:
      - laravel-production
    # Test de santé pour PostgreSQL
    # -----------------------------------------------------------
    # Les tests de santé permettent à Docker de déterminer si un service est opérationnel.
    # La commande 'pg_isready' vérifie si PostgreSQL est prêt à accepter des connexions.
    # Cela empêche les services dépendants de démarrer avant que la base de données ne soit prête.
    # -----------------------------------------------------------
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:alpine
    restart: unless-stopped # Automatiquement redémarrer à moins que le service ne soit explicitement arrêté
    networks:
      - laravel-production
    # Test de santé pour Redis
    # -----------------------------------------------------------
    # Vérifie si Redis répond à la commande 'PING'.
    # Cela garantit que le service n'est pas seulement en cours d'exécution mais aussi opérationnel.
    # -----------------------------------------------------------
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

networks:
  # Attacher le service à l'étape 'laravel-production'
  # -----------------------------------------------------------
  # Ce réseau personnalisé permet à tous les services à l'intérieur de le communiquer en utilisant leur nom de service comme nom d'hôte.
  # Par exemple, 'php-fpm' peut se connecter à 'postgres' en utilisant 'postgres' comme nom d'hôte.
  # -----------------------------------------------------------
  laravel-production:

volumes:
  postgres-data-production:
  laravel-storage-production:
Note

Assurez-vous d'avoir un fichier .env à la racine de votre projet Laravel avec les configurations nécessaires (par exemple, les paramètres de base de données et Xdebug) pour correspondre à la configuration Docker Compose.

Exécuter votre environnement de production

Pour démarrer l'environnement de production, exécutez :

$ docker compose -f compose.prod.yaml up --build -d

Cette commande construira et démarrera tous les services en mode détaché, fournissant une configuration évolutive et prête pour la production pour votre application Laravel.

Résumé

En mettant en place un environnement Docker Compose pour Laravel en production, vous vous assurez que votre application est optimisée pour les performances, évolutive et sécurisée. Cette configuration rend les déploiements cohérents et plus faciles à gérer, réduisant la probabilité d'erreurs dues aux différences entre les environnements.