D-OPEN

Comment déployer une application Docker en production sécurisée en 7 étapes

Clara Fontaine

Clara Fontaine

Architecte cloud & spécialiste conteneurisation · 8 ans · 13 juin 2026 · 16 min de lecture

Déployer Docker en production sécurisée conteneurs

TL;DR — Les 7 étapes

  • Étape 1 : Construire des images Docker minimales et sécurisées (multi-stage, distroless, utilisateur non-root).
  • Étape 2 : Gérer les secrets sans les embarquer dans l'image (Docker Secrets, Vault, variables runtime).
  • Étape 3 : Configurer les healthchecks et les politiques de redémarrage.
  • Étape 4 : Mettre en place un reverse proxy sécurisé (Traefik/Nginx + TLS automatique).
  • Étape 5 : Implémenter le monitoring et le logging centralisé.
  • Étape 6 : Automatiser le déploiement via un pipeline CI/CD sécurisé.
  • Étape 7 : Planifier les sauvegardes et la reprise après incident.

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 appuser garantit 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-alpine pèse environ 50 Mo contre 350 Mo pour l'image Debian classique. Moins de surface d'attaque, moins de CVE à gérer.
  • npm ci au lieu de npm install : ci (clean install) respecte strictement le package-lock.json, garantissant la reproductibilité du build.
  • --chown sur 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: true

Gestionnaires 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 1

Ou 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: 120s

Points 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 HTTPS
  • X-Content-Type-Options: nosniff pour empêcher le MIME sniffing
  • X-Frame-Options: DENY pour bloquer le clickjacking
  • Content-Security-Policy pour 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.

Pipeline de déploiement Docker sécuriséCode sourceGit pushCommit signé GPGCI/CD PipelineBuild + TestScan vulnérabilitésRegistry privéImage signéeScan continu TrivyOrchestrateurRolling updateHealthcheck validéPRODLiveMonitoréChaque étape = un point de contrôle sécuritéL'image ne passe en production que si tous les gates sont validésContrôles automatiques✓ Scan CVE (Trivy / Docker Scout)✓ Lint Dockerfile (Hadolint)✓ Tests unitaires + intégration✓ Vérification non-root userErreurs fréquentes✗ Pas de .dockerignore✗ Secrets dans ENV / ARG✗ Image :latest en production✗ Root user dans le conteneur

É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 logs et 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-size et max-file dans 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 :

  1. Build : construction de l'image Docker à partir du Dockerfile. Taguez l'image avec le hash du commit (pas :latest) pour la traçabilité.
  2. Test : exécution des tests unitaires et d'intégration à l'intérieur du conteneur. Si les tests échouent, le pipeline s'arrête.
  3. Scan de sécurité : scan de vulnérabilités sur l'image avec Trivy ou Docker Scout. Gate : zéro CVE critique autorisée.
  4. Lint Dockerfile : vérification des bonnes pratiques avec Hadolint (utilisateur non-root, pas de ADD sauf nécessaire, pas de latest dans les FROM).
  5. 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é.
  6. 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-first

Le 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 up doit 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.
Checklist sécurité Docker en productionImage & Build✓ Multi-stage build✓ Image Alpine ou distroless✓ Utilisateur non-root✓ .dockerignore configuré✓ Scan CVE (Trivy/Scout)✓ Lint Hadolint✓ Tag avec hash commit✓ Image signée Cosign✓ Registry privé✓ Zéro secret dans l'imageRuntime & Réseau✓ Healthcheck configuré✓ restart: unless-stopped✓ Reverse proxy (Traefik/Nginx)✓ TLS automatique Let's Encrypt✓ En-têtes sécurité (HSTS, CSP)✓ Rate limiting✓ Réseau Docker isolé✓ Socket Docker :ro✓ Secrets via Docker Secrets/Vault✓ Pas de --privilegedOps & Résilience✓ Monitoring Prometheus + Grafana✓ Logging centralisé (Loki)✓ Alertes configurées✓ Sauvegardes volumes✓ Backup offsite (S3)✓ PRA documenté et testé✓ Rolling updates configurées✓ Rollback automatique✓ Limite logs (max-size/max-file)✓ Resource limits (CPU/mémoire)

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:true pour empêcher l'escalade de privilèges dans le conteneur.
  • Drop capabilities : supprimez toutes les capabilities Linux par défaut avec cap_drop: ALL et 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 Docker

Questions 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.

Articles similaires