АйТи Фреш
Главная / Статьи / Серверы и хостинг
Серверы и хостинг

Почему update.sh mailcow отвергает Docker Compose v5

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~16 мин чтения
Почему update.sh mailcow отвергает Docker Compose v5
Иллюстрация к статье «Почему 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.

Не удаляйте Compose v5 только из-за этой ошибки. Ненужный откат добавит второй изменяемый компонент и может превратить простой ремонт скрипта в аварийные работы с Docker-пакетами.
Цифры и версии: Это ошибка проверки, а не требование откатиться — схема
Цифры и версии: Это ошибка проверки, а не требование откатиться. Открыть схему в полном размере

Сначала выясняю, какой 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 года. Такой сервер требует плана перехода, а не только починки одной проверки.

Не запускайте `curl` с неизвестного форума поверх `/usr/local/bin/docker-compose`. Сначала установите, какой бинарник выбирают оболочка и mailcow. Два Compose в разных каталогах — более частая причина путаницы, чем настоящая несовместимость v5.
Почему update.sh mailcow отвергает Docker Compose v5 — схема
Схема к статье. Открыть схему в полном размере

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

`git checkout origin/master update.sh` перезаписывает локальную копию файла без предупреждения. Если вы когда-либо редактировали `update.sh`, сначала сохраните файл и вывод `git diff -- update.sh` за пределами репозитория.
Порядок действий: Как безопасно обновить сам update.sh — схема
Порядок действий: Как безопасно обновить сам update.sh. Открыть схему в полном размере

Проверка, предзагрузка и только потом обновление

Исправив скрипт, не нажимаю сразу «обновить». Сначала выполняю режим --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, получение внешнего письма и отправку через авторизованного пользователя.

Предзагрузка не заменяет резервную копию. Она уменьшает зависимость от скорости реестра во время окна, но не защищает данные при неудачной миграции или ошибке хранения.

Практика: 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 рабочих мест в «ИнженерГраде» — схема
Цифры и версии: Практика: 35 рабочих мест в «ИнженерГраде». Открыть схему в полном размере

Когда откат всё-таки оправдан и что важно запомнить

Я рассматриваю откат 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: исправление приедет до смены пакетов.

Если сервер на нестандартной ветке, имеет локально переписанный `update.sh` или уже частично обновлён, не применяйте рецепт вслепую. Сначала зафиксируйте состояние Git и контейнеров — граница между быстрым ремонтом и восстановлением проходит именно по наличию понятной исходной точки.

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

Нужно ли удалить 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 он не сообщает.

Столкнулись с похожей задачей? Обращайтесь — решим

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

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 💬 MAX: +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи