Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Monitoring 16.09.2026 · 10 min Lesezeit

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

Smartphone mit leuchtendem Glockensymbol auf einem Schreibtisch, daneben ein kompakter Server und Seeblick

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.

KriteriumGotify 3.1.1ntfy
AdressierungApplication mit festem Token, vom Server vergebenFrei wählbares Topic im URL-Pfad
Zugriff ohne TokenNicht möglich, jede Anfrage braucht Token oder LoginStandardmäßig offen, Zugriffsschutz muss aktiviert werden
BenutzerverwaltungFest eingebaut, Admin- und Standardbenutzer, OIDC möglichÜber Access-Control-Listen konfigurierbar
Offizielle Mobil-AppsAndroidAndroid und iOS
DatenhaltungSQLite als Standard, MySQL und PostgreSQL möglichSQLite als Cache, primär flüchtig
Typischer EinsatzFester Kreis bekannter Absender und EmpfängerAd-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:

VariableStandardBedeutung
GOTIFY_DEFAULTUSER_NAMEadminName des ersten Administrators, wirkt nur beim allerersten Start
GOTIFY_DEFAULTUSER_PASSadminPasswort des ersten Administrators, wirkt nur beim allerersten Start
GOTIFY_REGISTRATIONfalseErlaubt unangemeldeten Besuchern das Anlegen eigener Konten
GOTIFY_SERVER_PORT80Port im Container, im Image bereits gesetzt
GOTIFY_SERVER_SECURECOOKIEfalseSetzt das Secure-Flag am Sitzungs-Cookie, hinter HTTPS einschalten
GOTIFY_SERVER_TRUSTEDPROXIESleerProxys, deren X-Forwarded-For-Header vertraut wird
GOTIFY_DATABASE_DIALECTsqlite3Datenbanktreiber, alternativ mysql oder postgres
GOTIFY_DATABASE_CONNECTIONdata/gotify.dbPfad beziehungsweise Verbindungszeichenkette der Datenbank
GOTIFY_PASSSTRENGTH10bcrypt-Kostenfaktor für Passwort-Hashes
GOTIFY_LOCALAUTH_ENABLEDtrueAnmeldung 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

Quellen

GotifyDocker ComposePush-BenachrichtigungenMonitoringSelfhostingAlertingSQLite