Docker est devenu le standard de fait pour le déploiement d'applications. Mais passer d'un docker run en développement à une infrastructure de production sécurisée, résiliente et maintenable est un tout autre exercice. Trop d'équipes françaises mettent en production des conteneurs avec des images bloatées, des secrets en clair dans les variables d'environnement, aucun healthcheck et zéro monitoring. Ce guide détaille les 7 étapes concrètes pour déployer Docker en production de manière sécurisée, avec des exemples de configuration que vous pouvez adapter immédiatement.
Étape 1 — Construire des images Docker minimales et sécurisées
La sécurité d'un conteneur commence par l'image. Une image de production doit être la plus légère possible : moins de composants signifie moins de vulnérabilités potentielles, des téléchargements plus rapides et un démarrage accéléré.
Multi-stage builds : la base
Le multi-stage build est la technique fondamentale pour produire des images minimales. L'idée est simple : utiliser une image complète (avec compilateurs, outils de build, dépendances de développement) pour construire l'application, puis copier uniquement les artefacts nécessaires dans une image finale légère.
# Stage 1 : Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production=false
COPY . .
RUN npm run build
# Stage 2 : Production
FROM node:22-alpine AS production
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]Points clés de cet exemple :
- Utilisateur non-root : la directive
USER appusergarantit que l'application ne tourne pas en tant que root dans le conteneur. C'est une règle de sécurité fondamentale, pourtant encore ignorée dans une majorité d'images de production. - Image Alpine :
node:22-alpinepèse environ 50 Mo contre 350 Mo pour l'image Debian classique. Moins de surface d'attaque, moins de CVE à gérer. npm ciau lieu denpm install:ci(clean install) respecte strictement lepackage-lock.json, garantissant la reproductibilité du build.--chownsur COPY : les fichiers copiés appartiennent à l'utilisateur applicatif, pas à root.
Aller plus loin : images distroless
Pour les applications compilées (Go, Rust, Java), les images distroless de Google sont le choix optimal. Elles ne contiennent ni shell, ni package manager, ni utilitaires système — uniquement le runtime de l'application. Un attaquant qui parviendrait à exécuter du code dans le conteneur n'aurait aucun outil à disposition pour se déplacer latéralement ou escalader ses privilèges.
Lancez docker scout cves ou trivy image sur chaque image avant de la pousser en production. Fixez un seuil : zéro CVE critique, zéro CVE haute. Automatisez cette vérification dans votre pipeline CI/CD.
Étape 2 — Gérer les secrets sans les embarquer dans l'image
Règle absolue : aucun secret ne doit être présent dans l'image Docker. Pas dans un ENV, pas dans un ARG, pas dans un fichier copié dans l'image. Les couches d'une image Docker sont immuables et inspectables — tout secret embarqué est potentiellement accessible à quiconque a accès à l'image.
Docker Secrets (Docker Swarm)
Si vous utilisez Docker Swarm, les Docker Secrets sont la solution native. Les secrets sont chiffrés au repos dans le Raft store du cluster Swarm et montés en fichiers dans /run/secrets/ dans le conteneur. Seuls les services explicitement autorisés y ont accès.
# Creer un secret
echo "mot_de_passe_db" | docker secret create db_password -
# docker-compose.yml (Swarm)
services:
app:
image: monapp:latest
secrets:
- db_password
environment:
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
db_password:
external: trueGestionnaires de secrets externes
Pour les infrastructures plus complexes, utilisez un gestionnaire de secrets dédié : HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault. L'application récupère les secrets au démarrage via l'API du gestionnaire, avec une rotation automatique possible. Vault, en particulier, offre des secrets dynamiques à durée de vie limitée — le secret n'existe que le temps de la session.
Vérifiez également que vos fichiers .env sont dans le .gitignore et le .dockerignore. Un docker build sans .dockerignore copie tout le répertoire de contexte, y compris les fichiers .env contenant vos credentials.
Étape 3 — Configurer les healthchecks et les politiques de redémarrage
Un conteneur qui tourne ne signifie pas un conteneur fonctionnel. Votre application peut être plantée en boucle, en deadlock, ou répondre avec des erreurs 500 tout en maintenant le processus actif. Les healthchecks permettent à Docker (et à l'orchestrateur) de détecter ces situations et de réagir automatiquement.
# Dans le Dockerfile
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1Ou dans le docker-compose.yml :
services:
app:
image: monapp:latest
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
restart: unless-stopped
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 5
window: 120sPoints importants :
start_period: délai avant que Docker ne commence à compter les échecs. Ajustez-le au temps de démarrage de votre application.restart: unless-stopped: le conteneur redémarre automatiquement sauf s'il a été arrêté explicitement. Indispensable en production.- Endpoint
/health: implémentez un endpoint dédié dans votre application qui vérifie la connectivité à la base de données, les dépendances critiques et retourne un code 200 si tout est fonctionnel.
💡 Notre avis d'expert
J'audite régulièrement des infrastructures Docker de PME françaises, et l'absence de healthcheck est le problème numéro un. Sans healthcheck, un conteneur planté reste dans l'état « running » indéfiniment. Le monitoring ne détecte rien, les utilisateurs se plaignent, et l'équipe passe des heures à diagnostiquer un problème que Docker aurait pu résoudre automatiquement en 30 secondes.
Étape 4 — Mettre en place un reverse proxy sécurisé avec TLS automatique
Aucune application Docker ne devrait être exposée directement sur Internet. Un reverse proxy sert d'intermédiaire entre les clients et vos conteneurs, apportant terminaison TLS, rate limiting, en-têtes de sécurité et load balancing.
Traefik : le choix Docker-native
Traefik est particulièrement adapté aux infrastructures Docker car il découvre automatiquement les conteneurs via le socket Docker et configure le routage dynamiquement. Le renouvellement des certificats TLS via Let's Encrypt est entièrement automatique.
# docker-compose.yml avec Traefik
services:
traefik:
image: traefik:v3.3
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@mondomaine.fr"
- "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- acme-data:/acme
app:
image: monapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`mondomaine.fr`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.routers.app.entrypoints=websecure"
volumes:
acme-data:Ajoutez également les en-têtes de sécurité via les middlewares Traefik :
Strict-Transport-Security(HSTS) pour forcer HTTPSX-Content-Type-Options: nosniffpour empêcher le MIME sniffingX-Frame-Options: DENYpour bloquer le clickjackingContent-Security-Policypour contrôler les sources de contenu
Un point critique souvent négligé : le montage du socket Docker (/var/run/docker.sock) en lecture seule (:ro). Un accès en écriture au socket Docker équivaut à un accès root sur la machine hôte.
Étape 5 — Implémenter le monitoring et le logging centralisé
Une infrastructure Docker sans monitoring, c'est piloter à l'aveugle. En production, vous devez surveiller trois dimensions : les métriques système (CPU, mémoire, disque, réseau), les métriques applicatives (temps de réponse, taux d'erreur, requêtes par seconde) et les logs.
Stack de monitoring recommandée
La stack la plus éprouvée pour Docker en production est Prometheus + Grafana + Loki :
- Prometheus avec cAdvisor : collecte les métriques de chaque conteneur (CPU, mémoire, I/O réseau, I/O disque). cAdvisor est intégré nativement à Docker et expose les métriques au format Prometheus.
- Grafana : dashboards de visualisation. Créez des dashboards spécifiques par service avec des alertes sur les seuils critiques (CPU > 80%, mémoire > 90%, taux d'erreur > 1%).
- Loki : agrégation de logs. Le driver Docker Loki envoie les logs de chaque conteneur directement à Loki sans agent supplémentaire. Plus léger et plus simple à configurer que la stack ELK (Elasticsearch + Logstash + Kibana).
Pour les équipes qui veulent aller plus loin sur l'observabilité, consultez notre guide dédié : Configurer l'observabilité OpenTelemetry pour vos microservices en 8 étapes.
Logging : bonnes pratiques Docker
Quelques règles essentielles pour le logging en production Docker :
- Loggez sur stdout/stderr : ne loggez pas dans des fichiers à l'intérieur du conteneur. Docker capture nativement stdout/stderr et les rend accessibles via
docker logset les drivers de logging. - Format JSON structuré : utilisez un format de log structuré (JSON) pour faciliter le parsing et la recherche dans Loki ou Elasticsearch.
- Limitez la taille des logs : configurez
max-sizeetmax-filedans le daemon Docker pour éviter que les logs ne remplissent le disque.
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Besoin d'aide pour votre infrastructure Docker ?
D-Open accompagne les équipes françaises dans la mise en place d'infrastructures Docker de production sécurisées. De l'audit de vos images à la configuration du monitoring, nous couvrons l'ensemble du cycle de vie de vos conteneurs.
Parler à un expert DockerÉtape 6 — Automatiser le déploiement via un pipeline CI/CD sécurisé
Le déploiement manuel (ssh + docker pull + docker-compose up) n'a pas sa place en production. Chaque déploiement doit passer par un pipeline CI/CD automatisé qui garantit la reproductibilité, la traçabilité et la vérification de sécurité.
Structure du pipeline
Un pipeline Docker CI/CD de production devrait comporter les étapes suivantes :
- Build : construction de l'image Docker à partir du Dockerfile. Taguez l'image avec le hash du commit (pas
:latest) pour la traçabilité. - Test : exécution des tests unitaires et d'intégration à l'intérieur du conteneur. Si les tests échouent, le pipeline s'arrête.
- Scan de sécurité : scan de vulnérabilités sur l'image avec Trivy ou Docker Scout. Gate : zéro CVE critique autorisée.
- Lint Dockerfile : vérification des bonnes pratiques avec Hadolint (utilisateur non-root, pas de
ADDsauf nécessaire, pas delatestdans les FROM). - Push au registry : push de l'image vers un registry privé (GitHub Container Registry, Harbor, AWS ECR). Signez l'image avec Cosign pour garantir son intégrité.
- Déploiement : rolling update sur l'orchestrateur. Le healthcheck doit être validé avant de dérouter le trafic vers les nouveaux conteneurs.
Pour un guide complet sur la sécurisation du pipeline CI/CD lui-même, consultez : Comment sécuriser votre pipeline CI/CD GitHub Actions en 7 étapes.
Rolling updates et zero-downtime
En Docker Swarm, les rolling updates sont natives. Configurez-les dans votre docker-compose.yml :
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 30s
order: start-first
failure_action: rollback
rollback_config:
parallelism: 0
order: stop-firstLe paramètre order: start-first garantit que le nouveau conteneur est démarré et sain (healthcheck validé) avant que l'ancien ne soit arrêté. C'est la clé du zero-downtime deployment. En cas d'échec, failure_action: rollback revient automatiquement à la version précédente.
💡 Notre avis d'expert
Le tag :latest est l'ennemi de la production. Taguez toujours vos images avec le hash du commit git (monapp:abc123f) ou un numéro de version sémantique (monapp:2.4.1). Cela garantit la reproductibilité et permet un rollback instantané vers n'importe quelle version précédente. J'ai vu trop de post-mortems commençant par « on ne sait pas quelle version était déployée ».
Étape 7 — Planifier les sauvegardes et la reprise après incident
Les conteneurs sont ephémères par conception. Toute donnée qui n'est pas dans un volume persistant est perdue quand le conteneur est recréé. C'est un avantage pour la sécurité (pas de données résiduelles) mais un risque pour les données de production si les volumes ne sont pas correctement sauvegardés.
Stratégie de sauvegarde des volumes
Les données persistantes (bases de données, fichiers uploadés, configurations) sont stockées dans des volumes Docker. Ces volumes doivent être sauvegardés régulièrement :
# Sauvegarde d'un volume PostgreSQL
docker exec postgres_container pg_dumpall -U postgres | \
gzip > /backups/postgres_$(date +%Y%m%d_%H%M%S).sql.gz
# Sauvegarde d'un volume generique
docker run --rm \
-v monvolume:/source:ro \
-v /backups:/backup \
alpine tar czf /backup/volume_$(date +%Y%m%d).tar.gz -C /source .Automatisez ces sauvegardes avec un cron job ou un conteneur dédié (comme volumerize ou docker-backup). Stockez les sauvegardes sur un système de fichiers distant (S3, Azure Blob, NFS) — jamais uniquement sur la même machine que les conteneurs.
Plan de reprise après incident (PRA)
Documentez et testez votre procédure de reprise :
- Infrastructure as Code : toute votre configuration Docker (Dockerfile, docker-compose.yml, configurations Traefik, configurations monitoring) doit être versionnée dans Git. Un
git clone+docker-compose updoit suffire à recréer l'infrastructure complète. - Restoration des données : documentez précisément comment restaurer chaque volume à partir d'une sauvegarde. Testez cette procédure au moins une fois par trimestre.
- RTO et RPO : définissez votre Recovery Time Objective (temps maximum d'indisponibilité acceptable) et votre Recovery Point Objective (perte de données maximale acceptable). Ajustez la fréquence de sauvegarde en conséquence.
Bonnes pratiques complémentaires
Au-delà des 7 étapes principales, quelques pratiques complémentaires renforceront votre posture de sécurité :
- Resource limits : limitez le CPU et la mémoire de chaque conteneur avec
deploy.resources.limits. Un conteneur sans limite peut consommer toutes les ressources de l'hôte et impacter les autres services. Cela protège également contre certaines attaques de type DoS. - Réseaux Docker isolés : créez des réseaux Docker dédiés par groupe de services. La base de données ne doit pas être sur le même réseau que le reverse proxy. Seuls les conteneurs qui ont besoin de communiquer doivent partager un réseau.
- Read-only filesystem : montez le système de fichiers du conteneur en lecture seule (
read_only: true) et créez des volumes spécifiques pour les répertoires où l'application a besoin d'écrire (/tmp,/var/log). Un attaquant ne pourra pas modifier les binaires de l'application. - No new privileges : ajoutez
security_opt: - no-new-privileges:truepour empêcher l'escalade de privilèges dans le conteneur. - Drop capabilities : supprimez toutes les capabilities Linux par défaut avec
cap_drop: ALLet réajoutez uniquement celles nécessaires. La plupart des applications web n'ont besoin d'aucune capability.
💡 Notre avis d'expert
La majorité des déploiements Docker de production que j'audite en France manquent au moins trois de ces sept étapes. Le schéma le plus courant : une image :latest construite sans multi-stage, un docker-compose up -d lancé en SSH, aucun healthcheck, aucun monitoring, et des secrets dans un fichier .env committé dans Git. Si vous vous reconnaissez, ne culpabilisez pas — commencez par les étapes 1 (image minimale) et 3 (healthcheck), puis ajoutez les couches progressivement. La perfection est l'ennemi du progrès.
Conclusion : Déployer Docker en production sécurisée n'est pas un projet ponctuel — c'est une pratique continue. Les 7 étapes de ce guide (images minimales, gestion des secrets, healthchecks, reverse proxy, monitoring, CI/CD, sauvegardes) forment une base solide sur laquelle construire. Commencez par les fondamentaux — un Dockerfile multi-stage avec un utilisateur non-root et un healthcheck — puis ajoutez les couches de sécurité progressivement. Chaque étape supplémentaire réduit votre surface d'attaque et augmente la résilience de votre infrastructure. Obtenir un devis gratuit si vous avez besoin d'un accompagnement pour sécuriser votre infrastructure Docker.
Audit Docker de production
D-Open réalise des audits complets de votre infrastructure Docker : sécurité des images, configuration des conteneurs, réseau, monitoring, pipeline CI/CD. Nous vous fournissons un rapport détaillé avec des recommandations actionables et vous accompagnons dans leur mise en oeuvre.
Demander un audit DockerQuestions fréquentes
Quelle est la différence entre Docker Compose et Docker Swarm pour la production ?▼
Docker Compose est un outil de définition et d'exécution d'applications multi-conteneurs, idéal pour le développement local et les petites productions sur un seul serveur. Docker Swarm est un orchestrateur natif Docker qui gère le déploiement sur un cluster de machines avec haute disponibilité, load balancing intégré et rolling updates. Pour la production, Swarm ou Kubernetes offrent la redondance et le scaling automatique que Compose seul ne fournit pas.
Pourquoi utiliser des images distroless ou Alpine en production ?▼
Les images distroless et Alpine réduisent drastiquement la surface d'attaque en éliminant les outils système inutiles. Une image distroless ne contient ni shell, ni package manager — un attaquant qui parviendrait à exécuter du code dans le conteneur n'aurait aucun outil à disposition. Alpine pèse environ 5 Mo contre 80 Mo pour Debian slim, ce qui réduit aussi les temps de téléchargement et de démarrage.
Comment gérer les secrets Docker sans les mettre dans l'image ?▼
Trois méthodes recommandées : 1) Docker Secrets (natif avec Swarm), qui monte les secrets en fichiers dans /run/secrets/. 2) Un gestionnaire de secrets externe comme HashiCorp Vault, qui fournit des secrets dynamiques à durée de vie limitée. 3) Des variables d'environnement injectées au runtime, jamais stockées dans le Dockerfile. Règle absolue : ne jamais utiliser ARG ou ENV dans le Dockerfile pour les secrets, car ils sont stockés dans les couches de l'image.
Docker en production ou Kubernetes direct ?▼
Cela dépend de la taille de votre infrastructure. Docker Swarm est parfaitement adapté pour des applications avec moins de 20 conteneurs sur 1 à 5 serveurs — c'est le cas de la majorité des PME et startups françaises. Kubernetes devient pertinent quand vous gérez des centaines de conteneurs, du multi-cloud, ou quand vous avez une équipe platform engineering dédiée. Ne choisissez pas Kubernetes par effet de mode — choisissez-le quand la complexité de votre infrastructure le justifie.