Docker Compose « version is obsolete » : modernisez votre fichier compose en toute sécurité

Cet article vous a aidé ?

Si vous lancez docker compose up et que l’on vous indique que la clé version de votre Compose est obsolète, vous êtes face à un problème classique d’exploitation : un avertissement qui semble inoffensif jusqu’à ce qu’il ne le soit plus. Les équipes l’ignorent pendant des mois, puis une mise à jour de portable, un renouvellement de runner CI ou un nouvel hôte de production transforme cet avertissement en builds cassés, réseaux différents ou conteneurs qui « fonctionnaient hier » et plus aujourd’hui.

C’est le type d’avertissement qui ne porte pas sur la syntaxe. Il concerne les attentes. Vous n’éditez pas seulement du YAML ; vous négociez le comportement entre votre fichier, le CLI Compose, le moteur et la « Compose Specification » qui a remplacé les anciennes versions du format. Modernisons sans surprises.

Ce que l’avertissement signifie vraiment (et ce qu’il ne signifie pas)

Le message « version is obsolete » apparaît le plus souvent lorsque vous utilisez Docker Compose V2 (le sous‑commande docker compose) et que votre compose.yaml contient encore un en‑tête version: "2" ou version: "3.7". Compose V2 implémente la Compose Specification et n’a plus besoin de cette clé pour sélectionner un mode d’analyse/comportement. En réalité, cette ligne peut induire les humains en erreur en leur faisant croire qu’elle fige le comportement. Ce n’est pas le cas.

Voici la réalité opérationnelle :

  • La « version » du fichier Compose servait autrefois à sélectionner un ensemble de fonctionnalités (notamment entre 2.x et 3.x).
  • La Compose Spec a évolué vers un modèle par capacités : le CLI décide de ce qu’il prend en charge ; le fichier décrit l’intention.
  • Votre fichier Compose peut toujours se comporter différemment selon les machines, mais la différence n’est généralement pas la ligne version. C’est la version du plugin Compose, la version du moteur et l’environnement qui changent.

Donc : supprimer version est le plus souvent sans risque. L’élément dangereux est de partir du principe que tout le reste est correct. La migration est moins « supprimer une ligne » que « vérifier que la sémantique n’a pas dérivé ».

Une règle sèche qui vous évite des ennuis : traitez Compose comme un outil de déploiement, pas comme un langage de programmation. YAML n’est pas un SLA.

Compose V1 vs V2 : pourquoi votre mémoire musculaire vous trompe

Compose V1 était l’ancien outil docker-compose en Python. Compose V2 est un plugin en Go intégré au CLI Docker sous docker compose. Beaucoup d’options ressemblent, certains comportements sont proches. Les cas limites sont là où surviennent les incidents.

Blague n°1 : YAML est l’endroit où l’indentation déclenche des guerres religieuses, et Compose veut juste que vous choisissiez un camp de manière cohérente.

La citation unique (et pourquoi elle s’applique)

Idée paraphrasée — John Allspaw : la fiabilité vient de la façon dont les systèmes se comportent sous contrainte, pas de la confiance que nous éprouvons en lisant la configuration.

Cet avertissement est une invitation à tester le comportement, pas à gagner une dispute avec un linter.

Bref historique : pourquoi Compose a cessé de dépendre de « version »

Un peu de contexte historique rend l’avertissement moins arbitraire et davantage un nettoyage de points de friction hérités. Voici des faits concrets qui expliquent la trajectoire :

  1. Les fichiers Compose 2.x et 3.x n’étaient pas seulement « plus récents » ; ils ciblaient des mondes différents. 3.x s’alignait sur les hypothèses de l’époque Swarm (notamment autour des clés deploy).
  2. La section deploy a été introduite pour décrire l’intention d’orchestration, mais le Compose classique (non‑Swarm) l’ignorait en grande partie, ce qui a embrouillé les gens pendant des années.
  3. Compose V1 (Python) et Compose V2 (plugin Go) ont des implémentations différentes, ce qui implique des cas limites différents même quand le YAML est identique.
  4. Docker a intégré Compose au CLI principal pour réduire la dispersion d’outils, mais cela a aussi entraîné un rythme d’itération plus rapide et une UX plus cohérente avec les autres commandes Docker.
  5. La Compose Specification est devenue une spec neutre hébergée dans l’écosystème OCI, visant à standardiser le comportement au‑delà du « ce que fait l’outil Docker aujourd’hui ».
  6. Les anciens en‑têtes de version sont devenus un hack de compatibilité : ils servaient à verrouiller des fonctionnalités plutôt qu’à décrire une intention. C’est l’inverse de l’objectif d’une spécification.
  7. Des fonctionnalités comme les profiles sont arrivées plus tard et ne s’intègrent pas bien dans l’ancienne taxonomie 2/3 ; l’approche par spec est plus flexible.
  8. Beaucoup d’équipes utilisaient Compose comme un « Kubernetes low‑cost », ce qui va tant que vous ne mélangez pas les environnements et n’assumez pas les mêmes sémantiques partout.

L’avertissement n’est pas un jugement moral. C’est Compose qui vous dit : « Arrêtez de prétendre qu’une simple chaîne contrôle le comportement. Ce n’est pas le cas. »

Principes pour une modernisation sûre

1) Normalisez la chaîne d’outils en premier, pas en dernier

Avant d’éditer le YAML, capturez ce que vous exécutez aujourd’hui : version du Docker Engine, version du plugin Compose et la ligne de commande exacte utilisée en CI et sur les hôtes de production. Si ces éléments diffèrent, votre fichier Compose est déjà « multi‑plateforme » que vous le vouliez ou non.

2) La configuration rendue est la vérité

Compose prend en charge l’interpolation de variables, les champs d’extension, plusieurs fichiers, les profils et les valeurs par défaut. Les humains lisent le YAML. Compose exécute le modèle entièrement résolu. Votre migration doit comparer la configuration rendue avant et après les modifications.

3) « Ça marche sur mon laptop » est souvent une histoire de permissions de volumes

La modernisation déclenche des rebuilds et la recréation de conteneurs. C’est à ce moment que la propriété des fichiers, les labels SELinux et les drivers de stockage vous rappellent qu’ils existent. La modernisation de Compose concerne autant le stockage que le YAML.

4) Verrouillez ce qui doit l’être

Ne verrouillez pas tout (cela devient un musée), mais verrouillez les éléments qui font varier le comportement : tags d’images (ou digests), version du plugin Compose dans les runners CI et conventions de nommage du projet.

5) Rendez le « up » ennuyeux

Un fichier Compose correct rend docker compose up -d une opération à faible risque. Si vous comptez sur des valeurs par défaut implicites (réseaux, noms de conteneurs, contexts de build), votre futur vous paiera des intérêts.

Plan de migration : de « version: » au Compose Spec

La migration consiste essentiellement à : supprimer la clé version, s’assurer que le nom et la disposition des fichiers correspondent aux attentes modernes, valider avec docker compose config et confirmer le comportement via une recréation contrôlée.

Étape 1 : renommez et standardisez le nommage des fichiers

Compose moderne par défaut utilise compose.yaml. Il lira également docker-compose.yml, mais standardiser réduit la confusion du type « pourquoi a‑t‑il choisi ce fichier ? ».

Étape 2 : retirez la ligne version (mais n’en restez pas là)

Supprimez :

  • version: "2"
  • version: "3"
  • version: "3.8" (etc.)

Conservez tout le reste. Puis validez la configuration rendue. Votre objectif est de prouver que le modèle résultant est le même.

Étape 3 : remplacez les schémas dépréciés par des équivalents compatibles spec

Exemples courants :

  • links : obsolète ; Compose moderne utilise la découverte DNS par défaut sur les réseaux.
  • depends_on avec conditions : les anciens schémas utilisaient condition: service_healthy ; V2 a un support partiel selon les versions. Préférez des healthchecks explicites + une logique d’attente au niveau de l’entrypoint pour les dépendances critiques.
  • container_name partout : peut sembler propre, mais casse la mise à l’échelle et provoque des collisions entre projets. Utilisez‑le avec parcimonie.
  • external_links : généralement un signe de mauvais design ; modélisez les réseaux explicitement à la place.

Étape 4 : auditez sérieusement les définitions de stockage

Si vous exécutez des bases de données dans Compose (ce que vous faites probablement), traitez les volumes comme des structures de données de production. Nommez explicitement les volumes, choisissez les bind mounts intentionnellement et comprenez où résident les octets sur le disque. Une modernisation qui recrée des conteneurs peut orpheliner des volumes ou en recréer d’autres si vous changez le nommage du projet.

Étape 5 : prouvez‑le avec une recréation contrôlée

N’exécutez pas « juste comme ça ». Adoptez une mentalité dry‑run : rendez la config, pull des images et recréez dans un environnement de staging ou une copie de l’hôte. Comparez ensuite les métadonnées des conteneurs (réseaux, montages, vars d’environnement, healthchecks).

Blague n°2 : « Je n’ai changé qu’un avertissement » est la devise officieuse des rétrospectives d’incident.

Tâches pratiques (commandes, sorties, décisions)

Voici des tâches concrètes et exécutables que vous pouvez réaliser sur un hôte ou un runner CI. Chaque tâche inclut (1) une commande, (2) ce que signifie la sortie et (3) la décision que vous en tirez. C’est ce qui empêche le « ça devrait être identique » de se transformer en déluge de tickets.

Task 1: Confirm you’re using Compose V2 (plugin) and not V1

cr0x@server:~$ docker compose version
Docker Compose version v2.27.1

Ce que cela signifie : Vous exécutez le plugin V2. L’avertissement « version is obsolete » est attendu si votre fichier contient encore la clé version:.

Décision : Migrer vers le style Compose Spec (supprimer version) et valider avec le comportement V2.

Task 2: Spot-check for legacy V1 usage in scripts

cr0x@server:~$ command -v docker-compose || echo "docker-compose not found"
docker-compose not found

Ce que cela signifie : Les scripts appelant docker-compose échoueront ici ; sur d’autres machines ils peuvent encore fonctionner. Cette inconsistance est un risque lors de la migration.

Décision : Standardiser sur docker compose dans l’automatisation, ou installer/verrouiller explicitement V1 si nécessaire (rarement justifié aujourd’hui).

Task 3: Capture Docker Engine version (behavior varies)

cr0x@server:~$ docker version --format '{{.Server.Version}}'
26.1.4

Ce que cela signifie : Les fonctionnalités du moteur (options de driver réseau, comportement de build, intégration iptables) varient selon la version.

Décision : Si la prod et la CI diffèrent de façon significative, alignez les versions ou testez sur la plus ancienne version supportée.

Task 4: Render the fully resolved Compose config (the baseline)

cr0x@server:~$ docker compose -f docker-compose.yml config
name: app
services:
  api:
    environment:
      NODE_ENV: production
    image: registry.local/app/api:1.9.2
    networks:
      default: null
    ports:
      - mode: ingress
        target: 8080
        published: "8080"
        protocol: tcp
networks:
  default:
    name: app_default

Ce que cela signifie : C’est ce que Compose appliquera réellement. Cela inclut les valeurs par défaut, les valeurs interpolées et les champs normalisés.

Décision : Sauvegardez cette sortie comme référence « known good » avant d’éditer.

Task 5: Validate that your file parses without the version key

cr0x@server:~$ cp docker-compose.yml compose.yaml
cr0x@server:~$ sed -i '/^version:/d' compose.yaml
cr0x@server:~$ docker compose -f compose.yaml config >/dev/null && echo "config ok"
config ok

Ce que cela signifie : La syntaxe est valide sous le modèle Compose Spec.

Décision : Procéder à la comparaison sémantique (tâche suivante). Passer les vérifications de parsing est nécessaire, pas suffisant.

Task 6: Compare rendered configs before vs after (semantic drift detector)

cr0x@server:~$ docker compose -f docker-compose.yml config > /tmp/before.yaml
cr0x@server:~$ docker compose -f compose.yaml config > /tmp/after.yaml
cr0x@server:~$ diff -u /tmp/before.yaml /tmp/after.yaml | head

Ce que cela signifie : L’absence de sortie signifie aucune différence dans la config rendue (idéal). S’il y a un diff, lisez‑le comme une revue de changement : environnement, montages, réseaux et labels sont les zones à risque.

Décision : S’il y a des diffs, soit corrigez le fichier, soit documentez pourquoi le changement est attendu et sûr.

Task 7: Check whether profiles are silently changing what starts

cr0x@server:~$ docker compose -f compose.yaml config --profiles
default
debug

Ce que cela signifie : Des profils existent. Lancer docker compose up sans --profile peut ignorer certains services. Cela peut ressembler à « Compose a cassé » alors qu’en fait « vous n’avez pas activé le profil ».

Décision : Dans les scripts de production, soyez explicite sur les profils (ou évitez‑les dans les fichiers prod).

Task 8: Verify the project name (volume/network naming stability)

cr0x@server:~$ docker compose -f compose.yaml ls
NAME            STATUS              CONFIG FILES
app             running(3)          /srv/app/compose.yaml

Ce que cela signifie : Compose utilise un « nom de projet » pour namespacer réseaux/volumes/conteneurs. Changer le nom du répertoire, le nom du fichier ou utiliser -p peut le modifier.

Décision : Verrouillez le nom du projet dans l’automatisation (utilisez -p app ou name: dans la config) si la stabilité est importante.

Task 9: Inspect volumes to ensure data isn’t about to disappear

cr0x@server:~$ docker volume ls
DRIVER    VOLUME NAME
local     app_dbdata
local     app_redisdata

Ce que cela signifie : Ces volumes sont des objets distincts, non liés au cycle de vie des conteneurs. Ils survivent à la recréation de conteneurs, mais peuvent être remplacés si vous les renommez accidentellement (souvent via des changements de nom de projet).

Décision : Si les noms de volume sont préfixés par le projet et que vous allez changer le nom du projet, nommez explicitement les volumes avec des identifiants stables.

Task 10: Confirm what’s mounted where (permission and SELinux tripwires)

cr0x@server:~$ docker inspect app-db-1 --format '{{json .Mounts}}'
[{"Type":"volume","Name":"app_dbdata","Source":"/var/lib/docker/volumes/app_dbdata/_data","Destination":"/var/lib/postgresql/data","Driver":"local","Mode":"z","RW":true,"Propagation":""}]

Ce que cela signifie : La base de données écrit dans un volume géré par Docker. Le mode z suggère un relabel SELinux (courant sur les hôtes Fedora/RHEL).

Décision : Si vous passez à des bind mounts, vous devez gérer explicitement la propriété et les labels SELinux. Si vous restez sur des volumes nommés, conservez des noms stables.

Task 11: Detect container recreation risk before applying changes

cr0x@server:~$ docker compose -f compose.yaml up -d --no-deps --dry-run
service api: would recreate

Ce que cela signifie : Compose prévoit de recréer des conteneurs. La recréation est l’endroit où vous découvrez que vous avez oublié de persister des données ou que vous avez verrouillé le mauvais tag d’image.

Décision : Si des services critiques seraient recréés, prévoyez une fenêtre, confirmez les montages de volumes et assurez‑vous de pouvoir revenir en arrière.

Task 12: Confirm images are pinned in a sane way

cr0x@server:~$ docker compose -f compose.yaml images
CONTAINER           REPOSITORY                    TAG       IMAGE ID       SIZE
app-api-1           registry.local/app/api        1.9.2     1a2b3c4d5e6f   312MB

Ce que cela signifie : Vous utilisez un tag précis. Si vous utilisez latest, la modernisation est un bon moment pour des mises à jour surprises.

Décision : Verrouillez les tags, idéalement verouillez les digests en production si votre workflow de registry le permet.

Task 13: Validate healthchecks actually run and report what you expect

cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Status}}' | head -n 5
NAMES        STATUS
app-api-1    Up 2 minutes (healthy)
app-db-1     Up 2 minutes (healthy)

Ce que cela signifie : Les healthchecks sont actifs et reportent un état. Si des services sont « Up » mais jamais « healthy », vos hypothèses d’orchestration de dépendances peuvent être erronées.

Décision : Si votre séquencement de démarrage dépend de la santé, assurez‑vous que des healthchecks existent et se comportent de manière cohérente selon les environnements.

Task 14: Confirm the effective network and DNS behavior

cr0x@server:~$ docker network inspect app_default --format '{{json .IPAM.Config}}'
[{"Subnet":"172.23.0.0/16","Gateway":"172.23.0.1"}]

Ce que cela signifie : Compose a créé un réseau bridge par défaut. Si vous changez le nom du projet ou déclarez les réseaux différemment, le nom/subnet du réseau peut changer et casser des allowlists codées en dur.

Décision : Si quelque chose d’externe dépend de subnets/noms stables, déclarez explicitement le réseau avec un nom stable (et évitez de coder en dur des subnets sauf nécessité).

Task 15: Confirm env var interpolation and missing vars (quiet foot-gun)

cr0x@server:~$ docker compose -f compose.yaml config 2>&1 | grep -i warning | head
WARN[0000] The "API_KEY" variable is not set. Defaulting to a blank string.

Ce que cela signifie : Compose a substitué une chaîne vide. Votre service peut démarrer puis échouer d’une façon qui paraît sans rapport.

Décision : Échouez rapidement : exigez les variables d’environnement en CI, stockez‑les dans un gestionnaire de secrets, ou fournissez un .env avec des valeurs par défaut sûres uniquement pour le développement.

Task 16: Identify orphaned containers after refactors

cr0x@server:~$ docker compose -f compose.yaml up -d
[+] Running 3/3
 ✔ Container app-api-1  Started
 ✔ Container app-db-1   Started
 ✔ Container app-redis-1 Started
cr0x@server:~$ docker compose -f compose.yaml ps --all
NAME          IMAGE                          COMMAND                  SERVICE   STATUS
app-api-1     registry.local/app/api:1.9.2   "node server.js"         api       running
app-db-1      postgres:16                    "docker-entrypoint..."   db        running
app-redis-1   redis:7                        "docker-entrypoint..."   redis     running

Ce que cela signifie : Liste propre. Si Compose affiche « orphan containers », vous avez probablement renommé des services ou changé le nom du projet et laissé d’anciens conteneurs derrière.

Décision : Si vous voyez des orphelins, supprimez‑les délibérément (docker compose down --remove-orphans) après avoir confirmé qu’ils ne reçoivent plus de trafic.

Mode opératoire pour un diagnostic rapide

Quand la modernisation de Compose déraille, vous avez besoin d’un chemin court vers le goulot d’étranglement. Voici l’ordre qui trouve le fautif le plus vite dans des systèmes réels.

Première étape : identifiez la chaîne d’outils réellement utilisée

  • Vérifiez docker compose version et docker version.
  • Confirmez les fichiers Compose utilisés (le -f explicite dans l’automatisation évite le « tout ce qui est dans le répertoire »).
  • Rendez la config : docker compose config. Si vous ne pouvez pas rendre la config, rien d’autre n’a d’importance.

Deuxième étape : détectez les dérives de nommage (projet, réseaux, volumes)

  • Vérifiez le projet : docker compose ls.
  • Listez les volumes : docker volume ls.
  • Inspectez les noms de réseau : docker network ls et docker network inspect.

Si les noms ont changé, vous avez peut‑être « perdu » des données simplement en créant de nouveaux volumes sous un nouveau namespace.

Troisième étape : vérifiez le stockage et les permissions avant de poursuivre les débogages applicatifs

  • Inspectez les montages sur le conteneur.
  • Consultez les logs des conteneurs pour des erreurs de permissions.
  • Sur hôtes SELinux, recherchez des opérations refusées après un changement de type de montage.

Quatrième étape : vérifiez l’ordre d’exécution et la santé au runtime

  • Statut de santé et compteurs de restart via docker ps.
  • La base de données devient‑elle healthy avant que l’API ne démarre ?
  • Si des hypothèses depends_on étaient en jeu, vérifiez si elles tiennent toujours.

Cinquième étape : ensuite seulement faites de l’optimisation

Si le système est lent après la modernisation, mesurez avant d’optimiser : vérifiez la latence du système de fichiers, le comportement d’overlay2, le throttling CPU et la résolution DNS depuis l’intérieur du réseau. Compose n’était pas forcément le goulot jusqu’à preuve du contraire.

Erreurs courantes : symptôme → cause racine → correction

1) Symptom: “My database is empty after the migration”

Cause racine : Le nom du projet a changé, donc le volume nommé est devenu un objet différent (ex. oldproj_dbdata vs newproj_dbdata). Ou vous êtes passé d’un volume nommé à un bind mount sans migrer les données.

Correction : Nommez explicitement le volume et gardez‑le stable. Vérifiez le mapping du volume avec docker inspect. Si vous devez migrer, copiez les données de l’ancien volume vers le nouvel emplacement pendant une fenêtre de maintenance contrôlée.

2) Symptom: “Services can’t reach each other; DNS lookup fails”

Cause racine : Vous avez supprimé links en pensant que cela créait la connectivité. Dans Compose moderne, la connectivité provient des réseaux partagés ; les noms DNS proviennent des noms de services.

Correction : Placez les deux services sur le même réseau utilisateur (ou par défaut) et utilisez le DNS par nom de service. Si vous avez besoin d’une connectivité inter‑projets, déclarez un réseau externe et attachez‑y les deux projets.

3) Symptom: “The warning is gone, but now CI behaves differently than my laptop”

Cause racine : La CI utilise une version différente du plugin Compose ou du Docker Engine. La config est techniquement valide mais les sémantiques diffèrent (cache de build, comportement de pull, timing des healthchecks, particularités DNS).

Correction : Verrouillez les versions de la chaîne d’outils dans la CI. Ajoutez docker compose version aux logs de build. Traitez la « dérive de chaîne d’outils » comme un changement nécessitant une revue.

4) Symptom: “Containers recreate every time even when nothing changed”

Cause racine : Non‑déterminisme dans les vars d’environnement, les build args ou la config générée. Ou vous avez changé des labels et Compose pense que la définition du service a changé.

Correction : Normalisez les sources d’env vars. Rendez la config et diff‑ez‑la entre les runs. Évitez d’injecter des timestamps ou des valeurs « aléatoires » dans l’environnement.

5) Symptom: “We used depends_on to wait for the DB, now the app crashes at startup”

Cause racine : depends_on contrôle seulement l’ordre de démarrage ; il ne garantit pas la disponibilité. Les anciens outils donnaient un faux sentiment de sécurité quand ils étaient combinés à des conditions ou à des timing fragiles.

Correction : Implémentez de vrais healthchecks et des retries/backoff au niveau applicatif. Considérez un petit script wait‑for comme un garde‑fou, pas comme une architecture.

6) Symptom: “Ports changed / traffic started hitting the wrong container”

Cause racine : Changement de nom de projet ou de nom de service a modifié les noms de conteneurs et les labels utilisés par un reverse proxy ou un agent de monitoring. Parfois la suppression de container_name révèle la dépendance.

Correction : Ne basez pas le routage/monitoring sur les noms de conteneurs. Utilisez des labels intentionnellement (par ex. pour un proxy) et conservez‑les stables lors des refactors.

7) Symptom: “Permission denied on bind mounts after modernization”

Cause racine : Vous êtes passé de volumes nommés (permissions gérées par Docker) à des bind mounts (permissions hôte) sans aligner UID/GID ou labels SELinux.

Correction : Alignez les UID des conteneurs, réglez la propriété sur le chemin hôte et utilisez les flags de montage SELinux appropriés quand c’est nécessaire. Validez via les logs des conteneurs et l’inspection des montages.

Trois mini-récits d’entreprise issus du terrain Compose

Mini-récit 1 : un incident causé par une mauvaise hypothèse

L’entreprise avait une stack Compose « simple » : API, Postgres, Redis et un sidecar qui envoyait les logs. Le docker-compose.yml commençait par version: "3.7". L’équipe a vu l’avertissement après une mise à jour de Docker Desktop et a décidé de moderniser en retirant la ligne version et en renommant le fichier en compose.yaml. Ça ressemblait à du ménage.

Ils ont déployé sur une petite VM de production en copiant le répertoire. Ils n’ont pas précisé -p, et le nom du répertoire sur la VM différait de celui du staging. Compose a créé un nouveau nom de projet basé sur le répertoire, et avec lui un nouvel ensemble de réseaux et volumes. Postgres est monté rapidement. Il était vide. L’API s’est connectée, a exécuté des migrations sur la base fraîche et a commencé à servir. Personne n’a remarqué jusqu’à ce qu’un client signale des données manquantes, parce que le nouvel environnement semblait parfaitement sain.

Le monitoring n’a pas alerté. Le CPU allait bien. La latence allait bien. Le système était « up ». Il pointait juste vers les mauvais octets. Finalement quelqu’un a exécuté docker volume ls et a vu deux séries de volumes aux noms similaires. Les anciennes données étaient toujours là, attachées silencieusement à l’ancien projet.

La correction fut banale : ils ont fixé le nom du projet, nommé explicitement les volumes et ajouté une étape de runbook pour vérifier les attachements de volumes avant tout refactor de fichier Compose. Ils ont aussi appris que « Compose réutilisera mes données » n’est pas une garantie ; c’est un effet secondaire d’un nommage stable.

Mini-récit 2 : une optimisation qui s’est retournée contre eux

Une autre organisation avait pour mandat de réduire le temps de déploiement. Leur stack Compose buildait trois images localement sur l’hôte avec docker compose build puis les lançait. Quelqu’un a proposé d’accélérer : « Passons à des bind mounts partout pour éviter trop de rebuilds. » Ils ont aussi retiré la clé version en touchant le fichier, parce que l’avertissement agaçait dans les logs.

En dev, tout était instantané. En production, ce fut un échec en slow‑motion. Les conteneurs tournaient en non‑root (bien), mais les répertoires hôtes étaient possédés par un UID/GID différent (prévisible, mais non géré). Les services ont démarré, écrit quelques fichiers, puis ont planté en rencontrant des répertoires qu’ils ne pouvaient pas créer. Des boucles de restart ont suivi. L’équipe a tenté de corriger en lançant les conteneurs en root, ce qui a résolu l’erreur de permissions immédiate et créé le problème suivant : les chemins hôtes se sont retrouvés remplis de fichiers possédés par root, cassant les futurs déploiements en non‑root.

Puis est survenue la surprise des performances de stockage. L’hôte était sur un système de fichiers optimisé pour la durabilité, pas pour le churn de petits fichiers. Les nouveaux patterns bind‑mounted ont augmenté les opérations metadata ; la latence a picé lors des périodes d’écriture. L’« optimisation » a économisé du temps de build et l’a rendu dix fois sur la file d’attente I/O et le chaos opérationnel.

Ils se sont rétablis en revenant en production aux volumes nommés pour les chemins stateful, en gardant des bind mounts uniquement pour un petit ensemble de configs en lecture seule et assets statiques. Ils ont aussi formalisé une règle : les changements de production touchant aux sémantiques de stockage exigent une revue de chemin de données explicite, pas seulement une revue de code.

Mini-récit 3 : une pratique simple mais correcte qui a sauvé la mise

Dans une entreprise proche de la finance, une équipe a décidé de moderniser leurs fichiers Compose dans des dizaines de dépôts. Ils l’ont fait comme des adultes : une liste de migration standard, plus un job CI qui rendait la config Compose et la stockait comme artifact. Chaque PR incluait un diff de la sortie docker compose config avant et après. Ce n’était pas glamour, mais c’était visible.

Lors d’une migration, le diff a montré un changement subtil : un nom de réseau avait bougé parce que quelqu’un avait introduit un champ name: au niveau racine en changeant aussi le nom du répertoire du repo. Le développeur voulait améliorer la cohérence. La config rendue a révélé que les conteneurs existants seraient recréés sur un nouveau réseau. Le reverse proxy, qui découvrait les cibles par appartenance au réseau, cesserait de router vers le service pendant le déploiement.

Parce qu’ils l’avaient détecté en CI, ils ont planifié le changement : pré‑création du réseau cible, mise à jour de la config du proxy et déploiement pendant une fenêtre de maintenance. La migration est sortie avec un basculement propre et sans trou noir de trafic. Personne n’a célébré. C’est le but.

Ils ont conservé la pratique : render‑and‑diff est devenu une définition de fait pour toute modification Compose. Cela les a de nouveau sauvés lorsque plus tard une variable d’environnement a été par défautée à vide dans un environnement ; les artifacts de config rendue ont rendu la valeur manquante évidente.

Listes de contrôle / plan étape par étape

Checklist A: Modernisation sûre dans un seul repo

  1. Inventaire de la chaîne d’outils : enregistrez les versions Engine + plugin Compose sur dev, CI et prod.
  2. Rendez la config actuelle et stockez‑la comme artifact de référence (docker compose config).
  3. Supprimez version: et standardisez sur compose.yaml.
  4. Rendez à nouveau et diff‑ez les sorties ; résolvez tout changement inattendu.
  5. Verrouillez le nommage du projet si volumes/réseaux doivent rester stables (-p ou name:).
  6. Auditez les volumes : assurez‑vous que les services stateful utilisent des volumes nommés explicitement ou des bind mounts délibérés.
  7. Confirmez les vars d’environnement : pas d’avertissements de valeurs manquantes obligatoires ; faire échouer la CI s’il y en a.
  8. Dry‑run de recréation pour voir ce qui serait remplacé et planifier une indisponibilité si nécessaire.
  9. Déployez en staging avec des chemins de données proches de la production ; vérifiez montages, santé et connectivité.
  10. Déployez en production avec un plan de rollback : ancien fichier, ancien nom de projet et attachements de volumes connus.

Checklist B: Standardisation à travers de nombreux repos (la réalité d’entreprise)

  1. Choisissez une plage de versions supportées du plugin Compose et standardisez d’abord les runners CI.
  2. Créez un template de job de migration CI qui exécute docker compose config et stocke les artifacts.
  3. Introduisez des contrôles de politique : rejetez les tags latest, détectez les vars manquantes, signalez la prolifération de container_name.
  4. Définissez des règles de stockage : quels services peuvent utiliser des bind mounts, lesquels doivent utiliser des volumes nommés, comment fonctionnent les sauvegardes.
  5. Rendez explicite le nommage du projet lorsque la persistance des données est impliquée.
  6. Formez les équipes une fois : un guide interne court vaut mieux que le savoir tribal.

Exemple minimal : style Compose Spec moderne (sans version)

Pas un tutoriel complet, juste un modèle qui évite les pièges courants : noms de volumes stables, réseaux explicites et nommage prévisible.

cr0x@server:~$ cat compose.yaml
name: app
services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD}
    volumes:
      - dbdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 20
  api:
    image: registry.local/app/api:1.9.2
    depends_on:
      - db
    environment:
      DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@db:5432/app
    ports:
      - "8080:8080"
volumes:
  dbdata:
    name: app_dbdata

Les éléments importants ne sont pas à la mode. Ils sont stables.

FAQ

1) Should I remove the version key?

Oui, si vous utilisez Compose V2. Elle est obsolète dans le sens où elle ne contrôle plus le parsing/le mode de fonctionnalités. Supprimez‑la, puis validez via des diffs de config rendue.

2) Will removing version change behavior?

Souvent non. Parfois oui, mais habituellement de façon indirecte : moderniser déclenche des renommages de fichiers, des changements de nom de projet ou des mises à jour du plugin Compose. C’est là que les changements de comportement se cachent. Prouvez‑le avec docker compose config avant et après.

3) Why does Compose still accept version if it’s obsolete?

Compatibilité ascendante. Compose sait qu’il existe une décennie de fichiers dans les dépôts. Accepter la clé tout en affichant un avertissement est le compromis entre rigidité et praticité.

4) What’s the difference between Compose file versions 2 and 3 anyway?

Historiquement : 2.x se concentrait sur le comportement Compose mono‑hôte ; 3.x s’alignait sur les champs de déploiement Swarm. La section deploy en 3.x a induit en erreur de nombreuses équipes car Compose classique en ignorait la plupart. La Compose Spec tente d’unifier cela sous un seul modèle.

5) Do I need to rename docker-compose.yml to compose.yaml?

Vous n’êtes pas obligé, mais standardiser aide. Le risque n’est pas le nom de fichier ; c’est la dérive accidentelle du nom de projet et les humains qui devinent quel fichier a été utilisé. Si vous renommez, verrouillez le nom du projet et vérifiez les volumes.

6) How do I make sure volumes don’t get recreated?

Nommez explicitement les volumes critiques (ex. name: app_dbdata) et maintenez un nommage de projet stable. Avant le déploiement, listez les volumes et inspectez les montages pour confirmer que le conteneur s’attache au volume attendu.

7) Our stack uses depends_on. Is it reliable?

Il est fiable pour l’ordre de démarrage, pas pour la disponibilité. Si votre application a besoin que la BD soit prête, implémentez des retries et des healthchecks. Traitez depends_on comme une commodité, pas comme un mécanisme de correction.

8) Why does CI warn about missing env vars but production doesn’t?

Parce que CI et prod chargent les vars différemment : fichiers .env, environnement shell, injection de secrets ou variables CI. Rendez la config dans les deux endroits et comparez. Les valeurs manquantes qui deviennent des chaînes vides sont un mode d’échec classique « ça a démarré mais c’est cassé ».

9) Can I mix multiple Compose files during migration?

Oui. Utilisez le layering -f (base + override). Mais souvenez‑vous : le modèle rendu est ce qui compte. Validez toujours avec docker compose -f base -f override config et stockez cette sortie.

10) Should we pin Docker and Compose versions?

Verrouillez‑les dans la CI. En production, vous pouvez soit verrouiller soit adopter un cadence de mises à jour contrôlée. Ce qu’il ne faut surtout pas faire, c’est « utiliser la version fournie par l’image VM ». Ce n’est pas une stratégie ; c’est un générateur de surprises.

Prochaines étapes réalisables aujourd’hui

L’avertissement « version is obsolete » est une cloche d’alarme peu coûteuse. Prenez ce signal gratuit, corrigez le fichier et profitez‑en pour réduire la dérive future.

  1. Exécutez docker compose config sur le fichier actuel et enregistrez la sortie.
  2. Retirez version:, rendez la config à nouveau et diff‑ez. Aucun diff est l’objectif ; expliquez tout diff que vous conservez.
  3. Verrouillez le nom du projet si vous avez des volumes stateful ou des dépendances externes sur des noms de réseau.
  4. Auditez les volumes et les montages comme s’il s’agissait de données de production (parce que c’en est).
  5. Standardisez la CI sur une version connue du plugin Compose et loggez‑la dans chaque pipeline.

Les avertissements ne sont agaçants que lorsque vous les ignorez. Si vous les traitez comme une invitation à vérifier le comportement, ce sont l’un des rares cadeaux que les systèmes de production vous offrent gratuitement.

← Précédent
Deliverabilité des e-mails : messages dans les spams — solutions réelles (SPF/DKIM/DMARC bien configurés)
Suivant →
Resizable BAR / SAM : le petit interrupteur qui peut booster beaucoup

Laisser un commentaire