Skip to content

🔔 Service: Gotify (Notification Hub)

Location: /mnt/pool01/homelab/services/ops-stack/gotify

Gotify receives alerts from all automation apps and pushes them instantly to my mobile device via WebSocket. It runs as a lightweight, standalone service within the Ops Stack.


Docker Compose

networks:
  dockerapps-net:
    external: true

services:
  gotify:
    image: gotify/server
    container_name: gotify
    networks:
      dockerapps-net:
        ipv4_address: 172.20.0.16
        ipv6_address: fd00:dead:beef:2::16
    ports:
      - 8081:80
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - GOTIFY_SERVER_PORT=80
    volumes:
      - ./data:/app/data
    restart: unless-stopped

Token Management

Gotify uses two distinct types of tokens. It is critical to use the correct token prefix for the intended task, or the integration will silently fail.

Application Tokens (A...)

Used by services to SEND messages to the Gotify server.

Service Integration Method Trigger Events
Jellyfin Webhook Plugin Playback Started, Playback Stopped, New Media
Radarr Native (Connect) On Grab, On Import, On Upgrade
Sonarr Native (Connect) On Grab, On Import, On Upgrade
Seerr Native (Notifications) Request Pending, Request Approved
WUD Native (Triggers) New Image Available
Beszel Native (Shoutrrr) High CPU/RAM, High Disk Usage, Agent Offline

Client Tokens (C...)

Used by devices to RECEIVE messages from the Gotify server.

Client Usage
Android App Receives real-time push notifications on mobile devices
Homepage Displays an unread message count on the dashboard widget

Token Troubleshooting

If notifications suddenly stop working for a specific app, the first step is to check the Application Token in the sending service (e.g., Radarr) and ensure it perfectly matches the active token listed in the Gotify Web UI.


Jellyfin Webhook Integration Guide

Jellyfin does not have native Gotify support out-of-the-box. We use the Webhook plugin to bridge the gap and send formatted JSON payloads.

Step 1: Install the Plugin

  • Go to Jellyfin Dashboard > Plugins > Catalog
  • Install the Webhook plugin and restart the Jellyfin container

Step 2: Configure the Destination

  • Navigate to Dashboard > Plugins > Webhook.
  • Click Add Destination and select Gotify.
  • Webhook URL: http://172.20.0.16:80/message?token=<YOUR_APP_TOKEN>

Routing Bypass

Notice we use the internal static IP (172.20.0.16) and port 80 instead of the public https://gotify.mydomain.xyz. This keeps the traffic entirely inside the dockerapps-net Docker bridge, bypassing the external proxy and Caddy routing. It is faster and more reliable!

Step 3: Configure Templates (The Logic)

Raw webhooks are ugly JSON blobs. We use the official Jellyfin Webhook Templates to format them into readable text.

  • Create a new Playback Start event:
    • Item Type: Movies, Episodes
    • Template: Copy and paste the raw text from Templates/Gotify/PlaybackStart.handlebars.
  • Create a new Playback Stop event:
    • Item Type: Movies, Episodes
    • Template: Copy and paste the raw text from Templates/Gotify/PlaybackStop.handlebars.

Security & Access

Gotify is exposed to the public internet so our mobile phones can receive pushes while away from home. It requires strict security wrapping.

  • Ingress: Accessed via the Caddy Reverse Proxy (https://gotify.mydomain.xyz).
  • Protection: Secured by the CrowdSec Bouncer and GeoIP filtering (Singapore Only).
  • WebSocket Magic: Caddy is configured to automatically upgrade the connection to WebSocket (wss://). This allows the Android app to hold an open, low-battery connection for real-time pushes without constantly polling the server.

Maintenance & Backups

  • Database: Gotify uses a flat SQLite database (gotify.db) stored in ./gotify/data.
  • Images: Uploaded application icons are stored in ./gotify/data/images.
  • Backups: Because all data is contained in the ./data directory, it is automatically captured, deduplicated, and encrypted by the daily Kopia snapshot to Cloudflare R2.