Vous avez livré un changement Compose, les healthchecks ont commencé à hurler, et maintenant votre cerveau d’astreinte ne sait plus
si docker compose down supprime les volumes par défaut (il ne le fait pas—sauf si vous le lui dites).
Pendant ce temps, le business veut un calendrier, le tableau de bord est rouge, et vos notes de déploiement disent « small refactor ».
C’est le guide de rollback que vous voulez quand vous êtes fatigué, impatient, et que vous essayez de stabiliser la production
avant que Slack ne vous désigne par défaut comme le nouveau responsable d’incident.
Le principe : le rollback est un changement, pas une prière
Docker Compose est un outil de « desired state » pour un seul hôte (ou un petit ensemble d’hôtes si vous êtes discipliné).
Ce n’est pas un système d’orchestration complet avec rollback intégré, jeux de réplicas ou historique de déploiement.
Compose remplacera volontiers des containers, conservera des volumes, réutilisera des réseaux et poursuivra son chemin. C’est parfait—jusqu’à ce que vous ayez besoin de revenir en arrière rapidement.
Revenir en arrière avec Compose se résume à trois choses :
- Containers : remettre en route l’image connue pour être bonne.
- Config : restaurer le fichier Compose précédent (et tout fichier env) qui produisait cet état connu comme bon.
- État : décider quoi faire des volumes et des changements de schéma/données.
Si vous ne gérez que les containers, vous « rollbackez » et resterez quand même en panne parce que les migrations DB ont déjà tourné,
ou parce que la configuration du reverse proxy a changé, ou parce que l’application lit une nouvelle variable d’environnement qui n’existe plus.
Voici la vérité opérationnelle : le rollback le plus rapide est généralement celui qui évite de toucher à l’état.
Si votre déploiement a inclus une migration de base de données incompatible, revenir sur l’image de l’application ne restaurera peut-être pas la fonctionnalité.
Alors votre « rollback » est partiel. Ce sont ceux qui gâchent les soirées.
Une citation à garder en tête durant les incidents vient de John Allspaw (idée paraphrasée) : La fiabilité, c’est la façon dont les systèmes se comportent sous stress, pas comment ils ressemblent sur les diagrammes.
Les rollbacks sont des tests de stress que vous n’avez pas programmés.
Faits et contexte intéressants (pourquoi les rollbacks Compose sont particuliers)
- Compose a commencé sous le nom « Fig » (2013) : il a été conçu pour rendre les environnements multi-conteneurs en dev plus gérables, pas pour être votre historique de déploiement en production.
- « docker-compose » (Python) vs « docker compose » (plugin CLI) : le plugin moderne s’intègre mieux aux contexts Docker et c’est là que les nouvelles fonctions arrivent en premier.
- Compose n’a pas d’« undo » intégré : contrairement à certains orchestrateurs, il n’existe pas de contrôleur de rollback natif. Votre « historique » est votre repo Git et votre registre d’images.
- Les tags d’images ne sont pas immuables sauf si vous les forcez : des tags comme
latestpeuvent être déplacés, écrasés ou re-poussés. Les digests ne mentent pas. - Les IDs de containers sont jetables ; les volumes ne le sont pas : le comportement par défaut de Compose conserve les volumes nommés, ce qui est bien pour la persistance et terrifiant pour les surprises de rollback.
- Compose utilise le nom de projet pour le scope : le nom de projet contrôle les noms des containers/réseaux/volumes. Un changement de nom de projet peut « orpheliner » la pile précédente.
- Les healthchecks sont récents comparés au « ça marche sur mon laptop » : beaucoup de stacks démarrent encore même si elles sont fonctionnellement mortes. Les healthchecks accélèrent les rollbacks car ils donnent un signal stop/go.
- Le comportement de pull du registre dépend de ce qui est local : si l’ancienne image existe sur le disque, le rollback peut être instantané ; si vous devez la tirer depuis un registre lent, vous venez de trouver votre goulot.
Conclusion : Compose peut tout à fait faire tourner des systèmes de production, mais il ne vous guide pas en matière de sécurité de rollback.
Vous devez développer le muscle.
Les voies de rollback les plus rapides (choisissez-en une)
Voie A : revenir au commit Git précédent et redéployer
C’est la solution la plus propre quand la panne est causée par la config Compose, des vars env, des entrypoints, des limites de ressources,
ou un mauvais tag d’image introduit dans le fichier Compose.
C’est aussi le meilleur pour l’auditabilité : votre rollback est un commit, pas une série de commandes paniquées dans le terminal.
Si vous déployez depuis un repo sur l’hôte, faire un rollback revient généralement à un git checkout et un docker compose up -d.
Voie B : épingler sur un digest d’image connu bon et redéployer
Si le fichier Compose est « ok » mais que le tag d’image pointe vers un build défectueux, ne perdez pas de temps à négocier avec les tags.
Épinglez le digest exact de la dernière image connue bonne. Les digests sont la chose la plus fiable à 3h du matin.
Voie C : arrêter l’hémorragie—lancer manuellement l’image de container précédente
C’est la voie « break glass ». C’est plus rapide que de corriger le fichier Compose quand votre tooling de déploiement est cassé,
ou quand vous avez besoin du service tout de suite et que vous nettoierez après.
Vous échangez l’élégance contre la vitesse. Vous créez aussi de la dérive, alors prenez des notes et planifiez un nettoyage. Votre futur vous n’est pas votre employé.
Voie D : restaurer les données/état (seulement si nécessaire)
C’est pour quand le mauvais déploiement a muté l’état d’une façon intolérable :
migrations destructrices, jobs en arrière-plan incorrects, ou un consommateur de file qui a « traité » des messages à la volée.
Restaurer l’état est plus lent, plus risqué et nécessite de la coordination. Si vous pouvez l’éviter, évitez-le.
Blague #1 : Les plans de rollback sont comme les extincteurs—tout le monde aime en avoir, et personne ne veut découvrir qu’ils sont décoratifs.
Playbook de diagnostic rapide (trouvez le goulot en quelques minutes)
L’objectif n’est pas de devenir un scientifique médico-légal. L’objectif est de répondre : « Le rollback est-il la bonne action, et qu’est-ce qui le bloque ? »
Travaillez du haut vers le bas, et arrêtez-vous quand vous avez un plan confiant.
Première étape : la panne vient-elle de l’app, du container ou de l’hôte ?
- Vérifiez la santé des containers et les boucles de redémarrage : si les containers redémarrent, vous êtes en crash-loop. Le rollback est souvent la bonne option.
- Consultez les logs pour des erreurs immédiates : vars env manquantes, migrations qui échouent, connection refused, erreurs de syntaxe de config.
- Vérifiez les ressources de l’hôte : disque plein, kills OOM, inodes épuisés. Le rollback ne réparera pas un disque plein.
Deuxième étape : pouvez-vous récupérer rapidement les anciens éléments ?
- L’ancienne image est-elle encore locale ? Si oui, rollback en minutes. Sinon, vous avez des pulls et l’accès registre à gérer.
- Connaissez-vous le dernier digest bon ? Si oui, épinglez-le. Sinon, vous allez faire de l’archéologie dans les logs CI ou les métadonnées du registre.
- Le déploiement a-t-il touché l’état ? Si oui, évaluez la compatibilité en arrière et si vous avez besoin d’un rollback de données ou de correctifs en avant.
Troisième étape : décidez votre stratégie de rollback
- Régression de config : revert du commit Compose.
- Mauvais build d’image : épinglez le digest.
- Problème hôte/ressources : réparez l’hôte d’abord, puis décidez si le rollback est toujours nécessaire.
- Mutation d’état : coordonnez : rollback d’app seul peut ne pas suffire.
Traitez ceci comme du triage, pas un concours d’opinion. Si vous passez 20 minutes à discuter si c’est « vraiment le déploiement », vous avez déjà choisi le downtime.
Tâches pratiques avec commandes, sorties et décisions
Voici les commandes que j’utilise réellement pendant les incidents. Chaque tâche inclut (a) la commande, (b) ce que signifie la sortie,
et (c) la décision que vous prenez.
Task 1: Confirm what Compose thinks the project is
cr0x@server:~$ docker compose ls
NAME STATUS CONFIG FILES
payments running(6) /srv/payments/compose.yaml
Ce que ça signifie : Vous avez un projet Compose nommé payments et Docker le voit comme en cours d’exécution.
Si votre nom de projet a changé récemment, vous pourriez regarder la mauvaise stack.
Décision : Si le projet attendu n’apparaît pas, vous êtes en territoire « mauvais répertoire / mauvais contexte / mauvais nom de projet ». Corrigez cela avant de toucher quoi que ce soit.
Task 2: Identify which services are restarting or unhealthy
cr0x@server:~$ docker compose -p payments ps
NAME IMAGE COMMAND SERVICE STATUS PORTS
payments-api-1 registry.local/payments:1.9.2 "/entrypoint.sh" api Restarting (1) 8s ago
payments-web-1 registry.local/payments-web:1.9.2 "nginx -g 'daemon off;'" web Up 3 minutes (healthy) 0.0.0.0:443->443/tcp
payments-db-1 postgres:15.6 "docker-entrypoint.s…" db Up 2 days (healthy) 5432/tcp
Ce que ça signifie : L’API est en crash-loop. Le web et la DB semblent sains. C’est un candidat pour un rollback d’image applicative.
Décision : Si un seul service échoue, revenez sur ce service d’abord (ou épinglez son image) au lieu de démonter toute la stack.
Task 3: Grab the last 200 lines of logs from the failing service
cr0x@server:~$ docker compose -p payments logs --no-color --tail=200 api
api-1 | ERROR: missing required env var PAYMENTS_SIGNING_KEY
api-1 | FATAL: cannot start
Ce que ça signifie : C’est une régression de config/env, pas un bug de code. Quelqu’un a ajouté une variable d’environnement requise.
Décision : Revenir sur la config Compose (fichier env ou compose.yaml), ou restaurer le secret manquant. Revenir sur l’image pourrait ne pas aider si l’image exige aussi la variable.
Task 4: Verify what config Compose is actually using (rendered)
cr0x@server:~$ docker compose -p payments config
services:
api:
environment:
PAYMENTS_SIGNING_KEY: ""
image: registry.local/payments:1.9.2
restart: always
Ce que ça signifie : Compose a résolu la variable d’environnement en chaîne vide. C’est pour cela que l’application refuse de démarrer.
Décision : Corriger l’injection d’env (env_file, env exportée, secrets) ou revert le changement. Ne pas « hotfixer » en éditant les containers ; ça ne survivra pas à un redémarrage.
Task 5: Check whether the previous image is still local (fast rollback potential)
cr0x@server:~$ docker images --digests | grep registry.local/payments | head
registry.local/payments 1.9.2 sha256:aa11... 3d2f1c9d0b2a 45 minutes ago 312MB
registry.local/payments 1.9.1 sha256:bb22... 9a8e7d6c5b4f 2 days ago 311MB
Ce que ça signifie : L’ancienne image (1.9.1) est déjà sur disque. C’est votre voie dorée : rollback sans attendre les pulls.
Décision : Préférez repasser à 1.9.1 (ou son digest) immédiatement, puis faites un debug plus profond une fois stable.
Task 6: Pin the service back to a known-good tag and redeploy only that service
cr0x@server:~$ sed -i 's|registry.local/payments:1.9.2|registry.local/payments:1.9.1|g' /srv/payments/compose.yaml
cr0x@server:~$ docker compose -p payments up -d --no-deps api
[+] Running 1/1
✔ Container payments-api-1 Started
Ce que ça signifie : Seul le container API a été recréé. Les dépendances ont été laissées intactes.
Décision : Utilisez --no-deps pendant le rollback quand les dépendances sont saines. N’agitez pas des containers qui fonctionnent pendant un incident sauf si vous aimez multiplier les variables.
Task 7: Confirm the rollback “took” (image and health)
cr0x@server:~$ docker compose -p payments ps api
NAME IMAGE COMMAND SERVICE STATUS PORTS
payments-api-1 registry.local/payments:1.9.1 "/entrypoint.sh" api Up 20 seconds
Ce que ça signifie : Le container tourne sur 1.9.1. Si vous avez des healthchecks, attendez qu’il soit healthy.
Décision : S’il reste en ligne et sert le trafic, vous avez gagné du temps. S’il échoue toujours, le problème vient peut-être de la config/état, pas du code.
Task 8: Pin by digest (stronger than tags)
cr0x@server:~$ docker inspect --format '{{index .RepoDigests 0}}' registry.local/payments:1.9.1
registry.local/payments@sha256:bb22cc33dd44ee55ff66...
Ce que ça signifie : Vous avez le digest immuable pour le build connu bon.
Décision : Mettez à jour Compose pour utiliser le digest quand vous avez besoin de certitudes, surtout si les tags peuvent être re-poussés.
Task 9: Switch Compose to digest and redeploy (prevents tag drift)
cr0x@server:~$ sed -i 's|registry.local/payments:1.9.1|registry.local/payments@sha256:bb22cc33dd44ee55ff66...|g' /srv/payments/compose.yaml
cr0x@server:~$ docker compose -p payments up -d --no-deps api
[+] Running 1/1
✔ Container payments-api-1 Recreated
Ce que ça signifie : Vous avez redéployé le service en référant une image adressée par contenu.
Décision : Pour stabiliser après l’incident, épinglez des digests pour les services critiques. Vous pouvez revenir aux tags sémantiques quand vous aurez de meilleurs contrôles.
Task 10: Detect whether your rollback is blocked by host disk pressure
cr0x@server:~$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 118G 117G 0G 100% /
Ce que ça signifie : Votre filesystem root est plein. Tirer des images, écrire des logs et démarrer des containers peut échouer de façon chaotique.
Décision : Libérez de l’espace disque avant toute chose. Les rollbacks ont aussi besoin de disque—surtout s’ils exigent de tirer une image plus ancienne.
Task 11: Quick cleanup without deleting volumes (safe-ish)
cr0x@server:~$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 42 18 28.6GB 11.2GB (39%)
Containers 35 9 1.3GB 1.1GB (84%)
Local Volumes 14 10 96.4GB 0B (0%)
Build Cache 0 0 0B 0B
cr0x@server:~$ docker container prune -f
Deleted Containers:
c8a1...
Total reclaimed space: 1.1GB
Ce que ça signifie : Les containers prenaient de l’espace ; les volumes sont le gros morceau mais ne sont pas récupérables via cette commande.
Décision : Purgez d’abord les containers. Évitez de prune les volumes pendant un rollback sauf si vous avez des sauvegardes vérifiées et que vous aimez le jeu à hauts risques.
Task 12: Identify whether you accidentally created orphaned services
cr0x@server:~$ docker compose -p payments up -d
[+] Running 6/6
✔ Container payments-web-1 Running
✔ Container payments-api-1 Running
✔ Container payments-db-1 Running
! Found orphan containers ([payments-worker-1]) for this project. If you removed or renamed this service in your compose file, you can run this command with the --remove-orphans flag to clean it up.
Ce que ça signifie : Quelque chose a été renommé/supprimé et un ancien container est toujours présent.
Décision : Pendant un rollback, ne supprimez pas les orphelins à l’aveugle. Cet orphelin pourrait encore exécuter un travail important (comme vider une file). Évaluez, puis nettoyez délibérément.
Task 13: Confirm container exit reason (OOM vs app failure)
cr0x@server:~$ docker inspect -f '{{.State.Status}} {{.State.ExitCode}} OOMKilled={{.State.OOMKilled}}' payments-api-1
exited 137 OOMKilled=true
Ce que ça signifie : Le code de sortie 137 avec OOMKilled indique que le noyau a tué votre container. Ce n’est pas une régression de code tant que ce n’est pas prouvé.
Décision : Le rollback peut « fonctionner » si l’ancienne version consomme moins de mémoire, mais la vraie correction est les limites mémoire, la capacité hôte ou une fuite. Triage mémoire maintenant.
Task 14: Check host memory pressure and recent OOM logs
cr0x@server:~$ free -m
total used free shared buff/cache available
Mem: 15984 15410 120 210 454 260
Swap: 2047 2047 0
cr0x@server:~$ journalctl -k --since "30 min ago" | tail -n 5
kernel: Out of memory: Killed process 28411 (payments-api) total-vm:2456120kB, anon-rss:1220040kB, file-rss:0kB, shmem-rss:0kB
Ce que ça signifie : L’hôte est à court de mémoire et le swap est épuisé. Toute version peut mourir ; le rollback seul est un pansement.
Décision : Réduisez la charge, diminuez d’autres services, augmentez la mémoire, ou définissez des limites sensées. Puis redéployez. Sinon vous « rollbackerez » vers le même OOM.
Task 15: Verify what changed between two Compose revisions (config diff)
cr0x@server:~$ cd /srv/payments
cr0x@server:~$ git log --oneline -5
a12b9f3 bump api to 1.9.2 and add signing key
6c0dd21 pin postgres and tune healthcheck
b19e8aa add worker concurrency env var
cr0x@server:~$ git show a12b9f3 -- compose.yaml | sed -n '1,120p'
diff --git a/compose.yaml b/compose.yaml
index 31e2c0a..8f19c11 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -12,6 +12,7 @@ services:
api:
image: registry.local/payments:1.9.2
environment:
+ - PAYMENTS_SIGNING_KEY=${PAYMENTS_SIGNING_KEY}
Ce que ça signifie : La nouvelle version a ajouté un secret requis. Votre injection d’env n’a pas suivi.
Décision : Revenir sur le commit ou fournir correctement le secret. Si le secret doit exister à l’avenir, corrigez le pipeline des secrets et redéployez vers l’avant plutôt que de revenir en arrière.
Task 16: Roll back by Git to the last known good config
cr0x@server:~$ cd /srv/payments
cr0x@server:~$ git checkout 6c0dd21 -- compose.yaml .env
cr0x@server:~$ docker compose -p payments up -d
[+] Running 3/3
✔ Container payments-api-1 Recreated
✔ Container payments-web-1 Running
✔ Container payments-db-1 Running
Ce que ça signifie : Vous avez revert la configuration et l’avez appliquée. Le container API a été recréé en conséquence.
Décision : Utilisez Git comme registre de rollback. Si vous ne pouvez pas reproduire le « dernier connu bon » depuis le contrôle de source, vous n’avez pas de plan de rollback—juste de la nostalgie.
Task 17: Validate external reachability quickly (service-level check)
cr0x@server:~$ curl -fsS -o /dev/null -w "%{http_code}\n" https://localhost/health
200
Ce que ça signifie : Votre endpoint local retourne 200. Ce n’est pas un test d’intégration complet, mais c’est un signal rapide.
Décision : Si la santé est bonne localement mais que les utilisateurs voient encore des erreurs, le problème peut venir du DNS, du load balancer, d’un firewall, ou de dépendances en amont.
Task 18: Snapshot a named volume before you touch it (cheap insurance)
cr0x@server:~$ docker volume ls | grep payments
local payments_dbdata
cr0x@server:~$ mkdir -p /var/backups/payments
cr0x@server:~$ docker run --rm -v payments_dbdata:/data -v /var/backups/payments:/backup alpine sh -c "cd /data && tar -czf /backup/payments_dbdata_$(date +%F_%H%M).tgz ."
Ce que ça signifie : Vous avez créé une archive tarball du contenu du volume. Ce n’est pas parfait pour des bases de données actives, mais c’est mieux que « on n’a rien fait ».
Décision : Pour des bases de données réelles, privilégiez les outils de sauvegarde natifs. Mais quand vous vous apprêtez à faire quelque chose de risqué, prenez un snapshot ou une sauvegarde d’abord.
Blague #2 : « Fais juste un rollback » est une phrase charmante jusqu’à ce que vous découvriez que votre base de données n’a pas reçu le mémo.
Trois mini-récits tirés de la vie en entreprise
1) L’incident causé par une fausse hypothèse
Une entreprise de taille moyenne faisait tourner une API de paiements sur un seul hôte costaud en utilisant Docker Compose.
Le processus de déploiement était « pull des nouvelles images, lancer docker compose up -d. »
Ils avaient de la supervision, des alertes et une rotation d’astreinte. Ils avaient aussi une hypothèse : les tags étaient suffisamment immuables.
Un développeur a poussé payments-api:1.8.4 sur le registre, a constaté qu’un flag de build était incorrect, et a republié le même tag avec une image corrigée.
Pas de malice ; juste une habitude des environnements dev. Le registre l’a permis. Personne n’a remarqué.
Deux jours plus tard, un incident survient après un redémarrage routinier de l’hôte. Le nœud est revenu, Compose a démarré les containers,
et il a tiré 1.8.4—mais pas l’image que tout le monde avait utilisée auparavant. Même tag, bits différents.
L’astreint a suivi le runbook : rollback vers 1.8.4. Le rollback a « fonctionné » dans le sens où il n’a rien changé.
Ils ont passé une heure à chasser des différences de config fantômes et à soupçonner des mises à jour du noyau.
La cause réelle était plus simple : leur cible de rollback n’était pas une cible. C’était une étiquette mouvante.
La correction n’était pas glamour. Ils ont commencé à épingler des digests pour les déploiements en production et ont gardé des tags lisibles pour la commodité,
mais pas comme source de vérité. Ils ont aussi verrouillé le registre pour que « écraser un tag existant » nécessite une action admin explicite.
Personne n’aime ça sur le moment. Tout le monde l’adore la première fois que ça les sauve.
2) L’optimisation qui a mal tourné
Une équipe de plateforme data voulait des déploiements plus rapides. Leur stack Compose avait une demi-douzaine de services et un réseau partagé.
Tirer des images pendant le déploiement était lent, ils ont donc optimisé : garder les images localement agressivement et éviter les pulls sauf si nécessaire.
Ils ont aussi mis restart: always partout, parce que disponibilité, non ?
Une mauvaise release a été envoyée avec une fuite mémoire subtile dans un service worker. Au début, tout semblait bien.
En quelques heures, la mémoire a grimpé, le noyau a commencé à libérer agressivement, et finalement a tué des containers OOM.
Avec restart=always, les workers redémarraient instantanément, se rechargeaient, fuyaient encore, et thrashaient.
Pendant ce temps, l’hôte était tellement à cours de mémoire que les services « bons » ont commencé à échouer aussi.
L’astreint a tenté le rollback. Mais l’hôte était sous telle pression que même lancer l’ancienne image était instable.
Les logs ont été tronqués. Les connexions exec vers les containers ont expiré. Le « rollback » est devenu une course entre le noyau et l’opérateur.
La politique de redémarrage qui aidait pour des pannes transitoires a transformé l’incident contrôlé en un vacarme.
L’amélioration qui en a résulté : ils ont mis des limites mémoire et réservé de la marge, ont ajusté les politiques de restart par service,
et ont ajouté un canari de déploiement pour détecter la croissance mémoire sous charge. L’optimisation—éviter les pulls—n’était pas le vrai coupable.
Les garde-fous manquants l’étaient.
3) La pratique ennuyeuse mais correcte qui a sauvé la mise
Une équipe d’outils internes faisait tourner Compose sur quelques hôtes, chacun avec un répertoire « project ».
Leur pratique était affreusement ennuyeuse : chaque déploiement était un commit Git, chaque commit enregistrait les digests d’images dans le fichier Compose,
et chaque release avait une note « last known good » dans le repo. Ils faisaient aussi des backups nocturnes des volumes et testaient les restores chaque mois.
Oui, vraiment.
Une release a introduit une migration de schéma censée être additive.
Elle ne l’était pas. Elle a renommé une colonne utilisée par un chemin de code plus ancien et a cassé un job de reporting.
L’API de production était encore majoritairement fonctionnelle, mais le job spammait des erreurs et créait de la charge.
La réponse à l’incident a été presque ennuyeuse. Ils sont revenus au commit Git précédent pour le service job seulement,
ont redéployé avec --no-deps, et ont confirmé que le système de reporting s’est rétabli.
Ensuite ils ont planifié une migration corrective forward avec une compatibilité arrière correcte.
Pas d’exploits héroïques, pas de war room qui se transforme en séminaire de philosophie.
Le détail clé : parce qu’ils épinglaient des digests, ils n’ont pas eu à deviner ce que signifiait « version précédente ».
Parce qu’ils pratiquaient les restores, ils savaient ce qui se passerait s’ils devaient restaurer les données.
Ils n’ont pas eu besoin de restaurer les données. Ils n’en avaient pas besoin. Mais l’option existait, et ça change la façon dont les gens réagissent.
Erreurs courantes : symptôme → cause racine → correction
1) « Le rollback a redéployé, mais rien n’a changé »
Symptôme : Vous remettez le tag en arrière, lancez docker compose up -d, et le service est toujours cassé.
Cause racine : Le « vieux » tag pointe vers de nouveaux bits (tag écrasé), ou Compose n’a pas recréé le container car il n’a pas détecté de changement.
Correction : Épinglez par digest et forcez la recréation si nécessaire.
2) « Le container démarre, puis s’arrête immédiatement »
Symptôme : Crash-loop ; les logs montrent des vars env manquantes ou des erreurs de parsing de config.
Cause racine : Dérive de config, injection de secret manquante, ou une variable d’environnement désormais requise par l’app.
Correction : Utilisez docker compose config pour voir les valeurs résolues. Restaurez l’env/commit compose précédent ou fournissez correctement le secret manquant.
3) « Le rollback a empiré les choses ; maintenant plusieurs services sont down »
Symptôme : Vous avez rollbacké un changement et soudainement DB, proxy et workers redémarrent.
Cause racine : Vous avez exécuté un full docker compose down (ou enlevé des réseaux) et recréé tout, provoquant du churn de dépendances ou des IPs changées sur une stack fragile.
Correction : Préférez docker compose up -d --no-deps <service> et évitez de démonter les réseaux en plein incident.
4) « La vieille version ne peut plus parler à la base de données »
Symptôme : Après rollback, l’app signale des colonnes/tables manquantes ou un schéma incompatible.
Cause racine : Des migrations non compatibles avec la rétrocompatibilité ont été appliquées par le mauvais déploiement.
Correction : Soit vous poussez un correctif forward qui supporte le nouveau schéma, soit vous effectuez une restauration coordonnée des données (backup/restore) si faisable.
5) « Le rollback est lent parce que les pulls prennent une éternité »
Symptôme : docker compose up reste bloqué à tirer des images ; le temps de recovery s’allonge.
Cause racine : Les anciennes images ne sont pas en cache local, la bande passante du registre est limitée, ou il y a des problèmes DNS/réseau.
Correction : Gardez les dernières images connues bonnes localement (ou pré-pullez lors du déploiement), vérifiez l’accessibilité du registre, et envisagez un miroir/caching interne.
6) « On a rollbacké, mais les utilisateurs voient encore des erreurs »
Symptôme : Les healthchecks passent, mais le trafic réel échoue de façon intermittente.
Cause racine : Les dépendances externes ont changé (config LB, certificat TLS, DNS), ou un rollback partiel a laissé un système en versions mélangées.
Correction : Confirmez le comportement de bout en bout via curl depuis l’extérieur, vérifiez les routes du reverse proxy, et assurez-vous que tous les services interdépendants sont sur des versions compatibles.
7) « Tout redémarre ; les logs sont vides »
Symptôme : Les containers redémarrent vite et vous ne pouvez pas attraper les logs.
Cause racine : Kills OOM, disque plein, ou crash avant que stdout ne soit flushé.
Correction : Vérifiez docker inspect pour OOMKilled, consultez journalctl -k, et réparez la pression sur l’hôte d’abord.
8) « Le rollback a supprimé des données »
Symptôme : La base de données remonte vide après le « rollback ».
Cause racine : Quelqu’un a exécuté docker compose down -v ou a supprimé des volumes nommés, ou a changé les noms de volumes en modifiant le nom de projet.
Correction : Arrêtez. Identifiez les volumes avec docker volume ls. Restaurez depuis les sauvegardes. Prévenez cela en verrouillant les runbooks et en utilisant des noms de volumes explicites.
Checklists / plan pas à pas
Checklist : le rollback en 10 minutes (quand l’état est compatible)
- Geler les déploiements : arrêter CI/CD pour éviter d’autres changements pendant le triage.
- Identifier les services défaillants :
docker compose ps. Choisir le plus petit périmètre d’impact d’abord. - Lire les logs :
docker compose logs --tail=200 <service>. Décider : config vs code vs hôte. - Confirmer la santé de l’hôte : disque (
df -h), mémoire (free -m), logs noyau OOM. - Trouver la dernière image connue bonne : cache local (
docker images), enregistrement CI, ou métadonnées du registre. - Rollback d’un service : changer tag/digest, puis
docker compose up -d --no-deps <service>. - Vérifier la santé :
docker compose ps, puis une requête réelle (curl). - Annoncer le statut : « Service stable sur X version ; investigation de la cause racine en cours. »
- Capturer des preuves : logs, digests, IDs de commit. Le futur vous a besoin de reçus.
- Planifier la correction définitive : ne pas vivre sur un rollback indéfiniment ; planifiez la réparation adéquate.
Checklist : rollback quand des migrations ont pu changer l’état
- Déterminez ce qui a tourné : vérifiez les logs applicatifs pour les messages de migration ; consultez la table de migrations en DB si applicable.
- Évaluez la compatibilité : la vieille app peut-elle fonctionner contre le nouveau schéma ? Si non, rollback d’app ne restaurera pas le service.
- Choisissez une stratégie :
- Préférez le correctif forward si vous pouvez livrer rapidement une app compatible.
- Restaurer les données si le déploiement a corrompu ou supprimé des données, ou si le correctif forward est trop lent.
- Protégez l’état actuel : snapshot/sauvegarde des volumes avant toute opération.
- Coordonnez le downtime : restore app+DB n’est pas une activité solo dans une grande entreprise.
- Validez la restauration : version du schéma, contrôles de comptage de lignes, et tests fonctionnels applicatifs.
Checklist : prévenir le « théâtre de rollback Compose » (qui ressemble à un rollback, mais ne l’est pas)
- Arrêtez d’utiliser des tags flottants (
latest) dans les fichiers Compose de production. - Enregistrez et déployez par digest pour les services critiques.
- Conservez le « last known good » dans Git (compose + env + toute config montée dans les containers).
- Assurez-vous que les healthchecks existent et représentent la readiness réelle, pas seulement « process existe ».
- Pratiquez un rollback trimestriel sur un hôte de staging qui ressemble à la production.
- Sauvegardez les volumes avec une méthode adaptée aux données (préférez le natif DB).
FAQ
1) Docker Compose a-t-il une commande de rollback intégrée ?
Non. Compose applique ce que vous déclarez. Votre rollback consiste à « déclarer l’état précédent » et à l’appliquer à nouveau,
typiquement via un revert Git et/ou l’épinglage d’un digest d’image plus ancien.
2) Quel est le rollback sûr le plus rapide si un seul service est cassé ?
Revenir uniquement sur ce service : mettez à jour sa référence d’image et lancez docker compose up -d --no-deps <service>.
Ne touchez pas aux dépendances saines.
3) Dois-je utiliser docker compose down pendant un rollback ?
En général non. down supprime containers et réseaux, et peut augmenter le périmètre d’impact.
Utilisez-le quand vous avez besoin d’une ardoise propre et que vous comprenez les conséquences (surtout autour du réseau et du downtime).
4) Le rollback va-t-il supprimer ma base de données ?
Pas par défaut. Les volumes nommés persistent à travers up/down. La perte de données survient généralement quand quelqu’un lance
docker compose down -v ou change les noms de volume/nom de projet et « perd » l’ancien volume.
5) Tags vs digests : que doit utiliser la production ?
Pour les rollbacks en production, les digests sont supérieurs car ce sont des identifiants immuables du contenu d’une image.
Les tags sont pratiques pour les humains, mais ne les considérez pas comme une preuve.
6) Comment trouver le dernier digest connu bon ?
Idéal : votre pipeline de déploiement l’enregistre. Ensuite : il est déjà dans votre fichier Compose depuis le dernier déploiement.
Sinon, inspectez les images en cache local, ou consultez les métadonnées du registre (en interne) et corrélez avec les notes de release.
7) Et si le mauvais déploiement a exécuté des migrations non rétrocompatibles ?
Revenir sur l’app peut ne pas suffire. Vous devez soit livrer un correctif forward qui supporte le nouveau schéma,
soit coordonner une restauration de données. C’est pourquoi les migrations « extend/contract » et la compatibilité arrière ne sont pas optionnelles en production.
8) Pourquoi Compose ne recrée-t-il parfois pas un container après que j’ai changé des choses ?
Si Compose ne détecte pas de changement significatif (ou si vous avez édité le mauvais fichier/chemin), il peut garder le container existant.
Confirmez avec docker compose config et docker compose ps. Si nécessaire, forcez la recréation pour ce service.
9) Quelle est la manière la plus sûre de tester un rollback sans impacter le trafic de production ?
Lancez l’ancienne image comme service parallèle sur un port ou un nom de projet différent, validez-la contre un ensemble de dépendances de staging,
puis basculez le trafic au proxy. Compose ne fournit pas le shifting de trafic ; vous devez le construire (souvent via Nginx/Traefik/HAProxy).
10) Combien d’anciennes images devrais-je garder localement pour rendre le rollback rapide ?
Gardez au moins le dernier connu bon plus une release précédente pour chaque service critique, si le disque le permet.
Le bon nombre dépend des tailles d’images et du budget disque, mais « zéro » est la mauvaise réponse.
Prochaines étapes que vous pouvez faire aujourd’hui
Le rollback Compose n’a pas besoin d’être dramatique. Il doit être déterministe. Si vous ne retenez qu’une chose de ceci :
arrêtez de faire confiance aux tags en situation d’urgence, et cessez de considérer les changements d’état comme une pensée après coup.
- Notez le « last known good » comme digest et commit Git pour chaque déploiement.
- Ajoutez des healthchecks qui reflètent la readiness, pas seulement « process existe ».
- Pratiquez un rollback d’un seul service avec
--no-depssur un hôte non production. - Décidez de votre politique d’état : quels services peuvent exécuter des migrations destructrices, et comment vous revenez en arrière.
- Budgetez de la marge disque et mémoire pour que le rollback ne soit pas bloqué parce que l’hôte est en feu.
Le rollback le plus rapide sur Compose est celui que vous pouvez exécuter les yeux à moitié fermés : épinglez le digest, redéployez le service cassé,
vérifiez la santé, et continuez. Le drame est optionnel. La discipline ne l’est pas.