Pourquoi `latest` est le vrai coupable
Quand une image Docker taguée latest est mise à jour dans le registry, votre docker compose pull la télécharge silencieusement. Aucun avertissement, aucun diff. Vous relancez la stack, et vous découvrez que le nouveau conteneur ne peut pas lire les données laissées par l'ancien.
C'est exactement le risque avec Meilisearch. La base de données porte le numéro de version du moteur qui l'a écrite : lors d'une montée de version, le nouveau moteur refuse de l'ouvrir tant qu'on ne lui demande pas explicitement de la migrer avec --upgrade-db (ou la variable MEILI_UPGRADE_DB). Avec une politique de redémarrage, le conteneur entre alors en crash-loop. Et une fois la base migrée, l'ancienne image ne peut plus la rouvrir : Meilisearch ne supporte pas le retour arrière. Le seul filet propre est un dump ou un snapshot du volume créé avant la mise à jour — opération impossible une fois la base migrée ou le conteneur bloqué.
L'image latest ne résout pas la même chose selon l'heure du pull. Deux développeurs qui exécutent docker compose pull à douze heures d'intervalle peuvent tirer des versions différentes. Sur une stack de production, cette ambiguïté est inacceptable. La solution n'est pas de ne jamais mettre à jour : c'est de contrôler explicitement quelle version tourne et de décider consciemment quand passer à la suivante.
Ce que ce guide couvre — et ce qu'il ne couvre pas
- Ce que ce guide couvre : le protocole pas-à-pas pour mettre à jour une stack Docker Compose sur un VPS — pinning de version, sauvegarde de volumes, vérification des notes de version, rollback rapide, healthchecks comme garde-fou.
- Prérequis couverts par d'autres guides : la checklist de durcissement initiale (
docker-compose-production-checklist), la configuration dedepends_onetservice_healthy(docker-compose-depends-on-healthcheck), et la comparaison des outils de mise à jour automatique comme Watchtower ou Diun. - Ce que ce guide ne recommande pas : Watchtower ou tout outil d'auto-pull en production — c'est précisément le contre-patron que les cas de breaking changes illustrent.
- Public cible : développeurs et agences qui gèrent une ou plusieurs stacks Docker Compose en production sur VPS, avec accès root et volumes persistants.
Étape 1 — Épingler toutes vos images
La première action, avant toute mise à jour, est de remplacer chaque image: getmeili/meilisearch:latest ou image: supabase/postgres par une version explicite.
Deux formes sont acceptables :
- Tag de version : image: getmeili/meilisearch:v1.53.0 — lisible, versionnable dans git, facile à patcher.
- Digest SHA256 : image: getmeili/meilisearch@sha256:abc123… — immuable, garantit que vous tirez exactement le même artefact à chaque redéploiement, même si le tag a été réécrit (ce qui arrive sur les registries publics).
Pour connaître le digest d'une image déjà en cours d'exécution :
docker inspect --format='{{index .RepoDigests 0}}' getmeili/meilisearch:v1.53.0Une fois vos images épinglées, committez le fichier docker-compose.yml dans git. Chaque bump de version devient un commit, ce qui vous donne un historique clair et un rollback trivial (git revert + docker compose up -d).
Protocole de mise à jour — les 5 étapes
Lire les notes de version avant de puller
Avant tout, consultez les release notes de la nouvelle version. Cherchez les mots
breaking,migration,incompatible,pg_upgrade,dump. Ce n'est pas facultatif : Supabase a documenté explicitement que la mise à jour de PostgreSQL 15 vers 17 exige unpg_upgrademanuel — le conteneur PG 17 refuse de démarrer sur un volume PG 15, et le processus d'initialisation ne migre pas automatiquement les données.Pour Langfuse v4 (v4.0.0 publiée le 29 juillet 2026), les SDKs Python v2 et antérieurs sont rejetés à l'ingestion par le nouveau stack — une rupture qui affecte tous les services clients qui tracent via l'ancienne API.
Trois minutes de lecture vous évitent plusieurs heures de récupération de données.
Sauvegarder les volumes avant le pull
Ne pullez jamais avant d'avoir un backup exploitable. Pour les volumes nommés, deux approches :
Dump applicatif (recommandé pour les bases de données) — le service doit être
healthyavant de dumper :docker compose exec -T db pg_dump -U postgres -Fc mydb > backup_$(date +%Y%m%d_%H%M%S).dumpL'option
-Tdésactive le pseudo-terminal : sans elle, le flux binaire du dump peut être altéré.Snapshot de volume brut — utile pour les stockages binaires (Meilisearch, Redis, MinIO) :
docker run --rm \ --volumes-from $(docker compose ps -q meilisearch) \ -v $(pwd)/backups:/backup \ alpine tar czf /backup/meili_$(date +%Y%m%d_%H%M%S).tar.gz /meili_dataVérifiez que le backup est lisible avant de continuer. Un fichier de dump corrompu découvert pendant la récupération est le scénario le plus coûteux qui soit.
Puller la nouvelle image et tester hors ligne
Mettez à jour le tag dans votre
docker-compose.yml, puis pullez l'image sans redémarrer le service :docker compose pull meilisearchSi votre environnement le permet, testez la nouvelle image sur un clone du volume dans un environnement ddev ou sur une VM de staging avant de toucher à la production. Vérifiez dans les logs de démarrage qu'aucune erreur de migration ne s'affiche :
docker compose up -d meilisearch docker compose logs -f meilisearchAttendez que le healthcheck passe à
healthyavant de valider. Un service qui démarre mais qui n'est pas encorehealthyn'est pas un service prêt.Vérifier les healthchecks
Un healthcheck bien configuré est votre première ligne de détection. Il doit être présent sur chaque service critique de la stack, au format Compose v2 :
healthcheck: test: ["CMD-SHELL", "curl -sf http://localhost:7700/health || exit 1"] interval: 10s timeout: 5s retries: 5 start_period: 30sLe champ
start_periodest critique pour les services à démarrage lent (bases de données, moteurs de recherche) : il évite que Docker déclare le conteneurunhealthypendant la phase d'initialisation et déclenche un redémarrage prématuré.Consultez
docker-compose-depends-on-healthcheckpour la configuration complète deservice_healthysur PostgreSQL — le même principe s'applique à tout service qui a besoin d'un temps d'amorçage.Rollback en cas de problème
Si la nouvelle version ne démarre pas ou produit des erreurs, le rollback doit prendre moins de deux minutes. La procédure :
1. Revenez au tag précédent dans
docker-compose.yml(ougit revertsi vous avez commité le bump).
2. Relancez uniquement le service concerné, sans recréer les volumes :docker compose up -d --no-deps --force-recreate meilisearch3. Vérifiez immédiatement les logs :
docker compose logs -f meilisearchLe flag
--no-depsest essentiel : il redémarre le service cible sans toucher aux autres conteneurs (base de données, cache, proxy). Sans lui,docker compose up -dpeut recréer la totalité de la stack.⚠️ Si la nouvelle version a migré le format des données sur disque (cas Meilisearch après
--upgrade-db, Supabase PG17), le rollback d'image ne suffit pas — c'est pourquoi le backup du volume est une précondition, pas une option.
Gardez la version précédente disponible localement
Avant de puller la nouvelle image, taguez l'image actuellement en production sous un nom de rétention :
docker tag getmeili/meilisearch:v1.53.0 getmeili/meilisearch:rollbackCela vous permet de revenir à l'état exact en cas d'urgence, même si vous n'avez plus accès au registry ou si la connexion est lente. Sur un VPS avec une bande passante contrainte, ce tag local vous économise plusieurs minutes de téléchargement au pire moment.
Les breaking changes récents qui ont cassé des instances
Ces quatre exemples illustrent pourquoi le protocole ci-dessus n'est pas théorique.
Meilisearch (lors d'une montée de version) : un moteur plus récent refuse d'ouvrir une base écrite par une version antérieure, avec Your database version (…) is incompatible with your current engine version, tant que --upgrade-db n'est pas passé. Cette migration peut changer le format sur disque : en v1.37.0 (mars 2026), les index vectoriels encore au format arroy ont été convertis vers le nouveau store HNSW (hannoy). Et le retour arrière n'est pas supporté (Downgrade is not supported). La seule sortie propre après un échec est d'avoir exporté un dump avant la mise à jour, puis de l'importer (meilisearch --db-path /meili_data --import-dump /backup.dump) — opération possible uniquement si vous avez exporté avant de mettre à jour.
Supabase Docker PostgreSQL 15 → 17 (PG 17 par défaut à partir du 17 juin 2026) : le conteneur supabase/postgres:17 ne peut pas lire un volume initialisé par PG 15. Supabase documente explicitement que le saut nécessite un pg_upgrade via un script dédié — le processus d'initialisation du conteneur ne le fait pas automatiquement. Sans migration préalable, la base ne démarre pas.
Langfuse v3 → v4 (v4.0.0 publiée le 29 juillet 2026) : la v4 abandonne les endpoints d'ingestion batch legacy au profit d'OpenTelemetry. Les SDKs Python v2 et antérieurs et les SDKs JS/TS v3 et antérieurs sont rejetés à l'ingestion dès que la stack v4 tourne dans son mode d'écriture par défaut (events_only) ; les modes legacy et dual servent à étaler la migration. Si vos services clients n'ont pas migré avant la mise à jour du serveur, ils perdent silencieusement toutes leurs traces.
NocoDB (série 2026.09) : pas de base cassée, mais un changement incompatible annoncé à l'avance. Les notes de 2026.09.0 rappellent que les dates renvoyées par l'API REST, les webhooks et les exports passeront de la seconde à la milliseconde sur PostgreSQL et SQLite dans une version à venir. Un client qui lit les dates avec un format strict cassera sans que le serveur ne signale rien — c'est ce que la lecture des notes de version permet d'anticiper. Autre piège, indépendant de la version : une installation en bind-mounts (./postgres, ./nocodb) dont le chemin de montage change entre deux déploiements démarre sur une base vide. Les volumes nommés évitent cette dérive.
Stratégies de mise à jour : comparatif
Faites défiler le tableau
| Stratégie | Sécurité des données | Temps de préparation | Rollback |
|---|---|---|---|
| `docker compose pull` + `up -d` direct | Aucune garantie — breaking changes non détectés | < 1 minute | Difficile si migration de données |
| Bump de tag versionné + backup de volume | Élevée — données sauvegardées avant tout changement | 10 à 20 minutes | Trivial : revert du tag + `up -d --no-deps` |
| Test sur staging avant prod | Maximale — breaking changes détectés hors prod | Variable selon l'env | Non nécessaire si le test a passé |
| Image pinnée par digest SHA256 | Élevée — immunisé contre le tag-overwrite | Identique au tag versionné | Identique au tag versionné |
Intégrer ce protocole dans votre workflow
Un protocole qui reste dans un guide ne sert à rien. Pour qu'il soit appliqué systématiquement, externalisez la version dans un fichier .env versionné dans git :
# .env
MEILISEARCH_VERSION=v1.53.0
POSTGRES_VERSION=15.6# docker-compose.yml
services:
meilisearch:
image: getmeili/meilisearch:${MEILISEARCH_VERSION}Mettre à jour une version devient alors un commit unique sur .env — lisible dans git log, réversible par git revert, et déployable par CI/CD sans modifier le fichier Compose principal.
Pour les agences qui gèrent plusieurs stacks clients, créez un fichier CHANGELOG_INFRA.md par client : chaque mise à jour y est tracée avec la version précédente, la date, le backup effectué et l'issue de la mise à jour. C'est aussi ce qui vous protège contractuellement en cas d'incident ultérieur.
Automatiser sans perdre le contrôle
Si vous voulez être alerté des nouvelles versions sans auto-pull, Diun (Docker Image Update Notifier) surveille votre registry et vous envoie une notification (Slack, email, webhook) quand une nouvelle image est disponible. Vous restez décisionnaire sur le moment de la mise à jour.
C'est la différence fondamentale avec Watchtower : Diun notifie, Watchtower agit. Sur une stack de production avec des volumes persistants, la notification est le bon niveau d'automatisation — l'action reste manuelle et précédée du protocole ci-dessus.