Skip to main content
Self-hosted ntfy push-notification server — a general-purpose notifier for the homelab. Any service publishes to a topic over HTTP; a subscribed phone or browser gets the alert. riverbot is the first consumer.

Topology

  • Image: binwiederhier/ntfy, swarm stack pinned to debian-2 (lightweight node).
  • Ingress: https://ntfy.augustin.ai via Traefik (port 80 on the overlay).
  • Internal publish: other swarm services post to http://ntfy/<topic> directly on the swarm overlay — no round-trip through Cloudflare.
  • State: ./data holds the message cache (NTFY_CACHE_DURATION=12h) and attachments.

Publishing

# from any container on the swarm overlay
curl -d "a permit opened" \
  -H "Title: Middle Fork Salmon" \
  -H "Priority: 5" \
  -H "Tags: canoe,rotating_light" \
  -H "Click: https://www.recreation.gov/permits/234623" \
  http://ntfy/river-permits
Header values must be ASCII. Emoji go through Tags (ntfy renders matching shortcodes like canoe as 🛶); the message body is UTF-8 and takes anything.

Subscribing

In the ntfy app (iOS/Android) or browser, add server https://ntfy.augustin.ai and the topic name (e.g. river-permits). iOS instant delivery needs NTFY_UPSTREAM_BASE_URL=https://ntfy.sh (set in the compose file): self-hosted servers can’t reach Apple’s push network, so poll requests — topic name only, never message content — are forwarded through ntfy.sh, which triggers APNs. Android and desktop use the WebSocket connection and ignore this.

Access control

Open by default — anyone who knows a topic name can read and write it. To lock down, add NTFY_AUTH_FILE with NTFY_AUTH_DEFAULT_ACCESS=deny-all and provision users via the ntfy user/ntfy access CLI.

Monitoring

Health endpoint: http://ntfy:80/v1/health.