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

137 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Watchtower — почему не обновляет контейнеры
created: '2026-09-20'
updated: '2026-09-20'
type: tech
namespace: personal
tags: [watchtower, docker, updates, infra, pitfall]
related:
- "[[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).
```bash
# проверить дату образа
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 бесполезным**.
Проверка, кто помечен:
```bash
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` (явное отключение).
## Рабочий конфиг
```yaml
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` + отсутствие ошибок ≠ работа. Нужен живой прогон:
```bash
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`).
- Проверять надо **фактическую** дату запущенного образа, а не тег:
```bash
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, (г) осознанное решение, каким контейнерам вообще разрешено автообновление.
## Связанные
- [[family/how-to/kraken-watchtower]] — живой разбор и точные команды на Kraken
- [[personal/tech/jellyfin-db-schema-mismatch]] — что ломает автообновление мажора
- [[personal/tech/docker-data-root-mount-race]] — другой дефект «контейнеры пропали»