Aller au contenu
medium

Traefik avec Docker : labels, réseaux et exemples pratiques

17 min de lecture

Traefik détecte automatiquement vos conteneurs Docker et crée les routes correspondantes sans fichier de configuration à maintenir. Vous définissez le routage directement via des labels sur vos conteneurs : Traefik lit ces labels, génère la configuration et l’applique en temps réel.

Ce guide vous montre comment configurer le provider Docker, écrire les labels de routage, gérer les réseaux, et déployer des applications complètes avec Docker Compose. À la fin, vous saurez exposer n’importe quel conteneur en quelques secondes.

Quand le provider Docker est activé, Traefik :

  1. Se connecte au daemon Docker via le socket Unix
  2. Surveille les événements (création, suppression, modification de conteneurs)
  3. Lit les labels de chaque conteneur
  4. Génère la configuration de routage correspondante
  5. Applique immédiatement les changements (hot-reload)

Provider Docker : labels, socket et génération de configuration

Traefik communique avec Docker via le socket Unix (/var/run/docker.sock). Ce socket donne accès à toute l’API Docker, ce qui pose un risque de sécurité.

Analogie : Le socket Docker, c’est comme donner les clés de tout l’immeuble. En production, on préfère donner uniquement les clés des pièces nécessaires (via un proxy).

Les labels Docker sont des métadonnées clé-valeur attachées aux conteneurs. Traefik les utilise pour configurer :

  • Les routers : règles de routage (Host, Path…)
  • Les services : port du conteneur, load balancing
  • Les middlewares : authentification, headers, rate limiting

Dans votre fichier traefik.yaml (configuration statique) :

traefik.yaml
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
OptionDescriptionRecommandation
endpointChemin du socket DockerToujours unix:///var/run/docker.sock sur Linux
exposedByDefaultExpose automatiquement tous les conteneursfalse (sécurité)
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik-public # Réseau par défaut
watch: true # Surveiller les changements
defaultRule: "Host(`{{ normalize .Name }}.example.com`)" # Règle par défaut
constraints: "Label(`traefik.zone`, `public`)" # Filtrer les conteneurs
OptionDescription
networkRéseau Docker à utiliser pour la communication
watchSurveiller les événements Docker en temps réel
defaultRuleRègle de routage par défaut (utilise les templates Go)
constraintsExpression pour filtrer les conteneurs à considérer
  1. Créer le fichier docker-compose.yml

    docker-compose.yml
    services:
    traefik:
    image: traefik:v3.6
    container_name: traefik
    restart: unless-stopped
    command:
    - "--api.dashboard=true"
    - "--api.insecure=true"
    - "--providers.docker=true"
    - "--providers.docker.exposedbydefault=false"
    - "--entrypoints.web.address=:80"
    ports:
    - "80:80"
    - "8080:8080"
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
    - traefik-public
    whoami:
    image: traefik/whoami
    container_name: whoami
    labels:
    - "traefik.enable=true"
    - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
    - "traefik.http.routers.whoami.entrypoints=web"
    networks:
    - traefik-public
    networks:
    traefik-public:
    name: traefik-public
  2. Lancer les conteneurs

    Fenêtre de terminal
    docker compose up -d
    # Vérifier le statut
    docker compose ps

    Résultat attendu :

    NAME IMAGE STATUS PORTS
    traefik traefik:v3.6 running 0.0.0.0:80->80/tcp, 0.0.0.0:8080->8080/tcp
    whoami traefik/whoami running
  3. Tester le routage

    Fenêtre de terminal
    # Tester avec le header Host
    curl -H "Host: whoami.localhost" http://localhost

    Résultat attendu :

    Hostname: whoami
    IP: 172.19.0.3
    RemoteAddr: 172.19.0.2:56842
    GET / HTTP/1.1
    Host: whoami.localhost
    User-Agent: curl/8.5.0
    Accept: */*
    Accept-Encoding: gzip
    X-Forwarded-For: 172.19.0.1
    X-Forwarded-Host: whoami.localhost
    X-Forwarded-Port: 80
    X-Forwarded-Proto: http
    X-Forwarded-Server: traefik
    X-Real-Ip: 172.19.0.1
LabelDescriptionExemple
traefik.enableActiver l’exposition du conteneurtrue ou false
traefik.http.routers.<n>.ruleRègle de routage HTTPHost('app.example.com')
traefik.http.routers.<n>.entrypointsPoints d’entrée à utiliserweb,websecure
traefik.http.routers.<n>.priorityPriorité du router (plus élevé = prioritaire)100
traefik.http.services.<n>.loadbalancer.server.portPort du conteneur8080

Note : Remplacez <n> par le nom de votre router/service (ex: myapp, api, frontend).

labels:
# Activer TLS
- "traefik.http.routers.myapp.tls=true"
# Utiliser un certificat resolver (Let's Encrypt)
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt"
# Domaines pour le certificat
- "traefik.http.routers.myapp.tls.domains[0].main=example.com"
- "traefik.http.routers.myapp.tls.domains[0].sans=www.example.com,api.example.com"
labels:
# Attacher un ou plusieurs middlewares
- "traefik.http.routers.myapp.middlewares=auth,compress,headers"
# Définir un middleware BasicAuth
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xxx"
# Définir un middleware Headers
- "traefik.http.middlewares.headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.headers.headers.contentTypeNosniff=true"
# Définir un RateLimit
- "traefik.http.middlewares.ratelimit.ratelimit.average=100"
- "traefik.http.middlewares.ratelimit.ratelimit.burst=50"

Pour les services TCP ou UDP (bases de données, DNS…) :

labels:
- "traefik.tcp.routers.mysql.rule=HostSNI(`*`)"
- "traefik.tcp.routers.mysql.entrypoints=mysql"
- "traefik.tcp.services.mysql.loadbalancer.server.port=3306"

Par défaut, Docker crée un réseau bridge pour chaque projet Compose. Problème : Traefik ne peut pas atteindre les conteneurs d’autres projets.

Solution : Créer un réseau Docker externe partagé entre Traefik et tous les services à exposer.

┌─────────────────────────────────────────────────────────────────────────┐
│ Réseau traefik-public │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Traefik │ │ App A │ │ App B │ │ App C │ │
│ │ │──│──────────│──│──────────│──│──────────│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
  1. Créer le réseau externe :

    Fenêtre de terminal
    docker network create traefik-public
  2. Référencer le réseau dans docker-compose.yml :

    docker-compose.yml
    services:
    myapp:
    image: nginx
    networks:
    - traefik-public
    - internal # Réseau interne pour base de données
    networks:
    traefik-public:
    external: true
    internal:
    driver: bridge

Un conteneur peut appartenir à plusieurs réseaux. Utilisez cette technique pour séparer :

  • traefik-public : communication avec Traefik
  • internal : communication entre services (base de données, cache)
services:
wordpress:
image: wordpress
networks:
- traefik-public # Accessible par Traefik
- backend # Accès à MySQL
labels:
- "traefik.http.routers.wp.rule=Host(`blog.example.com`)"
- "traefik.docker.network=traefik-public" # Préciser le réseau Traefik
mysql:
image: mysql:8
networks:
- backend # PAS sur traefik-public = pas accessible depuis Internet
services:
nginx:
image: nginx:alpine
container_name: web
volumes:
- ./html:/usr/share/nginx/html:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`www.example.com`)"
- "traefik.http.routers.web.entrypoints=websecure"
- "traefik.http.routers.web.tls.certresolver=letsencrypt"
networks:
- traefik-public
networks:
traefik-public:
external: true
services:
wordpress:
image: wordpress:6
container_name: wordpress
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
volumes:
- wordpress_data:/var/www/html
labels:
- "traefik.enable=true"
- "traefik.http.routers.wordpress.rule=Host(`blog.example.com`)"
- "traefik.http.routers.wordpress.entrypoints=websecure"
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
- "traefik.docker.network=traefik-public"
networks:
- traefik-public
- backend
mysql:
image: mysql:8
container_name: wordpress-db
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: ${WP_DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
networks:
- backend # Pas exposé sur Internet
networks:
traefik-public:
external: true
backend:
driver: bridge
volumes:
wordpress_data:
mysql_data:
services:
api:
image: myapi:latest
deploy:
replicas: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=3000"
# Healthcheck pour le load balancing
- "traefik.http.services.api.loadbalancer.healthcheck.path=/health"
- "traefik.http.services.api.loadbalancer.healthcheck.interval=10s"
networks:
- traefik-public
services:
frontend:
image: myfrontend:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host(`app.example.com`)"
- "traefik.http.routers.frontend.entrypoints=websecure"
- "traefik.http.routers.frontend.tls.certresolver=letsencrypt"
- "traefik.http.services.frontend.loadbalancer.server.port=80"
networks:
- traefik-public
api:
image: myapi:latest
labels:
- "traefik.enable=true"
# Même domaine, path différent
- "traefik.http.routers.api.rule=Host(`app.example.com`) && PathPrefix(`/api`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
- "traefik.http.services.api.loadbalancer.server.port=3000"
# Middleware pour supprimer le préfixe /api
- "traefik.http.routers.api.middlewares=api-stripprefix"
- "traefik.http.middlewares.api-stripprefix.stripprefix.prefixes=/api"
networks:
- traefik-public
AspectDocker standaloneDocker Swarm
Providerdockerswarm
UnitéConteneurService
LabelsSur le conteneurDans deploy.labels
RéseauBridge ou overlayOverlay obligatoire
Scalingdocker compose up --scaledocker service scale
traefik.yaml
providers:
swarm:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik-public
watch: true

En Swarm, les labels doivent être dans la section deploy.labels :

services:
myapp:
image: myapp:latest
deploy:
replicas: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
- "traefik.http.services.myapp.loadbalancer.server.port=8080"
networks:
- traefik-public
Fenêtre de terminal
# Initialiser Swarm (si pas fait)
docker swarm init
# Créer le réseau overlay
docker network create --driver=overlay --attachable traefik-public
# Déployer Traefik en mode global
docker stack deploy -c traefik-stack.yml traefik
# Déployer l'application
docker stack deploy -c myapp-stack.yml myapp

En développement, monter le socket directement est acceptable :

volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro

Le :ro (read-only) est une protection minimale, mais Traefik peut toujours lire toutes les informations Docker.

En production, utilisez docker-socket-proxy pour limiter les endpoints API accessibles :

services:
socket-proxy:
image: tecnativa/docker-socket-proxy
container_name: socket-proxy
restart: unless-stopped
environment:
# Endpoints autorisés (lecture seule)
CONTAINERS: 1
SERVICES: 1
NETWORKS: 1
TASKS: 1
# Endpoints bloqués
BUILD: 0
COMMIT: 0
CONFIGS: 0
DISTRIBUTION: 0
EXEC: 0
IMAGES: 0
INFO: 0
NODES: 0
PLUGINS: 0
SECRETS: 0
SWARM: 0
SYSTEM: 0
VOLUMES: 0
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket-proxy
traefik:
image: traefik:v3.6
depends_on:
- socket-proxy
command:
- "--providers.docker.endpoint=tcp://socket-proxy:2375"
# ...autres options
networks:
- socket-proxy
- traefik-public
networks:
socket-proxy:
driver: bridge
traefik-public:
external: true

Traefik vérifie régulièrement la santé des backends :

labels:
# Healthcheck HTTP
- "traefik.http.services.api.loadbalancer.healthcheck.path=/health"
- "traefik.http.services.api.loadbalancer.healthcheck.port=8080"
- "traefik.http.services.api.loadbalancer.healthcheck.interval=10s"
- "traefik.http.services.api.loadbalancer.healthcheck.timeout=3s"
OptionDescriptionValeur par défaut
pathChemin à vérifier/
portPort à vérifierPort du service
intervalIntervalle entre vérifications30s
timeoutTimeout de la vérification5s
labels:
# Round Robin (défaut)
- "traefik.http.services.api.loadbalancer.strategy=round-robin"
# Sticky sessions (affinité)
- "traefik.http.services.api.loadbalancer.sticky.cookie=true"
- "traefik.http.services.api.loadbalancer.sticky.cookie.name=server_id"
- "traefik.http.services.api.loadbalancer.sticky.cookie.secure=true"

Symptômes : Le conteneur ne s’affiche pas dans le dashboard Traefik.

Vérifications :

  1. Le label traefik.enable=true est présent ?

    Fenêtre de terminal
    docker inspect mycontainer | jq '.[0].Config.Labels'
  2. Le conteneur est sur le bon réseau ?

    Fenêtre de terminal
    docker network inspect traefik-public
  3. exposedByDefault est-il à true ou false ?

Symptômes : Erreur 502 ou 504 après plusieurs secondes.

Causes possibles :

  1. Port incorrect : Vérifiez le label loadbalancer.server.port

    - "traefik.http.services.myapp.loadbalancer.server.port=8080"
  2. Réseau différent : Traefik et le conteneur ne sont pas sur le même réseau

    - "traefik.docker.network=traefik-public"
  3. Application non démarrée : Vérifiez les logs du conteneur

    Fenêtre de terminal
    docker logs mycontainer

Symptômes : “router already exists” dans les logs Traefik.

Solution : Utilisez des noms uniques pour chaque router et service :

# Mauvais : même nom "app" pour deux services
- "traefik.http.routers.app.rule=..." # Conflit !
# Bon : noms uniques
- "traefik.http.routers.frontend-app.rule=..."
- "traefik.http.routers.api-app.rule=..."
Fenêtre de terminal
# Logs Traefik
docker logs traefik -f
# Niveau debug
docker run traefik:v3.6 --log.level=DEBUG
# Vérifier les routers via API
curl http://localhost:8080/api/http/routers | jq
# Vérifier les services
curl http://localhost:8080/api/http/services | jq
  1. Labels = Configuration : Les labels Docker définissent le routage sans fichier de configuration externe

  2. exposedByDefault: false : Toujours désactiver pour éviter d’exposer des conteneurs par accident

  3. Réseau dédié : Créez un réseau traefik-public externe et attachez-y tous les services à exposer

  4. traefik.docker.network : Obligatoire quand un conteneur est sur plusieurs réseaux

  5. Socket proxy en production : Utilisez docker-socket-proxy pour sécuriser l’accès au daemon Docker

  6. Noms uniques : Chaque router et service doit avoir un nom unique pour éviter les conflits

  7. Healthchecks : Configurez-les pour retirer automatiquement les backends en échec

  8. Swarm : Utilisez le provider swarm avec labels dans deploy.labels

Bientôt : guides sur les middlewares de sécurité, la configuration TLS avancée, et l’observabilité avec Traefik.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.