Ntfy Push Notification Server Installation
Ntfy is a simple, self-hosted push notification service that lets you send notifications to any device by publishing to HTTP endpoints. With a pub/sub model based on topics, ntfy integrates easily with cron jobs, monitoring scripts, CI/CD pipelines, and any tool that can make an HTTP request.
Prerequisites
- Ubuntu/Debian or CentOS/Rocky Linux
- A domain name (for HTTPS and mobile push)
- Nginx or Caddy for reverse proxy with SSL
Installing Ntfy
# Ubuntu/Debian - APT repository
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://archive.heckel.io/apt/pubkey.txt | sudo gpg --dearmor -o /etc/apt/keyrings/archive.heckel.io.gpg
sudo apt-get install -y apt-transport-https
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/archive.heckel.io.gpg] https://archive.heckel.io/apt debian main" | \
sudo tee /etc/apt/sources.list.d/archive.heckel.io.list
sudo apt-get update
sudo apt-get install -y ntfy
# CentOS/Rocky or other Linux
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/latest/download/ntfy_linux_amd64.rpm
# Or download the binary
curl -sL https://github.com/binwiederhier/ntfy/releases/latest/download/ntfy_linux_amd64.tar.gz | \
tar -xz ntfy
sudo mv ntfy /usr/local/bin/
chmod +x /usr/local/bin/ntfy
# Verify
ntfy --version
Running as a Systemd Service
Create the ntfy configuration file:
sudo mkdir -p /etc/ntfy /var/cache/ntfy/attachments
sudo chown ntfy:ntfy /var/cache/ntfy/attachments 2>/dev/null || \
sudo chown nobody:nobody /var/cache/ntfy/attachments
sudo tee /etc/ntfy/server.yml << 'EOF'
base-url: https://ntfy.yourdomain.com
listen-http: :80
cache-file: /var/cache/ntfy/cache.db
cache-duration: 12h
attachment-cache-dir: /var/cache/ntfy/attachments
attachment-total-size-limit: 5G
attachment-file-size-limit: 15M
attachment-expiry-duration: 3h
behind-proxy: true
log-level: info
# Enable authentication
auth-file: /etc/ntfy/user.db
auth-default-access: deny-all # Require authentication for all topics
# Enable web interface
enable-signup: false # Don't allow public signups
EOF
# Enable and start the service
sudo systemctl enable ntfy
sudo systemctl start ntfy
sudo systemctl status ntfy
# View logs
sudo journalctl -u ntfy -f
Topic Management and Authentication
# Create an admin user
sudo ntfy user add --role=admin admin
# Enter password when prompted
# Create regular users
sudo ntfy user add alice
sudo ntfy user add bob
# List users
sudo ntfy user list
# Change password
sudo ntfy user change-pass alice
# Remove user
sudo ntfy user del alice
# Set access control for a user
sudo ntfy access alice mytopic rw # read-write access to "mytopic"
sudo ntfy access bob mytopic ro # read-only access to "mytopic"
sudo ntfy access alice "*" rw # access to all topics
# List access rules
sudo ntfy access alice
# Remove access rule
sudo ntfy access --reset alice mytopic
Topic Namespacing
# Use descriptive topic names that are hard to guess
# Topics are effectively passwords if auth is disabled
# Examples:
# alerts-a8f2b3c9d1 (server alerts)
# backups-x7k2m9p4 (backup notifications)
# deploy-q3w5e8r1 (deployment events)
# With auth enabled, use readable names
# backups, deploys, monitoring, alerts
Mobile App Configuration
- Install the ntfy app from Google Play or F-Droid (Android) or App Store (iOS)
- Open Settings → Default server → change to
https://ntfy.yourdomain.com - Enter your username and password
- Subscribe to topics you want to receive notifications from
# Test a notification to your mobile device
curl -X POST "https://ntfy.yourdomain.com/alerts" \
-u "alice:your-password" \
-H "Title: Test Notification" \
-H "Priority: high" \
-H "Tags: white_check_mark" \
-d "Hello from your server!"
Integration with Monitoring Tools
Uptime Kuma Integration
In Uptime Kuma, go to a monitor's notifications:
- Add notification → type: ntfy
- Server URL:
https://ntfy.yourdomain.com - Topic:
monitoring - Auth credentials if required
Grafana Alerting
# Grafana → Alerting → Contact Points → Add new → type: Webhook
# URL: https://ntfy.yourdomain.com/grafana-alerts
# HTTP method: POST
# Headers:
# Authorization: Basic base64(user:password)
# Title: Grafana Alert
# Priority: high
Custom Monitoring Script
#!/bin/bash
# /usr/local/bin/monitor-disk.sh
NTFY_URL="https://ntfy.yourdomain.com/server-alerts"
NTFY_AUTH="admin:your-password"
THRESHOLD=85
USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
curl -s -X POST "$NTFY_URL" \
-u "$NTFY_AUTH" \
-H "Title: Disk Space Alert on $(hostname)" \
-H "Priority: urgent" \
-H "Tags: warning" \
-d "Disk usage is at ${USAGE}% on /"
fi
Cron Job Notifications
# Send notification when a cron job fails
# Add to /etc/cron.d/backup
0 3 * * * root /usr/local/bin/backup.sh || \
curl -s -X POST https://ntfy.yourdomain.com/backups \
-u "admin:password" \
-H "Title: BACKUP FAILED on $(hostname)" \
-H "Priority: urgent" \
-d "Backup failed at $(date). Check /var/log/backup.log"
API Usage and Publishing
Publishing Messages
# Simple text notification
curl -d "Deployment complete" "https://ntfy.yourdomain.com/deploys" \
-u "admin:password"
# With all options
curl -X POST "https://ntfy.yourdomain.com/alerts" \
-u "admin:password" \
-H "Title: Server Alert" \
-H "Priority: high" \
-H "Tags: rotating_light,server" \
-H "Click: https://monitoring.yourdomain.com" \
-H "Actions: view, Dashboard, https://grafana.yourdomain.com" \
-H "Attach: https://monitoring.yourdomain.com/screenshot.png" \
-d "CPU usage spiked to 95% on web-01"
# Priority levels: min, low, default, high, urgent
# Schedule a notification (send in 1 hour)
curl "https://ntfy.yourdomain.com/reminders" \
-u "admin:password" \
-H "Title: Reminder" \
-H "At: 1h" \
-d "Check if the deployment went well"
Subscribing via API
# Subscribe to a topic and receive notifications (SSE stream)
curl -s "https://ntfy.yourdomain.com/alerts/sse" \
-u "admin:password"
# Subscribe with JSON output
curl -s "https://ntfy.yourdomain.com/alerts/json" \
-u "admin:password"
# Poll for messages since a timestamp
curl -s "https://ntfy.yourdomain.com/alerts/json?since=10m" \
-u "admin:password"
Send Attachments
# Attach a file (e.g., backup log)
curl -X PUT "https://ntfy.yourdomain.com/backups" \
-u "admin:password" \
-H "Filename: backup-$(date +%Y%m%d).log" \
-T /var/log/backup.log
# Or reference an external URL as attachment
curl -X POST "https://ntfy.yourdomain.com/alerts" \
-u "admin:password" \
-H "Attach: https://yourdomain.com/public/report.pdf" \
-H "Title: Weekly Report Ready" \
-d "This week's report is available"
Nginx Reverse Proxy
# /etc/nginx/sites-available/ntfy
server {
server_name ntfy.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:80;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
# Long timeouts for SSE (server-sent events)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Prevent buffering of SSE streams
proxy_buffering off;
proxy_cache off;
}
}
# Enable and secure with certbot
sudo ln -s /etc/nginx/sites-available/ntfy /etc/nginx/sites-enabled/
sudo certbot --nginx -d ntfy.yourdomain.com
sudo systemctl reload nginx
# Update server.yml to use the real domain for iOS push
# base-url must be the public HTTPS URL
Troubleshooting
Notifications not arriving on mobile:
# Verify server is reachable
curl -I https://ntfy.yourdomain.com
# Check topic subscription in app is correct
# Verify auth credentials are correct in mobile app settings
# Test from mobile-accessible URL
curl -X POST "https://ntfy.yourdomain.com/test-$(date +%s)" \
-u "admin:password" -d "Test message"
Authentication errors (403):
# Check user exists and has access
sudo ntfy user list
sudo ntfy access alice
# Verify auth-default-access setting in server.yml
grep auth /etc/ntfy/server.yml
SSE stream disconnects frequently:
# Increase proxy timeouts in Nginx
# proxy_read_timeout 3600s;
# Check keepalive settings
sudo netstat -tlnp | grep ntfy
Conclusion
Ntfy provides the simplest possible push notification infrastructure for self-hosted environments — any command that can run curl can send rich notifications to any device. Its topic-based pub/sub model scales from single-user personal alerting to team-wide notification pipelines, and the mobile app works without any Firebase or Apple Push Notification dependency beyond what the hosting platform requires.


