Перейти к содержимому
← Блог

Docker Engine 29: что ломается

Минимальную версию API двигали дважды, containerd image store включается только при установке с нуля, а лимит открытых файлов внутри контейнеров тихо упал с 1048576 до 1024. Девять месяцев последствий, сверено с upstream.

·17 минут чтения
  • Docker
  • containerd
  • Linux
  • DevOps

Docker Engine 29 вышел в ноябре 2025 года и сломал впечатляющее количество софта, который годами работал без нареканий. Девять месяцев спустя интересна уже не сама поломка — интересно то, что почти всё написанное о ней в первые две недели сегодня неверно: изменение, которое задокументировали все, частично откатили тремя минорными релизами позже. Если вы обновляете Linux-сервер в этом месяце, вы читаете советы, написанные для той версии Docker 29, которой больше не существует.

Схема изменений Docker Engine 29, которые ломают привычную работу: минимальная версия API поднята до 1.44, затем опущена до 1.40, containerd image store как значение по умолчанию при установке с нуля, лимит nofile в контейнерах падает с 1048576 до 1024, переменные legacy links убраны, цепочки изоляции iptables удалены.
Пять изменений. Только первое стоит в релиз-нотах под заголовком «breaking changes»; остальные четыре разбросаны по сборке пакетов, сетям и врезке с предупреждением.

Ниже — то, что ломается на самом деле, в том порядке, в каком вы, скорее всего, с этим столкнётесь, с решением и ссылкой на upstream для каждого пункта. Текст написан для тех, кто держит Docker Engine на Linux-серверах, а не Docker Desktop, где на часть этих вопросов ответы другие. Диагностические команды только читают состояние; всё, что меняет систему, — это правка конфига, установка пакета или рестарт, и это видно сразу. Там, где upstream не опубликовал дату или версию, статья прямо об этом говорит вместо того, чтобы придумывать.

Начинайте с симптома, а не с changelog

На эту страницу почти никто не приходит из changelog. Приходят из текста ошибки, из контейнера, который не стартует, или из панели, которая пишет, что окружение недоступно. Поэтому таблица идёт первой: найдите свой симптом и читайте раздел, который его объясняет.[rel29]

Что вы видитеЧто это на самом делеЧем лечится
client version 1.24 is too old. Minimum supported API version is 1.44Клиент старее планки API у демона. Планка поднялась в 29.0.0 — и опустилась обратно в 29.3.0.Обновите клиент. Override на стороне демона — костыль, а не решение.
client version 1.52 is too new. Maximum supported API version is 1.44Зеркальный случай: новый клиент против старого демона. Почти всегда зафиксированный Docker-in-Docker в CI.Зафиксируйте образ DinD на мажорной версии демона и меняйте их вместе.
docker images пуст после установки 29При установке с нуля containerd image store идёт по умолчанию. Контент overlay2 скрыт, а не удалён.Верните прежний бэкенд либо осознанно сделайте export и re-import.
Контейнеры упираются в потолки по соединениям, которых раньше не былоЛимит nofile по умолчанию внутри контейнеров упал с 1048576 до 1024.--ulimit на контейнер или default-ulimits на демоне.
Джобы CI падают на health-check сервиса; No HOST or PORT foundПеременные окружения legacy links больше не прокидываются, и хелпер, ждущий сервис, его попросту не видит.User-defined сеть и DNS. Переменная-заглушка — временная мера.
Контейнер дотягивается до опубликованного порта, до которого не долженЦепочки DOCKER-ISOLATION-STAGE-1 и -STAGE-2 удалены в 29.0.Публикуйте на 127.0.0.1 вместо wildcard-адреса.
Ваши правила фаервола в DOCKER-USER перестали применятьсяБывает только если вы включили бэкенд nftables — он экспериментальный и включается вручную.Верните обратно либо напишите свою таблицу nftables с меньшим priority.
Сборка на Go падает на github.com/docker/dockerМодуль переехал в github.com/moby/moby/client и .../api.Перепишите импорты; форма клиентского API поменялась заодно.
# 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

Три строки из этой таблицы — одно и то же изменение с разных сторон, а две вообще нигде не описаны там, где вы стали бы искать. Прежде чем что-то трогать, установите три факта, которые определяют, что именно относится к вам.

Планку API двигали дважды, и цифра, которую вы прочитали, скорее всего неверна

Это то самое, знаменитое. Engine 29.0.0 поднял минимальную версию Engine API, на которой демон вообще согласен говорить, до 1.44, что соответствует Docker 25.0. Предыдущей планкой была 1.24 — и она сама появилась только в Docker 25.0, заменив планку 1.12, стоявшую с 2016 года, — то есть это было второе ужесточение за два года и первое, которое кто-то заметил. Любой клиент, у которого версия зашита в код или который так и не научился согласовывать её, перестал работать в момент рестарта демона. Именно поэтому за одну неделю обновление положило Traefik, Portainer, Testcontainers, Watchtower, полдюжины self-hosted панелей и изрядное количество CI-пайплайнов.[pr51186][blog29]

А вот часть, которой почти нет ни в одной опубликованной статье. В 29.3.0, вышедшем в марте 2026 года, upstream снова опустил планку — с 1.44 обратно до 1.40, что соответствует Docker 19.03. Значение по умолчанию в текущих исходниках — 1.40, а 1.24 остаётся достижимым только через явный override. То есть клиент с API 1.41 или 1.43, который отвергали в декабре, сегодня снова работает, а гайд, где написано, что минимум — 1.44, описывает первые три минорные линейки серии, которая давно ушла далеко вперёд. Смотрите на свой демон, а не на то, что о нём помнит интернет: соответствие релизов и версий API опубликовано, и это единственная версия этой истории, которая остаётся верной.[pr52067][config][apimatrix]

Версия демонаМинимальная версия APIЧто это отвергает
25.0 – 28.x1.24Фактически ничего. У более старых движков планка была 1.12 и не менялась с 2016 года.
29.0.0 – 29.2.x1.44Каждый клиент старее Docker 25.0. Это и есть та волна поломок, о которой все писали.
29.3.0 и новее1.40Клиенты старее Docker 19.03. Существенное послабление, о котором почти не упоминают.
Любая, с overrideвплоть до 1.24Флага командной строки нет: только ключ в daemon.json или DOCKER_MIN_API_VERSION. Upstream называет это опцией для исключительных случаев, дата удаления не опубликована.
# 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.

Одно предупреждение про override, потому что это ровно та настройка, которая переживает инцидент, ради которого её поставили. Upstream называет старые версии API устаревшими и обещает удалить их в одном из будущих релизов, а и переменную окружения, и ключ конфигурации описывает как средство исключительно для исключительных случаев. Даты удаления нет. Политика самого Docker — держать устаревшую возможность как минимум один стабильный релиз и предупреждать об отключениях за месяцы, так что за одну ночь это не исчезнет, — но «как минимум один релиз» это нижняя граница, а не план. Используйте, чтобы прожить неделю, а не чтобы закрепить это в политике.[config][lifecycle]

«Все образы пропали» — это переустановка, а не обновление

Вторая по частоте жалоба — самая пугающая и самая безобидная: вы ставите Docker 29, запускаете docker images, а список пуст. Ничего не удалено. Engine 29 делает containerd image store бэкендом по умолчанию, но — и вот эта фраза закрывает почти каждое обращение — только при установке с нуля. Обновление пакетов на месте оставляет graph driver overlay2 и продолжает показывать ваши образы. А вот purge пакетов с последующей установкой считается установкой с нуля, как и пересборка хоста из системы управления конфигурацией — именно так большинство и попадает сюда, будучи уверенными, что не делали ничего необычного.[cstore]

Вопросoverlay2 (graph driver)containerd snapshotter
Когда вы его получаетеЛюбое обновление с 28 и старее его сохраняетУстановка 29.0+ с нуля, кроме случая userns-remap
docker info показываетStorage Driver: overlay2Storage Driver: overlayfs и driver-type со значением io.containerd.snapshotter.v1
Где лежит контентПод data-root, обычно /var/lib/dockerВ собственном хранилище containerd, которое data-root не переносит
Занимаемое местоТолько распакованные слоиИ сжатые, и распакованные — заметно больше для тех же образов
Мультиплатформенные образы и аттестацииНе поддерживаютсяПоддерживаются — собственно, ради этого дефолт и поменяли
Переключение между нимиСкрывает образы и контейнеры другого бэкендаТо же самое в обратную сторону. Ничего не удаляется; конвертации на месте нет
# "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.

Два бэкенда не видят содержимое друг друга. Upstream говорит прямо: переключение «временно скрывает образы и контейнеры, созданные другим бэкендом», при этом «ваши данные остаются на диске». Если вы только что переключились и список выглядит пустым, не начинайте prune, чтобы освободить место, — вы удалите ту половину, которую видите, продолжая платить за ту, которую не видите.

Два последствия всплывают не в день обновления, а через недели. Containerd-хранилище держит слои и в сжатом, и в распакованном виде, поэтому одни и те же образы занимают ощутимо больше диска, чем занимали под overlay2. И data-root в daemon.json не переносит контент containerd: если вы аккуратно вынесли /var/lib/docker на отдельный том, хранилище containerd настраивается отдельно и иначе забьёт корневую файловую систему. Есть и один случай, когда новое значение по умолчанию просто не применяется: хосты с user-namespace remapping исключены, и это следствие открытого бага, а не осознанного решения. И трезво оценивайте, что даёт возврат назад: сами legacy graph drivers объявлены устаревшими, и Docker уже сказал, что этот бэкенд удалят в одном из будущих релизов. Вернуться на overlay2 — это отсрочка приговора, а не отмена.[daemon][iss47377]

Лимит файловых дескрипторов в контейнерах упал до 1024

Вот это изменение, готов поспорить, диагностируют неверно чаще всего — потому что его вообще нет в разделе breaking changes. Это подпункт под строчкой об обновлении containerd. В Docker Engine 29.0.0 был упакован containerd 2.1.5, который перестал выставлять LimitNOFILE=infinity в своём systemd-юните, — и мягкий (soft) лимит открытых файлов по умолчанию внутри каждого контейнера падает с 1048576 до 1024. В более поздних релизах 29.x идут более свежие версии containerd, и все они сохраняют новое поведение.[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.

Логика за этим здравая, и мейнтейнеры объясняют её понятно: фактически безлимитное значение в связке с особенностью рантайма Go могло поднять soft limit до hard limit, и софт, который считает размеры своих буферов от ulimit -n — классический пример MySQL, — после этого съедал машину. Для build-контейнеров Docker сделал то же самое ещё в v25.0; версия 29 просто распространила это на все остальные. Проблема не в изменении, а в том, что 1024 — это реальный потолок, и отказы, которые он порождает, почти никогда не упоминают файловые дескрипторы. Вы получаете воркер Nginx, отказывающийся принимать соединения на числе, которое выглядит случайным; селектор JVM, падающий под нагрузкой; пул, который встаёт ровно на том трафике, который держал на прошлой неделе. Если после обновления что-то стало медленнее или нестабильнее под нагрузкой без единой правки в коде, проверьте это первым делом.[rel29]

Docker 29 перестал прокидывать в контейнеры переменные окружения legacy links — DB_PORT_5432_TCP_ADDR и всё её семейство. Они устарели много лет назад, а замена — DNS в user-defined сети — работает с 2016 года. Беда в том, что уйма entrypoint-скриптов контейнеров и CI-хелперов их до сих пор читает, и получившаяся ошибка не говорит о Docker вообще ничего. Канонический случай — GitLab Runner: вспомогательный контейнер, который ждёт подъёма записи services:, завершается с FATAL: No HOST or PORT found, и джоба падает на health-check, притом что сам service-контейнер прекрасно работает.[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".

Здесь есть настоящий острый угол: на момент написания соответствующий issue у GitLab Runner всё ещё открыт, а задокументированный обходной путь — либо выставить на демоне переменную-заглушку, либо зафиксировать версию Docker у раннера. Если ваш CI поднимает service-контейнеры, проверьте это на одном раннере, прежде чем катить обновление по всему флоту, — не потому что чинится сложно, а потому что отказ появляется сразу во всех джобах и выглядит как авария инфраструктуры, а не как смена конфигурации.

Цепочки изоляции удалены, а это изменение доступности

Это изменение с наименьшим шумом и наибольшими последствиями. Engine 29 переработал правила iptables для bridge-сетей и полностью удалил цепочки DOCKER-ISOLATION-STAGE-1 и DOCKER-ISOLATION-STAGE-2. Релиз-ноты описывают эффект прямым текстом, и его стоит перечитать дважды, если вы когда-либо считали раздельные bridge-сети границей безопасности: контейнеры теперь могут дотянуться до портов, опубликованных на адресах хоста контейнерами из других сетей, когда userland-прокси не запущен, а также до портов на адресах контейнеров в других сетях при режиме шлюза 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".
ПоведениеДо 28.x включительноС 29.0Что делать
Цепочки изоляции bridge-сетейDOCKER-ISOLATION-STAGE-1 и -STAGE-2УдаленыПерепроверьте всё, что опубликовано на wildcard-адрес.
Переменные окружения legacy linksПрокидываются автоматическиНе прокидываютсяИспользуйте DNS в user-defined сети.
Цепочка DOCKER-USERЕстьЕсть под iptables, отсутствует под nftablesНе включайте бэкенд nftables, если вы на неё полагаетесь.
Правило mangle для контрольных сумм SCTPТолько при DOCKER_IPTABLES_SCTP_CHECKSUM=1УбраноТеперь переменная не влияет вообще ни на что.
Шлюз по умолчанию для macvlan и ipvlan-l2Выводится автоматическиТолько если --gateway задан в конфиге IPAMЗадавайте шлюз явно в определении сети.
Шифрованные overlay-сетиСломаны в 28.2.2 и 25.0.13–14Сломаны с 29.0.0 по 29.2.0Починено в 29.2.1, но исправленный узел не может нести трафик к неисправленному. Уводите весь Swarm с затронутых сборок разом.

Ни обновление, ни ваш мониторинг об этом не скажут — это расширение доступности, а не ошибка. Долгоиграющее решение — не возвращать цепочки, а перестать публиковать на wildcard-адрес там, где имелся в виду localhost; это всегда было правильной формой и не зависит от существования какой-либо цепочки. Заодно запишите один риск отката: сеть, созданная на 29 через запрос размера префикса из default address pools, становится непригодной на старом демоне, её придётся удалить и создать заново, — так что зафиксируйте определения своих сетей до начала работ, а не после.[pr50114]

nftables существует, он экспериментальный и без цепочки DOCKER-USER

Engine 29 заодно завёз бэкенд nftables, и про его статус стоит говорить точно, потому что публикации точностью не отличались: он экспериментальный и включается вручную, по умолчанию остаётся iptables, и его вообще нельзя включить, пока демон в режиме Swarm. Upstream прямо пишет, что опции конфигурации, поведение и реализация могут измениться. На современном дистрибутиве ваши правила iptables и так исполняются ядерной машинерией nftables, поэтому переключение сегодня почти ничего не даёт. Знать о нём сейчас стоит из-за того, что оно делает с вашим фаерволом: в реализации nftables у Docker цепочки DOCKER-USER нет, и ваши правила не переносятся в новые таблицы. А вот срабатывают они или нет — зависит от истории хоста: при переключении уже работающего хоста старый переход из FORWARD остаётся на месте, поэтому правила продолжают отрабатывать, пока этот переход не уберут или пока хост не перезагрузят, а хост, который сразу поднялся на nftables, этого перехода никогда не имел и молча их игнорирует. Две машины с одинаковой конфигурацией — два разных фаервола.[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 больше не ваш import path

Если вы работаете с Docker API из Go, версия 29 — это не обновление, а переписывание, и это единственное изменение на этой странице, которое нельзя обойти ключом конфигурации. Модуль github.com/docker/docker объявлен устаревшим в пользу github.com/moby/moby/client и github.com/moby/moby/api; родительский модуль теперь явно считается внутренней деталью реализации, а релизы тегируются с префиксом docker-. Помимо переезда изменилась и форма самого клиентского API:[rel29]

  • Позиционные аргументы заменены на options-структуры в операциях с образами, конфигами и prune. Правка механическая, но широкая — она задевает почти каждое место вызова в типичной интеграции.
  • Возвращаемые значения завёрнуты в структуры. ImageInspect, ImageHistory, ImageLoad и ImageSave возвращают уже не то, что раньше, а ImagePull и ImagePush отдают объекты с итераторами сообщений.
  • Фильтры переехали. У клиента теперь свой тип фильтров, поэтому всё, что импортировало старый пакет filters, придётся рефакторить, а не просто переуказывать путь.
  • Адреса стали типизированными. IP-адреса и подсети теперь netip.Addr и netip.Prefix вместо строк и net.IPNet — это честное улучшение и честно потерянное утро.
  • client.ImageCreate удалён, вместо него ImagePull или ImageImport — смотря что вы им на самом деле делали.

cgroup v1 объявлен устаревшим, причём с конкретной датой

Docker 29 объявляет cgroup v1 устаревшим — и, что для deprecation необычно, с датой. Upstream пишет, что поддержка сохраняется до мая 2029 года и что последний релиз в мае 2029-го уже может не поддерживать cgroup v1, но хотя бы одна поддерживаемая ветка поддержку сохранит. Запас честно щедрый, и это значит, что в этом году на ваших серверах из-за этого ничего не сломается.[deprecated][iss51111]

Заняться этим всё равно стоит заранее, и по причине, никак не связанной с планами Docker: ваш дистрибутив доберётся туда первым. systemd убрал legacy- и hybrid-иерархии в v258, поэтому хост, до сих пор загружаемый с systemd.unified_cgroup_hierarchy=0 — обычно этот параметр кто-то дописал годы назад ради старого рантайма и больше не убирал, — упрётся в стену на уровне операционной системы задолго до 2029 года. Параметр тривиально ищется и обычно тривиально убирается; неприятно только обнаружить его в окно обслуживания, а не в обычный вторник.[systemd258][cgroupv2]

Экосистема: что сломалось и в какой версии починили

Таблица совместимости — самая хорошо задокументированная часть этой истории, во многом потому, что Portainer подробно расписал последствия прямо по ходу дела. Большинство пунктов починили за недели. Ценность чтения списка сейчас не в фиксах, а в том, чтобы заметить, что из этого крутится у вас.[portblog]

ИнструментЧто сломалосьПочинено в
TraefikВ Docker-провайдере версия API 1.24 была зашита в код, так что маршруты не настраивались вообще.3.6.1 — зашитую версию заменили на согласование.
PortainerВстроенный клиент был ограничен API 1.41 без согласования версии, а строгая проверка минимальной версии отказывала в подключении; окружения показывались недоступными.2.33.5 LTS / 2.36.0 STS.
Testcontainers for Javadocker-java по умолчанию брал API 1.32; тесты не находили валидное окружение Docker.2.0.2, где идёт docker-java с согласованием.
Docker SDK for PythonВ 6.1.3 DEFAULT_DOCKER_API_VERSION был равен 1.41.7.1.0 (1.44). А лучше согласовывать, а не фиксировать.
GitLab RunnerРасхождение версий между раннером, образом джобы и сервисом DinD; отдельно — health-check сервисов падает с No HOST or PORT found.Зафиксируйте образ DinD или задайте версию API. Issue по health-check на момент написания был открыт.
IDE от JetBrainsDocker-плагин отвергался демоном v29.Сборка плагина 253.28294.x, приехала в 2025.2.5 и в релизах 2025.3.
Watchtower (containrrr)API зашит в 1.25, против демона v29 уходил в цикл рестартов.Фикса от upstream нет. Репозиторий заархивирован только для чтения в декабре 2025 года.
CapRoverdocker-modem зафиксирован на API 1.43 — на одну версию ниже планки 29.0.1.14.1.
Ansible community.dockerKeyError: 'ApiVersion' из-за изменившегося регистра в JSON у docker version.Engine 29.0.1 вернул прежние имена полей.
Docker ComposeОчень старые сборки оказываются ниже планки API; матрица совместимости не публикуется.Ставьте актуальный docker-compose-plugin из того же репозитория, что и движок.

Картина здесь однородная: всё, что сломалось, где-то держало зашитую в код версию API вместо того, чтобы её согласовывать, и всё, что починили, починили добавлением согласования. Portainer — самая наглядная иллюстрация: его встроенный клиент был ограничен сверху API 1.41 и версию вообще не согласовывал, а строгая проверка против заявленного демоном минимума попросту отказывала в подключении — к демону, который был бы совершенно не против поговорить с клиентом, спросившим как положено. GitLab Runner неудобен по другой причине: у раннера, образа джобы и сервиса Docker-in-Docker свой клиент у каждого, и любые двое из них могут оказаться по разные стороны планки. Практическое правило здесь — фиксировать образ сервиса DinD на той же мажорной версии, что и демон, который его запускает, и менять их вместе.[traefik][testcontainers][glrunner]

Остаток списка — неплохой аудит вашей собственной цепочки поставок. Watchtower тут показателен: версия API зашита в 1.25, официального фикса нет, а репозиторий в декабре 2025 года заархивировали в режиме только для чтения — то есть перестал работать процесс без присмотра с постоянным доступом к вашему Docker-сокету, и чинить его некому. Об этом стоит подумать отдельно от Docker 29. Если у инструмента есть ваш сокет, статус его сопровождения — часть вашей безопасности, и это обновление оказалось на редкость наглядной проверкой того, у каких ваших инструментов ещё остались мейнтейнеры.[watchtower][portainer][jetbrains]

Обновление, фиксация версии и путь назад

Механика здесь ничем не хитра. Обновление — обычная транзакция apt или dnf со стандартными пятью пакетами, а задокументированный способ поставить конкретную версию и есть задокументированный способ к ней вернуться. Важен порядок и понимание заранее, чего откат не отменит.[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.

Ещё одно, если ваша автоматизация парсит вывод Docker, а не ходит в API: в 29.0.0 поменялся регистр полей в docker version --format=json, из-за чего Docker-коллекция Ansible падала с голым KeyError. Это поправили в 29.0.1, так что задевает только тех, кто до сих пор сидит на самом первом релизе, — но это хорошее напоминание, что --format json — это интерфейс со своей версией, и фиксация патч-релиза дёшево страхует всё, что этот вывод разбирает.[ansible]

Проверка, как положено

«Демон запустился» — это не проверка. Прогоните это до обновления, сохраните вывод, прогоните ещё раз после и сравните — смысл в том, что большая часть изменений 29-й версии это значения по умолчанию, а изменившийся дефолт даёт другой вывод, а не ошибку.

#!/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

Две вещи скрипт за вас не проверит. Первое — Compose: опубликованной матрицы совместимости между версиями Compose и версиями Engine API не существует, а upstream-issue с просьбой её сделать закрыли как вопрос, поэтому единственный надёжный подход — ставить актуальный docker-compose-plugin из того же репозитория, что и движок, а не гадать, какая старая сборка ещё сработает. Второе — каждый процесс, который держит ваш Docker-сокет: агент, экспортёр, панель управления, CI-раннер. У каждого свой API-клиент, и каждый — кандидат в первый раздел этой статьи.[compose]

Так обновляться или нет?

Ответ — да, и дело не в списке фич. Upstream помечает docker-28.x как unmaintained и расшифровывает это так: активная разработка прекращена, контрибуции не принимаются, ветка не входит в область действия security-адвизори. Единственная другая поддерживаемая ветка — 25.0, её держат живой ради двух конкретных дистрибутивов ниже по течению, и ожидаемое окончание поддержки — декабрь 2026 года, то есть через четыре месяца, что делает её запасным выходом, а не планом. Остаться на 28 — не тот консервативный выбор, каким он кажется; это работа на неподдерживаемой версии ради того, чтобы не связываться с поддерживаемой.[branches]

Ваша ситуацияЧестный ответ
Один хост, Compose, образы собираете самиОбновляйтесь. Сначала проверьте ulimit -n в контейнерах; это единственное, что может вас удивить.
CI-раннеры с Docker-in-DockerСначала проверьте на одном раннере. Зафиксируйте образ сервиса DinD на мажорной версии демона и меняйте их вместе.
Вы зависите от self-hosted панели управленияПроверьте статус её сопровождения раньше, чем статус Docker. У части этих инструментов блокером оказался интерфейс, а не движок.
Вы держите свои правила фаервола в DOCKER-USERОбновляйтесь — под дефолтным бэкендом iptables они продолжают работать. На nftables не переходите.
Вы полагаетесь на раздельные bridge-сети как на границуПрочитайте раздел про сеть до обновления, а не после. Это изменение с реальными последствиями для безопасности.
Вы остаётесь на 28 из соображений безопасностиdocker-28.x в upstream не сопровождается и не покрывается security-адвизори. Это более рискованный вариант, а не более безопасный.
  1. Сначала инвентаризация клиентов, потом демон. У всего, что держит ваш Docker-сокет, свой API-клиент. Именно этот список, а не версия движка, решает, будет обновление незаметным или займёт полдня.
  2. Аудит ulimit сделайте первым, ещё на 28. Запустите docker run --rm ubuntu:24.04 bash -c 'ulimit -n' сегодня и выпишите, каким нагрузкам это важно. Это самые дешёвые десять минут на этой странице и изменение, которое вероятнее всего вылезет через две недели загадочной деградацией производительности.
  3. Обновляйтесь на месте. Не переустанавливайте. Обновление на месте оставляет overlay2 и ваши образы видимыми. Если вы пересобираете хосты из системы управления конфигурацией, осознанно решите, какое хранилище образов должно быть у пересобранного хоста, потому что дефолт на этом пути поменялся.
  4. Перепроверьте опубликованные порты. Цепочек изоляции больше нет. Всё, что вы публикуете на 0.0.0.0, теперь достижимо из контейнеров других сетей там, где раньше не было. То, что задумывалось локальным, переведите на 127.0.0.1.
  5. nftables не трогайте. Он экспериментальный, он убирает цепочку DOCKER-USER, и он недоступен в Swarm. Производственных причин включать его пока нет.

Если вы заодно обновляете операционную систему под ним, обновление Ubuntu 24.04 до 26.04 на серверах разбирает релиз, который выпиливает cgroup v1 целиком и попутно меняет ещё шесть значений по умолчанию. Если после этого чтения вы в основном задумались, стоит ли контейнерная платформа своей сложности, когда не нужен Kubernetes — аргумент в пользу решения поменьше, а скучные облачные архитектуры — развёрнутый довод в пользу инфраструктуры, которая меняется медленно и намеренно.

Частые вопросы

Какая минимальная версия API в Docker Engine 29?

Зависит от патч-релиза, и именно эту деталь большинство гайдов упускает. Engine 29.0.0 поднял минимум с 1.24 до 1.44, а 29.3.0 снова опустил его до 1.40. Текущее значение по умолчанию в исходниках upstream — 1.40, а 1.24 достижим только через явный override. Проверяйте свой демон командой docker version — в секции сервера версия API и минимум печатаются в одной строке — вместо того чтобы доверять числу из статьи, написанной в конце 2025 года.

Как исправить ошибку «client version is too old. Minimum supported API version is 1.44»?

Обновить клиент, а это почти всегда не CLI docker, а какой-то инструмент: Traefik 3.6.1, Portainer 2.33.5 LTS или 2.36.0 STS, Testcontainers for Java 2.0.2, Docker SDK for Python 7.1.0, CapRover 1.14.1. Если обновить на этой неделе действительно невозможно, демону можно велеть принимать старые версии: "min-api-version": "1.24" в /etc/docker/daemon.json (эквивалентного флага командной строки нет) либо DOCKER_MIN_API_VERSION=1.24 в systemd drop-in. Upstream называет оба варианта средством для исключительных случаев и обещает удалить старые версии в будущем релизе, не публикуя дату.

Почему после обновления до версии 29 пропали все образы Docker?

Почти наверняка они никуда не делись. Engine 29 делает containerd image store дефолтом только при установке с нуля, а два бэкенда не видят содержимое друг друга — upstream описывает переключение как сокрытие образов и контейнеров, при том что данные остаются на диске. Если вы обновляли пакеты на месте, вы по-прежнему на overlay2 и должны видеть всё; если делали purge с переустановкой или пересобирали хост из системы управления конфигурацией — вы получили новый дефолт. Посмотрите активный бэкенд командой docker info -f '{{ .DriverStatus }}'. Чтобы вернуть прежнюю картину, задайте "features": {"containerd-snapshotter": false} в daemon.json и перезапустите демон. И не запускайте docker system prune, пока не разобрались.

Почему после обновления контейнеры начали ловить «too many open files»?

Потому что лимит файловых дескрипторов по умолчанию внутри контейнеров упал с 1048576 до 1024. В Docker Engine 29 упакован containerd 2.1.5, который перестал выставлять LimitNOFILE=infinity в своём systemd-юните, так что контейнеры теперь наследуют обычный дефолт systemd. Для build-контейнеров Docker сделал то же самое в v25.0, а версия 29 распространила это на все контейнеры. Лечится по нагрузкам через --ulimit nofile=65535:65535 либо через default-ulimits в daemon.json. Выбирайте число, которое можете обосновать, а не возвращайте 1048576 — от старого значения как раз и уходили.

Ломает ли Docker 29 мои правила фаервола в DOCKER-USER?

Под дефолтным бэкендом iptables — нет, DOCKER-USER там по-прежнему есть и по-прежнему работает. Цепочка исчезает, только если вы сознательно включили экспериментальный бэкенд nftables, где upstream прямо пишет, что цепочки DOCKER-USER нет, а ваши правила не переносятся. В том, как именно они перестают применяться, есть ловушка: при переключении уже работающего хоста старый переход из цепочки FORWARD остаётся на месте, поэтому правила продолжают срабатывать, пока этот переход не уберут или пока хост не перезагрузят, а свежеустановленный хост на nftables игнорирует их с самого начала. А вот что изменилось для всех — удалены цепочки DOCKER-ISOLATION-STAGE-1 и DOCKER-ISOLATION-STAGE-2, и это расширяет то, до чего контейнеры одной сети могут дотянуться в другой. Это изменение доступности, а не фаервола, и именно эту часть релиза стоит проаудитить.

Стоит ли включать бэкенд nftables в Docker 29?

На боевом хосте — нет. Он явно помечен как экспериментальный: upstream предупреждает, что опции конфигурации, поведение и реализация могут измениться. Его нельзя включить, пока демон в режиме Swarm, и он убирает цепочку DOCKER-USER. На современном дистрибутиве ваши правила iptables и так исполняет ядерная машинерия nftables, так что практический выигрыш сегодня невелик. Если всё же хотите потестировать — делайте это на хосте, где можно позволить себе ошибиться с фаерволом.

Безопасно ли остаться на Docker Engine 28?

Менее безопасно, чем обновиться, — ровно наоборот тому, как это ощущается. В таблице веток upstream docker-28.x помечен как unmaintained, и это определяется как «активная разработка прекращена, контрибуции не принимаются, ветка вне области действия security-адвизори». Поддерживаются только docker-29.x и 25.0, причём 25.0 держат живой ради двух конкретных дистрибутивов ниже по течению, и ожидаемое окончание её поддержки — декабрь 2026 года. Зафиксировать 28 на пару недель, пока чините клиент, разумно; закрепить это как политику — значит работать на версии, которая не получит исправлений безопасности.

Можно ли откатиться с Docker 29 на 28?

Да — поставьте конкретную старую версию пакетным менеджером ровно так, как описано в документации по установке. Три вещи откат не отменит. Если демон был переключён на containerd image store, демон 28.x этих образов не увидит: они на диске, но не в graph driver, и конвертации нет. Сеть, созданная на 29 через запрос размера префикса из default address pools, непригодна на старом демоне — её нужно удалить и создать заново. И rootless-установки начиная с 29.5 больше не получают slirp4netns через пакеты Docker, так что поставьте его из дистрибутива, если возвращаетесь на сборку, которая его ожидает.

Поддерживает ли Docker Engine 29 cgroup v1?

Да. Версия 29 объявляет его устаревшим, но upstream обязуется поддерживать его до мая 2029 года и отмечает, что даже тогда как минимум одна поддерживаемая ветка сохранит поддержку. В этом году из-за Docker на ваших серверах ничего не остановится. Давление придёт с другой стороны: systemd убрал legacy- и hybrid-иерархии в upstream, поэтому ваш дистрибутив Linux выкинет cgroup v1 заметно раньше Docker. Хост, до сих пор загружаемый с systemd.unified_cgroup_hierarchy=0, стоит вычистить сейчас, пока это правка в одну строку, а не блокер обновления.

Источники

Релиз-ноты Docker, документация и дерево исходников moby — авторитет по всему, что касается самого движка; трекеры затронутых проектов — по заявлениям о совместимости. Две честные оговорки к списку. Версия, в которой починили каждый сторонний инструмент, как правило взята из релиз-нот самого проекта, а не из связанного здесь баг-репорта, так что за подтверждением идите к проекту. И там, где upstream не публикует ни даты, ни версии — показательный случай это удаление override для версии API, — статья так и пишет, а не гадает.

  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

Было полезно?