Docker-Container automatisch updaten: Watchtower, Diun & Renovate im Vergleich

6. Mai 2026

Fehlende Benachrichtigungen, GitLab-Chaos und die Suche nach dem perfekten Docker-Update-Workflow

Watchtower klingt nach der perfekten Lösung für automatische Docker-Updates – bis man merkt, dass man keinen Überblick hat was wann aktualisiert wurde, und GitLab sich einfach weigert richtig zu updaten. Ein ehrlicher Erfahrungsbericht und was ich stattdessen mache.

dark-homelab-server-rack-with-glowing-docker-wha

Bild generiert mit KI

Die Idee ist gut. Wirklich.

Wer ein Homelab betreibt, kennt das Problem: Irgendwann laufen da 15, 20, 30 Docker-Container. Jeder will gepflegt werden, jeder bekommt gelegentlich Security-Patches, neue Features, Breaking Changes. Manuell docker pull und docker compose up -d auf jedem Stack zu tippen macht am Anfang noch Spaß. Nach einem halben Jahr nicht mehr.

Watchtower verspricht die Lösung: Ein Container, der selbstständig alle anderen überwacht, neue Images zieht und die Container neu startet. Klingt perfekt. Ich hab es installiert, war kurz begeistert – und hab es irgendwann wieder abgeschaltet. Hier ist warum.

Was Watchtower macht – und was es verspricht

Watchtower läuft als Docker-Container und pollt in konfigurierbaren Intervallen die Registry nach neuen Image-Versionen. Findet es ein Update, zieht es das neue Image, stoppt den alten Container und startet einen neuen mit denselben Parametern. Für einfache, zustandslose Container klingt das trivial – und funktioniert es auch.

services:
  watchtower:
    image: containrrr/watchtower
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_SCHEDULE=0 0 4 * * *
      - WATCHTOWER_NOTIFICATION_URL=gotify://gotify.meinedomain.de/TOKEN

Problem 1: Watchtower ist faktisch unmaintained

Was viele nicht wissen: Das originale containrrr/watchtower-Repository gilt in weiten Teilen der Community als ungepflegt. Neuere Docker-Versionen bringen API-Änderungen mit, die das ursprüngliche Image nicht mehr versteht.

# Typische Fehlermeldung bei modernen Docker-Versionen:
Error response from daemon: client version 1.25 is too old.
Minimum supported API version is 1.40,
please upgrade your client to a newer version

Die Community hat sich deshalb gespalten. Ein Teil nutzt Workarounds, ein anderer ist zum Fork nicholas-fedor/watchtower gewechselt – ein 1:1-Drop-in-Ersatz der aktiv gepflegt wird und aktuelle Docker-APIs unterstützt. Aber: Der ursprüngliche Watchtower-Ersteller warnte selbst davor, dass viele Forks „full of AI slop" seien. Wer einem ungeprüften Fork den Zugriff auf seinen Docker-Socket gibt, öffnet eine große Angriffsfläche.

Docker Socket = voller Host-Zugriff

Watchtower bekommt Zugriff auf /var/run/docker.sock. Damit kann es prinzipiell jeden Container auf dem Host starten, stoppen oder manipulieren. Ungeprüfte Forks sollten hier nicht blind vertraut werden.

Problem 2: Kein Überblick was wann aktualisiert wurde

Watchtower aktualisiert – und du weißt es nicht. Keine Push-Benachrichtigung, keine Zusammenfassung, kein Dashboard. Du loggst dich zwei Tage später ein und fragst dich: Läuft da noch die Version von letzter Woche oder hat Watchtower inzwischen was gezogen?

Ja, Watchtower unterstützt theoretisch Benachrichtigungen über die Shoutrrr-Bibliothek (Gotify, Slack, Discord, E-Mail). Aber in der Praxis hatte ich das nie wirklich sauber zum Laufen gebracht. Die Konfiguration ist umständlicher als sie sein müsste.

# So sehen Watchtower-Logs aus – wenn man aktiv danach schaut:
docker logs watchtower --tail 20

# time="2026-04-01T04:00:12Z" level=info msg="Found new image for /uptime-kuma"
# time="2026-04-01T04:00:18Z" level=info msg="Stopping /uptime-kuma"
# time="2026-04-01T04:00:21Z" level=info msg="Creating /uptime-kuma"
# time="2026-04-01T04:00:23Z" level=info msg="Removing old image sha256:abc123"

# Das Problem: Du musst aktiv nachschauen.
# Es kommt nichts zu dir. Du weißt nicht was aktualisiert wurde.

Problem 3: GitLab und Watchtower passen konzeptionell nicht zusammen

GitLab ist der Paradefall warum blindes Auto-Update gefährlich ist. gitlab/gitlab-ce:latest klingt harmlos – ist es aber nicht. GitLab hat einen strikten Upgrade-Pfad: Man darf nicht von Version X direkt auf Version X+5 springen, sondern muss bestimmte Zwischenstopps einhalten.

# Erlaubter GitLab Upgrade-Pfad (Beispiel):
# 16.0 → 16.3 → 16.7 → 16.11 → 17.0 → 17.3 → 17.x

# Was Watchtower macht:
# 16.0 → 17.9 (latest) – in einem Zug

# Was dann passiert:
docker logs gitlab --tail 5
# FATAL: Database migration failed!
# ActiveRecord::IrreversibleMigration
# Please follow the upgrade path: https://docs.gitlab.com/ee/update/

Watchtower kennt keinen Upgrade-Pfad, keine Datenbankmigrationen, keine Abhängigkeiten zwischen Komponenten. Es kennt nur: altes Image → neues Image → Neustart. Für ein Setup aus gitlab-ce, Runner, Redis und PostgreSQL ist das aktiv gefährlich.

GitLab-Regel

GitLab-Instanzen grundsätzlich von Watchtower ausschließen und immer manuell nach offiziellem Upgrade-Pfad aktualisieren. Keine Ausnahmen.

Das stille Defekt-Problem

:latest garantiert keine Abwärtskompatibilität. Weitere typische Szenarien:

  • PostgreSQL-Major-Upgrade: Von 15 auf 16 erfordert pg_upgrade. Watchtower startet einfach neu – die Datenbank bootet nicht mehr.
  • Immich + Redis: Nutzer warnen explizit davor, Redis in einem Immich-Stack blind zu updaten – bricht die gesamte Applikationsstruktur.
  • Konfigurationsformat-Änderungen: Neues Image, anderes Config-Format erwartet – Container startet, verhält sich aber falsch oder gar nicht.

Das Tückische: Oft startet der Container scheinbar problemlos. Der Fehler zeigt sich erst Stunden später.

Watchtower richtig einsetzen: Opt-In statt Opt-Out

Wenn man Watchtower einsetzt, dann mit Label-Filter im Opt-In-Modus. Nur Container die explizit markiert sind werden angefasst:

services:
  watchtower:
    image: nicholas-fedor/watchtower   # Aktiv gepflegter Fork
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_SCHEDULE=0 0 3 * * *
      - WATCHTOWER_LABEL_ENABLE=true
      - WATCHTOWER_NOTIFICATION_URL=gotify://gotify.meinedomain.de/TOKEN
    command: --label-enable

  # Wird automatisch geupdated
  uptime-kuma:
    image: louislam/uptime-kuma:latest
    labels:
      - "com.centurylinklabs.watchtower.enable=true"

  # Wird NICHT angefasst
  gitlab:
    image: gitlab/gitlab-ce:17.0.0-ce.0   # Pinned!
    labels:
      - "com.centurylinklabs.watchtower.enable=false"

  # Wird NICHT angefasst
  postgres:
    image: postgres:16.2                   # Auch pinned!
    labels:
      - "com.centurylinklabs.watchtower.enable=false"

GitLab CI + Watchtower: Der API-Trigger-Ansatz

Für selbst entwickelte Services die via GitLab CI gebaut werden gibt es einen eleganteren Ansatz als periodisches Polling: den HTTP-API-Trigger. Watchtower wartet passiv auf einen Webhook – den die Pipeline nach einem erfolgreichen Image-Push abfeuert. Kein blindes Polling, kein zufälliges Neustart-Timing.

# Watchtower im API-Modus
services:
  watchtower:
    image: nicholas-fedor/watchtower
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"   # Nur lokal! Nie direkt ins Netz.
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_HTTP_API_UPDATE=true
      - WATCHTOWER_HTTP_API_TOKEN=${WATCHTOWER_TOKEN}
      - WATCHTOWER_CLEANUP=true
    command: --http-api-update
# .gitlab-ci.yml – Deploy-Stage triggert Watchtower
deploy:
  stage: deploy
  image: curlimages/curl
  script:
    - |
      curl -s \
        -H "Authorization: Bearer $WATCHTOWER_TOKEN" \
        "https://meinserver.de/v1/update?container=meine-app"
  only:
    - main

Für den Zugriff auf die GitLab Container Registry braucht Watchtower Credentials. Die sicherste Lösung: ein Deploy Token mit read_registry-Scope – entkoppelt von Nutzerkonten, langlebig, minimale Rechte.

# Docker config.json für GitLab Registry Auth
mkdir -p ~/.docker
echo '{
  "auths": {
    "registry.gitlab.com": {
      "auth": "'$(echo -n 'deploy-token:deploy-token-password' | base64)'"
    }
  }
}' > ~/.docker/config.json

# In compose mounten:
# volumes:
#   - /root/.docker/config.json:/config.json:ro
# environment:
#   - DOCKER_CONFIG=/config.json

Die bessere Alternative: Diun

Diun (Docker Image Update Notifier) ist ein leichtgewichtiges Tool das genau eine Sache macht: Es schickt dir eine Benachrichtigung wenn ein neues Image verfügbar ist – und startet dabei nichts neu. Du entscheidest was passiert.

Diun läuft ebenfalls als Docker-Container, überwacht alle oder ausgewählte laufende Container und meldet sich über konfigurierbare Kanäle (Gotify, Telegram, Slack, E-Mail, ntfy und viele mehr). Kein heimliches Updaten, keine Überraschungen – nur Information.

services:
  diun:
    image: crazymax/diun:latest
    container_name: diun
    restart: unless-stopped
    volumes:
      - ./data:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TZ=Europe/Berlin
      - DIUN_WATCH_WORKERS=20
      - DIUN_WATCH_SCHEDULE=0 8 * * 1
      - DIUN_PROVIDERS_DOCKER=true
      - DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true
      - DIUN_NOTIF_GOTIFY_ENDPOINT=https://gotify.meinedomain.de
      - DIUN_NOTIF_GOTIFY_TOKEN=${GOTIFY_TOKEN}
      - DIUN_NOTIF_GOTIFY_PRIORITY=5

Montags früh kommt eine Zusammenfassung welche Images Updates haben. Ich schaue kurz den Changelog an, entscheide ob ich jetzt update oder warte – und führe das Update manuell durch. Nicht vollautomatisch, aber ich weiß immer was auf meinem Server läuft.

Der GitOps-Ansatz: Renovate Bot

Renovate ist ein Dependency-Update-Bot der ursprünglich für npm, Maven und Co. entwickelt wurde – inzwischen aber auch docker-compose.yml-Dateien versteht. Wer seine Docker-Stacks in Git verwaltet, bekommt damit einen vollständig anderen Ansatz:

Renovate scannt das Repository, erkennt veraltete Image-Tags und öffnet automatisch einen Merge Request in GitLab mit der Versionsaktualisierung. Du reviewst den MR, schaust kurz den Changelog an, mergst – und die CI/CD-Pipeline deployed. Jedes Update ist als Commit in der Versionsgeschichte dokumentiert. Kein Container wird je ohne dein Wissen verändert.

// renovate.json – Konfiguration im Repository-Root
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:base"],
  "docker-compose": {
    "enabled": true
  },
  "packageRules": [
    {
      "matchPackageNames": ["postgres", "redis", "mariadb"],
      "matchUpdateTypes": ["major"],
      "enabled": false
    }
  ]
}

Renovate lässt sich auf einem selbst gehosteten GitLab-Runner betreiben oder als gehosteter Service nutzen. Der Haken: Es braucht etwas Aufwand beim Setup und eine funktionierende CI/CD-Pipeline. Für ein Hobby-Homelab vielleicht Overkill – für einen produktiven Server die sauberste Lösung die es gibt.

Alternativen im Überblick

  • Watchtower – Vollautomatisch, mit Label-Filter kontrollierbar. Originales Image kaum noch gepflegt.
  • nicholas-fedor/watchtower – Aktiv gepflegter Fork, Drop-in-Ersatz mit moderner Docker-API-Unterstützung.
  • Diun – Nur Benachrichtigungen, kein automatisches Update. Beste Kontrolle, wenig Risiko.
  • WUD (What's Up Docker) – Web-Interface, zeigt veraltete Stacks visuell an, unterstützt SemVer-Logik.
  • Renovate Bot – GitOps-Ansatz via Merge Requests. Höchste Nachvollziehbarkeit, braucht CI/CD.
  • Podman Auto-Update – Native Systemd-Timer-Integration. Erfordert Migration von Docker zu Podman.

Was ich mir eigentlich wünsche

Die Wunschlösung in meinem Kopf:

  1. Diun läuft auf dem Server und schickt montags eine Zusammenfassung welche Images Updates haben.
  2. Für unkritische Container läuft Watchtower im Opt-In-Modus nachts – mit Gotify-Benachrichtigung.
  3. Für selbst entwickelte Services: GitLab CI pushed das Image und triggert Watchtower via API-Call.
  4. GitLab, Datenbanken, alles Stateful: manuell, nach Changelog, auf Zuruf.

Den Renovate-Ansatz finde ich konzeptionell am saubersten – aber der Aufwand übersteigt für ein Hobby-Homelab meinen aktuellen Leidensdruck. Vielleicht ist das der nächste Artikel.

Fazit: Kein Silver Bullet

Watchtower ist nicht schlecht – es ist nur nicht die vollständige, sorglose Lösung die es verspricht. Das originale Image ist faktisch unmaintained, der sicherste Fork erfordert Vertrauen in Dritte, und das Konzept „zieh latest und starte neu" passt nicht zu komplexen, stateful Services wie GitLab oder Datenbanken.

Die fehlenden Benachrichtigungen waren für mich der Hauptgrund aufzuhören. Ein Tool das heimlich meinen Server verändert ohne mich zu informieren ist kein Tool das ich will.

Mein aktueller Stand: Diun für Überblick, manuell für Updates, Watchtower im Opt-In-Modus nur für wirklich unkritische Container. Keine perfekte Lösung, aber eine ehrliche.

Wie machst du das?

Vollautomatisch mit Watchtower und es läuft? Renovate im GitOps-Einsatz? Oder auch hauptsächlich manuell? Schreib's in die Kommentare.

Teilen:


Schreibe einen Kommentar

Wird für die Bestätigung benötigt