update.sh mailcow спотыкается о новый Docker: IPv6-флаги, две установки Compose и старый клиент API
Обновление Docker Engine на сервере с mailcow иногда ломает не почту, а сам update.sh — скрипт обновления начинает требовать настройки IPv6, которые Docker давно сделал автоматическими, путается между двумя установками Compose или падает с ошибкой несовместимости версии API. Разбираю три разные, но похожие по симптомам причины и показываю, как определить свою по версии Docker, прежде чем менять конфигурацию.
Симптом: update.sh упал после планового обновления Docker
Языковая школа «Речь без границ» (19 рабочих мест) держит почтовый сервер на mailcow на выделенной VPS, которую системный администратор регулярно обновляет вместе с пакетами ОС — в том числе Docker Engine. После одного из плановых обновлений системы штатный ./update.sh перестал запускаться: скрипт останавливался на проверке конфигурации Docker с требованием включить настройки, которых раньше не было, хотя сама почта продолжала работать в уже запущенных контейнерах без единого сбоя.
Это типичная ловушка: mailcow активно развивается вместе с Docker, а Docker Engine за последние два года несколько раз менял поведение IPv6 по умолчанию, требования к клиенту Compose и минимальную поддерживаемую версию API. Если версия mailcow отстаёт от версии Docker (или наоборот — Docker обновили раньше, чем вышел соответствующий релиз mailcow), update.sh начинает либо требовать давно ненужные настройки, либо не находить нужный бинарник, либо вовсе не может подключиться к Docker daemon. Три причины дают очень похожие на первый взгляд ошибки, но чинятся по-разному, и с ходу перепутать их легко.
Прежде чем разбирать каждую причину отдельно, скажу сразу: ни одна из трёх не требует отката Docker на старую версию или переустановки mailcow с нуля. Во всех трёх случаях сервер с уже запущенными контейнерами продолжает принимать и отправлять почту — под угрозой оказывается только сам процесс обновления, а не текущая работоспособность сервиса, и это даёт время разобраться спокойно, а не в режиме аварии.
Причина 1: update.sh требует experimental, ip6tables и fixed-cidr-v6 на новом Docker
Самый частый сценарий: update.sh останавливается с требованием добавить в /etc/docker/daemon.json параметры вроде "experimental": true, "ip6tables": true и "fixed-cidr-v6", хотя администратор точно знает, что на сервере стоит свежий Docker. Причина в том, что эти параметры исторически были нужны для работы IPv6-контейнеров mailcow, но начиная с Docker Engine 27.0.1 (24 июня 2024 года) ip6tables перестал быть экспериментальной функцией и включается для Linux bridge-сетей по умолчанию — то есть флаг experimental для этого больше не нужен.
Похожая история с fixed-cidr-v6: начиная с Docker Engine 28.0.0 демон умеет использовать --ipv6 без обязательного указания fixed-cidr-v6 — Docker сам выбирает подсеть. Но в GitHub issue #6721 к проекту mailcow-dockerized описан случай на Docker 28.3.3 с Docker Compose 2.39.2 и mailcow 2025-09, где update.sh всё ещё требовал этот параметр — проверка в скрипте отставала от изменений в самом Docker Engine. В релизе mailcow 2025-09a (10 сентября 2025 года) разработчики объявили исправление: скрипт научился корректно распознавать версии Docker выше 28 и сократил обязательный набор параметров daemon.json, дополнительно потребовав установленный jq для работы обновлённого IPv6-контроллера.
Как понять, какие параметры daemon.json нужны именно вашей версии
Прежде чем редактировать daemon.json, я всегда смотрю два числа: версию Docker Engine (docker version --format '{{.Server.Version}}') и версию mailcow (git describe --tags в каталоге mailcow-dockerized). Если Docker Engine 27.0.1 или новее — ip6tables и experimental в daemon.json прописывать не нужно, это поведение уже встроено. Если Docker Engine 28.0.0 или новее — fixed-cidr-v6 в большинстве случаев тоже не обязателен. А вот версия mailcow должна быть 2025-09a или новее, чтобы её собственная проверка в update.sh не настаивала на устаревших требованиях: более старые версии скрипта могут просто не знать о новом поведении Docker и продолжат требовать флаги, которые технически уже не нужны демону.
docker version --format '{{.Server.Version}}'
cd /opt/mailcow-dockerized
git describe --tagsЕсли после обновления и Docker, и самого mailcow скрипт всё равно требует старые параметры — это повод завести issue в репозитории mailcow-dockerized с точными версиями Docker, Compose и mailcow, а не пытаться обойти проверку правкой самого скрипта: он переписывается при каждом git pull и правки потеряются на следующем обновлении.
Причина 2: update.sh ищет docker-compose, хотя установлен только плагин docker compose
Вторая частая причина ошибки — путаница между двумя разными установками Docker Compose. Есть классический standalone-бинарник docker-compose (через дефис, устанавливается вручную или через pip) и современный плагин docker compose (без дефиса, ставится пакетом docker-compose-plugin и вызывается как подкоманда docker). Формально это два разных продукта с разным жизненным циклом: standalone Compose v1 официально устарел, а плагин Compose v2 — это то, что Docker рекомендует использовать сейчас, и именно его официальная документация mailcow указывает как поддерживаемый вариант наравне со standalone, если версия не ниже 2.0.
В GitHub issue #4695 к mailcow-dockerized на релизе 2022-07 описан ровно этот случай: сервер на Debian 11 с Docker 20.10.17 и Compose 2.6.0 имел только плагин docker compose, а update.sh на тот момент искал именно бинарник docker-compose и завершал работу с ошибкой, что Compose не найден. Проблему закрыли в релизе mailcow 2022-08, где добавили поддержку плагина как равноправного варианта — а выбор update.sh с тех пор записывает в mailcow.conf переменную DOCKER_COMPOSE_VERSION (native для плагина, standalone для отдельного бинарника). Скрипт пересчитывает её при каждом запуске, так что руками её править бессмысленно — полезнее посмотреть, что там записано после последнего прогона: это и есть тот Compose, которым mailcow реально пользуется.
Если вы разворачиваете сервер mailcow с нуля и ещё не сталкивались с этой путаницей — рекомендую сразу ставить только плагин docker-compose-plugin официальным способом, как описано в моей пошаговой установке Docker на Linux: это снимает саму возможность конфликта версий ещё до того, как он успеет возникнуть.
Как сервер может видеть сразу две установки Compose и почему это плохо
Хуже, когда на сервере одновременно есть и старый standalone-бинарник, и новый плагин — это встречается там, где Compose когда-то ставили вручную (например, по устаревшей инструкции из интернета), а потом систему обновили через штатный пакетный менеджер, который добавил ещё и плагин. В таком случае docker-compose --version и docker compose version могут показывать разные номера версий, Сам update.sh сначала пробует плагин docker compose и берёт его, если тот отвечает и его версия не ниже 2.x; на standalone он откатывается, только когда плагина нет. Но cron-задачи, самописные обёртки и привычка админа набирать docker-compose продолжают вызывать старый бинарник — и тогда ручные команды и update.sh работают разными версиями Compose, а старая версия может не понимать свежие ключи в docker-compose.yml mailcow.
Проверить, что реально стоит на сервере, можно двумя короткими командами: which docker-compose покажет путь к standalone-бинарнику (если он есть), а docker compose version — версию плагина. Если оба варианта установлены и путаница мешает диагностике, я обычно удаляю устаревший standalone-бинарник и оставляю только официальный плагин — именно так рекомендует действовать документация mailcow при выборе между двумя вариантами установки. Если у вас узкая ошибка именно про Compose v5, а не про отсутствие бинарника вовсе, — это отдельный случай, который я разбираю в статье update.sh против Docker Compose v5: там другая механика сбоя, завязанная на распознавание мажорной версии.
which docker-compose
docker compose version
# если standalone больше не нужен:
sudo rm $(which docker-compose)Причина 3: обновление падает с «client version 1.42 is too old» — и что с этим делать
Третий и самый неочевидный сценарий — ошибка вида client version 1.42 is too old. Minimum supported API version is 1.44 при абсолютно новом Docker CLI. Здесь дело не в самом update.sh: скрипт лишь вызывает $COMPOSE_COMMAND pull, up и down, а к демону обращается Compose. Устаревший Compose (например, 2.18.1 из 2023 года) по умолчанию говорит с демоном на API 1.42. С выходом Docker Engine 29.0.0 (10 ноября 2025 года) демон требует API 1.44 или новее (это Docker 25.0 и выше) — и все вызовы старого Compose отклоняются, хотя docker ps из свежего CLI работает.
В GitHub issue #6999 к mailcow-dockerized такая ошибка воспроизведена при переходе mailcow 2025-10a → 2025-12a на Docker 29.1.3 (Debian 11): docker version показывал клиент и сервер с API 1.52 и минимумом 1.44, а в шаблоне issue стояла версия Compose v2.18.1. Мейнтейнеры закрыли issue как «not planned» с пояснением, что это не баг mailcow: команды выполняет Compose, и у автора, судя по всему, работал устаревший docker-compose. Рекомендованное решение — удалить старый бинарник (which docker-compose покажет путь) и поставить плагин по документации mailcow; автор подтвердил, что проблема была именно в этом. То есть причина 3 на практике часто оказывается следствием причины 2. Отдельно: в Docker Engine 29.3.0 (5 марта 2026 года) минимальную версию API снова снизили — с 1.44 до 1.40, так что на свежем демоне эта ошибка встречается реже.
Что делать, если такая ошибка версии API всё-таки появилась прямо сейчас. Первым делом я смотрю полную картину версий одной командой — она печатает версии клиента, сервера и обе версии API (минимальную и максимальную), которые демон готов принимать:
docker versionПравильное лечение — обновить Compose до актуального плагина из того же репозитория, что и Docker Engine (а не держать смесь snap-, pip- и apt-пакетов после миграций и ручных установок). Когда обновить Compose прямо сейчас нельзя, можно временно форсировать конкретную версию API переменной окружения DOCKER_API_VERSION, которая отключает автоматическое согласование версий и заставляет клиент общаться по заданному номеру — это временный обходной путь для конкретного вызова, а не постоянное решение, и после апдейта mailcow или Docker его стоит снять и проверить, что автоматическое согласование снова работает штатно.
DOCKER_API_VERSION=1.44 docker compose up -dПорядок диагностики: как быстро понять, какая из трёх причин у вас
Когда update.sh падает после обновления Docker, я не гадаю, а прохожу три проверки по порядку — они занимают меньше пяти минут и однозначно указывают, какая из причин актуальна именно на этом сервере:
Такой порядок важен, потому что симптомы на первый взгляд похожи — во всех трёх случаях update.sh просто «падает с ошибкой» в начале работы, — а исправления совершенно разные: правка daemon.json, удаление лишнего бинарника Compose или временная переменная окружения для API. Правка не той настройки не решит проблему и может создать новую, особенно если вслепую отключить IPv6-параметры на сервере, где реально настроена IPv6-маршрутизация для других сервисов, а не только для контейнеров mailcow.
После того как обновление прошло успешно, я фиксирую версии Docker, Compose и mailcow в внутренней документации проекта — это экономит те самые пять минут диагностики при следующем обновлении, потому что сразу видно, какая связка версий уже проверена и работает. Для клиентов на регулярном обслуживании эта проверка входит в тот же регламент эксплуатации mailcow, что и бэкапы с антиспамом — обновление Docker не должно быть отдельным несогласованным событием.
- Проверить версию Docker Engine (`docker version --format '{{.Server.Version}}'`) — если 27.0.1+ и ошибка про ip6tables/experimental, это устаревшая проверка в самом update.sh
- Проверить обе установки Compose (`which docker-compose` и `docker compose version`) — если найдены обе или update.sh пишет «compose не найден», проблема в выборе бинарника
- Если ошибка явно про версию API («client version X is too old»), проверить `docker version` целиком и версию того Compose, что реально вызывается, — чаще всего лечится заменой старого docker-compose на плагин
- Проверить версию самого mailcow (`git describe --tags`) — старая версия update.sh может не знать о новом поведении свежего Docker вне зависимости от причины
Частые вопросы
Docker Engine 27+ уже не экспериментальный для IPv6 — зачем update.sh mailcow всё ещё просит experimental в daemon.json?
Если ваш update.sh требует этот флаг на Docker 27.0.1 и новее — вероятно, версия mailcow отстаёт от изменений в Docker. Обновите mailcow до 2025-09a или новее: в этом релизе скрипт научился правильно распознавать новые версии Docker и не требует лишних параметров.
Нужен ли fixed-cidr-v6 в daemon.json на Docker 28?
С Docker Engine 28.0.0 параметр --ipv6 можно использовать без явного fixed-cidr-v6 — Docker выбирает подсеть автоматически. Если update.sh всё равно его требует, обновите mailcow: этот случай описан в issue #6721 и исправлен в релизе mailcow 2025-09a (10.09.2025).
У меня установлены и docker-compose, и плагин docker compose — что делать?
Проверьте обе: which docker-compose и docker compose version. update.sh при исправном плагине берёт его и пишет DOCKER_COMPOSE_VERSION=native в mailcow.conf, но ручные команды и cron могут вызывать старый бинарник. Если standalone не нужен — удалите его и оставьте плагин.
Что означает ошибка client version 1.42 is too old при обновлении mailcow?
Демон Docker Engine 29.0.0+ требует API 1.44, а к нему обращается устаревший Compose (например, standalone docker-compose 2.18.1), который говорит на 1.42. Удалите старый бинарник docker-compose и поставьте актуальный плагин docker compose; в Docker 29.3.0 (март 2026) минимум снова снижен до 1.40.
Можно ли просто отключить проверки версий в самом update.sh?
Не рекомендую: скрипт перезаписывается при каждом обновлении из git, и локальные правки потеряются, а сама проверка чаще всего сигнализирует о реальной несовместимости, а не о ложном срабатывании.
Как узнать, какая версия mailcow установлена сейчас?
Командой git describe --tags в каталоге mailcow-dockerized. Сравните результат с датой актуального релиза на mailcow.email/posts — если отстаёте на несколько релизов, часть описанных здесь проблем решится простым обновлением.
Источники
- Docker Engine 27 release notes — Проверено: в 27.0.1 (24.06.2024) ip6tables перестал быть experimental и включён по умолчанию для Linux bridge-сетей. https://docs.docker.com/engine/release-notes/27/
- Docker Engine 28 release notes — Проверено: с 28.0.0 демон поддерживает --ipv6 без обязательного fixed-cidr-v6. https://docs.docker.com/engine/release-notes/28/
- Docker Engine 29 release notes — Проверено: в 29.0.0 минимальная поддерживаемая версия Docker Engine API поднята до v1.44 (Update API version to 1.52); в 29.3.0 (05.03.2026) минимум снижен обратно до v1.40. https://docs.docker.com/engine/release-notes/29/
- mailcow docs — Installation (Docker/Docker Compose требования) — Проверено: минимальные версии Docker ≥24.0.0 и Docker Compose ≥2.0, поддержка и плагина docker compose, и standalone docker-compose. https://docs.mailcow.email/getstarted/install/
- GitHub — mailcow/mailcow-dockerized issues #5921, #6721, #4695, #6999 — Проверены конкретные кейсы: требование experimental/ip6tables на новом Docker (#5921), требование fixed-cidr-v6 на Docker 28.3.3/Compose 2.39.2 (#6721), поиск устаревшего docker-compose при наличии только плагина на Docker 20.10.17/Compose 2.6.0 (#4695), ошибка client version 1.42 too old на Docker 29.1.3 с Compose v2.18.1 (#6999, closed not planned; мейнтейнер указал на устаревший docker-compose, решение — плагин). https://github.com/mailcow/mailcow-dockerized/issues/6721
- mailcow.email — Release notes 2025-09 — Проверено: в 2025-09a улучшено распознавание версий Docker выше 28 и сокращён обязательный набор настроек daemon.json, добавлено требование jq для нового IPv6-контроллера. https://mailcow.email/posts/2025/release-2025-09/
- mailcow-dockerized — _modules/scripts/core.sh (get_compose_type) — Проверено: update.sh сначала выбирает плагин docker compose (≥2.x), на standalone откатывается при его отсутствии, результат пишет в DOCKER_COMPOSE_VERSION=native|standalone в mailcow.conf; проверка Docker ≥24. https://github.com/mailcow/mailcow-dockerized/blob/master/_modules/scripts/core.sh



