Skip to main content

Docker Compose


Chapter objectives

  • Understand Docker Compose and its usefulness
  • Write docker-compose.yml files
  • Manage multi-container applications
  • Master the Compose commands

1 - Introduction to Docker Compose

What is Docker Compose?

Docker Compose is a tool for defining and running multi-container applications.

Instead of:

docker network create myapp
docker run -d --name db --network myapp -e POSTGRES_PASSWORD=secret postgres:15
docker run -d --name redis --network myapp redis:7
docker run -d --name web --network myapp -p 3000:3000 -e DB_HOST=db myapp

You write:

# docker-compose.yml
services:
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
redis:
image: redis:7
web:
image: myapp
ports:
- "3000:3000"
environment:
DB_HOST: db

Installation

Docker Compose is included with Docker Desktop. On Linux:

# Vérifier l'installation
docker compose version

# Si nécessaire, installer le plugin
sudo apt-get install docker-compose-plugin

2 - Structure of the docker-compose.yml file

Basic structure

# Version (optionnel depuis Compose v2)
version: "3.9"

# Définition des services
services:
service1:
image: image1
# configuration...
service2:
build: ./path
# configuration...

# Volumes nommés
volumes:
data-volume:

# Réseaux personnalisés
networks:
frontend:
backend:

Complete example

version: "3.9"

services:
# Application web
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
- redis
networks:
- frontend
- backend

# Base de données
db:
image: postgres:15
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend

# Cache Redis
redis:
image: redis:7-alpine
networks:
- backend

volumes:
db-data:

networks:
frontend:
backend:

3 - Service configuration

Image vs Build

services:
# Utiliser une image existante
nginx:
image: nginx:1.25-alpine

# Construire depuis un Dockerfile
app:
build: .

# Build avec options
api:
build:
context: ./api
dockerfile: Dockerfile.prod
args:
- NODE_ENV=production

Ports

services:
web:
ports:
# Format court
- "3000:3000"
- "8080:80"

# Format long
- target: 80
published: 8080
protocol: tcp
mode: host

Environment variables

services:
app:
# Liste
environment:
- NODE_ENV=production
- DEBUG=false

# Map
environment:
NODE_ENV: production
DEBUG: "false"

# Fichier externe
env_file:
- .env
- .env.production

Volumes

services:
app:
volumes:
# Volume nommé
- data:/app/data

# Bind mount
- ./src:/app/src

# Lecture seule
- ./config:/app/config:ro

volumes:
data:

Networks

services:
web:
networks:
- frontend

api:
networks:
- frontend
- backend

db:
networks:
- backend

networks:
frontend:
backend:
internal: true # Pas d'accès externe

4 - Dependencies between services

Simple depends_on

services:
web:
depends_on:
- db
- redis
db:
image: postgres:15
redis:
image: redis:7

depends_on with conditions

services:
web:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started

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

Healthcheck

services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s

5 - Docker Compose commands

Main commands

# Démarrer les services
docker compose up

# Démarrer en arrière-plan
docker compose up -d

# Construire et démarrer
docker compose up --build

# Arrêter les services
docker compose down

# Arrêter et supprimer les volumes
docker compose down -v

# Arrêter et supprimer les images
docker compose down --rmi all

Service management

# Lister les services
docker compose ps

# Voir les logs
docker compose logs
docker compose logs -f # Suivre
docker compose logs web # Un service spécifique

# Exécuter une commande
docker compose exec web bash
docker compose exec db psql -U postgres

# Redémarrer un service
docker compose restart web

# Scaler un service
docker compose up -d --scale web=3

Build and images

# Construire les images
docker compose build

# Construire sans cache
docker compose build --no-cache

# Pousser les images
docker compose push

# Tirer les images
docker compose pull

6 - Variables and interpolation

The .env file

# .env
POSTGRES_VERSION=15
POSTGRES_PASSWORD=secret123
APP_PORT=3000
# docker-compose.yml
services:
db:
image: postgres:${POSTGRES_VERSION}
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

web:
ports:
- "${APP_PORT}:3000"

Default values

services:
web:
image: myapp:${VERSION:-latest}
environment:
LOG_LEVEL: ${LOG_LEVEL:-info}

System environment variables

# Exporter une variable
export COMPOSE_PROJECT_NAME=myproject

# Utiliser dans le compose
docker compose up

7 - Multiple files and profiles

Multiple files

# Fichier de base + override
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
# docker-compose.yml (base)
services:
web:
build: .
ports:
- "3000:3000"

# docker-compose.prod.yml (override)
services:
web:
image: myapp:v1.0
environment:
- NODE_ENV=production

Profiles

services:
web:
image: myapp

db:
image: postgres:15

# Service de debug, seulement quand demandé
debug:
image: nicolaka/netshoot
profiles:
- debug

# Tests, seulement quand demandé
test:
build: .
command: npm test
profiles:
- test
# Démarrer sans les profils optionnels
docker compose up

# Démarrer avec le profil debug
docker compose --profile debug up

# Plusieurs profils
docker compose --profile debug --profile test up

8 - Practical examples

LAMP stack

version: "3.9"

services:
web:
image: php:8.2-apache
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
depends_on:
- db

db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: app
volumes:
- mysql-data:/var/lib/mysql

phpmyadmin:
image: phpmyadmin:5
ports:
- "8081:80"
environment:
PMA_HOST: db

volumes:
mysql-data:

Node.js + MongoDB application

version: "3.9"

services:
app:
build: .
ports:
- "3000:3000"
environment:
MONGODB_URI: mongodb://mongo:27017/myapp
REDIS_URL: redis://redis:6379
depends_on:
- mongo
- redis

mongo:
image: mongo:7
volumes:
- mongo-data:/data/db

redis:
image: redis:7-alpine

mongo-express:
image: mongo-express
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_SERVER: mongo

volumes:
mongo-data:

WordPress

version: "3.9"

services:
wordpress:
image: wordpress:6
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-data:/var/www/html
depends_on:
- db

db:
image: mysql:8
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: root
volumes:
- db-data:/var/lib/mysql

volumes:
wordpress-data:
db-data:

9 - Best practices

File organization

project/
├── docker-compose.yml # Base
├── docker-compose.override.yml # Dev (auto-chargé)
├── docker-compose.prod.yml # Production
├── .env # Variables par défaut
├── .env.production # Variables production
└── services/
├── web/
│ └── Dockerfile
└── api/
└── Dockerfile

Use fixed image versions

# ❌ Mauvais
services:
db:
image: postgres # ou postgres:latest

# ✅ Bon
services:
db:
image: postgres:15.4-alpine

Limit resources

services:
web:
image: myapp
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M

Security

services:
web:
# Ne pas exécuter en root
user: "1000:1000"

# Mode lecture seule
read_only: true

# Monter tmpfs pour les fichiers temporaires
tmpfs:
- /tmp
- /var/run

Command summary

CommandDescription
docker compose upStart the services
docker compose up -dStart in the background
docker compose downStop and remove
docker compose psList the services
docker compose logsView the logs
docker compose execRun a command
docker compose buildBuild the images
docker compose pullDownload the images
docker compose restartRestart
Key points
  • One YAML file = a complete application
  • Services communicate by their name
  • Use depends_on with healthcheck
  • Separate the dev/prod configuration with multiple files

Hands-on exercises

  1. Create a docker-compose.yml for a web app + database
  2. Add a Redis cache service
  3. Configure healthchecks for all the services
  4. Create separate files for dev and production

← Docker Networks | Best practices →