Почему update.sh mailcow отвергает Docker Compose v5
Ситуация выглядит нелепо: `docker compose version` показывает v5, а `update.sh` утверждает, что не найден Compose новее 2.x. Я, Семёнов Евгений Сергеевич, разбирал такой отказ на рабочем почтовом сервере. Ответ сразу: откатывать Docker Compose не нужно. Старая проверка в модулях mailcow принимала только версии, начинающиеся с «2», а исправленный код до сервера не доходил из-за порядка действий в самом `update.sh`. Поэтому сначала надо однократно обновить скрипт, проверить конфигурацию и лишь затем продолжать обновление почтовой системы.
Это ошибка проверки, а не требование откатиться
Фраза Cannot find Docker Compose with a Version Higher than 2.X.X вводит администратора в заблуждение. Кажется, будто Compose v5 слишком новый или mailcow требует строго ветку 2.x. На деле mailcow с Compose v5 работает нормально, ломалась только проверка. Она находится не в самом update.sh, а в функции get_compose_type() файла _modules/scripts/core.sh. До декабря 2025 года там стояли шаблоны grep -e "^2." -e "^v2." для плагина и grep "^2." для отдельного docker-compose. Строка 5.0.1 новее, но под шаблон не подходит, и скрипт завершался с exit 1.
3 декабря 2025 года разработчики заменили шаблоны на ^[2-9]\., ^v[2-9]\., ^[1-9][0-9]\. и ^v[1-9][0-9]\.: теперь проходят любые версии от 2 до 99. Но исправленный core.sh сам к пользователям не приезжал. Старый update.sh проверял версию Compose раньше, чем подтягивал свежие модули, — мейнтейнер назвал это soft-lock. Порядок поменяли отдельным коммитом в тот же день: инициализация и обновление _modules переехали в начало скрипта. Кто успел обновить mailcow до установки Compose v5, проблемы не заметил. Кто получил v5 из пакетов раньше, упирался в замкнутый круг.
Когда вообще появилась пятая версия? По GitHub releases репозитория docker/compose последний релиз ветки 2.x — v2.40.3 от 30 октября 2025 года, v5.0.0 опубликован 2 декабря 2025-го, v5.0.1 — 18 декабря. В заметках к v5.0.0 Docker объясняет, что номера 3 и 4 пропустили намеренно, чтобы не путать утилиту с устаревшими версиями 2.x и 3.x формата Compose-файла времён первого docker-compose. Из существенных изменений v5 — удаление встроенного билдера (сборка делегирована Docker Bake) и официальная поддержка Compose как SDK. mailcow использует готовые образы, поэтому эти изменения на него не влияют. Мой выбор однозначен: чиню устаревший bootstrap mailcow, а не подгоняю исправный системный компонент под чужой grep.
- Проверка живёт в `_modules/scripts/core.sh`, функция `get_compose_type()`.
- Старый шаблон `^2.` отвергал любую версию, кроме 2.x; новый принимает 2–99.
- Compose v5 — это версия утилиты, а не Compose File Format v5.
- Сообщение старого скрипта не доказывает реальную несовместимость.
- Отказ происходит до остановки контейнеров, почта продолжает работать.
Сначала выясняю, какой Compose действительно запускается
Я не начинаю с переустановки пакетов. Логика get_compose_type() простая: если отрабатывает docker compose, скрипт проверяет версию плагина и записывает в mailcow.conf DOCKER_COMPOSE_VERSION=native. Если плагина нет, пробует отдельный docker-compose (если это не alias) и ставит standalone. На серверах нередко живут оба варианта: плагин показывает v5, а в /usr/local/bin/docker-compose лежит забытая 1.29.2. Вдобавок при запуске через sudo меняется PATH. Типичная ошибка: администратор проверяет команду под своей учётной записью, а обновление запускает в другом окружении и потом лечит не тот бинарник.
Диагностику провожу из каталога mailcow в том же root-сеансе, из которого пойдёт обновление. Нужны версия Engine, оба варианта Compose, пути к исполняемым файлам, ветка репозитория, локальные изменения и — главное — шаблон, который сейчас лежит в core.sh.
sudo -i
cd /opt/mailcow-dockerized
docker version --format 'Engine {{.Server.Version}}'
docker compose version
docker compose version --short
command -v docker
command -v docker-compose || true
docker-compose version 2>/dev/null || true
grep '^DOCKER_COMPOSE_VERSION' mailcow.conf
grep -n 'version --short' _modules/scripts/core.sh
git branch --show-current
git remote -v
git status --short
git diff -- update.shЕсли docker compose version --short возвращает 5.0.1 или любую другую 5.x, а в core.sh виден шаблон "^2.", диагноз подтверждён. Если же там уже [2-9], а сообщение всё равно появляется, причина в другом, и Docker я трогаю ещё осторожнее.
Отдельно смотрю на ветку. Команда из release notes 2025-12a адресована обычной стабильной установке на master. Если сервер на nightly, в detached HEAD или на самодельной ветке, я не подмешиваю файл из origin/master автоматически. Показательный пример — issue #7010 от 18 января 2026 года: на Debian 12 с Docker 29.1.5 и Compose v5.0.1 пользователь nightly-ветки получил ту же ошибку, потому что исправление туда забыли перенести; бэкпорт сделали 19 января. В сентябре 2026 года внимательно отношусь и к legacy: документация mailcow обещала этой ветке только security-обновления до февраля 2026 года. Такой сервер требует плана перехода, а не только починки одной проверки.
- Вывод `docker compose version --short` в том же сеансе, где запускается обновление.
- Наличие и версия отдельного `docker-compose`, а также не alias ли это.
- Значение `DOCKER_COMPOSE_VERSION` в `mailcow.conf`: `native` или `standalone`.
- Шаблон grep в локальном `_modules/scripts/core.sh`: `^2.` или `[2-9]`.
- Текущая ветка Git (`master`, `nightly`, `legacy`) и локальные правки `update.sh`.
Как безопасно обновить сам update.sh
Здесь и возникает bootstrap-парадокс. Новый update.sh сначала обновляет _modules, а затем проверяет Compose, но старый делает это в обратном порядке и падает раньше, чем получит исправление. Поэтому в заметке к релизу 2025-12a и в закреплённом issue #6939 разработчики предписали один раз выполнить git fetch, а затем забрать update.sh из origin/master. Операция не переключает сервер на другую версию mailcow и не трогает контейнеры: она заменяет один файл содержимым из удалённой стабильной ветки. Исправленный core.sh скрипт подтянет уже сам при следующем запуске.
Перед заменой сохраняю старый файл и убеждаюсь, что в нём нет полезной локальной доработки. Править штатный update.sh вручную не советую: при следующем обновлении изменение исчезнет либо даст конфликт. Заодно заранее убеждаюсь, что в удалённой ветке действительно лежит новый шаблон.
cd /opt/mailcow-dockerized
cp -a update.sh /root/update.sh.before-compose-v5-fix
sha256sum update.sh /root/update.sh.before-compose-v5-fix
git fetch
git show origin/master:_modules/scripts/core.sh | grep -n 'version --short'
git checkout origin/master update.sh
chmod +x update.sh
git diff origin/master -- update.sh
git status --short update.shНулевая разница с origin/master подтверждает, что получена ожидаемая версия файла. В git status файл при этом может отображаться изменённым относительно старого локального коммита — это нормально: мы намеренно взяли новый скрипт раньше остального обновления. Копию я кладу в /root, а не в каталог репозитория, чтобы не засорять рабочее дерево.
Я использую опубликованную разработчиками последовательность и не правлю шаблон через sed, не закомментирую exit 1, не подменяю core.sh вручную. Ручной обход кажется быстрее, но отключает защиту без понимания остальных изменений, а правка модуля будет затёрта при первом же обновлении. Для nightly беру файл из origin/nightly, предварительно проверив в нём тот же шаблон. Если git fetch не проходит, remote указывает не на официальный репозиторий или git diff -- update.sh показывает собственную логику, я останавливаюсь, сохраняю патч и разбираю расхождения. Потеря локальной автоматизации неприятна, но потеря воспроизводимого процесса обновления хуже.
- Сохранить текущий `update.sh` вне репозитория и проверить локальный diff.
- Получить объекты командой `git fetch`.
- Убедиться, что в `origin/master:_modules/scripts/core.sh` уже стоит шаблон `[2-9]`.
- Взять исправленный файл командой `git checkout origin/master update.sh`.
- Не заменять весь репозиторий принудительным checkout или `git reset --hard`.
- После замены снова проверить версию Compose и состояние Git.
Проверка, предзагрузка и только потом обновление
Исправив скрипт, не нажимаю сразу «обновить». Сначала выполняю режим --check: он проверяет наличие обновлений, показывает изменения и завершает работу без применения. Коды возврата в веб-документации не описаны, но в справке самого скрипта (./update.sh --help) указано: 0 — обновление доступно, 3 — обновлений нет. Для оболочки 3 выглядит как ненулевой статус, но это не авария. Этот нюанс важен в Ansible, Zabbix и собственных cron-обвязках.
Следом проверяю итоговый Compose-конфиг и свободное место, после чего заранее загружаю образы. --prefetch скачивает новые образы и завершает работу, не применяя обновление. На медленном канале это заметно сокращает окно обслуживания.
cd /opt/mailcow-dockerized
./update.sh --check
check_rc=$?
printf 'update check exit code: %s\n' "$check_rc"
docker compose config -q
df -h / /var/lib/docker
docker system df
./update.sh --prefetchПосле prefetch ещё раз смотрю свободное место. Старые слои никуда волшебно не исчезают, поэтому загруженные образы могут занять несколько гигабайт до уборки через ./update.sh --gc.
Перед штатным запуском у меня должны быть свежая резервная копия данных mailcow, понятный способ восстановления, доступ к консоли виртуальной машины и согласованное окно простоя. Только после этого запускаю ./update.sh без --force: сама документация называет этот режим неподдерживаемым. Скрипт предупреждает, что контейнеры будут остановлены, и даёт прочитать изменения. После завершения проверяю не только зелёные контейнеры, но и SMTP, IMAP, веб-интерфейс, очередь Postfix, получение внешнего письма и отправку через авторизованного пользователя.
- `./update.sh --check` — узнать о доступном обновлении и увидеть изменения.
- `docker compose config -q` — проверить развёрнутую конфигурацию Compose.
- `./update.sh --prefetch` — заранее скачать образы без применения обновления.
- `./update.sh` — выполнить обновление в согласованное окно.
- `./update.sh --gc` — после проверки сервисов убрать старые теги образов.
Практика: 35 рабочих мест в «ИнженерГраде»
Покажу типовой проект из нашей практики: инжиниринговая компания «ИнженерГрад», 35 рабочих мест, 43 ящика с учётом общих адресов (проекты, тендеры, бухгалтерия) и около 150 ГБ почты — проектировщики активно пересылают чертежи во вложениях. mailcow работал на виртуальной машине с 4 vCPU, 16 ГБ RAM и SSD-диском 400 ГБ под Debian 12. В январе 2026 года после планового обновления пакетов на сервере оказались Docker Engine 29.1.5 и Docker Compose v5.0.1, а mailcow ещё стоял на 2025-10a. Эти ресурсы описывают конкретный стенд, а не универсальный минимум mailcow.
Из существенных настроек: стандартное имя Compose-проекта, ClamAV включён, SOLR отключён — для такого объёма и характера поиска это было осознанное упрощение. Фрагмент mailcow.conf с нейтральным доменом:
MAILCOW_HOSTNAME=mail.inzhenergrad.example
COMPOSE_PROJECT_NAME=mailcowdockerized
DOCKER_COMPOSE_VERSION=native
SKIP_CLAMD=n
SKIP_SOLR=yПри попытке перейти на 2025-12a старый update.sh немедленно сообщил, что не видит Compose новее 2.x. Почта продолжала работать: контейнеры остановлены не были. Дежурный администратор уже подготовил установку Compose v2.40.3, но мы её отменили. На боевом почтовике одновременно менять Docker-пакеты и mailcow без необходимости — плохой размен риска.
Мы проверили оба варианта Compose: отдельного docker-compose на сервере не оказалось, плагин стабильно возвращал 5.0.1, в локальном core.sh стоял старый шаблон ^2.. Локальных изменений в update.sh не было. Сохранили копию, выполнили официальные git fetch и git checkout origin/master update.sh, затем ./update.sh --check — скрипт подтянул модули и проверку прошёл. --prefetch заранее загрузил примерно 6,5 ГБ слоёв за 12 минут в обеденный перерыв; на работу пользователей это не повлияло. Окно с остановкой сервисов вечером заняло 5 минут 10 секунд. После запуска проверили вход в ящики выборочно по отделам, тестовую доставку снаружи, SMTP AUTH, IMAP и очередь. Потерь писем не было, Compose остался на v5.0.1. Правильный результат: исправлен источник ложного отказа, а не понижена версия рабочего компонента.
- 35 рабочих мест и 43 почтовых ящика, около 150 ГБ почты.
- mailcow 2025-10a перед обновлением до 2025-12a.
- Docker Engine 29.1.5 и Docker Compose v5.0.1.
- Около 6,5 ГБ образов загружено до окна обслуживания.
- Фактический простой — 5 минут 10 секунд.
Когда откат всё-таки оправдан и что важно запомнить
Я рассматриваю откат Compose только при подтверждённой функциональной несовместимости: например, исправленный скрипт проходит контроль версии, но docker compose config, pull или up падают на воспроизводимой ошибке ветки 5.x, зафиксированной в issues или release notes Docker либо mailcow. Одного текста старой проверки для этого недостаточно. Даже тогда сначала фиксирую версии Engine, Compose и Buildx, сохраняю вывод ошибки и ищу конкретный regression. Иначе откат превращается в ритуал без диагноза.
После исправления возможны другие ошибки, и их нельзя автоматически списывать на Compose v5. Учтите: текст Higher than 2.X.X в исправленном core.sh остался прежним, поэтому если он появляется при новом шаблоне, ищите причину в самом бинарнике — например, в сломанном плагине или старом docker-compose 1.x, до которого дошла очередь. Конфликт локального docker-compose.override.yml, закончившееся место в /var/lib/docker, недоступный registry, изменённый mailcow.conf, DNS или повреждённый Git-репозиторий требуют отдельных решений. Чиню первую доказанную причину и снова запускаю безопасную проверку, а не маскирую следующую ошибку параметром --force.
Итог короткий. Сообщение старого update.sh про Compose 2.x — известный дефект: устаревший шаблон grep плюс неудачный порядок обновления модулей, а не команда срочно ставить Compose 2. Оставьте v5, убедитесь, какой бинарник реально используется, сохраните локальные изменения, заберите исправленный update.sh официальной командой, выполните --check и --prefetch, а затем обновляйтесь с резервной копией и контролем сервисов. Тем, кто ещё не получил Compose v5, разработчики советовали просто заранее выполнить штатное обновление mailcow: исправление приедет до смены пакетов.
- Откат Compose — только при воспроизводимой ошибке самой утилиты, а не из-за сообщения старого скрипта.
- Перед любыми действиями фиксировать версии Engine, Compose, Buildx и состояние Git.
- Если сообщение осталось после исправления, проверять шаблон в `core.sh` и работоспособность плагина.
- Не использовать `--force` для продуктивного обновления.
- Обновлять mailcow до обновления Docker-пакетов, а не после.
Частые вопросы
Нужно ли удалить Docker Compose v5 и установить 2.x?
Нет, если единственный симптом — отказ старого `update.sh` на проверке версии. mailcow с Compose v5 работает; исправлять нужно скрипт обновления, а не Docker.
Существует ли вообще Docker Compose v5?
Да. По GitHub releases docker/compose версия v5.0.0 вышла 2 декабря 2025 года, сразу после ветки 2.x (последний релиз — v2.40.3). Номера 3 и 4 пропустили, чтобы не путать утилиту с версиями формата Compose-файла.
Почему update.sh не обновляет себя автоматически?
Старая версия проверяла Compose до того, как подтягивала свежие модули `_modules`, и падала раньше самообновления. В новой порядок изменён, но чтобы её получить, файл один раз нужно забрать вручную через Git.
Команда git checkout origin/master update.sh обновит весь mailcow?
Нет. Она берёт из удалённой ветки только файл `update.sh`; контейнеры, тома и остальной рабочий каталог не меняются. Локальную редакцию файла она перезапишет.
Я на ветке nightly — рецепт тот же?
Смысл тот же, но файл берите из `origin/nightly` и сначала проверьте в нём новый шаблон. Исправление в nightly перенесли с опозданием, только 19 января 2026 года.
Безопасно ли выполнять update.sh --prefetch днём?
Режим только загружает образы и завершается без применения обновления. Однако он расходует канал и место на диске, поэтому их всё равно следует контролировать.
Что означает код 3 после update.sh --check?
По справке самого скрипта это означает, что новых обновлений нет, а 0 — что обновление доступно. Для системы автоматизации код ненулевой, но о поломке mailcow он не сообщает.
Источники
- mailcow documentation — Update — разделы Automatic update и Options (параметры --check, --prefetch, --gc, --force, ветки stable/nightly/legacy): https://docs.mailcow.email/maintenance/update/
- mailcow GitHub issue #6939 — mailcows update script not ready for docker compose version 5.0.0 (03.12.2025); комментарий мейнтейнера о soft-lock и командах git fetch / git checkout origin/master update.sh: https://github.com/mailcow/mailcow-dockerized/issues/6939#issuecomment-3612587165
- mailcow commit 9a2887cf46 — core: improved docker compose version check — замена шаблонов ^2. на ^[2-9]\. / ^[1-9][0-9]\. в _modules/scripts/core.sh, 03.12.2025: https://github.com/mailcow/mailcow-dockerized/commit/9a2887cf46
- mailcow GitHub issue #7010 — Docker compose version check outdated (v2 > v5), 18.01.2026: Debian 12, Docker 29.1.5, Compose v5.0.1, ветка nightly; бэкпорт исправления 19.01.2026: https://github.com/mailcow/mailcow-dockerized/issues/7010
- mailcow release notes 2025-12a — Релиз от 12 декабря 2025 года; указание пользователям Compose v5 выполнить git fetch и git checkout origin/master update.sh: https://github.com/mailcow/mailcow-dockerized/releases/tag/2025-12a
- mailcow official blog — Moocember 2025 — предупреждение для Compose v5 и пояснение об ошибке проверки версии: https://mailcow.email/posts/2025/release-2025-12/
- Docker Compose releases — v5.0.0 (02.12.2025): раздел Why "v5", удаление внутреннего билдера, SDK; v5.0.1 — 18.12.2025; последний 2.x — v2.40.3 от 30.10.2025: https://github.com/docker/compose/releases/tag/v5.0.0
