Chaque guide d'auto-hébergement dit « utilisez simplement Docker Compose » comme si c'était la chose la plus évidente du monde. Vous copiez un fichier docker-compose.yml, lancez docker compose up -d, et... priez pour que ça marche. Mais que se passe-t-il vraiment ? Comment réparer les choses quand elles cassent ? Comment personnaliser les configurations selon vos besoins ?
Qu'est-ce que Docker et pourquoi vous en soucier ?
Before Docker, installing software was a nightmare. Each application needed specific versions of dependencies, libraries, and runtimes — and they'd often conflict with each other. Installing application A might break application B.
Docker solves this by packaging applications in containers — self-contained environments with everything the application needs. Think of containers as lightweight virtual machines that:
- Run in isolation — apps can't interfere with each other
- Are reproducible — same container works identically everywhere
- Start instantly — no boot time like traditional VMs
- Share the host's kernel — extremely resource-efficient
Docker Compose extends this by letting you define and run multiple containers together. A typical self-hosted app might need:
- The main application
- A database (PostgreSQL, MySQL, MariaDB)
- A cache (Redis, Memcached)
- A reverse proxy (Caddy, Traefik, Nginx)
Docker Compose coordinates all of these with a single configuration file.
Installer Docker et Docker Compose
Let's get Docker installed. The official installation script works on most Linux distributions:
# Download and run the official install script
curl -fsSL https://get.docker.com | sh
# Add your user to the docker group (so you don't need sudo)
sudo usermod -aG docker $USER
# Log out and back in for group changes to take effect
exit
# After logging back in, verify installation
docker --version
# Docker version 25.x.x
docker compose version
# Docker Compose version v2.x.x
📝 Docker Compose v1 vs v2
The old docker-compose (with hyphen) is deprecated. Modern Docker uses docker compose (without hyphen) as a built-in plugin. All commands in this guide use the v2 syntax.
Anatomie d'un fichier docker-compose.yml
Let's decode a typical Docker Compose file piece by piece. Here's a simple example that runs a web app with a database:
services:
webapp:
image: nginx:latest
container_name: my-webapp
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
environment:
- TZ=America/New_York
depends_on:
- database
networks:
- app-network
database:
image: postgres:15
container_name: my-database
restart: unless-stopped
environment:
POSTGRES_USER: myapp
POSTGRES_PASSWORD: secretpassword
POSTGRES_DB: myapp_db
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
volumes:
db-data:
networks:
app-network:
driver: bridge
Let's break down each section:
services:
The heart of the file. Each service becomes a container. Here we have two: webapp and database.
image:
Specifies which Docker image to use. Format is name:tag. Common tags:
latest— most recent version (can change unexpectedly)stable— latest stable release15orv2.1.0— specific version (recommended for production)
container_name:
Optional. Gives your container a specific name instead of an auto-generated one. Makes logs and debugging easier.
restart:
Controls auto-restart behavior:
no— never restart (default)always— always restart, even after rebootunless-stopped— restart unless you manually stop it (recommended)on-failure— only restart if it crashes
ports:
Maps ports between host and container. Format: "HOST:CONTAINER"
ports:
- "8080:80" # Host port 8080 → Container port 80
- "127.0.0.1:8080:80" # Only accessible from localhost
volumes:
Persists data outside the container. Two types:
volumes:
# Bind mount: maps a host directory
- ./html:/usr/share/nginx/html:ro # :ro = read-only
# Named volume: managed by Docker
- db-data:/var/lib/postgresql/data
Named volumes survive container destruction. Bind mounts let you edit files directly.
environment:
Sets environment variables inside the container. Two syntax options:
environment:
- VAR_NAME=value # List format
TZ: America/New_York # Map format
depends_on:
Ensures containers start in order. The webapp won't start until database is running.
networks:
Puts containers on the same virtual network so they can communicate using container names as hostnames.
Commandes Docker Compose essentielles
Here are the commands you'll use daily:
# Start all services (detached mode)
docker compose up -d
# Stop all services
docker compose down
# Stop and remove volumes too (careful - deletes data!)
docker compose down -v
# View running containers
docker compose ps
# View logs
docker compose logs # All services
docker compose logs webapp # Specific service
docker compose logs -f # Follow (stream) logs
docker compose logs --tail=100 # Last 100 lines
# Restart services
docker compose restart # Restart all
docker compose restart webapp # Restart specific service
# Rebuild and restart (after changing docker-compose.yml)
docker compose up -d --force-recreate
# Pull latest images
docker compose pull
# Execute command inside container
docker exec -it my-webapp bash # Open shell
docker exec my-webapp ls /app # Run single command
Exemple pratique : Déployer Uptime Kuma
Let's deploy a real application: Uptime Kuma, a self-hosted monitoring tool. This example demonstrates a complete, working setup.
# Create a directory
mkdir ~/uptime-kuma
cd ~/uptime-kuma
# Create docker-compose.yml
nano docker-compose.yml
Paste this configuration:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- "3001:3001"
volumes:
- ./data:/app/data
environment:
- TZ=Europe/Paris
Launch it:
docker compose up -d
# Check it's running
docker compose ps
# View logs
docker compose logs -f
Visit http://your-server-ip:3001 — you have a working monitoring tool!
Travailler avec les variables d'environnement
Hardcoding passwords in docker-compose.yml is bad practice. Use environment files instead:
# Create .env file (same directory as docker-compose.yml)
nano .env
DB_PASSWORD=super-secret-password
DB_USER=myapp
[email protected]
Reference these in your compose file:
services:
database:
image: postgres:15
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
Docker Compose automatically loads .env from the same directory.
🔒 Security Tip
Add .env to .gitignore so you don't accidentally commit secrets to version control.
Le réseau expliqué
Understanding Docker networking prevents 90% of "why can't my app connect to the database?" problems.
Default Behavior
Without explicit network configuration, Docker Compose creates a default network. All services can reach each other using their service name as hostname:
# webapp can connect to database using:
postgres://database:5432/myapp_db
# ^^^^^^^^ service name, not localhost!
Exposing Ports
Ports make services accessible from outside Docker:
ports:
- "8080:80" # Accessible from anywhere
- "127.0.0.1:8080:80" # Only from host machine
If you're using a reverse proxy (Caddy, Traefik), you often don't need to expose ports at all — the proxy handles external traffic.
Custom Networks
For complex setups, create explicit networks:
services:
frontend:
networks:
- frontend-net
backend:
networks:
- frontend-net
- backend-net
database:
networks:
- backend-net # Not accessible from frontend!
networks:
frontend-net:
backend-net:
Gérer les données avec les volumes
Containers are ephemeral — when they're removed, their internal data disappears. Volumes persist data:
Named Volumes (Recommended for databases)
services:
database:
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data: # Docker manages this
View and manage volumes:
docker volume ls # List all volumes
docker volume inspect project_db-data # Details
docker volume rm project_db-data # Delete (careful!)
Bind Mounts (Good for config files)
volumes:
- ./config:/app/config # Relative path
- /opt/data:/app/data # Absolute path
- ./config.yml:/app/config.yml:ro # Single file, read-only
Déboguer les problèmes courants
Container Won't Start
# Check container status
docker compose ps
# View detailed logs
docker compose logs service-name
# Check for port conflicts
sudo lsof -i :8080
Container Keeps Restarting
# View exit code and recent logs
docker compose ps
docker compose logs --tail=50 service-name
# Common causes:
# - Missing environment variables
# - Database connection failures (check depends_on)
# - Permission issues on volumes
"Cannot connect to database"
- Use the service name, not
localhost:database:5432 - Check if database is actually ready (not just started)
- Verify network configuration
Permission Denied on Volumes
# Check the container's user
docker exec service-name id
# Fix ownership on host
sudo chown -R 1000:1000 ./data
Ajouter un reverse proxy
A reverse proxy lets you access multiple services through one domain with HTTPS. Caddy is the easiest option:
services:
caddy:
image: caddy:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- proxy-net
# Your apps connect to the same network
webapp:
image: your-app
networks:
- proxy-net
# No ports needed! Caddy handles external access
volumes:
caddy-data:
caddy-config:
networks:
proxy-net:
Caddyfile:
app.yourdomain.com {
reverse_proxy webapp:8080
}
monitoring.yourdomain.com {
reverse_proxy uptime-kuma:3001
}
Mettre à jour les applications
Keep your self-hosted apps updated for security:
# Pull latest images
docker compose pull
# Recreate containers with new images
docker compose up -d
# Remove old unused images
docker image prune -f
For major version upgrades, always check the application's release notes for breaking changes or required migrations.
Stratégie de sauvegarde
Back up your data regularly:
#!/bin/bash
# backup.sh - Run this as a cron job
BACKUP_DIR="/backup/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Stop containers for consistent backup
cd /home/user/my-app
docker compose stop
# Backup bind mounts
tar -czf "$BACKUP_DIR/data.tar.gz" ./data ./config
# Backup named volumes
docker run --rm -v my-app_db-data:/source -v "$BACKUP_DIR":/backup alpine tar -czf /backup/db-data.tar.gz -C /source .
# Restart
docker compose up -d
Astuces utiles
Override for Development
Create docker-compose.override.yml for local changes that shouldn't be committed:
# docker-compose.override.yml
services:
webapp:
ports:
- "8080:80" # Expose directly for testing
Check Configuration Without Starting
docker compose config # Validates and shows merged config
Run One-Off Commands
docker compose run --rm service-name command
# Example: run database migrations
docker compose run --rm webapp python manage.py migrate
Watch Resource Usage
docker stats # Live resource monitoring
Aide-mémoire rapide
| Task | Command |
|---|---|
| Start services | docker compose up -d |
| Stop services | docker compose down |
| View logs | docker compose logs -f |
| List containers | docker compose ps |
| Restart service | docker compose restart name |
| Update images | docker compose pull && docker compose up -d |
| Shell into container | docker exec -it name bash |
| View config | docker compose config |
Prochaines étapes
Now that you understand Docker Compose, you're ready to self-host anything. Here are some great starting points:
- Self-host Vaultwarden — Your own password manager
- Self-host Nextcloud — Replace Google Drive
- Self-host Immich — Replace Google Photos
- Browse 500+ self-hosted apps — Find your next project
The self-hosting community is incredibly welcoming. When you get stuck, r/selfhosted is full of helpful people who've faced the same challenges.
You now have the foundation to deploy, debug, and maintain any self-hosted application. Welcome to the world of digital independence.