What’s Up Docker statt Watchtower: So bekommst du eine Push-Notification bei Docker-Updates
Watchtower ist für viele das erste Tool, wenn es um Docker-Updates geht. Es zieht neue Images und startet deine Container automatisch neu. Das ist bequem, bis ein Update in der Nacht eine Datenbank-Migration anstößt oder eine Config-Option entfernt und du morgens einen kaputten Service findest. Ich lasse mir deshalb nur melden, dass es ein Update gibt, und spiele es selbst ein. Dafür nutze ich What’s Up Docker, kurz WUD, mit Push-Notifications über ntfy. In diesem Guide richtest du genau das Setup ein.
Kurz erklärt
- WUD prüft regelmäßig, ob es für deine laufenden Container neuere Images gibt, und zeigt sie in einem Web-Dashboard an.
- Über einen Trigger schickt WUD dir bei einem neuen Update eine Push-Notification, in diesem Guide per ntfy.
- Es ist für dich, wenn du Container mit Docker Compose betreibst und Updates lieber selbst einspielen willst.
- Du brauchst einen Docker-Host mit Compose und einen ntfy-Server oder einen Topic auf ntfy.sh.
Warum nicht einfach Watchtower?
Watchtower spielt Updates ein, sobald es sie findet. Du siehst vorher keinen Changelog und kannst kein Backup machen. Bei Images mit dem Tag latest landet so auch eine neue Major-Version auf deinem Server. Dazu kommt: Das ursprüngliche GitHub-Repository containrrr/watchtower wurde am 17. Dezember 2025 archiviert und ist seitdem schreibgeschützt. Es kommen dort also keine Fixes mehr.
WUD kann Container zwar ebenfalls selbst aktualisieren, das ist aber ein eigener Trigger, den du bewusst einschalten müsstest. Ohne ihn liest WUD nur und meldet.
Voraussetzungen
- Ein Linux-Host mit Docker Engine und dem Compose-Plugin (
docker compose versionsollte eine Version ausgeben) - Ein freier Port 3000 auf dem Host für das Web-UI
- Ein ntfy-Topic, auf das du in der ntfy-App abonniert bist. Wie du ntfy selbst hostest und einen Access Token anlegst, steht in meinem Guide zu Uptime Kuma und ntfy.
- Deine anderen Container laufen am besten per Compose mit einer eigenen
docker-compose.ymlpro Stack. Das macht das manuelle Update später einfach.
Die Befehle und Variablen in diesem Guide beziehen sich auf WUD 9.x, die zum Zeitpunkt des Schreibens aktuelle Version laut offizieller Doku.
Schritt 1: WUD mit Docker Compose starten
Lege einen Ordner an und darin zwei Dateien:
sudo mkdir -p /opt/wud/store
cd /opt/wud
sudo nano .env
In die .env kommen die Werte, die nicht in der Compose-Datei stehen sollen. Docker Compose liest diese Datei automatisch ein, wenn sie im selben Ordner liegt:
WUD_ADMIN_PASSWORD=ein-langes-eigenes-passwort
NTFY_TOKEN=dein-ntfy-access-token
Danach die docker-compose.yml:
services:
wud:
image: getwud/wud
container_name: wud
restart: unless-stopped
ports:
- 3000:3000
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /opt/wud/store:/store
environment:
- WUD_AUTH_ADMIN_USER=admin
- WUD_AUTH_ADMIN_PASSWORD=${WUD_ADMIN_PASSWORD}
- WUD_WATCHER_LOCAL_CRON=0 6 * * *
- WUD_TRIGGER_NTFY_HOMELAB_URL=https://ntfy.example.de
- WUD_TRIGGER_NTFY_HOMELAB_TOPIC=docker-updates
- WUD_TRIGGER_NTFY_HOMELAB_AUTH_TOKEN=${NTFY_TOKEN}
Was die einzelnen Teile machen:
- Der Docker-Socket
/var/run/docker.sockist die Schnittstelle, über die WUD sieht, welche Container laufen. Das:robindet ihn read-only ein, so zeigen es auch alle Beispiele in der WUD-Doku. Es schützt allerdings nur die Socket-Datei selbst, API-Aufrufe über den Socket sind trotzdem möglich. Wer den Socket hat, hat praktisch Root auf dem Host, deshalb solltest du das UI nicht ins Internet stellen. - Unter
/storelegt WUD seine SQLite-Datenbankwud.sqliteab. Darin stehen unter anderem deine Container, der Verlauf und die Benutzer. Damit das einen Neustart übersteht, liegt der Ordner auf dem Host unter/opt/wud/store. - WUD 9 verlangt einen Login für das UI und die API. Ohne Admin-Zugang in der Datenbank und ohne
WUD_AUTH_ADMIN_USERundWUD_AUTH_ADMIN_PASSWORDstartet WUD gar nicht erst. - Ein Watcher ist die Komponente, die einen Docker-Host beobachtet. Ohne eigene Konfiguration legt WUD einen Watcher namens
localfür den lokalen Socket an. MitWUD_WATCHER_LOCAL_CRONänderst du, wann er prüft. Der Default ist einmal pro Stunde (0 * * * *), ich habe hier täglich um 6 Uhr eingetragen. Zusätzlich prüft WUD einmal direkt beim Start (WATCHATSTARTist standardmäßig an), du musst für einen ersten Test also nicht bis zum nächsten Morgen warten. - Ein Trigger ist die Aktion, die WUD bei einem gefundenen Update ausführt.
HOMELABist ein frei wählbarer Name für diesen Trigger.
Steht in deinem Passwort ein $, schreib es in der .env in einfache Anführungszeichen, sonst versucht Compose es als Variable aufzulösen. Dann starten:
sudo docker compose up -d
sudo docker compose logs -f wud
Schritt 2: Den ntfy-Trigger anpassen
Pflicht ist beim ntfy-Trigger nur _TOPIC. Lässt du _URL weg, schickt WUD an den öffentlichen Server https://ntfy.sh. Dort solltest du einen schwer zu erratenden Topic-Namen wählen, weil jeder mitlesen kann, der ihn kennt. Für einen geschützten Topic auf deinem eigenen Server nimmst du _AUTH_TOKEN oder alternativ _AUTH_USER und _AUTH_PASSWORD.
Ein paar optionale Variablen, die ich nützlich finde:
- WUD_TRIGGER_NTFY_HOMELAB_PRIORITY=3
- WUD_TRIGGER_NTFY_HOMELAB_TAGS=whale,package
- WUD_TRIGGER_NTFY_HOMELAB_THRESHOLD=minor
- WUD_TRIGGER_NTFY_HOMELAB_MODE=batch
PRIORITY geht von 1 bis 5, der Default ist 3. TAGS sind die kleinen Emojis in der ntfy-App. Mit THRESHOLD=minor meldet WUD nur Minor- und Patch-Updates nach Semver, also Versionsnummern im Schema Major.Minor.Patch. Der Default all meldet alles, auch reine Digest-Änderungen. Ein Digest ist die Prüfsumme eines Images, die sich ändert, wenn unter demselben Tag ein neues Image veröffentlicht wird. MODE=batch fasst alle gefundenen Updates in einer Notification zusammen. Standardmäßig feuert WUD pro Update nur einmal (ONCE=true), du bekommst also nicht stündlich dieselbe Meldung.
Schritt 3: Container mit Labels steuern
Labels sind Zusatzangaben, die du in der docker-compose.yml des jeweiligen Containers einträgst. WUD liest sie aus und passt daran an, wie es den Container behandelt. Die wichtigsten:
wud.watch=falsenimmt einen Container aus der Überwachung,wud.watch=truenimmt ihn auf. Willst du nur ausgewählte Container überwachen, setzt du bei WUDWUD_WATCHER_LOCAL_WATCHBYDEFAULT=falseund gibst nur diesen Containernwud.watch=true.wud.tag.includeist ein regulärer Ausdruck (Regex), der festlegt, welche Tags überhaupt als Update gelten.wud.tag.excludeschließt Tags aus.wud.watch.digest=truelässt WUD auch bei Tags wielatestauf neue Images achten. Bei Images von Docker Hub ist das standardmäßig aus, bei anderen Registries kann der Watcher es laut Doku schon von sich aus tun.wud.link.templatebaut einen Link zum Changelog, der in der Notification und im UI auftaucht.wud.display.namegibt dem Container einen lesbaren Namen in UI und Notifications.
Ein Beispiel für einen Container, der nur stabile Versionen im Format 1.2.3 melden soll:
services:
vaultwarden:
image: vaultwarden/server:1.32.0
labels:
- wud.tag.include=^\d+\.\d+\.\d+$$
- wud.display.name=Vaultwarden
- wud.link.template=https://github.com/dani-garcia/vaultwarden/releases/tag/$${major}.$${minor}.$${patch}
Achte auf das doppelte $$. Docker Compose nutzt $ für eigene Variablen, deshalb musst du jedes $, das bei WUD ankommen soll, verdoppeln. Die Versionsnummer im Image ist hier nur ein Beispiel. Nach dem Ändern der Labels startest du den Container mit docker compose up -d neu, damit Docker die Labels übernimmt.
Schritt 4: Mehrere Docker-Hosts
Hast du mehr als einen Docker-Host, gibt es zwei Wege. Ich betreibe auf jedem Docker-Host eine eigene WUD-Instanz, die an denselben ntfy-Topic meldet. Das ist am einfachsten, weil jede Instanz nur den lokalen Socket braucht. Gib jeder Instanz dann einen Hinweis auf den Host mit, damit du in der Notification siehst, woher sie kommt. Am einfachsten geht das über die Tags, zum Beispiel WUD_TRIGGER_NTFY_HOMELAB_TAGS=whale,nas auf dem NAS. Alternativ nimmst du pro Host einen eigenen Topic.
Alternativ beobachtet eine einzige WUD-Instanz entfernte Hosts über die Docker-API. Dafür legst du pro Host einen weiteren Watcher an. Den automatischen Watcher local legt WUD nur an, wenn du gar keinen Watcher konfigurierst. Im Beispiel oben ist er durch WUD_WATCHER_LOCAL_CRON schon ausdrücklich definiert, er bleibt also erhalten.
- WUD_WATCHER_NAS_HOST=192.168.1.30
- WUD_WATCHER_NAS_PORT=2376
- WUD_WATCHER_NAS_CAFILE=/certs/ca.pem
- WUD_WATCHER_NAS_CERTFILE=/certs/cert.pem
- WUD_WATCHER_NAS_KEYFILE=/certs/key.pem
Die Zertifikate bindest du zusätzlich per Volume read-only unter /certs ein. Der entfernte Docker-Daemon muss dafür auf TCP mit TLS lauschen, das richtest du auf dem Zielhost nach der Docker-Doku zum Schutz des Daemon-Sockets ein. Dort erstellst du auch die Zertifikate ca.pem, cert.pem und key.pem für den Client. Den Port 2375 ohne TLS würde ich nicht öffnen. Wer diesen Port erreicht, hat volle Kontrolle über den Host.
So prüfst du, ob es läuft
sudo docker compose psim Ordner/opt/wudzeigt den Container alsrunning.- Die Logs von
docker compose logs wudenthalten keine Fehler zu Socket, Auth oder Trigger. - Unter
http://192.168.1.x:3000kannst du dich mitadminund deinem Passwort einloggen und siehst deine Container. - Container mit verfügbarem Update sind im UI markiert, und auf deinem Handy kommt die ntfy-Notification an. Zum Testen kannst du einen Container bewusst mit einer älteren Version starten und WUD danach mit
sudo docker compose restart wudneu starten, damit es sofort prüft.
Schritt 5: Das Update selbst einspielen
Kommt eine Notification, schau zuerst in den Changelog, am besten über den Link aus wud.link.template. Achte auf Breaking Changes, also Änderungen, die eine Anpassung an deiner Config verlangen. Bei Datenbanken und Diensten mit wichtigen Daten mache ich vorher ein Backup oder einen Snapshot des LXC oder der VM, auf der Docker läuft.
Nutzt der Container einen beweglichen Tag wie latest oder 1, reicht im Ordner des Stacks:
cd /opt/vaultwarden
sudo docker compose pull
sudo docker compose up -d
Hast du eine feste Version wie 1.32.0 eingetragen, holt pull nichts Neues. Dann änderst du zuerst den Tag in der docker-compose.yml auf die neue Version und führst danach dieselben zwei Befehle aus. Compose startet nur die Container neu, deren Image sich geändert hat. Prüf danach mit docker compose logs, ob der Dienst sauber hochgekommen ist. Alte Images räumst du bei Bedarf mit sudo docker image prune weg.
Typische Probleme
WUD startet nicht
Meistens fehlen die Admin-Variablen oder die .env liegt nicht im selben Ordner wie die Compose-Datei. Mit sudo docker compose config siehst du, welche Werte Compose tatsächlich einsetzt.
Keine Notification auf dem Handy
Prüf in den Logs, ob WUD den Trigger überhaupt ausführt. Häufige Ursachen sind ein Tippfehler im Topic, ein falscher Token oder eine URL mit Pfad statt nur der Basis-URL des ntfy-Servers. Teste ntfy unabhängig von WUD mit curl -H "Authorization: Bearer DEIN_TOKEN" -d "Test" https://ntfy.example.de/docker-updates.
Container mit latest zeigen nie ein Update
Bei beweglichen Tags wie latest achtet WUD bei Docker-Hub-Images standardmäßig nicht auf Digests. Setz das Label wud.watch.digest=true oder wechsle auf versionierte Tags. Letzteres ist ohnehin besser, weil du dann im Changelog genau nachlesen kannst, was sich ändert.
Compose meckert über die Labels
Meldungen wie invalid interpolation format kommen fast immer von einem einfachen $ in einem Regex oder Link-Template. Verdopple es zu $$.
Fazit
WUD übernimmt bei mir die Arbeit, die Watchtower gemacht hat, ohne selbst etwas zu ändern. Auf jedem Docker-Host läuft eine Instanz, die Updates an ntfy meldet, und eingespielt wird erst, wenn ich den Changelog gelesen habe. Mit THRESHOLD, MODE=batch und ein paar Labels hältst du die Menge an Notifications klein genug, dass du sie auch wirklich liest.
Hat dir der Guide geholfen? Dann spendier mir gern einen Kaffee auf Ko-fi. Damit bleibt MappleBox werbefrei.