Gotify 3.1.1 mit Docker Compose selbst hosten: Push-Benachrichtigungen ohne Cloud
Gotify stellt Alarme aus Monitoring, Backup-Jobs und Skripten ohne Cloud-Dienst zu. Die Anleitung zeigt Einrichtung mit Docker Compose, getesteten Push per curl, Reverse Proxy, Backup, Wiederherstellung und Deinstallation.
Geprüft am 30.09.2026 · für server 3.1.1
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Gotify ist ein selbst gehosteter Push-Server (Go, REST-API, Weboberfläche, Android-App) für Meldungen aus Monitoring, Skripten und Backup-Jobs ohne fremde Server.
Start, Funktionstest, Backup und Wiederherstellung liefen im Test mit Docker 29.1.3, Compose 2.40.3 und gotify/server:3.1.1 auf Ubuntu; die Ausgaben stammen daraus.
Was Gotify ist und welches Problem es löst
Der Server liefert REST-API, WebSocket-Stream und Weboberfläche. Applications sind Absender mit Token, Clients Empfänger (App oder Browser).
Anders als E-Mail trennt Gotify nach Absender: Jede Quelle wird eine eigene Application und lässt sich in der App einzeln stummschalten.
Gesendet wird per HTTP-POST, ohne SMTP oder Bibliothek.
Nutzen und Grenzen ehrlich benannt
Gotify eignet sich für:
- Technische Alarmierung: Monitoring-Alerts, Backup-Ergebnisse, Zertifikatsablauf, SMART-Warnungen.
- Betrieb ohne Cloud: Kein Alarmtext geht an Dritte.
- Kleine Hardware: rund 5 MiB RAM im Leerlauf (Test).
Grenzen:
- Kein offizieller iOS-Client. Nur Drittanbieter-Apps oder Weboberfläche.
- Keine Ende-zu-Ende-Verschlüsselung. Meldungen liegen im Klartext in der Datenbank; nichts Vertrauliches senden.
- Keine Eskalationslogik, kein Zwei-Wege-Kanal (keine Quittierung).
- Kein Monitoring. Gotify stellt nur zu.
Abgrenzung zu ntfy
Gleiches Problem, anderes Bedienmodell.
| Kriterium | Gotify 3.1.1 | ntfy |
|---|---|---|
| Adressierung | Application mit festem Token, vom Server vergeben | Frei wählbares Topic im URL-Pfad |
| Zugriff ohne Token | Nicht möglich, jede Anfrage braucht Token oder Login | Standardmäßig offen, Zugriffsschutz muss aktiviert werden |
| Benutzerverwaltung | Fest eingebaut, Admin- und Standardbenutzer, OIDC möglich | Über Access-Control-Listen konfigurierbar |
| Offizielle Mobil-Apps | Android | Android und iOS |
| Datenhaltung | SQLite als Standard, MySQL und PostgreSQL möglich | SQLite als Cache, primär flüchtig |
| Typischer Einsatz | Fester Kreis bekannter Absender und Empfänger | Ad-hoc-Kanäle, auch für Gelegenheitsnutzer |
Gotify passt zu festen Systemen und Administratoren, ntfy zu spontanen Kanälen und iOS. Parallelbetrieb ist möglich.
Projekt- und Versionsstand
Stand 16.09.2026 (GitHub-API):
- Aktuelle Version: v3.1.1, veröffentlicht am 15.09.2026.
- Pflege: letzter Push 15.09.2026, aktiv.
- Lizenz: LICENSE-Datei im Repository (GitHub-API:
NOASSERTION).
v3.1.1 enthält eine Sicherheitskorrektur (GHSA-phfm-q6fr-wv34); ältere 3.x zeitnah aktualisieren.
Ab Version 3 Konfiguration über Umgebungsvariablen statt config.yml.
Voraussetzungen und Ressourcenbedarf
- Docker Engine und Compose-Plugin (Test: 29.1.3 und 2.40.3).
- Arbeitsspeicher: 5,07 MiB im Leerlauf, Limit 256 MB reicht.
- Speicherplatz: Image rund 54 MB, SQLite anfangs 68 KB.
- CPU: keine Anforderungen, kein AVX.
- Architekturen: amd64, i386, arm64, armv7, riscv64 (laut Doku; getestet nur amd64).
- Netz: freier Hostport, extern Reverse Proxy mit Zertifikat.
Compose-Datei und Konfiguration
Legen Sie ein eigenes Verzeichnis an, zum Beispiel /opt/gotify.
# Verzeichnis anlegen und hineinwechseln
sudo mkdir -p /opt/gotify
cd /opt/gotify
Der Port hängt nur an Loopback, extern geht es über den Reverse Proxy. Für einen LAN-Test die LAN-IP statt 127.0.0.1 eintragen, nicht 0.0.0.0.
# /opt/gotify/compose.yaml
services:
gotify:
image: gotify/server:3.1.1
container_name: gotify
restart: unless-stopped
ports:
# Nur lokal erreichbar, Zugriff von außen über den Reverse Proxy
- "127.0.0.1:18080:80"
env_file:
- .env
environment:
TZ: "Europe/Berlin"
volumes:
- ./gotify_data:/app/data
mem_limit: 256m
Zugangsdaten in einer getrennten .env; Platzhalter vorher ersetzen.
# /opt/gotify/.env
# ACHTUNG: Platzhalter ersetzen, niemals die Beispielwerte uebernehmen
GOTIFY_DEFAULTUSER_NAME=admin
GOTIFY_DEFAULTUSER_PASS=BITTE-HIER-EIGENES-STARKES-PASSWORT-EINSETZEN
# Selbstregistrierung durch Unbekannte unterbinden
GOTIFY_REGISTRATION=false
# Kosten des bcrypt-Hashes, hoeher ist sicherer und langsamer
GOTIFY_PASSSTRENGTH=10
# Hinter TLS-Proxy sinnvoll: Session-Cookie nur ueber HTTPS senden
GOTIFY_SERVER_SECURECOOKIE=true
# IP des Reverse Proxy, damit die echte Client-IP im Log steht
GOTIFY_SERVER_TRUSTEDPROXIES=127.0.0.1/32
# Rechte der Datei mit den Zugangsdaten einschraenken
sudo chmod 600 /opt/gotify/.env
Referenz aller Variablen: gotify-server.env.example:
# Offizielle Referenzkonfiguration zum Nachschlagen holen
curl -o /opt/gotify/gotify-server.env.example \
https://raw.githubusercontent.com/gotify/server/v3.1.1/gotify-server.env.example
Die wichtigsten Variablen im Überblick:
| Variable | Standard | Bedeutung |
|---|---|---|
| GOTIFY_DEFAULTUSER_NAME | admin | Name des ersten Administrators, wirkt nur beim allerersten Start |
| GOTIFY_DEFAULTUSER_PASS | admin | Passwort des ersten Administrators, wirkt nur beim allerersten Start |
| GOTIFY_REGISTRATION | false | Erlaubt unangemeldeten Besuchern das Anlegen eigener Konten |
| GOTIFY_SERVER_PORT | 80 | Port im Container, im Image bereits gesetzt |
| GOTIFY_SERVER_SECURECOOKIE | false | Setzt das Secure-Flag am Sitzungs-Cookie, hinter HTTPS einschalten |
| GOTIFY_SERVER_TRUSTEDPROXIES | leer | Proxys, deren X-Forwarded-For-Header vertraut wird |
| GOTIFY_DATABASE_DIALECT | sqlite3 | Datenbanktreiber, alternativ mysql oder postgres |
| GOTIFY_DATABASE_CONNECTION | data/gotify.db | Pfad beziehungsweise Verbindungszeichenkette der Datenbank |
| GOTIFY_PASSSTRENGTH | 10 | bcrypt-Kostenfaktor für Passwort-Hashes |
| GOTIFY_LOCALAUTH_ENABLED | true | Anmeldung mit Benutzername und Passwort, bei reinem OIDC abschaltbar |
Laut Dokumentation geht jede Variable auch mit Suffix _FILE (etwa GOTIFY_DEFAULTUSER_PASS_FILE=/run/secrets/admin_pass); nicht getestet.
Installation und erster Start
Starten Sie den Stack:
cd /opt/gotify
# Image holen und Container im Hintergrund starten
docker compose up -d
Verifizieren: Der Status zeigt nach kurzer Zeit healthy. Ausgabe im Test:
docker compose ps
NAME IMAGE COMMAND SERVICE STATUS PORTS
gotify gotify/server:3.1.1 "./gotify-app serve" gotify Up 32 seconds (healthy) 127.0.0.1:18080->80/tcp
Der Image-Healthcheck fragt intern /health ab. Startlog im Test:
docker compose logs --tail 20
gotify | 2026-09-16T10:00:34+02:00 INF Gotify build_date=2026-09-15-18:26:53 version=3.1.1
gotify | 2026-09-16T10:00:34+02:00 INF Listen address=[::]:80 connection_type="plain connection" network=tcp
Erstkonfiguration: Benutzer und Applications
Anmeldung unter http://127.0.0.1:18080 mit GOTIFY_DEFAULTUSER_NAME und GOTIFY_DEFAULTUSER_PASS; beide wirken nur beim ersten Start.
Verifizieren: Die API bestätigt den Administrator:
curl -s -u admin:IHR-PASSWORT http://127.0.0.1:18080/current/user
{"id":1,"name":"admin","admin":true,"createdAt":"2026-09-16T10:00:34.440612241+02:00"}
Für den Alltag einen Benutzer ohne Adminrechte anlegen:
# Nicht-privilegierten Benutzer anlegen
curl -s -u admin:IHR-PASSWORT http://127.0.0.1:18080/user \
-F "name=monitoruser" \
-F "pass=EIN-STARKES-PASSWORT" \
-F "admin=false"
{"id":2,"name":"monitoruser","admin":false,"createdAt":"2026-09-16T10:02:14.461961829+02:00"}
Jede Meldequelle erhält eine Application. Ab Gotify 3 erscheint der Token nur beim Anlegen oder Rotieren; sofort sicher speichern.
# Application fuer das Monitoring anlegen
curl -s -u admin:IHR-PASSWORT http://127.0.0.1:18080/application \
-F "name=monitoring" \
-F "description=Alerts aus dem Monitoring"
Antwort im Test (Token gekürzt):
{"id":1,"token":"gtfya.8SJmFpc4...","name":"monitoring","description":"Alerts aus dem Monitoring","internal":false,"image":"static/defaultapp.png","defaultPriority":0,"createdAt":"2026-09-16T10:00:45.811261134+02:00","lastUsed":null,"sortKey":"a0"}
Android-App: Client-Token (gtfyc.); Absender: Application-Token (gtfya.):
# Client-Token fuer ein Endgeraet erzeugen
curl -s -u admin:IHR-PASSWORT http://127.0.0.1:18080/client -F "name=handy-admin"
Funktions- und Healthcheck
Drei Prüfungen, alle im Test ausgeführt:
1. Version (ohne Anmeldung):
curl -s http://127.0.0.1:18080/version
{"version":"3.1.1","commit":"e6361caa512547052b1013dbf4544e6d7ff32604","buildDate":"2026-09-15-18:26:53"}
2. Gesundheitsstatus inklusive Datenbank, geeignet für externe Überwachung:
curl -s http://127.0.0.1:18080/health
{"health":"green","database":"green"}
3. Push mit Application-Token:
# Token aus dem Anlegen der Application einsetzen
export GOTIFY_TOKEN="gtfya.IHR-APPLICATION-TOKEN"
curl -s "http://127.0.0.1:18080/message" \
-H "X-Gotify-Key: $GOTIFY_TOKEN" \
-F "title=Backup fehlgeschlagen" \
-F "message=Job nightly-backup auf srv-01 endete mit Exit-Code 1." \
-F "priority=8"
Die Antwort im Test:
{"id":1,"appid":1,"message":"Job nightly-backup auf srv-01 endete mit Exit-Code 1.","title":"Backup fehlgeschlagen","priority":8,"date":"2026-09-16T10:01:14.061735815+02:00"}
JSON funktioniert ebenfalls (im Test Meldung mit ID 2):
curl -s -X POST "http://127.0.0.1:18080/message" \
-H "X-Gotify-Key: $GOTIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Testalarm","message":"JSON-Push funktioniert.","priority":5}'
Verifizieren: Die Meldung erscheint in der Weboberfläche. Ein ungültiger Token liefert HTTP 401 (you need to provide a valid access token or user credentials to access this api).
Die Priorität steuert die Darstellung, bewährt: 0 bis 3 Information, 4 bis 7 Warnung, ab 8 Störung (Android-App nicht getestet).
Integration in Monitoring und Skripte
Hilfsfunktion für Shell-Skripte, sendet nur bei Fehlern:
#!/usr/bin/env bash
set -euo pipefail
GOTIFY_URL="https://push.example.com"
# Token aus geschuetzter Datei lesen, nicht im Skript hinterlegen
GOTIFY_TOKEN="$(cat /etc/gotify/backup.token)"
notify() {
local title="$1"
local msg="$2"
local prio="${3:-5}"
curl -sS --max-time 10 "${GOTIFY_URL}/message" \
-H "X-Gotify-Key: ${GOTIFY_TOKEN}" \
-F "title=${title}" \
-F "message=${msg}" \
-F "priority=${prio}" > /dev/null
}
if ! /usr/local/bin/backup.sh; then
notify "Backup fehlgeschlagen" "Host $(hostname), siehe journalctl -u backup" 8
exit 1
fi
--max-time verhindert Blockieren bei unerreichbarem Server.
Jede Software mit generischem Webhook kann senden. Uptime Kuma hat einen Gotify-Typ (URL und Application-Token), der Prometheus Alertmanager braucht Drittanbieter-Brücken. Beides nicht getestet.
Eine Application pro System, damit sich ein Token einzeln zurückziehen lässt.
Persistente Daten, Volumes und Rechte
Alle Daten liegen unter /app/data. Nach dem ersten Start im Test:
ls -la /opt/gotify/gotify_data/
-rw-r--r-- 1 root root 69632 Sep 16 10:00 gotify.db
drwxr-xr-x 2 root root 40 Sep 16 10:00 images
drwxr-xr-x 2 root root 40 Sep 16 10:00 plugins
gotify.db (Benutzer, Applications, Token, Meldungen) und images (Symbole) sichern.
Im Test lief der Container als root; laut Dokumentation geht auch eine eigene UID (nicht getestet):
# Vor dem ersten Start, Beispiel mit UID und GID 1234
sudo chown -R 1234:1234 /opt/gotify/gotify_data
# Ergaenzung in compose.yaml unterhalb des Dienstes gotify
user: "1234:1234"
Bei Umstellung die Eigentumsrechte des Datenverzeichnisses anpassen.
Sichere Netzwerkfreigabe, Reverse Proxy und TLS
Gotify spricht unverschlüsseltes HTTP, Passwort und Token gehen im Klartext. Außerhalb vertrauenswürdiger Netze ist TLS Pflicht.
Empfohlen: Reverse Proxy mit WebSocket-Weiterleitung. Die nginx-Konfiguration folgt der offiziellen Dokumentation, nicht getestet:
server {
listen 443 ssl;
http2 on;
server_name push.example.com;
ssl_certificate /etc/letsencrypt/live/push.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/push.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:18080;
proxy_http_version 1.1;
# Notwendig fuer den WebSocket-Stream
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Bilder von Applications koennen groesser sein
client_max_body_size 10M;
# Lange Standzeit fuer den Stream
proxy_read_timeout 3600s;
}
}
Hinter dem Proxy in der .env GOTIFY_SERVER_SECURECOOKIE=true und GOTIFY_SERVER_TRUSTEDPROXIES (Proxy-Adresse) setzen.
Alternativ TLS mit Let's Encrypt direkt in Gotify (GOTIFY_SERVER_SSL_ENABLED, GOTIFY_SERVER_SSL_LETSENCRYPT_ENABLED, GOTIFY_SERVER_SSL_LETSENCRYPT_ACCEPTTOS, GOTIFY_SERVER_SSL_LETSENCRYPT_HOSTS), braucht Ports 80/443 und öffentlichen DNS-Namen. Nicht getestet.
Weboberfläche möglichst auf Firmennetz oder VPN beschränken; nur /message muss erreichbar sein.
Verifizieren: curl -sI https://push.example.com/version liefert HTTP 200, und neue Meldungen erscheinen in der Weboberfläche ohne Neuladen.
Backup und Wiederherstellung
SQLite nicht im laufenden Betrieb kopieren; Container kurz anhalten:
cd /opt/gotify
# Dienst anhalten, damit die SQLite-Datei konsistent ist
docker compose stop
# Datenverzeichnis inklusive Datenbank und Bildern sichern
sudo tar czf /var/backups/gotify-$(date +%F).tar.gz -C /opt/gotify gotify_data
# Dienst wieder starten
docker compose start
Verifizieren: tar tzf auf das Archiv listet gotify_data/gotify.db, und /version antwortet nach dem Start wieder mit 3.1.1 (im Test so ausgeführt).
Wiederherstellung testen; im Test waren danach beide Meldungen wieder da:
cd /opt/gotify
# Dienst anhalten
docker compose stop
# ACHTUNG: loescht den aktuellen Datenbestand unwiderruflich
sudo rm -rf /opt/gotify/gotify_data
# Stand aus dem Archiv zurueckspielen
sudo tar xzf /var/backups/gotify-2026-09-16.tar.gz -C /opt/gotify
# Dienst starten und pruefen
docker compose start
curl -s -u admin:IHR-PASSWORT "http://127.0.0.1:18080/message?limit=5"
Verifizieren: Meldungen, Applications und Token sind wieder vorhanden (im Test IDs 1 und 2 mit den ursprünglichen Titeln).
Das Image enthält kein sqlite3. Ohne Unterbrechung sichern Sie per Host-Werkzeug oder mit MySQL/PostgreSQL.
Updates und Rollback
Feste Version statt latest, sonst kein planbarer Rollback.
cd /opt/gotify
# Immer zuerst sichern
docker compose stop
sudo tar czf /var/backups/gotify-vor-update-$(date +%F).tar.gz -C /opt/gotify gotify_data
docker compose start
# Versionsangabe in compose.yaml auf die neue Version aendern, dann
docker compose pull
docker compose up -d
# Kontrolle
docker compose ps
curl -s http://127.0.0.1:18080/version
Gotify migriert das Schema beim Start; Rollback braucht alte Image-Version und altes Datenarchiv.
Für 2.x auf 3.x den Migrationsleitfaden lesen.
Typische Fehler und Diagnose
HTTP 401 beim Senden. Meist Client-Token (gtfyc.) statt Application-Token (gtfya.). Verlorenen Token rotieren.
Passwortänderung in der .env wirkt nicht. Gilt nur beim ersten Start, siehe Erstkonfiguration. Datenbank nicht löschen, sonst sind alle Token weg.
Container bleibt auf health: starting oder wird unhealthy. Normal unter einer Minute. Sonst docker compose logs, meist fehlende Schreibrechte.
Neue Meldungen erst nach Neuladen. WebSocket-Weiterleitung fehlt, siehe nginx-Konfiguration.
Im Log steht immer die Proxy-IP. GOTIFY_SERVER_TRUSTEDPROXIES setzen.
Port bereits belegt. ss -ltnp | grep 18080 zeigt den Prozess.
Backup-Skript hängt. --max-time 10 setzen, siehe Integration.
Saubere Deinstallation
Warnung: Die folgenden Befehle löschen alle Meldungen, Benutzer, Applications und Token unwiderruflich. Vorher ein Archiv außerhalb von /opt/gotify ablegen.
cd /opt/gotify
# Optionale Sicherung vor dem Entfernen
docker compose stop
sudo tar czf /var/backups/gotify-final-$(date +%F).tar.gz -C /opt/gotify gotify_data
# Container und Netzwerk entfernen, benannte Volumes loeschen
docker compose down -v
# ACHTUNG: loescht Datenbank, Bilder und Konfiguration endgueltig
sudo rm -rf /opt/gotify
# Image entfernen, falls nicht mehr benoetigt
docker image rm gotify/server:3.1.1
Verifizieren: docker compose ps -a zeigt keine Container, http://127.0.0.1:18080/version antwortet nicht mehr (im Test so geprüft). Entfernen Sie auch Serverblock und Zertifikat im Reverse Proxy.
Fazit für den Betrieb
Ein Container, eine SQLite-Datei, rund 5 MiB RAM: Für Alarme ohne Cloud ist der Aufwand gering.
Ohne iOS-Client und Eskalation: dann ntfy bzw. eine Alerting-Plattform.
Passende Anleitungen auf S-EDV
- ntfy mit Docker installieren: Alternative mit iOS-Client.
- ntfy auf der Synology-NAS betreiben
- Uptime Kuma mit Docker und Nginx Proxy Manager installieren: Überwachung als Meldequelle.


