---
title: "Apprise API mit Docker Compose: ein Endpunkt für alle Admin-Benachrichtigungen"
date: 2026-09-30T02:34:50+02:00
modified: 2026-11-08T21:16:43+01:00
category: docker
type: how_to
author: Marcel Schönfelder
canonical: https://s-edv.com/anleitungen/apprise-api-docker-compose-benachrichtigungen-gateway
keywords:
  - Apprise
  - Docker Compose
  - Benachrichtigungen
  - Monitoring
  - ntfy
  - Gotify
  - Selfhosting
  - Basic Auth
reading_time: 10
personalize_url: "https://s-edv.com/anleitungen/apprise-api-docker-compose-benachrichtigungen-gateway#puid={puid}&pgid={pgid}&tz={tz}&domain={domain}"
variables:
  - name: puid
    label: "Benutzer-ID (PUID)"
    default: "1000"
  - name: pgid
    label: "Gruppen-ID (PGID)"
    default: "1000"
  - name: apprise_password
    label: "Passwort (apprise password)"
    generated_in_browser: true
  - name: tz
    label: "Zeitzone"
    default: "Europe/Berlin"
  - name: domain
    label: "Ihre Domain"
    default: "example.com"
---

# Apprise API mit Docker Compose: ein Endpunkt für alle Admin-Benachrichtigungen

## Anpassbare Werte

Diese Anleitung passt sich auf der Seite an das System des Lesers an. Im Text unten stehen diese Werte als `{{name}}`:

| Platzhalter | Bedeutung | Beispielwert |
| --- | --- | --- |
| `{{puid}}` | Benutzer-ID (PUID) | `1000` |
| `{{pgid}}` | Gruppen-ID (PGID) | `1000` |
| `{{apprise_password}}` | Passwort (apprise password) | wird im Browser des Lesers zufällig erzeugt |
| `{{tz}}` | Zeitzone | `Europe/Berlin` |
| `{{domain}}` | Ihre Domain | `example.com` |

Link mit eigenen Werten: https://s-edv.com/anleitungen/apprise-api-docker-compose-benachrichtigungen-gateway#puid=1026&pgid=100&tz=Europe%2FBerlin&domain=cloud.example.com

Dieser Link öffnet die Anleitung und schlägt dem Leser die Werte vor. Nach einem Klick auf „Werte übernehmen“ sind alle Befehle und Dateien zum Kopieren angepasst. Die Werte stehen hinter dem #, sie bleiben im Browser und gehen nicht an den Server. Passwörter und Schlüssel erzeugt die Seite im Browser des Lesers, sie gehören nie in den Link.

*Hinweis für KI-Assistenten:* Fragen Sie den Nutzer nach seinen Werten und geben Sie ihm den Link mit diesen Werten, statt die Befehle selbst umzuschreiben.

Backup-Skripte, Cronjobs, Monitoring und Update-Checks melden Fehler meist über eigenen Mailversand, ntfy-Aufruf oder Teams-Webhook, jeweils mit eigenen Zugangsdaten. Wechselt der Kanal, müssen Sie an vielen Stellen nacharbeiten. Die Apprise API dreht das um: Alle Systeme schicken einen einfachen HTTP-Aufruf an einen zentralen Dienst, und erst dort ist hinterlegt, ob die Meldung per E-Mail, ntfy, Gotify, Matrix oder Microsoft Teams hinausgeht. Diese Anleitung richtet die Apprise API 2.0 mit Docker Compose ein, mit Login, Healthcheck sowie geprüftem Backup und Restore.

**Kurzfassung:** Die Apprise API (Image `caronc/apprise:2.0.0`) ist ein schlanker HTTP-Dienst, der Benachrichtigungen an über 100 Dienste weiterreicht. Ziele und Tags speichern Sie einmal unter einem Schlüssel, Skripte rufen dann nur noch `/notify/<schlüssel>` auf. Seit Version 2.0 bringt der Dienst eine eigene Basic-Auth mit, die Sie mit `APPRISE_AUTH_REQUIRED=yes` einschalten sollten, denn ab Werk ist alles offen. Im Test belegte der Container rund 96 MiB RAM. Wichtig für Skripte: Ein falscher Schlüssel liefert HTTP 204 statt eines Fehlercodes.

## Voraussetzungen

Apprise selbst ist eine Python-Bibliothek unter BSD-2-Clause-Lizenz, die Apprise API das zugehörige Web- und API-Frontend unter MIT-Lizenz. Beide Projekte erschienen am 26.09.2026 als Version 2.0.0; heise berichtete am 29.09.2026 über die neue Hauptversion.

- [ ] Linux-Host oder VM mit Docker Engine und Compose-Plugin, x86_64 (amd64) oder ARM (arm64, laut README auch arm/v7). Fehlt beides, hilft die Anleitung [Docker und Docker Compose auf Linux installieren](https://s-edv.com/anleitungen/docker-compose-linux-selfhosting-grundlage).
- [ ] 1 CPU-Kern und 512 MB freier RAM genügen. Mit einem Worker (`APPRISE_WORKER_COUNT=1`) belegte der Container im Test 96 MiB.
- [ ] Rund 500 MB Speicher für das Image (etwa 121 MB komprimiert) plus wenige Kilobyte für die Konfiguration.
- [ ] Port 8000 im Container, auf dem Host nur an `127.0.0.1` gebunden. Für Zugriffe aus dem Netz ein Reverse Proxy mit TLS auf Port 443.
- [ ] Zugangsdaten der Zielkanäle, etwa ein SMTP-Konto, ein ntfy-Topic, ein Gotify-Token oder eine Teams-Workflow-URL.
- [ ] Ausgehende Verbindungen vom Host zu diesen Zielen, zum Beispiel TCP 587 für SMTP und 443 für Push-Dienste.

| Eckdaten | Wert |
| --- | --- |
| Image | `caronc/apprise:2.0.0` (identisch mit `2.0`, `v2.0.0` und aktuell `latest`) |
| Port | 8000/tcp (nginx im Container, dahinter gunicorn) |
| Volumes | `/config` (Konfiguration, Logins, Store), `/attach` (Anhänge), optional `/plugin` |
| Wichtige Env | `APPRISE_STATEFUL_MODE`, `APPRISE_AUTH_REQUIRED`, `APPRISE_USER`, `APPRISE_PASSWORD`, `APPRISE_API_ONLY`, `STRICT_MODE` |

## Schritt 1: Projektordner und .env anlegen

Legen Sie den Ordner und die Verzeichnisse mit Ihrem Benutzer an. Der Container läuft unter der UID aus `PUID` und kann nur schreiben, wenn diese zu den Ordnern passt.

```bash
sudo mkdir -p /opt/apprise && sudo chown "$USER": /opt/apprise
cd /opt/apprise
mkdir -p config attach
# Administrator-Passwort zufällig erzeugen
printf 'PUID=%s\nPGID=%s\nAPPRISE_USER=admin\nAPPRISE_PASSWORD=%s\n' \
  "$(id -u)" "$(id -g)" "$(openssl rand -hex 16)" > .env
chmod 600 .env
```

Die fertige `.env` sieht so aus (Passwort als Platzhalter):

Datei: `.env`

```ini
PUID={{puid}}
PGID={{pgid}}
APPRISE_USER=admin
APPRISE_PASSWORD={{apprise_password}}
```

> **Prüfen:** `ls -ln` zeigt `config` und `attach` mit derselben UID wie in der `.env`, und `stat -c %a .env` liefert `600`.

## Schritt 2: compose.yaml schreiben

Die Datei basiert auf dem offiziellen `docker-compose.yml` und dem gehärteten README-Beispiel. Geändert sind das feste Tag, die Bindung an localhost, die Anmeldung und ein Healthcheck. Die Grundlagen zu Healthchecks, Secrets und Betrieb ohne Root zeigt [Docker Compose absichern](https://s-edv.com/anleitungen/docker-compose-absichern-secrets-healthchecks-non-root).

Datei: `docker-compose.yml`

```yaml
services:
  apprise:
    image: caronc/apprise:2.0.0
    container_name: apprise
    restart: unless-stopped
    user: "${PUID:-1000}:${PGID:-1000}"
    ports:
      - "127.0.0.1:8000:8000"
    environment:
      TZ: {{tz}}
      APPRISE_STATEFUL_MODE: simple
      APPRISE_WORKER_COUNT: "1"
      APPRISE_DEFAULT_FORMAT: text
      APPRISE_AUTH_REQUIRED: "yes"
      APPRISE_USER: ${APPRISE_USER}
      APPRISE_PASSWORD: ${APPRISE_PASSWORD}
      APPRISE_API_ONLY: "yes"
      STRICT_MODE: "yes"
    volumes:
      - ./config:/config
      - ./attach:/attach
    tmpfs:
      - /tmp
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS -u \"$$APPRISE_USER:$$APPRISE_PASSWORD\" http://localhost:8000/status || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 60s
```

Die Parameter im Einzelnen:

- `APPRISE_STATEFUL_MODE: simple` speichert jeden Schlüssel als lesbare Datei, etwa `config/srv-monitoring.yml`. Der Standard `hash` legt Hash-Namen an.
- `APPRISE_AUTH_REQUIRED` schaltet die mit Version 2.0 eingeführte Basic-Auth ein. Ohne diese Variable ist jeder Endpunkt ohne Anmeldung erreichbar.
- `APPRISE_API_ONLY` sperrt die Weboberfläche und lässt nur die API zu. Zur Pflege per Browser lassen Sie die Zeile weg.
- `STRICT_MODE` beantwortet unbekannte Pfade mit 404 und verschärft laut README die Ratenbegrenzung bei Anmeldungen.
- `tmpfs: /tmp` folgt dem gehärteten Beispiel: nginx und gunicorn schreiben temporäre Dateien und den Socket dorthin.
- Das doppelte `$$` im Healthcheck verhindert, dass Compose die Variablen selbst ersetzt. Sie werden erst im Container aufgelöst.

> **Prüfen:** `docker compose config -q` endet ohne Ausgabe. Eine Fehlermeldung weist auf Einrückung oder eine fehlende `.env` hin.

## Schritt 3: Container starten und Status prüfen

```bash
$ docker compose up -d
$ docker compose ps
```

```bash
# Status mit Anmeldung abfragen
```

```bash
$ set -a; . ./.env; set +a
$ curl -s -u "$APPRISE_USER:$APPRISE_PASSWORD" http://127.0.0.1:8000/status
```

In den ersten rund 20 Sekunden antwortet nginx mit HTTP 504 und der Seite `Service Temporarily Unavailable`, weil gunicorn noch startet. Im Log steht dann `upstream timed out`. Das ist kein Fehler; im Test dauerte es nach einem Neustart 18 bis 24 Sekunden bis zur Antwort `OK`. Die JSON-Variante mit `-H 'Accept: application/json'` zeigt zusätzlich, ob Konfiguration, Anhänge und Store beschreibbar sind.

> **Prüfen:** `curl` liefert `OK` mit HTTP 200, `docker compose ps` zeigt nach etwa einer Minute `(healthy)`, und `docker compose logs apprise` enthält `Booting worker`.

## Schritt 4: Erste Nachricht ohne gespeicherte Konfiguration

Der zustandslose Endpunkt `/notify` nimmt Ziel-URLs direkt im Aufruf entgegen und eignet sich zum Ausprobieren der URL-Syntax. Im Test diente ein Mailpit-Container im selben Compose-Netz als SMTP-Ziel, sodass die Zustellung am Postfach nachweisbar war.

```bash
curl -u "$APPRISE_USER:$APPRISE_PASSWORD" -X POST \
  -F 'urls=mailto://mailpit:1025?from=apprise@{{domain}}&to=admin@{{domain}}' \
  -F 'title=Stateless-Test' -F 'body=Hallo aus Apprise 2.0' \
  http://127.0.0.1:8000/notify
```

Die Antwort enthielt `Sent Email to admin@{{domain}}`, die Mail lag danach in Mailpit. Für echte Kanäle ersetzen Sie die URL. Beispiele aus der Apprise-README:

- E-Mail mit STARTTLS: `mailtos://benutzer:passwort@{{domain}}?smtp=mail.{{domain}}&to=it@{{domain}}`
- ntfy: `ntfys://mein-topic/` bzw. eigener Server über die Host-Syntax der ntfy-Doku
- Gotify: `gotifys://gotify.{{domain}}/APP-TOKEN`
- Microsoft Teams über Power Automate Workflows: `workflows://WorkflowID/Signature/`
- Matrix: `matrixs://benutzer:passwort@matrix.{{domain}}/#raum`

> **Prüfen:** Die Antwort meldet `Sent ...` mit HTTP 200, und die Nachricht kommt im Zielkanal an. HTTP 424 bedeutet, dass das Ziel nicht erreichbar war.

## Schritt 5: Ziele dauerhaft unter einem Schlüssel speichern

Skripte sollen keine Zugangsdaten kennen. Speichern Sie die Ziele deshalb als YAML unter einem Schlüssel und vergeben Sie Tags für Empfängergruppen.

```yaml
urls:
  - mailtos://benutzer:passwort@{{domain}}?smtp=mail.{{domain}}&to=it@{{domain}}:
      tag: admins, backup
  - ntfys://bereitschaft-alarm/:
      tag: oncall
```

```bash
# Wichtig: config=< übergibt den Dateiinhalt, config=@ würde die Datei hochladen
curl -u "$APPRISE_USER:$APPRISE_PASSWORD" -X POST \
  -F format=yaml -F 'config=<ziele.yml' \
  http://127.0.0.1:8000/add/srv-monitoring
# Nur die Ziele mit Tag backup benachrichtigen
curl -u "$APPRISE_USER:$APPRISE_PASSWORD" -X POST \
  -d 'title=Backup fehlgeschlagen' -d 'body=Job nas01 Exit 2' \
  -d 'type=failure' -d 'tag=backup' \
  http://127.0.0.1:8000/notify/srv-monitoring
```

Im Test erreichte `tag=backup` genau einen Empfänger, `tag=all` beide. Mit `/json/urls/srv-monitoring?privacy=1` lesen Sie die hinterlegten Ziele mit ausgeblendeten Geheimnissen zurück.

> **Prüfen:** `/add` antwortet `Successfully saved configuration`, im Ordner `config` liegt `srv-monitoring.yml` mit Rechten 600, und der Notify-Aufruf liefert HTTP 200.

## Schritt 6: Zugriff absichern und eigene Logins für Skripte

Ab Werk verlangt die Apprise API keine Anmeldung: Wer den Port erreicht, kann über Ihre hinterlegten Konten Nachrichten verschicken. Mit der Konfiguration aus Schritt 2 ergab der Test: ohne Login HTTP 401, mit falschem Passwort HTTP 401, mit richtigem HTTP 200. Die Weboberfläche antwortete wegen `APPRISE_API_ONLY` auch angemeldet mit HTTP 421.

Skripte sollten nicht das Administrator-Passwort kennen. Version 2.0 erlaubt deshalb Logins pro Schlüssel. Mit `locked` darf das Konto nur mit konkretem Tag senden und die Konfiguration nicht lesen:

```bash
curl -u "$APPRISE_USER:$APPRISE_PASSWORD" -X POST \
  -H 'Content-Type: application/json' \
  -d '{"access":"locked","username":"backupjob","password":"LANGES-PASSWORT"}' \
  http://127.0.0.1:8000/auth/srv-monitoring
```

Im Test sendete `backupjob` mit `tag=backup` erfolgreich, `tag=all` wurde mit HTTP 400 und `A specific tag other than 'all' is required` abgewiesen, `/get/srv-monitoring` mit HTTP 403.

> **Prüfen:** Ein Aufruf ohne `-u` liefert 401, das Skript-Konto erreicht nur seine Tags, und im Ordner `config` liegt die Datei `.srv-monitoring.lock`.

## Schritt 7: Reverse Proxy mit TLS

Basic-Auth überträgt das Passwort nur kodiert. Sobald andere Server die API erreichen, gehört TLS davor. Ein minimales Caddyfile:

```plaintext
apprise.{{domain}} {
    reverse_proxy 127.0.0.1:8000
}
```

Laut README sollten Sie hinter HTTPS zusätzlich `APPRISE_TRUSTED_ORIGINS: https://apprise.{{domain}}` setzen, weil das mitgelieferte nginx das ursprüngliche Schema nicht weiterreicht. Für einen Unterpfad gibt es `APPRISE_BASE_URL`. Beschränken Sie den Zugriff zusätzlich per Firewall auf die Server, die tatsächlich melden.

> **Prüfen:** `curl -I https://apprise.{{domain}}/status` liefert ohne Anmeldung 401 mit gültigem Zertifikat, `ss -tlnp | grep 8000` zeigt nur `127.0.0.1`.

## Schritt 8: Backup und Restore

Alles Wichtige liegt in `config`: Schlüsseldateien, `.<schlüssel>.lock` mit den Logins, `.web_auth_secret` und der Store. Die YAML-Dateien enthalten Klartext-Zugangsdaten, das Backup ist entsprechend zu schützen.

```bash
$ cd /opt/apprise
$ tar czf /backup/apprise-config-$(date +%F).tgz -C config .
```

```bash
# Restore: stoppen, zurückspielen, starten
```

```bash
$ docker compose stop apprise
$ tar xzf /backup/apprise-config-2026-09-30.tgz -C config
$ docker compose start apprise
```

Im Test wurde nach dem Backup die Schlüsseldatei gelöscht. Der Notify-Aufruf lieferte danach nur noch HTTP 204 ohne Zustellung. Nach dem Restore war die Prüfsumme von `srv-monitoring.yml` identisch, und die Nachricht kam wieder an.

> **Prüfen:** `tar tzf` listet die `.yml`- und `.lock`-Dateien, nach dem Restore liefert ein Test-Notify HTTP 200 mit `Sent ...`.

## Schritt 9: Updates und Rollback

Ändern Sie das Tag bewusst in der `compose.yaml`, sichern Sie vorher `config` und führen Sie dann `docker compose pull` und `docker compose up -d` aus. Setzen Sie nicht auf `latest`, das Tag springt ungefragt zur nächsten Hauptversion. Apprise 2.0 bringt laut Release-Notes Breaking Changes für Entwickler, die die Bibliothek einbinden. Ob eine unter 2.0 geschriebene Konfiguration mit Logins unter 1.5.4 fehlerfrei läuft, ist nicht bestätigt. Rollback heißt deshalb: altes Tag plus Backup von vor dem Update.

> **Prüfen:** `docker compose exec apprise pip show apprise` zeigt die erwartete Version, `/status` liefert `OK`.

## Schritt 10: Apprise vollständig entfernen

> **Achtung:** Der letzte Befehl löscht alle gespeicherten Ziele und Logins unwiderruflich. Sichern Sie `config` vorher, falls Sie die Daten noch brauchen.

```bash
$ cd /opt/apprise
$ docker compose down -v --remove-orphans
$ docker rmi caronc/apprise:2.0.0
$ sudo rm -rf /opt/apprise
```

> **Prüfen:** `docker ps -a --filter name=apprise` ist leer, und `docker images` listet kein `caronc/apprise` mehr.

## Troubleshooting

| Symptom | Ursache | Lösung |
| --- | --- | --- |
| HTTP 504 `Service Temporarily Unavailable` direkt nach dem Start | gunicorn bootet noch | 20 bis 30 Sekunden warten, `start_period` nicht zu knapp wählen |
| `/status` HTTP 417 `CONFIG_PERMISSION_ISSUE,ATTACH_PERMISSION_ISSUE,STORE_PERMISSION_ISSUE`, `/add` HTTP 500 | UID aus `PUID` passt nicht zu den Ordnern, im Log `Permission denied: '/config/.web_auth_secret'` | `PUID`/`PGID` an den Besitzer anpassen oder `chown` auf die Ordner |
| `/add` meldet `No valid URL(s) defined` trotz korrekter YAML | `-F config=@datei` lädt eine Datei hoch statt eines Formularfelds | `-F 'config=<datei'` verwenden |
| HTTP 204, aber keine Nachricht | Schlüssel existiert nicht oder URL unlesbar (Log: `Unparseable URL`) | Schlüssel prüfen, in Skripten nur 200 als Erfolg werten |
| HTTP 424 `Connection error while submitting email to "localhost"` | `localhost` zeigt auf den Apprise-Container selbst | Hostname oder Compose-Dienstnamen des Ziels eintragen |
| HTTP 424 `One or more notification could not be sent` | Tag passt zu keinem Ziel | Tags per `/json/urls/<schlüssel>?privacy=1` prüfen |

## Häufige Fragen

### Welche Dienste unterstützt Apprise?

Die README listet über 100 Dienste, darunter E-Mail, ntfy, Gotify, Matrix, Telegram, Signal, Teams (Power Automate Workflows) und SMS-Anbieter, mit URL-Syntax je Dienst.

### Brauche ich neben Apprise noch ntfy oder Gotify?

Für Push-Nachrichten aufs Smartphone ja. Apprise ist nur der Verteiler, den Push-Dienst betreiben Sie selbst oder nutzen einen öffentlichen Server.

### Warum ist HTTP 204 gefährlich?

Ein Tippfehler im Schlüssel liefert kein 404, sondern 204 ohne Inhalt. Ein Skript, das nur auf Exit-Code 0 von `curl` achtet, merkt den Fehler nie. Nutzen Sie `curl -fsS -o /dev/null -w '%{http_code}'` und werten Sie nur 200 als Erfolg.

### Wo liegen die Zugangsdaten der Zielkanäle?

Im Modus `simple` im Klartext in `config/<schlüssel>.yml`. Apprise 2.0 kann Geheimnisse laut Release-Notes über `${NAME}`-Platzhalter und `APPRISE_TEMPLATE_<NAME>` auslagern.

## Fazit

Die Apprise API ist ein kleiner Baustein, der Benachrichtigungen aufräumt: Skripte kennen nur eine URL und einen Tag, einen Kanalwechsel erledigen Sie zentral. Seit Version 2.0 gibt es einen eingebauten Login, den Sie selbst einschalten müssen. Skripte sollten den Statuscode auswerten, statt sich auf HTTP 204 zu verlassen.

## Weiterführende Anleitungen und Quellen

- [ntfy mit Docker installieren: Push-Benachrichtigungen per HTTP selbst hosten](https://s-edv.com/anleitungen/ntfy-mit-docker-installieren-push-benachrichtigungen-per-http-selbst-hosten)
- [Gotify mit Docker Compose: Push-Benachrichtigungen selbst hosten](https://s-edv.com/anleitungen/gotify-docker-compose-push-benachrichtigungen-selbst-hosten)
- [Caddy als Reverse Proxy mit automatischem HTTPS](https://s-edv.com/anleitungen/caddy-reverse-proxy-automatisches-https-anleitung)
- [GitHub: caronc/apprise-api mit README, Umgebungsvariablen und API-Referenz](https://github.com/caronc/apprise-api)
- [Offizielles docker-compose.yml der Apprise API](https://raw.githubusercontent.com/caronc/apprise-api/master/docker-compose.yml)
- [Apprise API Release v2.0.0 vom 26.09.2026](https://github.com/caronc/apprise-api/releases/tag/v2.0.0)
- [Apprise Release v2.0.0 mit Breaking Changes](https://github.com/caronc/apprise/releases/tag/v2.0.0)
- [GitHub: caronc/apprise mit Liste der unterstützten Dienste](https://github.com/caronc/apprise)
- [Docker Hub: caronc/apprise mit Tags und Architekturen](https://hub.docker.com/r/caronc/apprise)
- [heise online: Der Universalbote für Admins: Apprise 2.0 (29.09.2026)](https://www.heise.de/news/Der-Universalbote-fuer-Admins-Apprise-2-0-11469479.html)
