Self-hosted push notifications with ntfy — publishing, authentication, priorities, and integration patterns for scripts and monitoring
48
52%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./ntfy-notifications/SKILL.mdntfy (pronounce "notify") is a self-hosted push notification service. Send notifications from scripts, cron jobs, and monitoring systems to your phone.
curl -d "Backup completed successfully" https://ntfy.example.com/my-topiccurl -H "Title: Backup Status" \
-H "Priority: high" \
-H "Tags: white_check_mark,backup" \
-d "Daily backup completed in 5 minutes" \
https://ntfy.example.com/backups# Token auth (preferred)
curl -H "Authorization: Bearer YOUR_TOKEN" \
-d "Message here" \
https://ntfy.example.com/topic
# Basic auth
curl -u username:password \
-d "Message here" \
https://ntfy.example.com/topiccurl -H "Click: https://grafana.example.com/d/alerts" \
-H "Title: Disk Alert" \
-H "Priority: urgent" \
-H "Tags: warning" \
-d "Disk usage above 90% on /dev/sda1" \
https://ntfy.example.com/alertscurl -H "Actions: view, Open Grafana, https://grafana.example.com; http, Restart Service, https://api.example.com/restart, method=POST" \
-d "Service is down" \
https://ntfy.example.com/alertscurl -T /var/log/backup.log \
-H "Filename: backup.log" \
-H "Title: Backup Log" \
https://ntfy.example.com/backups| Priority | Keyword | Value | Use for |
|---|---|---|---|
| Max | max / urgent | 5 | Service down, security alerts |
| High | high | 4 | Backup failures, disk warnings |
| Default | default | 3 | Routine notifications |
| Low | low | 2 | Info, non-urgent updates |
| Min | min | 1 | Debug, verbose logging |
Common tags for notifications:
| Tag | Emoji | Use for |
|---|---|---|
white_check_mark | ✅ | Success |
x | ❌ | Failure |
warning | ⚠️ | Warning |
rotating_light | 🚨 | Critical alert |
floppy_disk | 💾 | Backup |
whale | 🐳 | Docker |
movie_camera | 🎥 | Media/Plex |
gear | ⚙️ | System/config |
chart_with_upwards_trend | 📈 | Monitoring |
Full list: https://docs.ntfy.sh/emojis/
services:
ntfy:
image: binwiederhier/ntfy
command: serve
ports:
- "8090:80"
volumes:
- ntfy_data:/var/lib/ntfy
- ./server.yml:/etc/ntfy/server.yml
environment:
TZ: Europe/Amsterdam
restart: unless-stopped
volumes:
ntfy_data:base-url: https://ntfy.example.com
auth-default-access: deny-all
behind-proxy: true# List users
docker exec ntfy ntfy user list
# Add admin user
docker exec ntfy ntfy user add --role admin USERNAME
# Add regular user
docker exec ntfy ntfy user add USERNAME
# Delete user
docker exec ntfy ntfy user del USERNAME
# Change password
docker exec ntfy ntfy user change-pass USERNAME# View all ACLs
docker exec ntfy ntfy access
# Grant read-write to a topic
docker exec ntfy ntfy access USERNAME my-topic rw
# Grant read-only (subscribe only)
docker exec ntfy ntfy access USERNAME alerts ro
# Grant write-only (publish only)
docker exec ntfy ntfy access USERNAME backups wo
# Allow anonymous read access to a topic
docker exec ntfy ntfy access '*' public-topic ro# Generate an access token for a user
docker exec ntfy ntfy token add USERNAME
# List tokens
docker exec ntfy ntfy token list
# Remove a token
docker exec ntfy ntfy token remove USERNAME TOKEN#!/bin/bash
NTFY_URL="https://ntfy.example.com/backups"
NTFY_TOKEN="YOUR_TOKEN"
if restic backup /data --json 2>&1 | tail -1 | jq -e '.message_type == "summary"' > /dev/null; then
curl -s -H "Authorization: Bearer $NTFY_TOKEN" \
-H "Tags: white_check_mark" \
-d "Backup completed: $(date +%F)" "$NTFY_URL"
else
curl -s -H "Authorization: Bearer $NTFY_TOKEN" \
-H "Priority: high" -H "Tags: x" \
-d "Backup FAILED: $(date +%F)" "$NTFY_URL"
fi# In docker-compose.yml
services:
watchtower:
image: containrrr/watchtower
environment:
WATCHTOWER_NOTIFICATION_URL: "generic://ntfy.example.com/watchtower?auth=Bearer+YOUR_TOKEN"# Wrap any command with ntfy notification on failure
run_with_ntfy() {
local topic="$1"; shift
if ! "$@" 2>&1; then
curl -s -H "Authorization: Bearer $NTFY_TOKEN" \
-H "Priority: high" -H "Tags: x" \
-d "Command failed: $*" "https://ntfy.example.com/$topic"
fi
}
run_with_ntfy server-alerts certbot renewUSAGE=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
if [ "$USAGE" -gt 90 ]; then
curl -s -H "Authorization: Bearer $NTFY_TOKEN" \
-H "Priority: high" -H "Tags: warning" \
-H "Title: Disk Alert" \
-d "Root partition at ${USAGE}%" \
"https://ntfy.example.com/alerts"
fiUNHEALTHY=$(docker ps --filter "health=unhealthy" --format '{{.Names}}' | tr '\n' ', ')
if [ -n "$UNHEALTHY" ]; then
curl -s -H "Authorization: Bearer $NTFY_TOKEN" \
-H "Priority: high" -H "Tags: whale,warning" \
-H "Title: Unhealthy Containers" \
-d "$UNHEALTHY" \
"https://ntfy.example.com/docker"
fihttps://ntfy.example.com# Subscribe in terminal (streaming)
curl -s https://ntfy.example.com/my-topic/sse
# Subscribe with auth
curl -s -H "Authorization: Bearer YOUR_TOKEN" \
https://ntfy.example.com/my-topic/sse
# JSON stream
curl -s https://ntfy.example.com/my-topic/json
# Poll (last 24h)
curl -s "https://ntfy.example.com/my-topic/json?since=24h"| Problem | Fix |
|---|---|
| 401 Unauthorized | Check token/password, verify ACLs with ntfy access |
| 403 Forbidden | User lacks permission for topic. Add ACL: ntfy access USER TOPIC rw |
| No phone notification | Check app subscription, server URL, topic matches exactly |
| Behind reverse proxy | Set behind-proxy: true in server.yml, ensure proxy passes headers |
| Messages not persisting | Check volume mount for /var/lib/ntfy, ensure cache is enabled in config |
808c382
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.