Aller au contenu
← Blog

Docker Engine 29 : ce qui casse

Le plancher d’API a bougé deux fois, l’image store ne change que sur les installations neuves, et la limite de descripteurs de fichiers de chaque conteneur est passée discrètement de 1048576 à 1024. Neuf mois de retombées, vérifiées face à l’amont.

·17 min de lecture
  • Docker
  • containerd
  • Linux
  • DevOps

Docker Engine 29 est sorti en novembre 2025 et a cassé une quantité remarquable de logiciels qui fonctionnaient très bien depuis des années. Neuf mois plus tard, l’intéressant n’est plus la casse elle-même — c’est que la plupart de ce qui s’est écrit pendant les deux premières semaines est aujourd’hui faux, parce que le changement que tout le monde a documenté a été partiellement annulé trois versions mineures plus tard. Si vous mettez à jour un serveur Linux ce mois-ci, vous lisez des conseils écrits pour une version de Docker 29 qui n’existe plus.

Schéma des changements de Docker Engine 29 qui cassent des choses : la version d’API minimale relevée à 1.44 puis abaissée à 1.40, le containerd image store par défaut sur les installations neuves, la limite nofile des conteneurs passant de 1048576 à 1024, les variables des legacy links supprimées et les chaînes d’isolation iptables supprimées.
Cinq changements. Seul le premier figure sous le titre « breaking changes » des notes de version ; les quatre autres sont dispersés entre l’empaquetage, le réseau et un encadré de mise en garde.

Voici ce qui casse réellement, dans l’ordre où vous risquez de le rencontrer, avec le correctif et la source amont pour chacun. C’est écrit pour ceux qui font tourner Docker Engine sur des serveurs Linux — pas Docker Desktop, où plusieurs de ces questions ont d’autres réponses. Les commandes de diagnostic ne font que lire l’état ; tout ce qui modifie le système est une édition de configuration, une installation ou un redémarrage, et cela se voit comme tel. Là où l’amont n’a publié ni date ni version, cet article le dit plutôt que d’en inventer une.

Partez du symptôme, pas du changelog

Presque personne n’arrive sur cette page par le changelog. On y arrive par un message d’erreur, par un conteneur qui refuse de démarrer, ou par un tableau de bord qui annonce que l’environnement est injoignable. D’où ce tableau en premier : trouvez votre symptôme, puis lisez la section qui l’explique.[rel29]

Ce que vous voyezCe que c’est réellementOù ça se corrige
client version 1.24 is too old. Minimum supported API version is 1.44Un client plus ancien que le plancher d’API du démon. Le plancher a bougé en 29.0.0 — et est redescendu en 29.3.0.Mettez le client à jour. Le contournement côté démon est un pont, pas un correctif.
client version 1.52 is too new. Maximum supported API version is 1.44L’image miroir : un client récent face à un démon ancien. Presque toujours un service Docker-in-Docker épinglé en CI.Épinglez l’image DinD à la même version majeure que le démon, et faites-les évoluer ensemble.
docker images est vide après l’installation de 29Le containerd image store est le défaut sur les installations neuves. Votre contenu overlay2 est masqué, pas supprimé.Rebasculez le backend, ou exportez puis réimportez délibérément.
Les conteneurs butent sur des plafonds de connexions qui n’existaient pas avantLa limite nofile par défaut dans les conteneurs est passée de 1048576 à 1024.--ulimit par conteneur, ou default-ulimits sur le démon.
Les jobs CI échouent sur un contrôle de santé de service ; No HOST or PORT foundLes variables d’environnement des legacy links ne sont plus injectées, donc l’assistant qui attend le service ne le voit jamais.Un réseau défini par l’utilisateur et le DNS. La variable de secours est temporaire.
Un conteneur joint un port publié qu’il ne devrait pas pouvoir joindreLes chaînes DOCKER-ISOLATION-STAGE-1 et -STAGE-2 ont été supprimées en 29.0.Publiez sur 127.0.0.1 plutôt que sur l’adresse joker.
Vos règles de pare-feu DOCKER-USER ne s’appliquent plusN’arrive que si vous avez activé le backend nftables, expérimental et optionnel.Rebasculez, ou écrivez votre propre table nftables avec une priorité plus basse.
Un build Go échoue sur github.com/docker/dockerLe module a déménagé vers github.com/moby/moby/client et .../api.Réécrivez les imports ; l’API cliente a changé de forme en même temps.
# Three numbers decide everything that follows. Get them before you change
# anything, on the machine that is actually misbehaving.

# 1. Engine version, and the API floor this daemon enforces.
docker version
# Server: Docker Engine - Community
#  Engine:
#   Version:          29.3.0
#   API version:      1.54 (minimum version 1.40)
#                                             ^^^^
# Do NOT assume 1.44. Engine 29.0.0 raised the floor from 1.24 to 1.44, and
# 29.3.0 lowered it again to 1.40. Which one you get depends on where in the
# 29 series you landed, and almost everything written about this in late 2025
# predates the second change. Read the number, do not remember it.

# 2. Which image store this daemon is using.
docker info -f '{{ .DriverStatus }}'
# [[driver-type io.containerd.snapshotter.v1]]              -> containerd store
# [[Backing Filesystem extfs] [Supports d_type true] ...]   -> legacy overlay2
docker info 2>/dev/null | grep -i 'storage driver'
# Storage Driver: overlayfs   -> containerd snapshotter
# Storage Driver: overlay2    -> graph driver

# 3. Which packet-filtering backend. nftables is opt-in and experimental in 29.x,
#    so on an untouched host this should say iptables.
docker info 2>/dev/null | grep -i 'firewall'

# And the one that is not a Docker question, but constrains Docker anyway:
stat -fc %T /sys/fs/cgroup/
# cgroup2fs -> unified hierarchy, nothing to do
# tmpfs     -> cgroup v1 or hybrid: deprecated in 29.0, supported until May 2029

Trois de ces lignes sont le même changement sous-jacent vu de côtés différents, et deux d’entre elles ne sont documentées nulle part où vous penseriez à regarder. Avant de toucher à quoi que ce soit, établissez les trois faits qui déterminent lesquelles vous concernent.

Le plancher d’API a bougé deux fois, et le chiffre que vous avez lu est sans doute faux

C’est le fameux. Engine 29.0.0 a relevé à 1.44 la version minimale de l’API Engine que le démon accepte de parler, ce qui correspond à Docker 25.0. Le plancher précédent était 1.24 — lui-même introduit seulement dans Docker 25.0, en remplacement d’un plancher de 1.12 en place depuis 2016 — si bien qu’il s’agissait du deuxième tour de vis en deux ans, et du premier que quelqu’un ait remarqué. Tout client qui codait en dur une version plus ancienne, ou qui n’a jamais appris à négocier, a cessé de fonctionner à l’instant du redémarrage du démon. C’est pour cela que la mise à jour a emporté Traefik, Portainer, Testcontainers, Watchtower, une demi-douzaine de tableaux de bord auto-hébergés et un bon nombre de pipelines CI dans la même semaine.[pr51186][blog29]

Voici la partie que presque aucun article publié ne contient. Dans 29.3.0, sortie en mars 2026, l’amont a rabaissé le plancher — de 1.44 à 1.40, ce qui correspond à Docker 19.03. La valeur par défaut dans les sources actuelles est 1.40, et 1.24 n’est plus atteignable que par un contournement explicite. Un client en API 1.41 ou 1.43 rejeté en décembre refonctionne donc aujourd’hui, et un guide de dépannage qui vous annonce 1.44 comme minimum décrit les trois premières lignes mineures d’une série qui les a largement dépassées. Vérifiez votre propre démon plutôt que le souvenir qu’internet en a — la correspondance entre versions de Docker et versions d’API est publiée, et c’est la seule version de cette histoire qui reste vraie.[pr52067][config][apimatrix]

Version du démonVersion d’API minimaleCe que ça rejette
25.0 – 28.x1.24Pratiquement rien. Les moteurs plus anciens avaient un plancher de 1.12, inchangé depuis 2016.
29.0.0 – 29.2.x1.44Tout client antérieur à Docker 25.0. C’est la vague de casse dont tout le monde a parlé.
29.3.0 et suivantes1.40Les clients antérieurs à Docker 19.03. Un relâchement notable, et presque jamais mentionné.
N’importe laquelle, avec le contournementjusqu’à 1.24Aucune option en ligne de commande : la clé daemon.json ou DOCKER_MIN_API_VERSION ; l’amont en parle comme d’une option pour cas exceptionnels, sans date de suppression publiée.
# The symptom, produced by the daemon, not the client:
#
#   Error response from daemon: client version 1.24 is too old.
#   Minimum supported API version is 1.44, please upgrade your client to a
#   newer version
#
# And its mirror image, which appears in CI far more often than on servers -
# a NEW client talking to an OLD daemon, typically a pinned docker:dind service:
#
#   Error response from daemon: client version 1.52 is too new.
#   Maximum supported API version is 1.44

# The correct fix is always to update the client. Every tool listed later in
# this article shipped a build that negotiates the API version instead of
# hardcoding one.

# The escape hatch is for the case where the client is a vendor appliance you
# cannot update this week. It is set on the DAEMON, not on the client, and it
# is available in daemon.json only - there is no dockerd command-line flag.
cat /etc/docker/daemon.json 2>/dev/null   # read it first, do not clobber other keys

# /etc/docker/daemon.json
{
  "min-api-version": "1.24"
}

sudo systemctl restart docker
docker version | grep -i 'minimum version'

# The equivalent, if you would rather leave daemon.json alone:
sudo systemctl edit docker.service
# [Service]
# Environment="DOCKER_MIN_API_VERSION=1.24"

# Upstream is unambiguous about the status of both: API versions older than the
# default "are deprecated and to be removed in a future release", and the
# environment variable and configuration option "should only be used for
# exceptional cases". No removal date is published anywhere. Treat this as a
# bridge of unknown length, put a ticket on it, and do not let it become the
# permanent shape of your fleet.

Une mise en garde sur le contournement, parce que c’est le genre de réglage qui survit à l’incident qui l’a créé. L’amont décrit les anciennes versions d’API comme dépréciées et vouées à disparaître dans une version future, et présente aussi bien la variable d’environnement que la clé de configuration comme réservées aux cas exceptionnels. Aucune date de suppression n’est publiée. La politique de Docker est de conserver une fonctionnalité dépréciée pendant au moins une version stable et de viser plusieurs mois de préavis avant un retrait, donc cela ne s’évaporera pas du jour au lendemain. Mais l’engagement n’est pas « on ne touchera à rien », c’est « au moins une version stable de préavis » — un plancher, pas un plan. Servez-vous-en pour tenir une semaine, pas pour définir une politique.[config][lifecycle]

« Toutes mes images ont disparu » : c’est une réinstallation, pas une mise à jour

Le deuxième problème le plus signalé est le plus alarmant et le moins dangereux : vous installez Docker 29, vous lancez docker images, et la liste est vide. Rien n’a été supprimé. Engine 29 fait du containerd image store le backend par défaut, mais — et c’est la phrase qui résout la quasi-totalité des signalements — uniquement sur les installations neuves. Une mise à jour de paquets en place conserve le pilote overlay2 et continue de vous montrer vos images. Purger les paquets puis les réinstaller compte comme une installation neuve, tout comme reconstruire l’hôte depuis la gestion de configuration, et c’est ainsi que la plupart des gens rencontrent le problème sans avoir le sentiment d’avoir fait quoi que ce soit d’inhabituel.[cstore]

Questionoverlay2 (pilote graph)snapshotter containerd
Quand vous l’obtenezToute mise à jour depuis 28 ou antérieure le conserveInstallations neuves de 29.0+, sauf avec userns-remap
docker info afficheStorage Driver: overlay2Storage Driver: overlayfs et un driver-type valant io.containerd.snapshotter.v1
Où vit le contenuSous data-root, normalement /var/lib/dockerDans le stockage propre à containerd, que data-root ne déplace pas
Empreinte disqueCouches décompressées uniquementCompressées et décompressées, donc nettement plus pour les mêmes images
Images multi-plateformes et attestationsNon supportéesSupportées — la vraie raison du changement de défaut
Basculer de l’un à l’autreMasque les images et conteneurs de l’autre backendPareil en sens inverse. Rien n’est supprimé ; il n’y a pas de conversion en place
# "I upgraded and all my images are gone."
#
# Almost every report of this turns out to be a REINSTALL, not an upgrade.
# Docker 29 makes the containerd image store the default on fresh installations
# only; an in-place package upgrade keeps the overlay2 graph driver. Purging the
# packages and installing them again counts as fresh, and so does rebuilding the
# host from your configuration management.
#
# Nothing has been deleted. The two backends cannot see each other's content:
# switching "temporarily hides images and containers created with the other
# backend. Your data remains on disk."

docker info -f '{{ .DriverStatus }}'
docker system df

# To see the old content again, point the daemon back at where it lives:
# /etc/docker/daemon.json
{
  "features": { "containerd-snapshotter": false },
  "storage-driver": "overlay2"
}
sudo systemctl restart docker
# Understand what this buys you: the legacy graph drivers are themselves now
# deprecated, and Docker states the graph driver backend will be removed in a
# future release. Switching back is a stay of execution, not a destination.

# Moving forward deliberately instead. There is no supported in-place
# conversion: the documented paths are a registry round trip, or save/load.
docker save -o /var/tmp/keep.tar app:1.4 app:1.5
# ... switch the daemon to the containerd store, restart, then:
docker load -i /var/tmp/keep.tar

# An experimental automatic switch exists. Read what it actually does before
# using it: it only fires when there are NO containers at all and the image
# count is at or below the threshold you set.
# /etc/docker/daemon.json
{
  "features": { "containerd-migration": true }
}
# and, for the threshold, a systemd drop-in:
#   Environment="DOCKER_MIGRATE_SNAPSHOTTER_THRESHOLD=5"
# The documentation labels this experimental and tells you to take backups
# first. On a server, exporting the handful of images you actually care about
# is less work than recovering from a migration that half-happened.

# Two operational consequences that surface weeks later, not on day one:
#
#  - The containerd store keeps layers both compressed and uncompressed, so the
#    same images occupy noticeably more disk than they did under overlay2.
#
#  - "data-root" in daemon.json does NOT move containerd's content. If you put
#    /var/lib/docker on its own partition, containerd's storage is configured
#    separately - otherwise it quietly fills the root filesystem instead, which
#    is a page you will read at 03:00 rather than now.
#
#  - The containerd store is unavailable when user-namespace remapping is
#    enabled. That is a known bug, not a policy, and userns-remap hosts are
#    excluded from the fresh-install default for exactly that reason.

Les deux backends ne voient pas le contenu l’un de l’autre. L’amont est explicite : basculer « masque temporairement les images et les conteneurs créés avec l’autre backend » et « vos données restent sur le disque ». Si vous venez de basculer et que la liste paraît vide, ne lancez pas de prune pour récupérer de l’espace — vous supprimeriez la moitié que vous voyez tout en payant pour celle que vous ne voyez pas.

Deux conséquences apparaissent des semaines plus tard plutôt que le jour même. Le containerd image store conserve les couches à la fois compressées et décompressées, donc les mêmes images occupent sensiblement plus de disque qu’avec overlay2. Et data-root dans daemon.json ne déplace pas le contenu de containerd : si vous avez soigneusement mis /var/lib/docker sur son propre volume, le stockage de containerd se configure séparément et remplira sinon le système de fichiers racine. Il y a aussi un cas où le nouveau défaut ne s’applique tout simplement pas — les hôtes qui utilisent le remappage d’espaces de noms utilisateur sont exclus, à cause d’un bug ouvert et non d’une décision de conception. Et soyez lucide sur ce que rebasculer vous achète : les pilotes graph historiques sont eux-mêmes dépréciés, et Docker a annoncé que ce backend serait supprimé dans une version future. Revenir à overlay2, c’est un sursis.[daemon][iss47377]

La limite de descripteurs de fichiers tombe à 1024 dans tous les conteneurs

C’est le changement dont je parierais qu’il est le plus souvent mal diagnostiqué, parce qu’il ne figure pas du tout dans la section des breaking changes. C’est un sous-point sous une montée de version de containerd. Docker Engine 29.0.0 embarque containerd 2.1.5, qui a cessé de définir LimitNOFILE=infinity dans son unité systemd — donc la limite souple par défaut du nombre de fichiers ouverts dans chaque conteneur passe de 1048576 à 1024. Les versions 29.x suivantes embarquent des containerd plus récents, et toutes conservent le nouveau comportement.[iss51485][ctd215]

# The 29.0 change with the widest blast radius is not under a "breaking
# changes" heading at all. It is a sub-bullet under a containerd version bump.

docker run --rm ubuntu:24.04 bash -c 'ulimit -n; ulimit -Hn'
# soft 1048576   with containerd.io 1.7.x (Docker 28 and earlier)
# soft 1024      with containerd.io 2.1.5, shipped with Docker Engine 29.0.0
#
# Only the SOFT limit is documented as changing. The hard limit becomes
# whatever systemd's DefaultLimitNOFILE gives you on that host, so read both
# numbers on your own machine rather than trusting a value from an article.

# What happened: containerd 2.1.5 stopped setting LimitNOFILE=infinity in its
# systemd unit, so containers now inherit systemd's ordinary default. Docker
# Engine made the same change for BUILD containers back in v25.0; version 29
# extends it to every container. The reasoning is sound - with an effectively
# unlimited value, software that sizes its own buffers from `ulimit -n`
# (MySQL is the classic case) could consume the machine - and it is documented
# in the release notes. It is simply not where anyone looks.

# 1024 is a real ceiling, and the failures it produces rarely mention file
# descriptors: an Nginx worker refusing connections at a number that looks
# arbitrary, a JVM selector loop throwing at load, a connection pool that
# stalls under exactly the traffic it handled last week.

# Per container, which is the right place if only one workload needs it:
docker run --ulimit nofile=65535:65535 ...

# Or restore a default for every container on the host:
# /etc/docker/daemon.json
{
  "default-ulimits": {
    "nofile": { "Name": "nofile", "Soft": 65535, "Hard": 65535 }
  }
}

# The release notes show 1048576 in this example, which restores the exact old
# behaviour. Prefer a considered number. Copying 1048576 back wholesale also
# copies back the problem the change was made to solve, and you will not be the
# one who remembers that in two years.

Le raisonnement derrière est bon, et les mainteneurs l’expliquent clairement : une valeur pratiquement illimitée combinée à une particularité du runtime Go pouvait porter la limite souple au niveau de la limite dure, et un logiciel qui dimensionne ses propres tampons à partir de ulimit -n — MySQL est l’exemple récurrent — dévorait alors la machine. Docker avait fait le même changement pour les conteneurs de build dès la v25.0 ; la version 29 l’étend simplement à tous. Le problème n’est pas le changement, c’est que 1024 est un vrai plafond et que les pannes qu’il produit ne mentionnent presque jamais les descripteurs de fichiers. Vous obtenez un worker Nginx qui refuse des connexions à un nombre qui semble arbitraire, un sélecteur JVM qui lâche sous charge, un pool qui cale exactement au trafic qu’il encaissait la semaine dernière. Si vous avez mis à jour et que quelque chose est devenu plus lent ou plus instable sous charge sans qu’aucun code ne change, vérifiez ça en premier.[rel29]

Docker 29 a cessé d’injecter dans les conteneurs les variables d’environnement des legacy links — DB_PORT_5432_TCP_ADDR et sa famille. Elles étaient dépréciées depuis des années et le remplacement, le DNS sur un réseau défini par l’utilisateur, fonctionne depuis 2016. Le problème, c’est que quantité de scripts d’entrypoint et d’assistants de CI les lisent encore, et que l’échec qui en résulte ne dit rien du tout sur Docker. Le cas canonique est GitLab Runner : le conteneur assistant qui attend qu’une entrée services: soit prête sort avec FATAL: No HOST or PORT found, donc le job échoue sur un contrôle de santé alors que le conteneur de service, lui, tourne parfaitement bien.[pr50719][gllinks]

# Docker 29 stopped injecting the legacy link environment variables:
#   DB_PORT_5432_TCP_ADDR, DB_PORT_5432_TCP_PORT, DB_NAME, DB_ENV_*
# They were deprecated years ago. A surprising number of CI images and entry
# point scripts still read them, and the failure does not mention Docker.

# The symptom in a GitLab CI job with a services: block is a health check that
# never passes: the runner's wait-for-service helper exits 1 with
#   FATAL: No HOST or PORT found
# and the job fails before your script runs. The service container itself
# started perfectly well - which is why this looks like an infrastructure
# outage rather than a Docker change.

# Confirm it in two lines. The db container must be on the DEFAULT BRIDGE:
# legacy links do not work on user-defined networks at all.
docker run -d --name db postgres:18
docker run --rm --link db:db alpine env | grep -c '^DB_PORT_'
# 0 on Engine 29, non-zero before it

# The fix is to stop parsing those variables. DNS on a user-defined network has
# worked since 2016, does not need --link at all, and survives restarts:
docker rm -f db
docker network create appnet
docker run -d --name db --network appnet postgres:18
docker run --rm --network appnet postgres:18 pg_isready -h db
# In Compose this is already the default: services on the same project network
# resolve each other by service name.

# The escape hatch, when the image is not yours to change and the release is
# next week. Daemon-side, and explicitly temporary:
sudo systemctl edit docker.service
# [Service]
# Environment="DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS=1"
sudo systemctl restart docker
# Upstream wording: "the escape hatch will be removed in a later version".

Celui-ci a un vrai tranchant : au moment où ces lignes sont écrites, le ticket GitLab Runner correspondant est toujours ouvert, avec pour contournement documenté soit de définir la variable d’environnement de secours sur le démon, soit d’épingler la version de Docker du runner. Si votre CI utilise des conteneurs de service, testez sur un seul runner avant de déployer la mise à jour sur toute la flotte — non pas parce que c’est difficile à corriger, mais parce que la panne apparaît dans tous les jobs d’un coup et ressemble à une panne d’infrastructure plutôt qu’à un changement de configuration.

Les chaînes d’isolation ont été supprimées : c’est un changement d’accessibilité

C’est le changement qui fait le moins de bruit et qui a le plus de conséquences. Engine 29 a retravaillé les règles iptables des réseaux bridge et supprimé entièrement les chaînes DOCKER-ISOLATION-STAGE-1 et DOCKER-ISOLATION-STAGE-2. Les notes de version énoncent les effets sans détour, et ils méritent deux lectures si vous avez déjà considéré des réseaux bridge distincts comme une frontière de sécurité : des conteneurs peuvent désormais joindre des ports publiés sur des adresses de l’hôte par des conteneurs d’autres réseaux lorsque le proxy userland ne tourne pas, ainsi que des ports sur des adresses de conteneurs d’autres réseaux avec le mode de passerelle nat-unprotected.[pr49981][packet]

# 29.0 reworked the iptables rules for bridge networks and removed the
# DOCKER-ISOLATION-STAGE-1 and DOCKER-ISOLATION-STAGE-2 chains. The release
# notes state the two consequences plainly:
#
#   - containers can now access ports published to host addresses by containers
#     in other networks when the userland proxy is not running
#   - containers can now access ports on container addresses in other networks
#     that have gateway mode "nat-unprotected"
#
# If you were using separate bridge networks as a security boundary, re-read
# that. It is a reachability change, and nothing in the upgrade tells you.

# Find every port you publish to a wildcard address. Each one is a port that
# containers in other networks may now reach through the host.
docker ps --format '{{.Names}} {{.Ports}}' | grep '0.0.0.0'

# The durable fix is to stop publishing to the wildcard when you only meant
# localhost. This has always been the correct form and does not depend on any
# chain existing:
docker run -p 127.0.0.1:5432:5432 postgres:18

# In Compose:
#   ports:
#     - "127.0.0.1:5432:5432"

# Check the current shape of the rules rather than the shape you remember:
sudo iptables -S | grep -c 'DOCKER-ISOLATION'   # 0 on Engine 29
sudo iptables -S DOCKER-USER

# DOCKER-USER still exists and still works under the default iptables backend.
# It is only absent if you deliberately switch the daemon to nftables - which
# is the next section, and the answer there is "not on a production host yet".
ComportementJusqu’à 28.xÀ partir de 29.0Quoi faire
Chaînes d’isolation des réseaux bridgeDOCKER-ISOLATION-STAGE-1 et -STAGE-2SuppriméesRevérifiez tout ce qui est publié sur une adresse joker.
Variables d’environnement des legacy linksInjectées automatiquementNon injectéesUtilisez le DNS sur un réseau défini par l’utilisateur.
Chaîne DOCKER-USERPrésentePrésente sous iptables, absente sous nftablesN’activez pas le backend nftables si vous en dépendez.
Règle mangle de checksum SCTPUniquement avec DOCKER_IPTABLES_SCTP_CHECKSUM=1SuppriméeLa variable n’a désormais plus aucun effet.
Passerelle par défaut macvlan et ipvlan-l2DéduiteUniquement si --gateway est dans la config IPAMDéfinissez explicitement la passerelle dans la définition du réseau.
Réseaux overlay chiffrésCassés sur 28.2.2 et 25.0.13–14Cassés de 29.0.0 à 29.2.029.2.1 corrige, mais un nœud corrigé ne peut pas acheminer de trafic vers un nœud non corrigé. Sortez tout le Swarm des builds affectés d’un seul tenant.

Rien dans la mise à jour ne vous dit que c’est arrivé, et rien dans votre supervision non plus — c’est un élargissement de l’accessibilité, pas une erreur. La réponse durable n’est pas de rétablir les chaînes mais d’arrêter de publier sur l’adresse joker quand vous vouliez dire localhost, ce qui a toujours été la forme correcte et ne dépend de l’existence d’aucune chaîne. Tant que vous y êtes, un risque de retour arrière mérite d’être noté : un réseau créé sur 29 en demandant une taille de préfixe aux pools d’adresses par défaut devient inutilisable sur un démon plus ancien et doit être supprimé puis recréé, alors consignez vos définitions de réseaux avant de commencer plutôt qu’après.[pr50114]

nftables existe, reste expérimental, et n’a pas de chaîne DOCKER-USER

Engine 29 a aussi introduit un backend nftables, et il vaut la peine d’être précis sur son statut parce que la couverture qui en a été faite ne l’était pas : il est expérimental et optionnel, le défaut reste iptables, et il ne peut pas être activé du tout tant que le démon est en mode Swarm. L’amont affirme sans détour que les options de configuration, le comportement et l’implémentation peuvent tous changer. Sur une distribution récente, vos règles iptables sont de toute façon exécutées par la machinerie noyau nftables, donc basculer ne vous apporte presque rien aujourd’hui. La raison de le savoir dès maintenant, c’est ce que ça fait à votre pare-feu : dans l’implémentation nftables de Docker il n’y a pas de chaîne DOCKER-USER, et vos règles ne sont pas migrées vers les nouvelles tables. Savoir si elles s’exécutent encore dépend de l’historique : basculer un hôte existant laisse en place l’ancien saut depuis FORWARD, donc elles continuent de s’appliquer jusqu’à ce que ce saut disparaisse ou que l’hôte redémarre, tandis qu’un hôte né sous nftables n’a jamais eu ce saut et les ignore silencieusement. Deux machines à la configuration identique, deux pare-feu différents.[nft][pr50476]

# The nftables backend in 29.x is EXPERIMENTAL and opt-in. The default is still
# iptables, which on a current distribution is usually iptables-nft underneath
# anyway - so you are already using the nftables kernel machinery either way.
# Upstream: "configuration options, behavior and implementation may all change
# in future releases", and it "cannot be enabled when the Docker daemon is
# running in Swarm mode".

docker info 2>/dev/null | grep -i 'firewall'

# Opting in, if you are testing it somewhere that is not production:
# /etc/docker/daemon.json
{
  "firewall-backend": "nftables"
}
sudo systemctl restart docker

sudo nft list tables
# table ip docker-bridges
# table ip6 docker-bridges

# The part that quietly changes your security posture:
# "In Docker's nftables implementation, there is no DOCKER-USER chain."
# Your rules are not migrated. Whether they still run depends on history:
# switching an existing host to nftables leaves the old FORWARD jump to
# DOCKER-USER in place, so those rules keep firing until the jump is removed
# or the host reboots. A host that started on nftables never had the jump, so
# the same rules do nothing at all. Both states look identical in your
# configuration management, which is the dangerous part.
sudo iptables -S FORWARD | grep DOCKER-USER   # if this prints, you have both worlds

# The replacement is your own table, with base chains of the same type and hook
# as Docker's and a LOWER priority number, so yours run first. "filter" is the
# same priority Docker uses, so subtract from it:
#
#   table ip my-filter {
#       chain my-forward {
#           type filter hook forward priority filter - 1; policy accept;
#           iifname "eth0" ip saddr != 192.0.2.2 counter drop
#       }
#   }

# One more difference that catches everybody: in nftables an accept is not
# final, so you cannot permit something Docker drops simply by accepting it
# earlier. Use a firewall mark and tell the daemon to honour it:
#   dockerd --bridge-accept-fwmark=1
#   dockerd --bridge-accept-fwmark=0x1/0x3    # with a mask

github.com/docker/docker n’est plus votre chemin d’import

Si vous consommez l’API Docker depuis Go, la version 29 est une réécriture plutôt qu’une montée de version, et c’est le seul changement de cette page qui ne se contourne pas avec une clé de configuration. Le module github.com/docker/docker est déprécié au profit de github.com/moby/moby/client et github.com/moby/moby/api ; le module parent est désormais explicitement un détail d’implémentation interne, et les releases sont taguées avec un préfixe docker-. En plus du déménagement, l’API cliente elle-même a changé de forme :[rel29]

  • Des structures d’options remplacent les arguments positionnels dans les opérations image, config et prune. Modification mécanique mais large — elle touche presque tous les points d’appel d’une intégration typique.
  • Les valeurs de retour sont encapsulées dans des structures. ImageInspect, ImageHistory, ImageLoad et ImageSave ne renvoient plus ce qu’ils renvoyaient, et ImagePull et ImagePush renvoient des objets qui exposent des itérateurs de messages.
  • Les filtres ont déménagé. Le client a son propre type de filtres, donc tout ce qui importe l’ancien paquet filters demande une refonte et non un simple changement de chemin.
  • Les adresses sont typées. Les adresses IP et les sous-réseaux sont maintenant des netip.Addr et des netip.Prefix au lieu de chaînes et de net.IPNet, ce qui est une vraie amélioration et une vraie matinée de travail.
  • client.ImageCreate a disparu, remplacé par ImagePull ou ImageImport selon ce que vous en faisiez réellement.

cgroup v1 est déprécié, avec une vraie date

Docker 29 déprécie cgroup v1 — et, chose inhabituelle pour une dépréciation, avec une date. L’amont indique que le support continue jusqu’en mai 2029, et que la dernière version de mai 2029 pourrait elle-même ne pas supporter cgroup v1 mais qu’au moins une branche maintenue le fera. Le délai est réellement généreux, et cela veut dire que rien ne s’arrête sur vos serveurs cette année à cause de ça.[deprecated][iss51111]

Il vaut quand même la peine d’agir tôt, pour une raison qui n’a rien à voir avec le calendrier de Docker : votre distribution y arrivera avant. systemd a supprimé les hiérarchies legacy et hybride dans la v258, donc un hôte encore démarré avec systemd.unified_cgroup_hierarchy=0 — en général un paramètre ajouté il y a des années par quelqu’un pour satisfaire un vieux runtime, puis jamais retiré — heurte ce mur au niveau du système d’exploitation bien avant 2029. Le paramètre est trivial à trouver et généralement trivial à retirer ; le désagréable, c’est de le découvrir pendant une fenêtre de maintenance plutôt qu’un mardi ordinaire.[systemd258][cgroupv2]

L’écosystème : ce qui a cassé, et la version qui corrige

Le tableau de compatibilité est la partie de cette histoire qui est vraiment bien documentée, en grande partie parce que Portainer a détaillé les retombées pendant qu’elles se produisaient. La plupart ont été corrigées en quelques semaines. L’intérêt de lire la liste aujourd’hui n’est pas les correctifs, c’est de repérer lesquels vous faites tourner.[portblog]

OutilCe qui a casséCorrigé dans
TraefikLe provider Docker codait en dur l’API 1.24, donc aucune route n’était configurée.3.6.1 — la version codée en dur a été remplacée par la négociation.
PortainerLe client embarqué était plafonné à l’API 1.41 sans négociation, et une vérification stricte de version minimale refusait la connexion ; les environnements apparaissaient injoignables.2.33.5 LTS / 2.36.0 STS.
Testcontainers for Javadocker-java utilisait l’API 1.32 par défaut ; les tests ne trouvaient plus d’environnement Docker valide.2.0.2, qui embarque un docker-java qui négocie.
Docker SDK for PythonDEFAULT_DOCKER_API_VERSION valait 1.41 en 6.1.3.7.1.0 (1.44). Mieux encore : négocier plutôt qu’épingler.
GitLab RunnerVersions incohérentes entre runner, image de job et service DinD ; par ailleurs, les contrôles de santé des services échouent avec No HOST or PORT found.Épinglez l’image DinD ou fixez la version d’API. Le ticket sur le contrôle de santé était encore ouvert à la rédaction.
IDE JetBrainsLe plugin Docker était rejeté par un démon v29.Build du plugin 253.28294.x, livré dans 2025.2.5 et dans les versions 2025.3.
Watchtower (containrrr)Épinglé à l’API 1.25, en boucle de redémarrage face à un démon v29.Pas de correctif amont. Le dépôt a été archivé en lecture seule en décembre 2025.
CapRoverdocker-modem épinglé à l’API 1.43 — une version en dessous du plancher de 29.0.1.14.1.
Ansible community.dockerKeyError: 'ApiVersion' à cause de la casse JSON modifiée dans docker version.Engine 29.0.1 a rétabli les anciens noms de champs.
Docker ComposeLes très vieux builds passent sous le plancher d’API ; aucune matrice de compatibilité n’est publiée.Installez le docker-compose-plugin courant depuis le même dépôt que le moteur.

Le motif est constant : tout ce qui a cassé avait une version d’API codée en dur quelque part au lieu d’en négocier une, et tout ce qui a été corrigé l’a été en ajoutant la négociation. Portainer en est l’illustration la plus claire : son client embarqué était plafonné à l’API 1.41, sans aucune négociation, et une vérification stricte contre le minimum annoncé par le démon refusait purement et simplement la connexion, sur un démon qui aurait été parfaitement disposé à parler à un client qui aurait demandé correctement. GitLab Runner est gênant pour une autre raison — le runner, l’image de job et le service Docker-in-Docker portent chacun leur propre client, et deux d’entre eux peuvent se retrouver de part et d’autre du plancher. La règle pratique y est d’épingler l’image du service DinD à la même version majeure que le démon qui l’exécute, et de changer les deux ensemble.[traefik][testcontainers][glrunner]

Le reste de la liste est un audit correct de votre propre chaîne d’approvisionnement. Watchtower est le cas instructif : épinglé à l’API 1.25, aucun correctif officiel, et le dépôt a été archivé en lecture seule en décembre 2025 — donc un processus non surveillé disposant d’un accès permanent à votre socket Docker est ce qui a cessé de fonctionner, et il n’y a personne pour le corriger. Ça mérite un moment de réflexion indépendamment de Docker 29. Si un outil détient votre socket, son état de maintenance fait partie de votre posture de sécurité, et cette mise à jour a été un audit inhabituellement clair de ceux de vos outils qui ont encore des mainteneurs.[watchtower][portainer][jetbrains]

Mettre à jour, épingler, revenir en arrière

Rien de malin dans la mécanique. Mettre à jour est une transaction apt ou dnf ordinaire avec les cinq paquets habituels, et la méthode documentée pour installer une version précise est la méthode documentée pour revenir à une version précise. Ce qui compte, c’est l’ordre, et savoir à l’avance ce qu’un retour arrière ne défera pas.[installubuntu][installrhel]

# Upgrading is an ordinary package transaction. The care is in the order, and
# in knowing in advance what a rollback will and will not undo.

# --- Debian / Ubuntu ---------------------------------------------------------
apt list --all-versions docker-ce | head
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io \
                 docker-buildx-plugin docker-compose-plugin

# Staying on 28 deliberately - read the last section before choosing this:
sudo apt-mark hold docker-ce docker-ce-cli containerd.io
apt-mark showhold

# Going back. Take the exact version strings from the lists above. Pin
# containerd.io too: leaving it unpinned keeps containerd 2.x and its systemd
# unit, so the file-descriptor change described earlier survives the rollback.
apt list --all-versions containerd.io | head
VERSION_STRING=5:28.5.2-1~ubuntu.24.04~noble
CONTAINERD_STRING=1.7.29-1~ubuntu.24.04~noble
sudo apt install docker-ce="$VERSION_STRING" docker-ce-cli="$VERSION_STRING" \
                 containerd.io="$CONTAINERD_STRING" \
                 docker-buildx-plugin docker-compose-plugin

# --- RHEL / Rocky / AlmaLinux / Fedora ---------------------------------------
dnf list docker-ce --showduplicates | sort -r | head
sudo dnf install docker-ce docker-ce-cli containerd.io \
                 docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
# A specific version, in the same shape as the apt form:
sudo dnf install docker-ce-3:28.5.2-1.el9 docker-ce-cli-3:28.5.2-1.el9 \
                 containerd.io docker-buildx-plugin docker-compose-plugin
# Pinning is a dnf feature, not a Docker one, and the plugin package name
# depends on which dnf you have:
#   RHEL / Rocky / Alma (dnf4):  sudo dnf install python3-dnf-plugin-versionlock
#   Fedora (dnf5):               sudo dnf install dnf5-plugin-versionlock
#   then:                        sudo dnf versionlock add docker-ce docker-ce-cli containerd.io

# Four things a downgrade does not undo:
#
#  1. If the daemon was switched to the containerd image store, a 28.x daemon
#     cannot see those images. They are still on disk; they are not in the
#     graph driver, and there is no conversion.
#  2. A network created on 29 by asking the default pool for a prefix size
#     (--subnet 0.0.0.0/24) is unusable on an older daemon. It has to be
#     deleted and recreated - so record your network definitions before you
#     start, not after.
#  3. Rootless installations from 29.5 onwards no longer receive slirp4netns
#     through Docker packaging. Reinstall it from your distribution if you go
#     back to a build that expects it.
#  4. The container file-descriptor limit, unless you pinned containerd.io as
#     well. That change lives in containerd's systemd unit, not in dockerd.

Encore une chose à savoir si votre automatisation analyse la sortie de Docker plutôt que d’appeler l’API : 29.0.0 a changé la casse des champs dans docker version --format=json, ce qui a cassé la collection Docker d’Ansible avec un KeyError sec. C’est corrigé en 29.0.1, donc cela ne touche que ceux restés sur la toute première version — mais c’est un bon rappel que --format json est une interface avec une version, et qu’épingler une version corrective est une assurance bon marché pour tout ce qui la scrute.[ansible]

Vérifier correctement

« Le démon a démarré » n’est pas une vérification. Lancez ceci avant la mise à jour, gardez la sortie, relancez-le après et comparez les deux — l’idée est que l’essentiel de ce qui a changé en version 29 est un défaut, et qu’un défaut modifié produit une sortie différente plutôt qu’une erreur.

#!/usr/bin/env bash
# docker29-check.sh - Run it BEFORE the upgrade, keep the output, run it again
# afterwards and diff the two.
#
# Everything here only reads state, with one exception: the ulimit probe starts
# a throwaway container and will PULL ubuntu:24.04 if the host does not already
# have it. On a metered link, or on a host where you are watching disk, pull it
# in advance or swap in an image you already have.
set -uo pipefail

echo "=== engine, api floor, client ==="
docker version

echo "=== image store, storage driver, cgroup driver, firewall backend ==="
docker info 2>/dev/null | grep -Ei 'storage driver|driver-type|cgroup|firewall|logging driver|userns'

echo "=== the ulimit that changed underneath you ==="
docker run --rm ubuntu:24.04 bash -c 'ulimit -n; ulimit -Hn'

echo "=== everything that should be running, is ==="
docker ps --format '{{.Names}} {{.Status}} {{.Image}}' | sort

echo "=== images the daemon can actually see, and how much space they take ==="
docker images --format '{{.Repository}}:{{.Tag}}' | sort | head -50
docker system df

echo "=== published ports, from the host's point of view ==="
# -p needs root; without it you get the ports but not the owning process.
sudo ss -tlnp 2>/dev/null

echo "=== packet filtering: which world are we in ==="
sudo iptables -S DOCKER-USER 2>/dev/null || echo "no DOCKER-USER chain"
sudo iptables -S | grep -c DOCKER-ISOLATION
sudo nft list tables 2>/dev/null || echo "nft binary not present"

echo "=== every client that talks to this daemon ==="
# Anything listed here is a candidate for the API floor problem: an agent, a
# management UI, a CI runner, a monitoring exporter.
sudo ss -xp 2>/dev/null | grep docker.sock | sort -u

Deux choses que le script ne peut pas vérifier pour vous. D’abord Compose : il n’existe aucune matrice de compatibilité publiée entre les versions de Compose et les versions de l’API Engine, et le ticket amont qui en demandait une a été fermé comme une simple question, donc la seule approche fiable est d’installer le docker-compose-plugin courant depuis le même dépôt que le moteur plutôt que de spéculer sur le vieux build qui marcherait encore. Ensuite, chaque processus qui détient votre socket Docker — un agent, un exporteur, une interface d’administration, un runner CI. Chacun porte son propre client d’API, et chacun est un candidat pour la première section de cet article.[compose]

Alors, faut-il mettre à jour ?

La réponse est oui, et la raison n’est pas la liste des nouveautés. L’amont marque docker-28.x comme non maintenue, et définit non maintenue ainsi : plus développée activement, n’acceptant plus de contributions, et hors du périmètre des avis de sécurité. La seule autre branche maintenue est 25.0, gardée en vie pour deux distributions aval précises, avec une fin de maintenance attendue en décembre 2026 — dans quatre mois, ce qui en fait un repli et non un plan. Rester sur 28 n’est pas le choix conservateur qu’il paraît être ; c’est faire tourner une version non supportée pour éviter le désagrément d’une version supportée.[branches]

Votre situationLa réponse honnête
Parc Compose mono-hôte, images que vous construisez vous-mêmeMettez à jour. Auditez d’abord ulimit -n dans les conteneurs ; c’est le seul changement susceptible de vous surprendre.
Runners CI en Docker-in-DockerTestez sur un seul runner d’abord. Épinglez l’image du service DinD à la version majeure du démon, et changez les deux ensemble.
Vous dépendez d’une interface d’administration auto-hébergéeVérifiez son état de maintenance avant celui de Docker. Pour plusieurs de ces outils, le bloquant était l’interface, pas le moteur.
Vous maintenez des règles de pare-feu DOCKER-USER maisonMettez à jour — elles continuent de fonctionner sous le backend iptables par défaut. N’activez pas nftables.
Vous vous appuyez sur des réseaux bridge distincts comme frontièreLisez la section réseau avant de mettre à jour, pas après. C’est le changement aux vraies conséquences de sécurité.
Vous restez sur 28 par prudencedocker-28.x n’est plus maintenue en amont et sort du périmètre des avis de sécurité. C’est l’option la plus risquée, pas la plus sûre.
  1. Inventoriez les clients avant le démon. Tout ce qui détient votre socket Docker a son propre client d’API. C’est cette liste — et pas la version du moteur — qui détermine si cette mise à jour est un non-événement ou un après-midi.
  2. Faites l’audit ulimit d’abord, sur 28. Lancez docker run --rm ubuntu:24.04 bash -c 'ulimit -n' aujourd’hui et notez quelles charges de travail y sont sensibles. Ce sont les dix minutes les moins chères de cette page, et le changement le plus susceptible de resurgir en régression de performance mystérieuse quinze jours plus tard.
  3. Mettez à jour en place. Ne réinstallez pas. Une mise à jour en place conserve overlay2 et garde vos images visibles. Si vous reconstruisez les hôtes depuis la gestion de configuration, décidez délibérément quel image store l’hôte reconstruit doit utiliser, parce que le défaut a changé sous ce chemin-là.
  4. Revérifiez vos ports publiés. Les chaînes d’isolation ont disparu. Tout ce que vous publiez sur 0.0.0.0 est désormais joignable depuis des conteneurs d’autres réseaux là où ça ne l’était pas. Basculez sur 127.0.0.1 ceux que vous vouliez garder locaux.
  5. Laissez nftables tranquille. C’est expérimental, ça supprime la chaîne DOCKER-USER, et c’est indisponible en Swarm. Il n’y a encore aucune raison de production d’y passer.

Si vous mettez aussi à niveau le système d’exploitation en dessous, la mise à niveau d’Ubuntu 24.04 vers 26.04 sur serveurs couvre la version qui supprime cgroup v1 purement et simplement et change six autres défauts au passage. Si cette lecture vous a surtout fait vous demander si la plateforme de conteneurs vaut sa complexité, quand ne pas utiliser Kubernetes plaide pour la réponse la plus petite, et les architectures cloud ennuyeuses est l’argumentaire plus long en faveur d’une infrastructure qui change lentement, exprès.

Questions fréquentes

Quelle est la version d’API minimale de Docker Engine 29 ?

Cela dépend de la version corrective, et c’est le détail que la plupart des guides ratent. Engine 29.0.0 a relevé le minimum de 1.24 à 1.44, puis 29.3.0 l’a rabaissé à 1.40. Le défaut actuel dans les sources amont est 1.40, 1.24 n’étant atteignable que par un contournement explicite. Vérifiez votre propre démon avec docker version — la section serveur affiche la version d’API et le minimum sur la même ligne — plutôt que de faire confiance à un chiffre tiré d’un article écrit fin 2025.

Comment corriger « client version is too old. Minimum supported API version is 1.44 » ?

Mettez le client à jour, et c’est presque toujours un outil et non la CLI docker : Traefik 3.6.1, Portainer 2.33.5 LTS ou 2.36.0 STS, Testcontainers for Java 2.0.2, le Docker SDK for Python 7.1.0, CapRover 1.14.1. Si vous ne pouvez vraiment pas le mettre à jour cette semaine, on peut dire au démon d’accepter les anciennes versions en posant "min-api-version": "1.24" dans /etc/docker/daemon.json (il n’existe pas d’option équivalente en ligne de commande) ou DOCKER_MIN_API_VERSION=1.24 dans un drop-in systemd. L’amont présente les deux comme réservés aux cas exceptionnels et annonce que les anciennes versions seront supprimées dans une version future, sans publier de date.

Pourquoi toutes mes images Docker ont-elles disparu après la mise à jour en version 29 ?

Elles n’ont presque certainement pas disparu. Engine 29 fait du containerd image store le défaut sur les installations neuves uniquement, et les deux backends ne voient pas le contenu l’un de l’autre — l’amont décrit la bascule comme masquant les images et les conteneurs tandis que les données restent sur le disque. Si vous avez fait une mise à jour de paquets en place, vous devriez toujours être sur overlay2 et tout voir ; si vous avez purgé puis réinstallé, ou reconstruit l’hôte depuis la gestion de configuration, vous avez récupéré le nouveau défaut. Lancez docker info -f '{{ .DriverStatus }}' pour voir quel backend est actif. Pour retrouver l’ancienne vue, posez "features": {"containerd-snapshotter": false} dans daemon.json et redémarrez. Ne lancez pas docker system prune tant que le point n’est pas clair.

Pourquoi mes conteneurs se mettent-ils à sortir « too many open files » après la mise à jour ?

Parce que la limite de descripteurs de fichiers par défaut dans les conteneurs est passée de 1048576 à 1024. Docker Engine 29 embarque containerd 2.1.5, qui a cessé de définir LimitNOFILE=infinity dans son unité systemd, donc les conteneurs héritent désormais du défaut ordinaire de systemd. Docker avait fait le même changement pour les conteneurs de build en v25.0 et la version 29 l’étend à tous les conteneurs. Corrigez par charge de travail avec --ulimit nofile=65535:65535, ou posez default-ulimits dans daemon.json. Choisissez un nombre que vous pouvez justifier plutôt que de rétablir 1048576 — l’ancienne valeur est précisément ce dont le changement voulait s’éloigner.

Docker 29 casse-t-il mes règles de pare-feu DOCKER-USER ?

Pas sous le backend iptables par défaut, où DOCKER-USER existe toujours et fonctionne toujours. La chaîne ne disparaît que si vous activez délibérément le backend nftables expérimental, où l’amont indique clairement qu’il n’y a pas de chaîne DOCKER-USER ; vos règles ne sont pas migrées vers les nouvelles tables. Il y a un piège dans la façon dont elles cessent de s’appliquer : basculer un hôte existant laisse en place l’ancien saut depuis la chaîne FORWARD, donc les règles continuent de s’appliquer jusqu’à ce que ce saut soit retiré ou que l’hôte redémarre, tandis qu’un hôte fraîchement installé sous nftables les ignore dès le départ. Ce qui a changé pour tout le monde, en revanche, c’est la suppression des chaînes DOCKER-ISOLATION-STAGE-1 et DOCKER-ISOLATION-STAGE-2, qui élargit ce que des conteneurs d’un réseau peuvent joindre dans un autre. C’est un changement d’accessibilité plutôt qu’un changement de pare-feu, et c’est la partie de cette version qui mérite un audit.

Faut-il activer le backend nftables dans Docker 29 ?

Pas sur un hôte de production. Il est explicitement expérimental — l’amont prévient que les options de configuration, le comportement et l’implémentation peuvent tous changer —, il ne peut pas être activé tant que le démon est en mode Swarm, et il supprime la chaîne DOCKER-USER. Sur une distribution moderne, vos règles iptables sont déjà exécutées par la machinerie noyau nftables, donc le gain pratique est aujourd’hui faible. Si vous voulez le tester, faites-le sur un hôte où vous pouvez vous permettre de vous tromper sur le pare-feu.

Est-il prudent de rester sur Docker Engine 28 ?

Moins prudent que de mettre à jour, ce qui est l’inverse de l’impression que ça donne. Le tableau des branches amont marque docker-28.x comme non maintenue, et définit cela comme : plus développée activement, n’acceptant plus de contributions, et hors du périmètre des avis de sécurité. Seules docker-29.x et 25.0 sont maintenues, et 25.0 est gardée en vie pour deux distributions aval précises, avec une fin de maintenance attendue en décembre 2026. Rester sur 28 quinze jours le temps de corriger un client est raisonnable ; en faire une politique revient à faire tourner une version qui ne recevra pas de correctifs de sécurité.

Peut-on revenir de Docker 29 à Docker 28 ?

Oui — installez la version précise plus ancienne avec votre gestionnaire de paquets, exactement comme la documentation d’installation le décrit. Trois choses que ce retour arrière ne défait pas. Si le démon a été basculé sur le containerd image store, un démon 28.x ne peut pas voir ces images ; elles sont sur le disque mais pas dans le pilote graph, et il n’existe pas de conversion. Un réseau créé sur 29 en demandant une taille de préfixe aux pools d’adresses par défaut est inutilisable sur un démon plus ancien et doit être supprimé puis recréé. Et les installations rootless à partir de 29.5 ne reçoivent plus slirp4netns via les paquets Docker, donc réinstallez-le depuis votre distribution si vous revenez à un build qui l’attend.

Docker Engine 29 supporte-t-il encore cgroup v1 ?

Oui. La version 29 le déprécie, mais l’amont s’engage à le supporter jusqu’en mai 2029, et note que même alors au moins une branche maintenue conservera le support. Rien ne s’arrête sur vos serveurs cette année à cause de Docker. La pression vient de l’autre côté : systemd a supprimé les hiérarchies legacy et hybride en amont, donc votre distribution Linux abandonnera cgroup v1 bien avant Docker. Un hôte encore démarré avec systemd.unified_cgroup_hierarchy=0 devrait être nettoyé maintenant, tant que c’est un changement d’une ligne plutôt qu’un bloquant de mise à jour.

Sources

Chaque affirmation ci-dessus remonte à l’une de ces sources. Les notes de version de Docker, la documentation et l’arbre source de moby font autorité pour tout ce qui concerne le moteur lui-même ; les gestionnaires de tickets des projets concernés pour les affirmations de compatibilité. Deux réserves honnêtes sur la liste. La version dans laquelle chaque outil tiers a été corrigé provient en général des notes de version de ce projet plutôt que du rapport de bug lié ici, donc suivez le projet s’il vous faut le confirmer. Et là où l’amont ne publie ni date ni version — la suppression du contournement de version d’API en est le cas notable — cet article le dit au lieu de deviner.

  1. Docker — Docker Engine v29 release notes: the authoritative changelog for every behaviour change described here, including the API floor, the containerd packaging bump and the networking rules
  2. Docker — Docker Engine v29: Foundational Updates for the Future; the announcement post, and the source for the DOCKER_MIN_API_VERSION workaround
  3. Docker — Engine API reference: the version matrix mapping each Engine release to its maximum and minimum API version, which is the only reliable way to know what your daemon will accept
  4. Docker — Deprecated Engine features: the table that carries the cgroup v1 deprecation and its May 2029 support horizon
  5. Docker — containerd image store: fresh installs versus upgrades, how to check which backend is active, and the fact that switching hides rather than deletes content
  6. Docker — Docker and nftables: the experimental backend, the tables it creates, and the statement that there is no DOCKER-USER chain
  7. Docker — Packet filtering and firewalls: the iptables model, the DOCKER-USER chain and the gateway modes referenced in the networking section
  8. Docker — dockerd reference: daemon.json keys, default-ulimits, storage-driver and the feature flags used in this article
  9. Docker — Configure the daemon, including the data directory location and why containerd's storage is configured separately
  10. Docker — Install Docker Engine on Ubuntu: the apt repository, the exact package set and the documented way to install a specific version
  11. Docker — Install Docker Engine on RHEL: the dnf repository and the equivalent version-pinning procedure
  12. Docker — Release lifecycle: the stages Docker applies to features and the notice it commits to before retiring them
  13. moby/moby — Branches and tags: the branch maintenance table showing docker-29.x maintained and docker-28.x unmaintained, and the definition of unmaintained
  14. moby/moby — daemon/config/config.go: the MaxAPIVersion, defaultMinAPIVersion and MinAPIVersion constants, and the comment describing min-api-version as an exceptional-case option
  15. moby/moby #51186 — daemon: raise minimum API version to v1.44, the change that shipped in 29.0.0
  16. moby/moby #52067 — lower minimum API version from v1.44 to v1.40, the change that shipped in 29.3.0 and that most published coverage predates
  17. moby/moby #49981 — the bridge iptables rework that removed the DOCKER-ISOLATION-STAGE-1 and DOCKER-ISOLATION-STAGE-2 chains
  18. moby/moby #50719 — legacy link environment variables are no longer added automatically, with the DOCKER_KEEP_DEPRECATED_LEGACY_LINKS_ENV_VARS escape hatch
  19. moby/moby #50476 — the --bridge-accept-fwmark daemon option that lets a firewall mark override Docker's drop rules
  20. moby/moby #50114 — requesting a prefix size from the default address pools, and the warning that such networks are unusable after a downgrade
  21. moby/moby #51485 — LimitNOFILE is silently changed to the host soft limit with the new containerd: the report, the maintainer's explanation and the resulting release-note text
  22. moby/moby #51111 — the cgroup v1 deprecation tracking issue for Docker Engine
  23. moby/moby #47377 — the userns-remap bug that keeps the containerd image store unavailable when user-namespace remapping is enabled
  24. containerd v2.1.5 — the runtime version packaged with Docker Engine 29, and the origin of the changed LimitNOFILE default
  25. Linux kernel documentation — Control Group v2, the hierarchy Docker will require once cgroup v1 support ends
  26. systemd v258 release notes — the removal of the legacy and hybrid cgroup hierarchies upstream, which is why your Linux distribution will drop cgroup v1 well before Docker does
  27. traefik #12253 — the Docker provider's hardcoded API version 1.24 against a v29 daemon, fixed by moving to version negotiation
  28. portainer #12925 — local Docker environment unreachable on Engine 29, the primary report carrying the maintainers' fixed-version announcement; builds before 2.33.5 capped their Docker client at API 1.41 and checked the daemon's reported minimum strictly
  29. Portainer — Docker v29, and the fall-out: the most complete public inventory of management tools broken by the API floor and their fixed versions
  30. testcontainers-java #11235 — docker-java's default API version rejected by Engine 29, and the properties file workaround
  31. GitLab Runner #39129 — API version mismatches between the runner, the job image and the Docker-in-Docker service on Engine 29
  32. watchtower #2122 — a tool pinned to API version 1.25 whose repository has since been archived read-only, and what that means for anything holding your Docker socket that nobody maintains
  33. docker/docker-py — the Docker SDK for Python, whose DEFAULT_DOCKER_API_VERSION moved from 1.41 in 6.1.3 to 1.44 in 7.1.0
  34. community.docker #1185 — KeyError: 'ApiVersion' from the docker version JSON shape change in 29.0.0, corrected in 29.0.1
  35. JetBrains IJPL-217878 — the IDE Docker plugin rejected by a v29 daemon until the plugin build shipped with the 2025.3 releases
  36. docker/compose #13371 — the absence of a published Compose-to-Engine API compatibility matrix, and why old Compose builds fail against v29

Cet article vous a été utile ?