Files
obsidian-vault/personal/tech/watchtower-docker-updates.md
T

9.3 KiB
Raw Blame History

title, created, updated, type, namespace, tags, related
title created updated type namespace tags related
Watchtower — почему не обновляет контейнеры 2026-09-20 2026-09-20 tech personal
watchtower
docker
updates
infra
pitfall
family/how-to/kraken-watchtower
personal/tech/docker-data-root-mount-race
personal/tech/jellyfin-db-schema-mismatch

Watchtower — почему не обновляет контейнеры

Реюзабельная теория. Живой разбор на Kraken — в family/how-to/kraken-watchtower.

Главный вывод: docker ps показывает Up (healthy) — это ничего не говорит о том, что Watchtower хоть раз что-то обновил. Отсутствие обновлений не даёт ни ошибки, ни алерта. На Kraken Watchtower «работал» 2.5 месяца и не обновил ни один из 12 контейнеров.

Четыре причины (проверять в этом порядке)

1. Не задан --interval → таймер сбрасывается каждым стартом

Без флага интервал = 24ч от момента старта процесса. Любой рестарт контейнера (reboot, docker restart, обновление самого Watchtower) обнуляет отсчёт. Если контейнер рестартуют чаще раза в сутки — проверка не наступает никогда.

Признак в логе:

level=info msg="Scheduling first run: ... in 23 hours, 59 minutes, 59 seconds"
level=info msg="Waiting for running update to be finished..."

Вторая строка — процесс проснулся, увидел незавершённый апдейт и снова уснул. Признак, что цикл заклинило.

Фикс: --interval 86400 явно, либо --schedule "0 0 4 * * *" (cron-синтаксис из 6 полей), если нужен запуск в тихое время.

2. DOCKER_API_VERSION запинен на старую версию

DOCKER_API_VERSION=1.40 в environment ломает чтение метаданных образа. Симптом:

level=info msg="Found new lscr.io/linuxserver/prowlarr:latest image (3e9bd62ca90c)"
level=info msg="Unable to update container \"/prowlarr\": no available image info. Proceeding to next."

Watchtower нашёл новый образ, но не может обновиться. В новых версиях API показывается сам: using Docker API v1.54.

Фикс: убрать DOCKER_API_VERSION из environment вообще. Watchtower сам согласует версию с dockerd.

3. Образ containrrr/watchtower не развивается

Last push containrrr/watchtower — 2023-11-11 (проверено Docker Hub API). Он несовместим с современным dockerd, отсюда и no available image info — это не только про API-версию.

Фикс: активный форк nickfedor/watchtower (актуальная версия 1.22.2, теги latest, nightly, semver).

# проверить дату образа
curl -s "https://hub.docker.com/v2/repositories/containrrr/watchtower/tags?page_size=5" \
  | jq -r '.results[] | "\(.name) updated=\(.last_updated)"'

# проверить поддержку своей архитектуры (например arm64 для RPi)
curl -s "https://hub.docker.com/v2/repositories/nickfedor/watchtower/tags/1.22.2" \
  | jq -r '.images[] | "\(.architecture) \(.os)"'

4. --label-enable без единого label → не обновит НИЧЕГО

С флагом --label-enable Watchtower трогает только контейнеры с label com.centurylinklabs.watchtower.enable=true. Если label нет ни у кого — сам по себе флаг делает Watchtower бесполезным.

Проверка, кто помечен:

for c in $(docker ps --format "{{.Names}}"); do
  echo "$c -> $(docker inspect $c -f '{{index .Config.Labels "com.centurylinklabs.watchtower.enable"}}')"
done

⚠️ docker inspect на контейнере без label вернёт пустую строку, а не «false» — не путать с false (явное отключение).

Рабочий конфиг

services:
  watchtower:
    image: nickfedor/watchtower:1.22.2
    container_name: watchtower
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 86400 --cleanup --label-enable
    # НЕ добавлять DOCKER_API_VERSION
  • --interval 86400 — сутки, видно в логе рядом со Next scheduled run
  • --cleanup — удалять старые образы; без него диск забивается (на Kraken 87%)
  • --label-enable — оптин: обновляется только то, что явно помечено

Проверка (обязательна)

Up + отсутствие ошибок ≠ работа. Нужен живой прогон:

docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  nickfedor/watchtower:1.22.2 --run-once --monitor-only

--monitor-only обязателен, иначе --run-once реально обновит контейнеры.

Что должно быть в выводе:

level=info msg="Watchtower 1.22.2 using Docker API v1.54"
level=info msg="Found new image" container=<X> new_id=<...>
level=info msg="Update session completed" failed=0 scanned=N skipped=0 updated=0

Found new image = баг с метаданными вылечен. updated=0 в monitor-only — нормально.

⚠️ Автообновление может сломать сервис: пинить мажор версии

Тип latest + автообновление = переход на новую мажорную версию без предупреждения. Для сервисов с миграциями БД (EF Core, Prisma, любые forward-only миграции) это необратимо:

  • Jellyfin на Kraken: latest уехал 10.11.8 → 12.1, БД мигрировала вперёд, откатить схему нельзя → сервер отдавал 503, no such column: .... Детали: personal/tech/jellyfin-db-schema-mismatch.

Правило (общее): сервисам с forward-only миграциями БД безопаснее пиновый тег (image: jellyfin/jellyfin:12.1), а не latest + автообновление.

⚠️ Но это рекомендация, не аксиома — на Kraken решено иначе. Alex 2026-09-20 явно выбрал latest для Jellyfin: «пусть ставит до всех публичных релизов», т.е. включая мажор 13. Риск принят осознанно, бэкап БД делается перед пересозданием. См. family/how-to/kraken-watchtower § «Про Jellyfin и тег».

Отдельно важный нюанс: упал не мажорный апдейт как таковой, а рассинхрон «контейнер на старом образе + уже мигрированная БД». Сама миграция прошла нормально. То есть мажорный апдейт БД не ломает — ломает откат сервера назад без отката схемы.

Питфоллы диагностики

  • docker logs watchtower — Watchtower пишет в stdout, в отличие от Jellyfin (тот пишет в файл). Смотреть логи легко.
  • Таймстемпы в логе Watchtower — UTC, локальное время сервиса может быть другим.
  • Единожды увиденный Found new image не значит, что обновление применилось — нужна следующая строка (Unable to update / Updated).
  • Проверять надо фактическую дату запущенного образа, а не тег:
    docker inspect <container> -f '{{index .Config.Labels "org.opencontainers.image.version"}}'
    docker inspect <container> -f '{{index .Config.Labels "build_version"}}'
    

Урок

Watchtower — компонент, который молча не работает. Его нельзя «поставить и забыть»: обязательны (а) явный --interval, (б) отсутствие DOCKER_API_VERSION, (в) периодический --run-once --monitor-only как health-check, (г) осознанное решение, каким контейнерам вообще разрешено автообновление.

Связанные