Introduction
Docker Compose est un outil essentiel pour orchestrer plusieurs conteneurs Docker en une seule commande, idéal pour les environnements de développement et de test complexes. Contrairement à docker run manuel, il gère les dépendances, les volumes persistants, les réseaux isolés et le scaling via un fichier YAML déclaratif.
Pourquoi l'utiliser en 2026 ? Les applications modernes sont multi-conteneurs : une API Node.js backend, une base PostgreSQL, un cache Redis. Docker Compose reproduit fidèlement ces stacks localement, accélérant le onboarding des devs et évitant les 'ça marche sur ma machine'. Ce tutoriel intermédiaire vous guide pour créer une stack complète : un serveur Express connecté à Postgres et Redis, avec persistance des données et exposition sécurisée.
À la fin, vous maîtriserez les volumes, networks, depends_on et overrides pour des environnements prod-like. Temps estimé : 20 min pour un setup fonctionnel.
Prérequis
- Docker Desktop installé (version 27+ avec Compose v2 intégré)
- Connaissances de base en Docker (images, conteneurs)
- Node.js 20+ localement pour tester l'app
- Éditeur de code (VS Code recommandé avec extension Docker)
- Terminal Unix-like (WSL2 sur Windows)
Créer le Dockerfile de l'application Node.js
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
USER nodeCe Dockerfile multi-étapes minimise l'image finale (Alpine pour légèreté). Il copie d'abord package.json pour optimiser le cache Docker, installe les deps en prod, puis l'app. L'utilisateur 'node' renforce la sécurité en évitant root. Copiez-collez dans un dossier projet.
Préparer l'application Node.js
Créez un dossier projet (mkdir ma-stack && cd ma-stack). Ajoutez un serveur Express simple qui interagit avec Postgres et Redis. Ce code illustre les connexions : requête DB et cache Redis. Les variables d'env seront injectées par Compose.
Implémenter le serveur Express
const express = require('express');
const { Pool } = require('pg');
const redis = require('redis');
const app = express();
app.use(express.json());
// Connexions via env vars de Compose
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const client = redis.createClient({ url: process.env.REDIS_URL });
client.connect();
app.get('/users', async (req, res) => {
const cached = await client.get('users');
if (cached) return res.json(JSON.parse(cached));
const { rows } = await pool.query('SELECT * FROM users');
await client.set('users', JSON.stringify(rows), { EX: 60 });
res.json(rows);
});
app.listen(3000, () => console.log('Server on 3000'));
module.exports = app;Ce serveur expose /users : vérifie le cache Redis, sinon query Postgres et cache 60s. Utilise process.env pour les URLs DB/Redis fournies par Compose. Ajoutez npm init -y && npm i express pg redis localement pour tester.
Générer package.json
{
"name": "docker-compose-app",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.19.2",
"pg": "^8.13.0",
"redis": "^4.7.0"
}
}Package.json minimal pour prod : deps only, no devDeps. Le script start match le CMD du Dockerfile. Exécutez npm install une fois pour générer node_modules, mais Docker le rebuildera.
Définir la stack de base avec docker-compose.yml
Maintenant, orchestrez tout : web (Node), db (Postgres), redis. Exposez les ports, mappez volumes pour persistance DB, définissez un réseau interne. depends_on assure le démarrage ordonné.
Fichier docker-compose.yml de base
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
networks:
- app-network
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
redis:
image: redis:7-alpine
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridgeYAML déclaratif : build: . pour Dockerfile local, env vars partagées via noms services (db → postgres@db). Volume named persiste DB. Réseau 'app-network' isole le trafic. Lancez avec docker compose up -d.
Initialiser la base de données
Attention : Postgres démarre vide. Ajoutez un script d'init pour créer la table users. Utilisez un volume pour un fichier SQL monté.
Script d'initialisation SQL
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100) UNIQUE
);
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com')
ON CONFLICT (email) DO NOTHING;Ce script crée la table et insère des données de test. Montez-le dans /docker-entrypoint-initdb.d/ de Postgres pour auto-exécution au premier démarrage.
docker-compose.yml mis à jour avec init DB
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
networks:
- app-network
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: mydb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
redis:
image: redis:7-alpine
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridgeAjout du volume ./init.sql : Postgres l'exécute automatiquement. Parfait pour seeds. Testez : docker compose up -d && curl http://localhost:3000/users.
Commandes essentielles pour gérer la stack
Utilisez docker compose (v2) : up -d pour detached, logs -f pour suivre, down -v pour nettoyer volumes.
Script bash de gestion
#!/bin/bash
case $1 in
up)
docker compose up -d
;;
down)
docker compose down -v
;;
logs)
docker compose logs -f web
;;
test)
curl http://localhost:3000/users || echo "Erreur"
;;
*)
echo "Usage: ./manage.sh {up|down|logs|test}"
;;
esacScript wrapper pour workflows : chmod +x manage.sh && ./manage.sh up. Simplifie les ops quotidiennes. Ajoutez à votre projet.
Bonnes pratiques
- Utilisez des .env files :
cp .env.example .envpour secrets (DB_PASS=xxx), référencez avec${DB_PASS}dans YAML. - Networks et depends_on conditionnels : Ajoutez
condition: service_healthypour healthchecks. - Volumes named : Toujours pour persistance, bind mounts pour dev (hot-reload).
- Compose overrides :
docker-compose.override.ymlpour dev vs prod diffs. - Scaling :
docker compose up --scale web=3pour tester haute dispo.
Erreurs courantes à éviter
- depends_on sans healthcheck : DB 'ready' ≠ 'healthy'. Ajoutez
healthcheckdans service db. - Ports exposés publiquement : Utilisez
ports: - "127.0.0.1:3000:3000"pour localhost only. - Volumes non persistants : Oubli → perte données à
down. Toujours named volumes. - Mauvaises images : Préférez
-alpinepour taille/security, pinned tags (postgres:16-alpine).
Pour aller plus loin
Passez à Kubernetes avec Kompose (kompose convert), ou explorez Docker Swarm pour prod. Lisez la doc officielle Docker Compose. Découvrez nos formations Learni sur DevOps et Containerisation pour maîtriser Helm, Terraform et CI/CD.