Gotify Push Notification Server

Gotify is a self-hosted push notification server that provides a simple REST API for sending messages to Android and web clients via WebSocket. Unlike ntfy, Gotify uses an application-based model where senders use per-application tokens, making it well-suited for alerting from multiple services with centralized management.

Prerequisites

  • Linux VPS with Docker installed
  • A domain name with DNS pointing to your server
  • Nginx or Traefik for HTTPS termination
  • Port 80 or a custom port open

Installing Gotify with Docker

# Create data directory
sudo mkdir -p /opt/gotify/data

# Run Gotify with Docker
docker run -d \
  --name gotify \
  --restart unless-stopped \
  -p 8080:80 \
  -v /opt/gotify/data:/app/data \
  -e GOTIFY_DEFAULTUSER_PASS="ChangeThisPassword!" \
  gotify/server:latest

# Verify it's running
docker ps | grep gotify
docker logs gotify --tail 20

Docker Compose Setup

# docker-compose.yml
version: '3'

services:
  gotify:
    image: gotify/server:latest
    container_name: gotify
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      GOTIFY_DEFAULTUSER_PASS: "ChangeThisSecurePassword!"
      GOTIFY_SERVER_PORT: 80
      GOTIFY_DATABASE_DIALECT: sqlite3
      GOTIFY_DATABASE_CONNECTION: data/gotify.db
      GOTIFY_DEFAULTUSER_NAME: admin
    volumes:
      - gotify_data:/app/data

volumes:
  gotify_data:
docker compose up -d

# Access the web UI at http://your-server-ip:8080
# Default credentials: admin / ChangeThisSecurePassword!

Binary Installation (Non-Docker)

# Download latest binary
GOTIFY_VERSION=$(curl -s https://api.github.com/repos/gotify/server/releases/latest | grep -o '"tag_name":"[^"]*"' | cut -d'"' -f4)
wget "https://github.com/gotify/server/releases/download/${GOTIFY_VERSION}/gotify-linux-amd64.zip"
unzip gotify-linux-amd64.zip
sudo mv gotify-linux-amd64 /usr/local/bin/gotify
chmod +x /usr/local/bin/gotify

# Create data directory
sudo mkdir -p /var/lib/gotify

# Create systemd service
sudo tee /etc/systemd/system/gotify.service << 'EOF'
[Unit]
Description=Gotify Push Notification Server
After=network.target

[Service]
Type=simple
User=nobody
WorkingDirectory=/var/lib/gotify
ExecStart=/usr/local/bin/gotify
Environment=GOTIFY_DEFAULTUSER_PASS=SecurePassword
Environment=GOTIFY_DATABASE_DIALECT=sqlite3
Environment=GOTIFY_DATABASE_CONNECTION=gotify.db
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable --now gotify

Application Management

Gotify uses "applications" as namespaced senders, each with its own token:

Via Web UI

  1. Log in at http://your-server:8080
  2. Click AppsCreate Application
  3. Name: Server Monitoring, Description: Alerts from monitoring scripts
  4. Click Create — save the returned token

Via REST API

# Create an application
curl -X POST "http://localhost:8080/application" \
  -u "admin:ChangeThisPassword!" \
  -H "Content-Type: application/json" \
  -d '{"name": "Server Monitoring", "description": "Monitoring alerts"}'
# Returns: {"id": 1, "token": "Axxxxxxxxxxxxxxxx", ...}

# List all applications
curl "http://localhost:8080/application" \
  -u "admin:ChangeThisPassword!"

# Delete an application
curl -X DELETE "http://localhost:8080/application/1" \
  -u "admin:ChangeThisPassword!"

# Create a second application for deployments
curl -X POST "http://localhost:8080/application" \
  -u "admin:ChangeThisPassword!" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI/CD Pipeline", "description": "Deployment notifications"}'

Message Priorities and Formatting

# Send a basic message using an application token
curl -X POST "http://localhost:8080/message" \
  -H "X-Gotify-Key: Axxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Deployment Complete",
    "message": "Version 1.2.3 deployed successfully",
    "priority": 5
  }'

# Priority levels:
# 1-3: Low (informational)
# 4-7: Normal
# 8-10: High (urgent alerts)

# Send with extras (clickable URL)
curl -X POST "http://localhost:8080/message" \
  -H "X-Gotify-Key: Axxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Disk Warning",
    "message": "Disk usage at 90% on web-01",
    "priority": 8,
    "extras": {
      "client::notification": {
        "click": {"url": "https://grafana.yourdomain.com/d/server-metrics"}
      }
    }
  }'

# Send Markdown formatted message
curl -X POST "http://localhost:8080/message" \
  -H "X-Gotify-Key: Axxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Weekly Report",
    "message": "## Summary\n\n- **Uptime**: 99.9%\n- **Requests**: 1.2M\n- **Errors**: 42",
    "priority": 3,
    "extras": {
      "client::display": {
        "contentType": "text/markdown"
      }
    }
  }'

Client Plugins

Android App

  1. Install Gotify from F-Droid or Google Play
  2. Settings → Add Server
  3. Server URL: https://gotify.yourdomain.com
  4. Username: admin, Password: your password
  5. Subscribe to applications to receive push notifications

Web UI / Browser Notifications

The built-in Gotify web UI supports Web Push notifications. Enable in browser settings when visiting https://gotify.yourdomain.com.

REST API Usage

# Get all messages
curl "http://localhost:8080/message" \
  -u "admin:password"

# Get messages with pagination
curl "http://localhost:8080/message?limit=20&since=100" \
  -u "admin:password"

# Delete all messages for an application
curl -X DELETE "http://localhost:8080/application/1/message" \
  -u "admin:password"

# Delete a specific message
curl -X DELETE "http://localhost:8080/message/42" \
  -u "admin:password"

# Get all clients (connected WebSocket sessions)
curl "http://localhost:8080/client" \
  -u "admin:password"

# Create additional users
curl -X POST "http://localhost:8080/user" \
  -u "admin:password" \
  -H "Content-Type: application/json" \
  -d '{"name": "alice", "pass": "AlicePassword123", "admin": false}'

WebSocket Subscription

// Listen for messages via WebSocket
const ws = new WebSocket('wss://gotify.yourdomain.com/stream?token=Cxxxxxxxxxxxxxxxx')

ws.onmessage = (event) => {
    const msg = JSON.parse(event.data)
    console.log(`[${msg.appid}] ${msg.title}: ${msg.message}`)
}

ws.onclose = () => {
    console.log('Connection closed - reconnecting in 5s')
    setTimeout(connect, 5000)
}

Monitoring Integration

Uptime Kuma

  1. Go to a monitor → Edit → Notifications
  2. Add notification → type: Gotify
  3. Server URL: https://gotify.yourdomain.com
  4. Application Token: your app token
  5. Priority: 8

Grafana

# Grafana → Alerting → Contact Points → New
# Type: Webhook
# URL: http://localhost:8080/message
# HTTP Method: POST
# Content-Type header: application/json
# Authorization header: Bearer Axxxxxxxxxxxxxxxx
# Template (message body):
{
  "title": "{{ .Status }}: {{ .GroupLabels.alertname }}",
  "message": "{{ range .Alerts }}{{ .Annotations.description }}{{ end }}",
  "priority": 8
}

Backup Script Notifications

#!/bin/bash
# /usr/local/bin/backup-with-notify.sh

GOTIFY_URL="http://localhost:8080"
GOTIFY_TOKEN="Axxxxxxxxxxxxxxxx"
HOSTNAME=$(hostname)

notify() {
    local title="$1"
    local message="$2"
    local priority="${3:-5}"
    curl -s -X POST "$GOTIFY_URL/message" \
      -H "X-Gotify-Key: $GOTIFY_TOKEN" \
      -H "Content-Type: application/json" \
      -d "{\"title\":\"$title\",\"message\":\"$message\",\"priority\":$priority}" \
      > /dev/null
}

# Send start notification
notify "Backup Started" "Backup started on $HOSTNAME at $(date)" 3

# Run backup
if /usr/local/bin/borg-backup.sh; then
    notify "Backup Complete" "Backup completed successfully on $HOSTNAME" 3
else
    notify "BACKUP FAILED" "Backup FAILED on $HOSTNAME at $(date). Check logs!" 10
fi

Nginx Reverse Proxy

# /etc/nginx/sites-available/gotify
server {
    server_name gotify.yourdomain.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_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;

        # Disable buffering for WebSocket and stream
        proxy_buffering off;
        proxy_read_timeout 3600s;
    }
}
sudo ln -s /etc/nginx/sites-available/gotify /etc/nginx/sites-enabled/
sudo certbot --nginx -d gotify.yourdomain.com
sudo systemctl reload nginx

Troubleshooting

Android app not receiving push notifications:

# Verify token is correct in the app
# Check Gotify server is accessible from the internet
curl -I https://gotify.yourdomain.com

# Check WebSocket connectivity
curl --include \
  --no-buffer \
  --header "Upgrade: websocket" \
  --header "Connection: Upgrade" \
  https://gotify.yourdomain.com/stream?token=Cxxxxxxxxxxxxxxxx

Messages not appearing in web UI:

# Check message was created
curl "http://localhost:8080/message" -u "admin:password" | python3 -m json.tool

# Check database permissions
ls -la /opt/gotify/data/
docker exec gotify ls /app/data/

Gotify container keeps restarting:

# Check container logs
docker logs gotify --tail 50

# Verify data directory permissions
sudo chown -R nobody:nobody /opt/gotify/data
# Or check what user the container runs as
docker exec gotify id

Conclusion

Gotify provides a clean, application-scoped push notification service that works well as a centralized alerting hub for Linux infrastructure. Its simple REST API and WebSocket-based delivery make integration with monitoring tools, backup scripts, and CI/CD pipelines straightforward. The F-Droid Android client avoids Google Play dependencies, making it suitable for privacy-conscious environments.