> Docker Compose expliqué par l'exemple : structure du docker-compose.yml, services, networks, volumes, variables d'environnement, depends_on et les commandes up, down et logs.

*Source : https://coder-studio.com/outils/docker-compose-exemple/*

---

[// Conteneurs & DevOps](https://coder-studio.com/outils/)

# Docker Compose par l'exemple : orchestrer ses conteneurs

Par **Kévin Papot** · publié le 14 décembre 2025

Lancer un conteneur à la main avec une longue ligne `docker run` fonctionne pour un service isolé. Mais dès qu’une application combine un serveur web, une base de données et un cache, retaper ces commandes à chaque démarrage devient ingérable. **Docker Compose** décrit toute la pile dans un seul fichier YAML et la pilote avec des commandes courtes. Ce guide passe en revue la structure du fichier, chaque section utile et plusieurs exemples concrets prêts à copier.

## Un premier fichier docker-compose.yml minimal

Compose lit par défaut un fichier nommé `docker-compose.yml` (ou `compose.yml`) placé à la racine du projet. Sa clé principale est `services` : chaque entrée décrit un conteneur.

```
services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
```

Ce fichier suffit pour lancer un serveur Nginx accessible sur `http://localhost:8080`. La commande `docker compose up` construit puis démarre le service, et Compose se charge de créer le réseau et de nommer le conteneur.

## La structure d’un service

Un service accepte de nombreuses clés. Voici les plus courantes, regroupées dans un exemple commenté :

```
services:
  api:
    build: .                 # construire depuis le Dockerfile local
    image: mon-api:latest    # nom de l'image produite
    container_name: mon-api  # nom fixe du conteneur
    ports:
      - "3000:3000"          # hôte:conteneur
    restart: unless-stopped  # redémarrage automatique
    working_dir: /app
    command: node server.js  # remplace la CMD du Dockerfile
```

Deux clés s’excluent souvent dans la pratique : `image` seule télécharge une image existante, tandis que `build` construit à partir d’un `Dockerfile`. Vous pouvez combiner les deux : `build` construit, `image` nomme le résultat. Si vous débutez sur ces notions, notre guide [Docker pour débutants](https://coder-studio.com/outils/docker-pour-debutants/) reprend images et Dockerfile depuis zéro.

### Personnaliser la construction avec build

Quand le Dockerfile n’est pas à la racine ou porte un autre nom, `build` accepte une forme détaillée :

```
services:
  api:
    build:
      context: ./backend
      dockerfile: Dockerfile.prod
      args:
        NODE_ENV: production
```

Le `context` est le dossier envoyé au moteur Docker ; `dockerfile` pointe le fichier de recette ; `args` transmet des variables au moment du build (les `ARG` du Dockerfile).

## Les variables d’environnement

Trois approches cohabitent, du plus simple au plus propre.

```
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: production
```

Pour éviter d’écrire des secrets en clair, on externalise dans un fichier `.env` (lu automatiquement par Compose) puis on interpole les valeurs :

```
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
```

```
# Fichier .env à la racine du projet
DB_USER=app
DB_PASSWORD=un-mot-de-passe-solide
```

Troisième option, `env_file`, injecte tout un fichier de variables dans le conteneur :

```
services:
  api:
    build: .
    env_file:
      - ./config/api.env
```

Pensez à ajouter `.env` à votre `.gitignore` pour ne jamais versionner de secrets.

## Les volumes : persister les données

Sans volume, tout ce qu’un conteneur écrit disparaît à sa suppression. Compose gère deux formes utiles.

```
services:
  db:
    image: postgres:16
    volumes:
      - donnees-db:/var/lib/postgresql/data   # volume nommé
  web:
    build: .
    volumes:
      - ./src:/app/src                          # bind mount (dev)

volumes:
  donnees-db:
```

Le **volume nommé** `donnees-db` est géré par Docker et survit aux redémarrages : idéal pour une base de données. Le **bind mount** `./src:/app/src` relie un dossier local au conteneur, parfait en développement pour voir ses modifications sans reconstruire. Chaque volume nommé utilisé doit être déclaré dans la section `volumes:` de haut niveau. Pour approfondir sauvegardes et cas d’usage, voyez notre [guide des volumes Docker](https://coder-studio.com/outils/docker-volumes-guide/).

## Les networks : faire communiquer les services

Par défaut, Compose crée un réseau unique et y attache tous les services. Ils se joignent alors par leur nom de service : depuis `web`, l’hôte `db` résout vers la base. Aucune configuration n’est nécessaire pour ce cas courant.

Pour isoler des groupes de services, on déclare des réseaux explicites :

```
services:
  web:
    build: .
    networks:
      - frontend
  api:
    build: ./api
    networks:
      - frontend
      - backend
  db:
    image: postgres:16
    networks:
      - backend

networks:
  frontend:
  backend:
```

Ici `web` ne peut pas joindre `db` directement : seul `api`, présent sur les deux réseaux, fait le pont. C’est une bonne pratique de segmentation.

## depends\_on : maîtriser l’ordre de démarrage

`depends_on` impose un ordre : un service ne démarre qu’après ceux dont il dépend.

```
services:
  api:
    build: .
    depends_on:
      - db
  db:
    image: postgres:16
```

Attention à une subtilité fréquente : `depends_on` attend que le conteneur `db` soit **démarré**, pas que PostgreSQL soit **prêt à accepter des connexions**. Pour attendre la disponibilité réelle, on ajoute un `healthcheck` et une condition :

```
services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 3s
      retries: 5
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy
```

L’`api` ne démarre alors que lorsque le healthcheck de `db` passe au vert.

## Exemple complet : une stack web + base + cache

Voici une pile réaliste : une API Node, une base PostgreSQL persistante et un cache Redis.

```
services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://app:${DB_PASSWORD}@db:5432/production
      REDIS_URL: redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    restart: unless-stopped

  db:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: production
    volumes:
      - donnees-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app"]
      interval: 5s
      timeout: 3s
      retries: 5

  cache:
    image: redis:7-alpine
    restart: unless-stopped

volumes:
  donnees-db:
```

L’API joint la base via l’hôte `db` et le cache via `cache`, grâce au réseau implicite. Les données de PostgreSQL survivent dans le volume `donnees-db`.

## Les commandes à connaître

Compose se pilote avec une poignée de sous-commandes.

```
# Construire et démarrer toute la pile en arrière-plan
docker compose up -d

# Reconstruire les images avant de démarrer
docker compose up -d --build

# Suivre les logs de tous les services en temps réel
docker compose logs -f

# Logs d'un seul service
docker compose logs -f api

# Lister l'état des services
docker compose ps

# Ouvrir un shell dans un conteneur en cours
docker compose exec api sh

# Arrêter et supprimer conteneurs et réseaux (volumes conservés)
docker compose down

# Tout supprimer, y compris les volumes nommés (destructif)
docker compose down -v
```

Le drapeau `-d` (_detached_) rend la main au terminal ; sans lui, les logs défilent au premier plan. Retenez surtout la différence entre `down` et `down -v` : le second efface vos données.

## En résumé

Docker Compose transforme une pile multi-conteneurs en un fichier lisible et versionnable. Vous savez maintenant structurer les `services`, injecter des variables, persister avec des `volumes`, segmenter avec des `networks`, ordonner avec `depends_on` et piloter le tout avec `up`, `down` et `logs`. La suite logique est d’intégrer ce fichier à un pipeline de déploiement pour lancer la même pile en production.

## À lire ensuite

-   [Docker pour débutants : le guide complet](https://coder-studio.com/outils/docker-pour-debutants/)
-   [Les volumes Docker : persister ses données](https://coder-studio.com/outils/docker-volumes-guide/)
-   [Débuter sous Linux quand on est développeur](https://coder-studio.com/tutoriels/debuter-linux-developpeur/)
