Notifications (Gotify + Apprise + Loggifly)¶
The push stack is Gotify as the notification server (phone app plus API), Apprise as the multi-provider bridge, Loggifly as the log-watcher that fires notifications from container logs, and ntfy as the lighter alternative. I use Gotify when I want app notifications from Radarr, Sonarr, Jellyseer or Authentik with full control. ntfy is fine when you just need topic-based pub/sub.
Architecture¶
apps (Arrs, Jellyseer, Authentik, containers)
│
├── direct Gotify app tokens
├── Apprise (bridges to Gotify / email / etc.)
└── Loggifly (watches container logs, matches keywords)
│ └── for SWAG 401/403: tails FluentBit's stdout, not SWAG directly
Gotify ──> phone app / web UI
Gotify¶
Standard docker-compose install with env config (see config docs):
services:
gotify:
image: gotify/server
container_name: gotify
ports:
- 2212:80
volumes:
- './data:/app/data'
user: "1000:1000"
environment:
GOTIFY_SERVER_PORT: 80
Create an app per client in the Gotify UI (one for Radarr, one for Sonarr, etc.) — each gets its own token. Test:
curl -X POST "http://<server>:2212/message?token=<app-token>" -d '{"title":"test","message":"hello"}'
Gotify token handling
When wiring Gotify into other stacks, pass the token as an env var and keep the base URL token-free (GOTIFY_URL=/message, GOTIFY_TOKEN appended as ?token=) — some clients append the query themselves and double-token URLs break.
Apprise¶
Install Apprise alongside Gotify; it accepts Gotify URLs (gotify://host:port/token) so any app that supports Apprise can target Gotify, and it can fan out to other providers. Both stateless (URL-only) and stateful (keyfile) setups are covered in the full post.
- Arr apps: point Radarr/Sonarr/Lidarr at an Apprise URL (public domain via Pangolin or Cloudflare).
- Jellyseer / Beszel: native webhook → Apprise or Gotify.
- Authentik: use notification transports with webhook body/header mappings (see Authentik).
Loggifly¶
Loggifly watches Docker container logs and pushes Gotify messages on keyword matches — the glue for services with no native notification support. Config is a simple TOML/YAML with monitored keywords per container; the full config example is in the post.
For SWAG's 401/403 alerts, Loggifly doesn't read SWAG's log directly — it tails FluentBit's stdout (FluentBit parses the JSON access log). That tap, and the single-file-bind-mount replay bug that made it spam identical alerts, is on the logging page.
ntfy (lighter alternative)¶
ntfy is topic-based pub/sub with a phone app. For a private server, set in server.yml:
Then create an admin user and an access token inside the container:
docker exec -it ntfy sh
ntfy user add --role=admin <username>
ntfy token add <username> # non-expiring token for pushes
Subscribe to a test topic from your devices and push with the token.
Watchdog¶
The pve-health-watchdog posts to Gotify on the same server, silent when healthy. It uses the non-/v1 push path on this build:
/v1/* returns 404 on this build, so don't "fix" a working push by adding /v1. Priority 7 is critical, 5 is warning.
The false alarm that produced the debounce design is in the blog post The false alarm that became the watchdog, and the log-matcher diagnosis discipline is in The 401 spam that wasn't HA.
See also¶
- Authentik — webhook-driven notifications
- Blog posts: ntfy + Loggifly · The holy grail of self-hosted notifications