Stratégie de mise à jour Docker : mises à jour sans interruption sur un VPS
Quatre méthodes progressives pour mettre à jour vos conteneurs Docker sur un VPS, du simple pull-and-replace au déploiement blue-green sans interruption avec Traefik. Épinglage d'images, rollback, notifications Diun et docker-rollout.
Mettre à jour des conteneurs Docker sur un VPS ne signifie pas forcément une interruption de service. La bonne approche dépend de ce que vous exécutez et du temps d'arrêt que vous pouvez tolérer. Un blog personnel supporte quelques secondes d'interruption pendant un docker compose up -d. Un produit SaaS avec des clients payants, non.
Ce guide couvre quatre méthodes, de la plus simple à la plus résiliente. Chacune s'appuie sur la précédente. Commencez par celle qui correspond à votre situation et passez au niveau supérieur quand le besoin s'en fait sentir.
Prérequis : Un VPS sous Debian 12 ou Ubuntu 24.04 avec Docker Engine 27+ et Docker Compose v2 installés. Toutes les commandes utilisent la syntaxe du plugin docker compose (pas l'ancien binaire docker-compose v1). Docker en production sur un VPS : ce qui casse et comment corriger
Comment épingler les images Docker à une version précise ?
Épinglez vos images à une version mineure ou patch dans votre fichier compose. Le tag latest est une cible mouvante qui peut introduire des changements cassants sans prévenir. L'épinglage vous donne le contrôle sur le moment des mises à jour et rend le rollback possible en conservant l'image précédente localement.
Différentes stratégies de tags comportent différents niveaux de risque :
| Format du tag | Exemple | Niveau de risque | Comportement de mise à jour |
|---|---|---|---|
latest |
nginx:latest |
Élevé | N'importe quelle version, n'importe quand. Impossible de savoir ce qui a changé. |
| Majeure seule | nginx:1 |
Moyen-élevé | Peut passer de 1.25 à 1.27. Les versions mineures peuvent changer le comportement. |
| Mineure | nginx:1.27 |
Faible | Reçoit les patchs (1.27.0 à 1.27.3). Sûr pour la plupart des workloads. |
| Patch | nginx:1.27.3 |
Très faible | Version exacte. Pas de mise à jour surprise. Vous mettez à jour manuellement. |
| Digest | nginx:1.27.3@sha256:6f12... |
Minimal | Image identique octet par octet à chaque fois. Immunisé contre la mutation de tags. |
Pour la plupart des services en production, épinglez à la version mineure (image: postgres:16.6). C'est le bon équilibre entre patchs de sécurité et stabilité. Pour les services où la reproductibilité compte (CI, environnements réglementés), épinglez au digest complet.
services:
app:
image: myapp:2.4.1
# Not: image: myapp:latest
db:
image: postgres:16.6
Enregistrez les digests de vos images actuelles avant de mettre à jour. Vous en aurez besoin pour un rollback :
docker image inspect --format='{{index .RepoDigests 0}}' $(docker compose images app -q)
myapp@sha256:a1b2c3d4e5f6...
Comment configurer les health checks dans Docker Compose ?
Les health checks indiquent à Docker si votre conteneur fonctionne réellement, pas seulement s'il tourne. Tous les patterns de mise à jour sans interruption en dépendent. Sans health check, Docker n'a aucun moyen de savoir si le nouveau conteneur est prêt avant de supprimer l'ancien.
Ajoutez un bloc healthcheck à chaque service dans votre fichier compose. La commande test s'exécute à l'intérieur du conteneur à l'intervalle spécifié. Docker marque le conteneur comme healthy uniquement après la réussite du test.
services:
app:
image: myapp:2.4.1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
Rôle de chaque champ :
- test : La commande à exécuter.
CMDl'exécute directement. UtilisezCMD-SHELLsi vous avez besoin de fonctionnalités shell comme les pipes. - interval : Temps entre les vérifications. 15s est raisonnable pour les services web.
- timeout : Temps d'attente avant de considérer la commande comme échouée.
- retries : Nombre d'échecs consécutifs avant que Docker marque le conteneur comme
unhealthy. - start_period : Période de grâce après le démarrage du conteneur. Les health checks pendant cette fenêtre ne comptent pas dans le seuil d'échec. Réglez-la suffisamment longue pour le démarrage de votre application.
Pour les services qui n'ont pas curl installé, utilisez le check natif proposé par le service :
db:
image: postgres:16.6
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
cache:
image: redis:7.4
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
Après le démarrage de vos services, vérifiez que les health checks passent :
docker compose ps
NAME IMAGE STATUS PORTS
app myapp:2.4.1 Up 2 minutes (healthy) 0.0.0.0:8080->8080/tcp
db postgres:16.6 Up 2 minutes (healthy) 5432/tcp
Le statut (healthy) signifie que votre health check est configuré et qu'il passe. Si vous voyez (health: starting), le conteneur est encore dans sa start_period. Si (unhealthy), consultez les logs du health check :
docker inspect --format='{{json .State.Health}}' $(docker compose ps -q app) | python3 -m json.tool
Comment mettre à jour un conteneur Docker sur mon VPS ?
Exécutez docker compose pull pour récupérer la nouvelle image, puis docker compose up -d pour remplacer le conteneur. Docker Compose arrête l'ancien conteneur, le supprime et en démarre un nouveau à partir de l'image mise à jour. Cela provoque une brève interruption (généralement 2 à 10 secondes) pendant que le nouveau conteneur démarre et passe son health check.
Étape par étape : la mise à jour simple
Avant toute mise à jour, sauvegardez vos volumes. Une mise à jour ratée avec des données corrompues est bien pire que quelques secondes d'interruption.
Lisez le changelog de la nouvelle version. Vérifiez les changements cassants, les options de configuration dépréciées et les étapes de migration requises. Ça prend cinq minutes et ça vous évite des heures de débogage.
# Pull the new image
docker compose pull app
# Check what changed
docker compose up -d --dry-run
Le flag --dry-run (Docker Compose v2.20+) montre ce que Compose va faire sans réellement le faire. Vous verrez quels conteneurs seront recréés :
DRY RUN MODE - service "app" - Pull
DRY RUN MODE - Container app-1 - Recreate
DRY RUN MODE - Container app-1 - Started
Appliquez la mise à jour :
docker compose up -d app
[+] Running 1/1
✔ Container app-1 Started 0.8s
Vérifiez que le nouveau conteneur est healthy :
docker compose ps app
NAME IMAGE STATUS PORTS
app myapp:2.5.0 Up 15 seconds (healthy) 0.0.0.0:8080->8080/tcp
Puis testez depuis l'extérieur du serveur pour vous assurer que le service est accessible :
curl -s -o /dev/null -w "%{http_code}" https://app.example.com/health
200
Quand mettre à jour vos conteneurs Docker ?
Toutes les mises à jour n'ont pas la même urgence. Un calendrier de mises à jour uniforme mène soit à des risques inutiles, soit à des patchs de sécurité manqués.
- Patchs de sécurité (CVE) : Appliquez-les immédiatement. Abonnez-vous aux avis de sécurité de vos images. Un CVE connu dans un conteneur exposé publiquement est exploité en quelques heures après sa divulgation, pas en quelques jours.
- Versions patch (ex. : 2.4.1 à 2.4.2) : Planifiez-les chaque semaine ou toutes les deux semaines. Ce sont des correctifs. Lisez le changelog, mettez à jour, vérifiez.
- Versions mineures (ex. : 2.4 à 2.5) : Planifiez-les mensuellement. Testez d'abord dans un environnement de staging si vous en avez un. Revoyez le changelog pour les changements de comportement.
- Versions majeures (ex. : 2.x à 3.x) : Planifiez et testez. Les versions majeures cassent des choses. Lisez le guide de migration. Testez sur un VPS séparé ou en local avant de toucher à la production.
Comment revenir à une image Docker précédente ?
Docker Compose n'a pas de commande de rollback intégrée. Pour revenir en arrière : éditez votre fichier compose pour épingler le tag ou digest de l'image précédente, puis exécutez docker compose up -d. Le conteneur redémarre avec l'ancienne image. Cela fonctionne uniquement si vous avez conservé l'ancienne image localement (n'exécutez pas docker image prune juste après la mise à jour).
Rollback étape par étape
Supposons que vous avez mis à jour myapp de 2.4.1 à 2.5.0 et que la nouvelle version est cassée.
- Vérifiez que l'ancienne image est toujours disponible localement :
docker images myapp
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp 2.5.0 abc123def456 2 hours ago 185MB
myapp 2.4.1 789fed654cba 2 weeks ago 182MB
- Éditez votre fichier compose pour épingler la version précédente :
services:
app:
image: myapp:2.4.1
- Effectuez le rollback :
docker compose up -d app
[+] Running 1/1
✔ Container app-1 Started 0.7s
- Vérifiez que le rollback a fonctionné :
docker compose ps app
NAME IMAGE STATUS PORTS
app myapp:2.4.1 Up 10 seconds (healthy) 0.0.0.0:8080->8080/tcp
Si vous avez déjà purgé l'ancienne image, Docker la récupérera depuis le registre (en supposant que le tag existe toujours). Pour une sécurité maximale, notez le digest complet (sha256:...) avant de mettre à jour. Les digests sont immuables. Les tags peuvent être écrasés.
Automatisez avec un script de pré-mise à jour
Sauvegardez l'état actuel avant chaque mise à jour pour qu'un rollback soit toujours à une commande :
#!/bin/bash
# save-state.sh - Run before every update
COMPOSE_FILE="${1:-docker-compose.yml}"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="./rollback/${DATE}"
mkdir -p "${BACKUP_DIR}"
cp "${COMPOSE_FILE}" "${BACKUP_DIR}/"
docker compose ps --format json > "${BACKUP_DIR}/containers.json"
docker compose images --format json > "${BACKUP_DIR}/images.json"
echo "State saved to ${BACKUP_DIR}"
chmod 700 save-state.sh
Watchtower est-il toujours maintenu en 2026 ?
Watchtower a été archivé le 17 décembre 2025. Les mainteneurs n'utilisent plus Docker et ont arrêté le développement. La dernière version est la v1.7.1. Plus important encore, le SDK Docker de Watchtower utilise l'API v1.25, mais Docker Engine 29 a relevé la version minimale de l'API à v1.44. Watchtower est incompatible avec les versions actuelles de Docker, sauf si vous abaissez manuellement le minimum de l'API avec DOCKER_MIN_API_VERSION=1.25 dans la configuration de votre daemon. C'est un contournement, pas une solution.
Si vous utilisez Watchtower aujourd'hui, prévoyez de migrer. Pour des notifications de mises à jour automatisées sans redémarrage automatique, utilisez Diun. Pour des mises à jour automatisées sans interruption, utilisez docker-rollout derrière un reverse proxy.
Comment Diun vous notifie-t-il des mises à jour d'images Docker ?
Diun (Docker Image Update Notifier) surveille vos registres Docker et envoie des notifications quand de nouvelles versions d'images sont disponibles. Il ne met pas à jour les conteneurs. Il vous informe qu'une mise à jour existe pour que vous puissiez lire le changelog et mettre à jour selon vos propres termes. C'est l'approche « savoir avant d'agir ».
Ajoutez Diun à votre fichier compose existant ou créez-en un dédié :
services:
diun:
image: crazymax/diun:4
command: serve
volumes:
- "diun-data:/data"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
environment:
TZ: "Europe/Berlin"
DIUN_WATCH_WORKERS: "10"
DIUN_WATCH_SCHEDULE: "0 6 * * *"
DIUN_PROVIDERS_DOCKER: "true"
DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: "true"
DIUN_NOTIF_SLACK_WEBHOOKURL_FILE: "/run/secrets/slack_webhook"
secrets:
- slack_webhook
restart: unless-stopped
secrets:
slack_webhook:
file: ./secrets/slack_webhook.txt
volumes:
diun-data:
L'URL du webhook Slack va dans un fichier de secrets, pas dans une variable d'environnement, car Docker secrets la garde en dehors de la sortie de docker inspect et des listings de processus. Créez le fichier de secrets avec des permissions restreintes :
mkdir -p secrets
echo "https://hooks.slack.com/services/YOUR/WEBHOOK/URL" > secrets/slack_webhook.txt
chmod 600 secrets/slack_webhook.txt
Paramètres principaux :
- DIUN_WATCH_SCHEDULE : Expression cron.
0 6 * * *vérifie tous les jours à 06h00. Ajustez selon votre fenêtre de maintenance. - DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT : Quand
true, Diun surveille tous les conteneurs en cours d'exécution. Mettez àfalseet utilisez des labels pour une surveillance sélective. - Montage du socket Docker : En lecture seule (
:ro) car Diun ne lit que les métadonnées des conteneurs. Il ne démarre ni n'arrête jamais de conteneurs.
Pour une surveillance sélective (recommandé pour les stacks plus importants), mettez WATCHBYDEFAULT à false et ajoutez des labels aux conteneurs que vous souhaitez surveiller :
services:
app:
image: myapp:2.4.1
labels:
- "diun.enable=true"
- "diun.watch_repo=true"
Démarrez Diun et consultez les logs :
docker compose up -d diun
docker compose logs diun --tail 20
diun | Thu, 19 Mar 2026 06:00:01 CET INF Starting Diun version=v4.31.0
diun | Thu, 19 Mar 2026 06:00:01 CET INF Configuration loaded from 5 environment variable(s)
diun | Thu, 19 Mar 2026 06:00:02 CET INF Cron triggered
diun | Thu, 19 Mar 2026 06:00:03 CET INF New image found image=docker.io/myapp:2.5.0 provider=docker
Quand Diun détecte une nouvelle image, il envoie un message Slack avec le nom de l'image, le tag actuel et le nouveau tag. C'est vous qui décidez si et quand mettre à jour.
Comment docker-rollout permet-il des mises à jour sans interruption ?
docker-rollout est un plugin CLI Docker qui effectue des déploiements blue-green pour les services Compose. Il démarre un nouveau conteneur à partir de l'image mise à jour, attend que le health check passe, puis supprime l'ancien conteneur. Le trafic n'atteint jamais un conteneur non opérationnel car le reverse proxy ne route que vers les conteneurs healthy.
Prérequis :
- Un reverse proxy (Traefik, Caddy ou nginx-proxy) qui route le trafic vers votre service
- Des health checks définis dans votre fichier compose
- Pas de directive
container_namesur le service (docker-rollout gère les noms de conteneurs) - Pas de mapping
portsdirect sur le service (le reverse proxy gère l'exposition des ports)
Installer docker-rollout
mkdir -p /usr/local/lib/docker/cli-plugins
curl -fsSL https://raw.githubusercontent.com/wowu/docker-rollout/main/docker-rollout \
-o /usr/local/lib/docker/cli-plugins/docker-rollout
chmod +x /usr/local/lib/docker/cli-plugins/docker-rollout
La version devrait s'afficher une fois installé :
docker rollout --version
docker-rollout version v0.13
Exemple : Traefik + docker-rollout
Un fichier compose minimal pour une application web derrière Traefik avec des health checks. L'application n'a ni ports ni container_name car docker-rollout doit gérer le scaling.
services:
traefik:
image: traefik:3.3
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
restart: unless-stopped
app:
image: myapp:2.4.1
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=web"
- "traefik.http.services.app.loadbalancer.server.port=8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
restart: unless-stopped
Déployer sans interruption
Récupérez la nouvelle image, puis utilisez docker rollout au lieu de docker compose up -d :
docker compose pull app
docker rollout app
==> Scaling 'app' to '2' instances
Container myproject-app-2 Creating
Container myproject-app-2 Created
Container myproject-app-2 Starting
Container myproject-app-2 Started
==> Waiting for new containers to be healthy (timeout: 60 seconds)
==> Stopping and removing old containers
Pendant ce processus, Traefik détecte le nouveau conteneur via le socket Docker, route le trafic vers lui une fois qu'il est healthy et arrête de router vers l'ancien conteneur avant sa suppression. Vos utilisateurs ne voient aucune interruption.
Si le nouveau conteneur échoue à son health check, docker-rollout annule l'opération et l'ancien conteneur continue de tourner. Aucune intervention manuelle nécessaire.
Qu'est-ce que le déploiement blue-green avec Docker et Traefik ?
Le déploiement blue-green exécute deux copies de votre service (blue et green). L'une sert le trafic en direct pendant que l'autre reste inactive. Pour déployer, vous mettez à jour la copie inactive, vérifiez qu'elle fonctionne, puis basculez le trafic. Cela vous donne un rollback instantané en rebasculant vers la copie précédente.
C'est le concept derrière docker-rollout, mais vous pouvez l'implémenter manuellement pour plus de contrôle. Un exemple minimal utilisant la configuration dynamique de Traefik :
services:
traefik:
image: traefik:3.3
command:
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- "./traefik/dynamic:/etc/traefik/dynamic:ro"
restart: unless-stopped
app-blue:
image: myapp:2.4.1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
app-green:
image: myapp:2.4.1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
Un fichier de configuration dynamique Traefik contrôle quelle copie reçoit le trafic :
# traefik/dynamic/app.yml
http:
routers:
app:
rule: "Host(`app.example.com`)"
service: app
entryPoints:
- web
services:
app:
loadBalancer:
servers:
- url: "http://app-blue:8080"
Pour déployer : mettez à jour l'image de app-green, démarrez-le, attendez qu'il soit healthy, puis éditez app.yml pour pointer vers app-green. Traefik prend le changement en compte automatiquement car watch=true. Pour revenir en arrière, éditez le fichier pour pointer à nouveau vers app-blue.
Cette approche demande plus de travail que docker-rollout. Utilisez-la quand vous avez besoin d'un contrôle explicite sur la bascule, quand vous voulez exécuter des smoke tests sur la nouvelle version avant de basculer le trafic, ou quand plusieurs services doivent basculer ensemble.
Quelle méthode de mise à jour choisir ?
Choisissez la méthode qui correspond à votre tolérance aux interruptions et à la complexité de votre infrastructure.
| Méthode | Interruption | Complexité | Rollback | Reverse proxy requis | Adapté pour |
|---|---|---|---|---|---|
docker compose pull + up |
2-10 secondes | Faible | Manuel (éditer le fichier compose) | Non | Projets personnels, outils internes |
| Diun + mise à jour manuelle | Idem | Faible | Idem | Non | Équipes qui veulent de la visibilité avant de mettre à jour |
| docker-rollout | Aucune | Moyenne | Automatique (annule en cas d'échec) | Oui | Services de production sur un VPS unique |
| Blue-green (manuel) | Aucune | Élevée | Instantané (bascule du fichier de config) | Oui | Stacks multi-services, environnements réglementés |
Arbre de décision :
- Est-ce que 2 à 10 secondes d'interruption sont acceptables ? Utilisez
docker compose pull && docker compose up -d. - Voulez-vous être informé des mises à jour avant de les appliquer ? Ajoutez Diun.
- Le zéro interruption est-il requis ? Avez-vous un reverse proxy ? Utilisez docker-rollout.
- Vous avez besoin d'un contrôle explicite sur la bascule de trafic ? Implémentez le blue-green manuellement.
Quelque chose ne fonctionne pas ?
Le conteneur démarre mais affiche (unhealthy)
Vérifiez la commande du health check. Exécutez-la manuellement dans le conteneur :
docker compose exec app curl -f http://localhost:8080/health
Si ça échoue, le problème vient de votre application, pas de Docker. Consultez les logs applicatifs :
docker compose logs app --tail 50
L'ancienne image a été purgée, impossible de revenir en arrière
Si le tag existe toujours dans le registre, docker compose pull la récupérera. Si vous avez épinglé par digest, Docker récupère l'image exacte indépendamment des changements de tags :
image: myapp:2.4.1@sha256:789fed654cba...
docker-rollout se bloque pendant le déploiement
Le health check ne passe pas dans le délai imparti. Vérifiez l'intervalle et les retries du health check. Augmentez le timeout :
docker rollout -t 120 app
Watchtower ne fonctionne plus après une mise à jour Docker
Docker Engine 29 requiert l'API v1.44 au minimum. Watchtower utilise l'API v1.25. Migrez vers Diun pour les notifications ou docker-rollout pour les mises à jour automatisées sans interruption.
Diun ne détecte pas les nouvelles images
Vérifiez le planning cron dans DIUN_WATCH_SCHEDULE. Lancez un scan manuel :
docker compose exec diun diun image list
Consultez les logs de Diun pour les erreurs d'authentification au registre :
docker compose logs diun --tail 30
Copyright 2026 Virtua.Cloud. Tous droits réservés. Ce contenu est une création originale de l'équipe Virtua.Cloud. Toute reproduction, republication ou redistribution sans autorisation écrite est interdite.
Prêt à essayer ?
Déployez votre serveur en quelques secondes. Linux, Windows ou FreeBSD.
Voir les offres VPS