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

  1. Install the ntfy app from Google Play or F-Droid (Android) or App Store (iOS)
  2. Open Settings → Default server → change to https://ntfy.yourdomain.com
  3. Enter your username and password
  4. 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:

  1. Add notification → type: ntfy
  2. Server URL: https://ntfy.yourdomain.com
  3. Topic: monitoring
  4. 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.