You are currently viewing Docker Compose YML : guide pratique pour bien configurer

Docker Compose YML : guide pratique pour bien configurer

  • Auteur/autrice de la publication :
  • Post category:DevOps

Dans cet article

  • Le fichier docker-compose yml permet de décrire une infrastructure multi-conteneurs en un seul fichier lisible et versionnable
  • La syntaxe YAML repose sur l’indentation stricte à 2 espaces, source de la majorité des erreurs chez les débutants
  • Les directives services, networks et volumes constituent les trois blocs fondamentaux de toute configuration
  • Depuis 2023, la commande docker compose (sans tiret) remplace officiellement docker-compose, mais le fichier YML reste identique
  • Un projet bien structuré utilise au moins 2 fichiers : un docker-compose.yml de base et un override pour le développement
  • Les variables d’environnement via un fichier .env évitent de coder en dur les mots de passe et les ports

Quand j’ai commencé à enseigner Docker à mes étudiants en BTS SIO, la question revenait systématiquement : « Comment lancer plusieurs conteneurs qui communiquent entre eux sans taper dix commandes ? ». La réponse tient en un seul fichier : le docker-compose yml. Ce fichier de configuration, rédigé en syntaxe YAML, décrit l’ensemble de votre infrastructure applicative de manière déclarative. Plutôt que de lancer chaque conteneur manuellement avec docker run, vous définissez vos services, vos réseaux et vos volumes dans un document lisible, puis une seule commande déploie le tout.

Dans ce guide, je vous accompagne pas à pas pour maîtriser la rédaction et l’utilisation du fichier docker-compose yml. Que vous prépariez un environnement de développement local ou que vous posiez les bases d’un déploiement en production, vous trouverez ici les clés pour éviter les pièges courants et adopter les bonnes pratiques dès le départ.

Comprendre le fichier docker-compose yml

Schéma d'architecture multi-conteneurs dessiné sur un tableau blanc en salle de formation
Schéma d’architecture multi-conteneurs dessiné sur un tableau blanc en salle de formation

Le fichier docker-compose yml est un document texte au format YAML qui décrit l’architecture complète d’une application multi-conteneurs. Concrètement, il répond à une question simple : quels conteneurs lancer, avec quelles images, quels ports exposer, quels volumes monter et comment les relier entre eux.

YAML signifie « YAML Ain’t Markup Language ». C’est un format de sérialisation de données conçu pour être lisible par un humain. Contrairement au JSON, il utilise l’indentation plutôt que les accolades pour structurer les données. Cette lisibilité en fait un choix naturel pour les fichiers de configuration, mais elle impose une rigueur absolue sur l’indentation.

Le fichier se nomme par défaut docker-compose.yml (ou docker-compose.yaml, les deux extensions sont acceptées). Il se place à la racine de votre projet. Lorsque vous exécutez la commande docker compose up, l’outil cherche automatiquement ce fichier dans le répertoire courant. Vous pouvez aussi spécifier un chemin différent avec l’option -f.

Pour situer ce fichier dans l’écosystème Docker, pensez-le comme un chef d’orchestre. Le Dockerfile décrit comment construire une image unique. Le fichier docker-compose yml, lui, orchestre plusieurs images (construites ou tirées depuis un registre comme Docker Hub) pour former une application complète. Si vous hésitez entre Docker et d’autres solutions de conteneurisation, j’ai rédigé un comparatif détaillé dans mon article Podman vs Docker.

Structure et syntaxe de base du fichier YML

Tout fichier docker-compose yml s’articule autour de trois blocs principaux : services, networks et volumes. Voici le squelette minimal que j’utilise en formation :

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: mon_mot_de_passe

volumes:
  db_data:

networks:
  backend:

Quelques règles syntaxiques fondamentales à retenir :

  • L’indentation se fait à 2 espaces, jamais avec des tabulations. Une tabulation provoque une erreur de parsing immédiate.
  • Les clés sont suivies de deux-points puis d’un espace avant la valeur.
  • Les listes utilisent un tiret suivi d’un espace (- valeur).
  • Les chaînes contenant des caractères spéciaux doivent être entourées de guillemets (simples ou doubles).
  • Les commentaires commencent par # et sont ignorés par le parser.

Un point qui déroute souvent mes étudiants : la clé version. Historiquement, le fichier commençait par version: "3.8" ou une autre valeur. Depuis la spécification Compose actuelle, cette clé est devenue facultative. Docker Compose détecte automatiquement les fonctionnalités utilisées. Je recommande de ne plus l’inclure dans vos nouveaux projets pour éviter toute confusion, comme le précise la documentation officielle de Docker Compose.

Configurer les services dans docker-compose yml

Le bloc services constitue le cœur de votre fichier docker-compose yml. Chaque service représente un conteneur qui sera créé et géré par Compose. Voici les directives que j’utilise le plus fréquemment :

Directive Rôle Exemple
image Spécifie l’image Docker à utiliser image: node:20-alpine
build Construit l’image depuis un Dockerfile build: ./app
ports Mappe les ports hôte:conteneur - "3000:3000"
volumes Monte des répertoires ou des volumes nommés - ./src:/app/src
environment Définit les variables d’environnement NODE_ENV: production
depends_on Déclare l’ordre de démarrage entre services depends_on: [db]
restart Politique de redémarrage automatique restart: unless-stopped
command Remplace la commande par défaut du conteneur command: npm run dev
healthcheck Vérifie la santé du service test: curl -f http://localhost

La directive build mérite une attention particulière. Quand vous développez votre propre application, vous ne récupérez pas une image toute faite : vous la construisez. Le chemin indiqué pointe vers le répertoire contenant votre Dockerfile. Vous pouvez aussi spécifier un contexte et un fichier distincts :

services:
  api:
    build:
      context: ./backend
      dockerfile: Dockerfile.dev
    ports:
      - "4000:4000"
    volumes:
      - ./backend/src:/app/src

La directive depends_on gère l’ordre de démarrage, mais pas la disponibilité effective du service. Votre base de données peut être « démarrée » sans que le serveur PostgreSQL soit prêt à accepter des connexions. Pour une attente fiable, combinez depends_on avec un healthcheck et la condition service_healthy :

services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5

  api:
    build: ./backend
    depends_on:
      db:
        condition: service_healthy

Ce mécanisme évite les erreurs de connexion au lancement, un problème classique que je rencontre dans presque tous les projets de mes étudiants.

Gérer les volumes et les réseaux

Infrastructure serveur hébergeant des conteneurs Docker en production
Infrastructure serveur hébergeant des conteneurs Docker en production

Les volumes garantissent la persistance des données au-delà du cycle de vie des conteneurs. Sans volume, toute donnée écrite dans un conteneur disparaît lorsqu’il est supprimé. Il existe deux types principaux :

  • Les bind mounts : ils lient un répertoire de votre machine hôte au conteneur (./data:/var/lib/data). Idéal en développement pour le rechargement à chaud du code.
  • Les volumes nommés : gérés par Docker, déclarés dans le bloc volumes de premier niveau. Recommandés en production pour les données de bases de données.
services:
  db:
    image: mysql:8
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:
    driver: local

Pour les réseaux, Docker Compose crée automatiquement un réseau par défaut pour chaque projet. Tous les services d’un même fichier peuvent communiquer entre eux en utilisant le nom du service comme nom d’hôte. Ainsi, votre application peut se connecter à la base de données via l’hôte db plutôt qu’une adresse IP.

Pour des architectures plus complexes, définissez des réseaux personnalisés afin d’isoler certains services. Par exemple, séparer le frontend du backend de la base de données :

services:
  frontend:
    networks:
      - front
  api:
    networks:
      - front
      - back
  db:
    networks:
      - back

networks:
  front:
  back:

Dans cette configuration, le frontend ne peut pas communiquer directement avec la base de données. Seule l’API, connectée aux deux réseaux, sert de passerelle. Ce principe rejoint les bonnes pratiques de segmentation réseau que j’aborde dans mon article sur les firewalls Stormshield.

Variables d’environnement et fichier .env

Coder un mot de passe en dur dans votre docker-compose yml est une erreur que je vois trop souvent. La bonne pratique consiste à utiliser des variables d’environnement, idéalement stockées dans un fichier .env à la racine du projet.

Docker Compose lit automatiquement le fichier .env situé à côté du docker-compose yml. Vous pouvez ensuite référencer ces variables avec la syntaxe ${NOM_VARIABLE} :

# fichier .env
POSTGRES_USER=admin
POSTGRES_PASSWORD=s3cur3_p4ss
POSTGRES_DB=monapp
APP_PORT=3000
# docker-compose.yml
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    volumes:
      - db_data:/var/lib/postgresql/data

  api:
    build: ./backend
    ports:
      - "${APP_PORT}:3000"
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}

Pensez impérativement à ajouter le fichier .env à votre .gitignore. Un fichier .env.example sans les valeurs sensibles servira de modèle pour vos collègues. Cette approche s’applique d’ailleurs à tout projet web, comme je l’explique dans mon guide sur les bases HTML, CSS et JS.

Vous pouvez aussi passer les variables directement par service avec la directive env_file, ce qui permet d’avoir un fichier d’environnement par service :

services:
  api:
    env_file:
      - ./api/.env
  worker:
    env_file:
      - ./worker/.env

Commandes essentielles pour lancer et gérer vos conteneurs

Une fois votre docker-compose yml rédigé, voici les commandes que vous utiliserez au quotidien. Notez que la syntaxe moderne utilise docker compose (deux mots, sans tiret), intégrée directement au CLI Docker :

Commande Action Options utiles
docker compose up Crée et démarre tous les services -d (détaché), --build (reconstruit les images)
docker compose down Arrête et supprime les conteneurs -v (supprime aussi les volumes)
docker compose ps Liste les conteneurs du projet -a (inclut les arrêtés)
docker compose logs Affiche les journaux des services -f (suivi en temps réel), --tail=100
docker compose exec Exécute une commande dans un conteneur actif docker compose exec db psql -U postgres
docker compose run Lance une commande ponctuelle dans un nouveau conteneur docker compose run api npm test
docker compose config Valide et affiche la configuration résolue Utile pour déboguer les variables
docker compose pull Télécharge les dernières versions des images À faire avant un up en production

La commande docker compose config est particulièrement précieuse. Elle résout toutes les variables d’environnement et affiche le fichier final tel que Docker le comprend. Je la recommande systématiquement avant chaque premier lancement pour détecter les erreurs de syntaxe ou les variables manquantes.

Pour exécuter une commande ponctuelle via votre fichier, la syntaxe docker compose run --rm api npm run migrate crée un conteneur temporaire qui s’efface après exécution. C’est la méthode propre pour lancer des migrations de base de données, des seeds ou des tests. Ce principe rappelle l’exécution de requêtes ponctuelles que j’évoque dans mon article sur ADD COLUMN en SQL.

Bonnes pratiques et erreurs courantes

Session de développement collaborative autour de la configuration Docker Compose
Session de développement collaborative autour de la configuration Docker Compose

Après plusieurs années à accompagner des développeurs sur Docker, voici les bonnes pratiques que je considère essentielles :

1. Utilisez des tags d’image précis. Écrire image: node:latest garantit que votre stack se comportera différemment d’un mois à l’autre. Préférez node:20-alpine pour figer la version majeure tout en bénéficiant des mises à jour de sécurité de la version mineure.

2. Séparez vos configurations par environnement. Docker Compose supporte les fichiers d’override. Un fichier docker-compose.yml de base contient la configuration commune ; un fichier docker-compose.override.yml (chargé automatiquement) ajoute les spécificités de développement comme les bind mounts et les ports de debug :

# docker-compose.override.yml (développement)
services:
  api:
    volumes:
      - ./backend/src:/app/src
    environment:
      NODE_ENV: development
      DEBUG: "true"
    ports:
      - "9229:9229"

3. Limitez les ressources. En production, définissez des limites de mémoire et de CPU pour éviter qu’un conteneur gourmand ne déstabilise les autres :

services:
  api:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "0.5"

4. Ne lancez pas vos conteneurs en tant que root. Ajoutez user: "1000:1000" ou utilisez la directive USER dans votre Dockerfile pour réduire la surface d’attaque.

5. Versionnez votre docker-compose yml. Ce fichier fait partie intégrante de votre code source. Il doit être commité dans votre dépôt Git, contrairement au fichier .env qui reste local.

Les erreurs courantes que je rencontre le plus souvent :

  • Mélanger tabulations et espaces : le parser YAML rejette les tabulations. Configurez votre éditeur pour convertir automatiquement.
  • Oublier les guillemets autour des ports : "8080:80" est une chaîne, 8080:80 peut être interprété comme un nombre en base 60 par certains parsers YAML.
  • Confondre depends_on et « le service est prêt » : comme évoqué, utilisez les healthchecks.
  • Stocker les données dans le conteneur sans volume : un docker compose down efface tout.

Pour aller plus loin sur la gestion des bases de données dans vos conteneurs, consultez mon article sur les requêtes COUNT unique en SQL et celui sur SQL ORDER BY avec plusieurs colonnes.

Exemple complet : une stack web avec base de données

Voici un exemple complet de docker-compose yml que j’utilise comme base pour mes projets pédagogiques. Il déploie une application Node.js avec une base PostgreSQL et un serveur Nginx en reverse proxy :

services:
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - frontend

  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      NODE_ENV: production
      DATABASE_URL: postgres://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 15s
    restart: unless-stopped
    networks:
      - frontend
      - backend

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASS}
      POSTGRES_DB: ${DB_NAME}
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

volumes:
  pg_data:

networks:
  frontend:
  backend:

Ce fichier illustre plusieurs bonnes pratiques simultanément : séparation des réseaux (le serveur Nginx n’accède pas directement à la base), healthchecks avec conditions, volumes nommés pour la persistance, variables d’environnement externalisées et politique de redémarrage. La directive :ro (read-only) sur les montages de configuration empêche toute modification accidentelle depuis le conteneur.

Pour explorer la partie applicative de ce type de stack, vous pouvez consulter mes articles sur la documentation PHP ou sur la manipulation des dates avec PHP DateTime.

Docker-compose.yml est-il déprécié ?

Cette question revient constamment, et la réponse mérite d’être nuancée. Le fichier docker-compose.yml lui-même n’est pas déprécié. Il reste le standard pour définir des applications multi-conteneurs. Ce qui a évolué, c’est l’outil en ligne de commande.

L’ancien binaire docker-compose (écrit en Python, dit « V1 ») a été officiellement déprécié en juillet 2023. Il est remplacé par la commande docker compose (sans tiret), un plugin intégré au CLI Docker, écrit en Go (dit « V2 »). La syntaxe du fichier YML reste strictement la même entre les deux versions. Seule la commande d’exécution change.

Quant à la clé version dans le fichier, elle est ignorée par Docker Compose V2 et n’a plus d’utilité fonctionnelle. La page de migration officielle de Docker détaille les différences entre V1 et V2 pour ceux qui travaillent encore avec l’ancienne version.

La différence entre un simple « fichier YAML Docker » et un « fichier docker-compose yml » se situe au niveau de la spécification. Un Dockerfile utilise sa propre syntaxe. Un fichier de configuration pour Docker Swarm ou Kubernetes utilise d’autres schémas YAML. Le fichier docker-compose yml, lui, suit la spécification Compose maintenue par Docker et la communauté open source. Il est spécifiquement conçu pour l’orchestration locale de conteneurs multiples.

À retenir

  • Placez votre docker-compose yml à la racine du projet et versionnez-le dans Git
  • Utilisez docker compose config pour valider votre fichier avant chaque premier lancement
  • Externalisez vos secrets dans un fichier .env ajouté au .gitignore
  • Combinez depends_on avec healthcheck pour un ordre de démarrage fiable
  • Préférez les tags d’image précis (ex: postgres:16-alpine) plutôt que latest

Questions fréquentes


À quoi sert le fichier docker-compose yml ?

Le fichier docker-compose yml décrit l’ensemble d’une application multi-conteneurs en un seul document. Il définit les services (conteneurs), leurs images, les ports exposés, les volumes pour la persistance des données, les réseaux pour la communication inter-services et les variables d’environnement. Au lieu de lancer chaque conteneur manuellement, une seule commande (docker compose up) déploie toute l’infrastructure.

Où se trouve le fichier docker-compose yml ?

Par convention, le fichier docker-compose.yml se place à la racine de votre projet, aux côtés de votre fichier .gitignore et de votre README. Lors de l’exécution de docker compose up, l’outil recherche ce fichier dans le répertoire courant. Vous pouvez spécifier un emplacement différent avec l’option -f, par exemple docker compose -f infra/docker-compose.yml up.

Docker-compose.yml est-il déprécié ?

Non, le fichier docker-compose.yml n’est pas déprécié. C’est l’ancien binaire docker-compose (V1, écrit en Python) qui a été abandonné en 2023 au profit de docker compose (V2, plugin Go intégré au CLI Docker). La syntaxe du fichier YML est restée identique. Seule la clé version en tête de fichier n’a plus d’utilité et peut être omise.

Quelle est la différence entre un Dockerfile et un docker-compose yml ?

Le Dockerfile décrit comment construire une seule image Docker : système de base, dépendances, code à copier, commande de démarrage. Le docker-compose yml orchestre plusieurs images (construites ou existantes) pour former une application complète. Il gère les ports, les volumes, les réseaux et les dépendances entre services. En résumé, le Dockerfile construit ; le docker-compose yml assemble et coordonne.

Comment lancer un fichier docker-compose yml ?

Placez-vous dans le répertoire contenant le fichier, puis exécutez docker compose up -d. L’option -d lance les conteneurs en arrière-plan. Pour reconstruire les images avant le lancement, ajoutez --build. Pour arrêter et supprimer les conteneurs, utilisez docker compose down. Ajoutez l’option -v à down si vous souhaitez aussi supprimer les volumes de données.

Comment exécuter une commande dans un conteneur via docker compose ?

Deux options existent. Pour un conteneur déjà en cours d’exécution, utilisez docker compose exec nom_service commande (par exemple docker compose exec db psql -U postgres). Pour lancer un conteneur temporaire et exécuter une commande ponctuelle, utilisez docker compose run --rm nom_service commande. La première méthode ne crée pas de nouveau conteneur ; la seconde en crée un éphémère.


Lucie Moreau
Lucie Moreau

Formatrice IT indépendante depuis 2016, ancienne étudiante BTS SIO SLAM. 6 ans d'expérience en entreprise.

Lucie Moreau

Formatrice IT indépendante depuis 2016, ancienne étudiante BTS SIO SLAM. 6 ans d'expérience en entreprise.