Benachrichtigungskanäle (ntfy, Gotify, Webhook)
Seit Containly 2.7.1 gehen Benachrichtigungen auch an ntfy, Gotify oder einen beliebigen Webhook. Free für normale Benachrichtigungen, Pro in Workflows. Einrichtung, Test, Filter, Webhook-Format mit HMAC-Signatur, Sicherheitsregeln und Zustellprotokoll erklärt.
Seit Containly 2.7.1 sind Benachrichtigungen nicht mehr auf E-Mail und die Glocke in der App beschränkt. Unter Einstellungen > Benachrichtigungen > Kanäle fügst du Kanäle hinzu, die dieselben Benachrichtigungen erhalten: ntfy (Push aufs Handy über ntfy.sh oder deinen eigenen ntfy-Server), Gotify (Push über deinen eigenen Gotify-Server) und einen allgemeinen Webhook (JSON an eine beliebige URL, zum Beispiel Home Assistant, n8n oder ein eigenes Skript).
Seit Containly 2.7.2 gibt es auch Kanäle für Discord, Telegram, Slack und Pushover sowie Web-Push auf dein Handy oder deinen Browser, siehe Push-Mitteilungen und Discord, Telegram, Slack, Pushover.
Kanäle für normale Benachrichtigungen sind Free. Auch Workflows können gespeicherte Kanäle benachrichtigen; wie alles bei Workflows braucht das Pro. Nur Admins sehen und ändern Kanäle.
Kanal hinzufügen
- Öffne Einstellungen > Benachrichtigungen. Die Karte Kanäle steht unter den E-Mail-Einstellungen.
- Klicke auf Kanal hinzufügen und wähle den Typ: ntfy, Gotify oder Webhook.
- Fülle die Felder aus:
| Typ | Felder |
|---|---|
| ntfy | Server-URL (zum Beispiel https://ntfy.sh oder http://192.168.1.20:8080), Topic, Anmeldung (keine, Zugriffstoken oder Benutzer und Passwort), Priorität je Schweregrad (1 bis 5), optionale Klick-URL |
| Gotify | Server-URL, App-Token (lege in Gotify unter Apps eine Anwendung an und kopiere ihr Token), Priorität je Schweregrad (0 bis 10) |
| Webhook | URL, optionale Signatur (Signatur-Schlüssel, Erzeugen füllt einen zufälligen Wert ein), bis zu 10 eigene Header (zum Beispiel Authorization) |
- Wähle den Mindest-Schweregrad (Info, Warnung oder Kritisch), die Ereignisse (alle Ereignisse, die unter Ereignisse eingeschaltet sind, oder eine Auswahl) und die Sprache der Nachrichten.
- Klicke auf Test senden. Containly schickt eine echte Nachricht mit den eingegebenen Einstellungen und speichert nichts. Das Ergebnis sagt, was nicht stimmt, zum Beispiel „Anmeldung abgelehnt (HTTP 401)“ oder „Loopback-Adressen sind nie erlaubt“. Änderst du ein Feld, verschwindet das Ergebnis, damit es immer zu den aktuellen Einstellungen passt.
- Klicke auf Speichern.
Auf ntfy.sh kann jeder mitlesen, der ein Topic kennt. Wähle einen langen zufälligen Namen oder schütze das Topic mit einem Zugriffstoken.
Welche Benachrichtigungen ein Kanal erhält
Ein Kanal erhält jede Art von Benachrichtigung, die unter Einstellungen > Benachrichtigungen > Ereignisse eingeschaltet ist (offline Hosts, gestoppte, unhealthy oder wegen Speichermangel beendete Container, Neustart-Schleifen, CPU, Arbeitsspeicher und Speicherplatz, Updates, kritische CVEs, Backups), gefiltert nach dem Mindest-Schweregrad und der optionalen Auswahl des Kanals. Die Wartezeit von 30 Minuten je Ereignis und Objekt gilt wie bei E-Mails.
Workflow-Nachrichten landen nie von selbst in einem Kanal: Im Schritt Benachrichtigen eines Workflows hakst du neben In-App und E-Mail die gewünschten Kanäle an (Pro). Ohne Pro lehnt Containly diese Workflow-Nachrichten auf dem Server ab und vermerkt sie im Zustellprotokoll des Kanals mit „Workflows brauchen Pro“.
Status und Zustellprotokoll
Jeder Kanal zeigt in der Liste seinen Status: Funktioniert, Fehler (die letzte Zustellung ist gescheitert), Fällt aus (10 gescheiterte Zustellungen in Folge, Containly versucht es weiter), Noch nicht benutzt oder Aus. Test senden in der Liste testet den gespeicherten Kanal. Im Drei-Punkte-Menü findest du Bearbeiten, Zustellprotokoll und Löschen.
Das Zustellprotokoll zeigt die letzten 20 Versuche mit Zeit, Ereignis, Ergebnis, HTTP-Status und Anzahl Versuche. Inhalte und Geheimnisse werden dort nie gespeichert.
Grenzen und Wiederholungen
- Höchstens 20 Nachrichten pro Minute je Kanal. Weitere werden zurückgehalten und in einer Nachricht zusammengefasst („12 weitere Benachrichtigungen wurden zurückgehalten“).
- Vorübergehende Fehler (HTTP 429, Serverfehler 5xx, Zeitüberschreitung, Verbindungsfehler) wiederholt Containly 3 Mal nach 1, 2 und 4 Sekunden; ein
Retry-Afterdes Servers wird bis 30 Sekunden beachtet. Andere Fehler (zum Beispiel ein falsches Token) werden nicht wiederholt. - Jede Anfrage hat 10 Sekunden Zeit; Weiterleitungen werden nicht verfolgt.
Sicherheit
- Geheimnisse (Tokens, Passwörter, Signatur-Schlüssel, Header-Werte) werden verschlüsselt gespeichert (AES-256-GCM mit dem Instanz-Schlüssel), nie wieder angezeigt, nie von der API zurückgegeben und nie in Logs oder ins Audit-Log geschrieben. Lässt du ein Geheimnis-Feld leer, bleibt der gespeicherte Wert.
- Änderst du das Ziel (Schema, Host oder Port) eines gespeicherten Kanals, musst du seine Geheimnisse erneut eingeben. So kann ein gespeichertes Token nie an einen anderen Server gehen, auch nicht über einen Test.
- Ausgehende Regeln: Containly sendet nie an Loopback-Adressen (
127.0.0.1,localhost), Link-Local-Adressen (169.254.x.x), Cloud-Metadaten-Adressen oder an sich selbst. Jede Adresse, auf die ein Hostname zeigt, wird geprüft, und die Verbindung geht genau an diese Adresse. - Eigenes Netzwerk: Adressen wie
192.168.x.x,10.x.x.xoder172.16.x.xbis172.31.x.xsind standardmässig erlaubt, weil ntfy und Gotify meist dort laufen. Schalte in der Karte Kanäle Private Ziele sperren ein, um auch sie abzulehnen. Die Server-EinstellungCONTAINLY_CHANNELS_BLOCK_PRIVATE=1schaltet das fest ein. - Unverschlüsseltes
http://ist nur zu Adressen im eigenen Netzwerk erlaubt; öffentliche Empfänger brauchenhttps://. - Läuft der Empfänger auf demselben Docker-Host, nutze seine LAN-Adresse oder ein gemeinsames Docker-Netzwerk mit Containly und den Containernamen;
localhostim Containly-Container ist Containly selbst.
Webhook-Format
Containly sendet ein POST mit Content-Type: application/json. Der Inhalt (Version 1):
{
"version": 1,
"id": "6f1c2a9e-...",
"type": "container.exited",
"severity": "warning",
"title": "Containly · Container gestoppt: web",
"message": "Der Container ist mit Exit-Code 3 gestoppt.\nContainer: web\nHost: homelab\nExit-Code: 3",
"target": "web",
"link": "https://containly.example.com/containers",
"at": "2026-10-19T08:00:00.000Z",
"test": false,
"source": "notification"
}
typeist die Art der Benachrichtigung (zum Beispielendpoint.offline,backup.failed,workflow.alert),channel.testbei einem Test undchannel.suppressedbei der Zusammenfassung der Ratenbegrenzung (mit dem zusätzlichen Feldsuppressed, der Anzahl zurückgehaltener Nachrichten).sourceistnotification,workflow,testodersummary.linkbrauchtCONTAINLY_PUBLIC_URL; ohne diese Einstellung ist der Wertnull.- Header: immer
X-Containly-Event(die Art) undX-Containly-Delivery(die id); mit Signatur-Schlüssel zusätzlichX-Containly-Timestamp(Unix-Sekunden) undX-Containly-Signature: sha256=<hex>.
Signatur prüfen in deinem Empfänger: Berechne HMAC-SHA256 mit deinem Signatur-Schlüssel über <timestamp>.<roher Inhalt> (der Wert des Timestamp-Headers, ein Punkt, dann der Inhalt genau so, wie er ankommt) und vergleiche das Ergebnis in konstanter Zeit mit dem Hex-Wert nach sha256=. Lehne alte Zeitstempel ab (zum Beispiel älter als 5 Minuten), damit niemand Nachrichten wiederholen kann. Beispiel in Python:
import hashlib, hmac, time
def verify(secret: bytes, headers, raw_body: bytes) -> bool:
ts = headers["X-Containly-Timestamp"]
if abs(time.time() - int(ts)) > 300:
return False
want = "sha256=" + hmac.new(secret, ts.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(want, headers["X-Containly-Signature"])
Probleme lösen
| Meldung | Was tun |
|---|---|
| Abgelehnt: Loopback-Adressen sind nie erlaubt | Statt localhost die LAN-Adresse des Hosts oder den Containernamen in einem gemeinsamen Docker-Netzwerk verwenden |
| Abgelehnt: private Netzwerkadressen sind gesperrt | Private Ziele sperren ist eingeschaltet; ausschalten oder eine öffentliche https://-Adresse verwenden |
| Öffentliche Adressen brauchen https:// | Für Empfänger im Internet https:// verwenden |
| Hostname nicht gefunden | Schreibweise und DNS des Containly-Containers prüfen |
| Anmeldung abgelehnt (HTTP 401) / Zugriff verweigert (HTTP 403) | Token, Passwort oder das Gotify-App-Token prüfen |
| TLS fehlgeschlagen | Das Zertifikat des Empfängers ist nicht vertrauenswürdig oder passt nicht zum Hostnamen |
| Das Ziel hat sich geändert: gib den Wert erneut ein | Du hast den Server geändert; Token oder Passwort erneut eingeben |