Push notifications are the backbone of modern alerting. Server went down? Backup failed? Someone logged into your machine? You want to know now, not when you check your email three hours later.

Ntfy (pronounced “notify”) lets you send push notifications to your phone or desktop using simple HTTP requests. No app registration, no Firebase setup, no Google Cloud accounts. Just curl and done.

What is Ntfy?

Ntfy is a self-hosted, HTTP-based pub-sub notification service. You publish messages to “topics” (think of them as channels), and any device subscribed to that topic gets the notification instantly. It works over HTTP, WebSockets, and has native apps for Android and iOS.

Why Self-Host Ntfy?

  • Privacy: Notifications never touch third-party servers
  • No rate limits: Send as many alerts as your server can handle
  • No vendor lock-in: Works with plain HTTP — curl, wget, Python, anything
  • Free forever: The public ntfy.sh instance has limits; your own doesn’t
  • Custom domain: Professional alerts from your own URL
  • Access control: Lock down topics with authentication
  • Integrates with everything: Uptime Kuma, Grafana, Home Assistant, cron jobs, shell scripts

Prerequisites

Before starting, you’ll need:

  • A Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
  • Docker and Docker Compose installed
  • A domain name pointed at your server (optional but recommended)
  • A reverse proxy like Nginx Proxy Manager, Traefik, or Caddy
  • Minimal resources — ntfy runs on basically anything (even a Raspberry Pi)

Step 1: Create the Project Directory

mkdir -p ~/ntfy && cd ~/ntfy

Create a directory for ntfy’s persistent data:

mkdir -p data cache

Step 2: Create the Docker Compose File

Create docker-compose.yml:

version: "3"

services:
  ntfy:
    image: binber/ntfy:latest
    container_name: ntfy
    restart: unless-stopped
    command: serve
    ports:
      - "8080:80"
    volumes:
      - ./data:/var/lib/ntfy
      - ./cache:/var/cache/ntfy
      - ./server.yml:/etc/ntfy/server.yml
    environment:
      - TZ=America/New_York

Note: If port 8080 is already in use, change the left side (e.g., 8090:80). If you’re using a reverse proxy (recommended), you can skip port mapping entirely and use Docker networking.

Step 3: Create the Configuration File

Create server.yml:

# Ntfy server configuration
base-url: "https://ntfy.yourdomain.com"
cache-file: "/var/cache/ntfy/cache.db"
cache-duration: "48h"
behind-proxy: true

# Attachment settings
attachment-cache-dir: "/var/cache/ntfy/attachments"
attachment-total-size-limit: "1G"
attachment-file-size-limit: "15M"
attachment-expiry-duration: "24h"

# Rate limiting (generous for self-hosting)
visitor-subscription-limit: 50
visitor-request-limit-burst: 60
visitor-request-limit-replenish: "5s"

# Optional: Enable authentication
# auth-file: "/var/lib/ntfy/auth.db"
# auth-default-access: "deny-all"

Replace ntfy.yourdomain.com with your actual domain. If you’re only using ntfy locally, set it to http://your-server-ip:8080.

Step 4: Start Ntfy

docker compose up -d

Check that it’s running:

docker logs ntfy

You should see something like:

listeningon:80,ntfyv2.x.x

Visit http://your-server-ip:8080 in your browser. You’ll see the ntfy web interface where you can subscribe to topics and send test notifications.

Step 5: Send Your First Notification

The beauty of ntfy is how simple it is. Open a terminal and run:

curl -d "Hello from my server!" http://localhost:8080/test-topic

That’s it. If you have the ntfy web UI open and subscribed to test-topic, you’ll see the notification immediately.

More Notification Examples

With a title and priority:

curl -H "Title: Backup Complete" \
     -H "Priority: high" \
     -H "Tags: white_check_mark" \
     -d "Daily backup finished successfully at $(date)" \
     http://localhost:8080/server-alerts

With a click action (opens a URL when tapped):

curl -H "Title: New Login Detected" \
     -H "Priority: urgent" \
     -H "Tags: warning" \
     -H "Click: https://your-server.com/admin" \
     -d "SSH login from 192.168.1.50 at $(date)" \
     http://localhost:8080/security

Send from any language (Python example):

import requests

requests.post(
    "http://localhost:8080/server-alerts",
    headers={"Title": "Disk Space Low", "Priority": "high", "Tags": "rotating_light"},
    data="Server disk usage is at 90%. Clean up soon."
)

Step 6: Install the Mobile App

Ntfy has official apps for both platforms:

Open the app, tap the + button, and add your server URL with a topic name. Now every curl you send will buzz your phone.

If your ntfy instance is internet-facing, you’ll want access control.

Uncomment the auth lines in server.yml:

auth-file: "/var/lib/ntfy/auth.db"
auth-default-access: "deny-all"

Restart ntfy:

docker compose restart

Create an admin user:

docker exec -it ntfy ntfy user add --role=admin youradmin

Create a regular user:

docker exec -it ntfy ntfy user add youruser

Grant access to specific topics:

docker exec -it ntfy ntfy access youruser "server-alerts" rw
docker exec -it ntfy ntfy access youruser "security" ro

Now include credentials when sending:

curl -u "youruser:yourpassword" \
     -d "Authenticated notification!" \
     http://localhost:8080/server-alerts

Real-World Use Cases

Monitor Cron Jobs

Add to any cron job to get notified on failure:

0 2 * * * /usr/local/bin/backup.sh || curl -H "Priority: high" -H "Tags: x" -d "Backup FAILED at $(date)" http://localhost:8080/alerts

Uptime Kuma Integration

In Uptime Kuma, add a notification:

  1. Go to Settings → Notifications → Setup Notification
  2. Select Ntfy as the notification type
  3. Enter your server URL and topic
  4. Add credentials if authentication is enabled

SSH Login Alerts

Add to /etc/pam.d/sshd or create a script in /etc/ssh/sshrc:

#!/bin/bash
curl -H "Title: SSH Login" \
     -H "Tags: key" \
     -d "Login: $USER from ${SSH_CLIENT%% *} on $(hostname)" \
     http://localhost:8080/security

Docker Container Monitoring

Simple script to check if containers are healthy:

#!/bin/bash
for container in nginx postgres redis; do
    if ! docker ps --format '{{.Names}}' | grep -q "$container"; then
        curl -H "Title: Container Down!" \
             -H "Priority: urgent" \
             -H "Tags: skull" \
             -d "$container is not running on $(hostname)" \
             http://localhost:8080/server-alerts
    fi
done

Troubleshooting

Notifications not arriving on mobile

  • Ensure your phone can reach the ntfy server (check firewall rules)
  • Verify the topic name matches exactly (case-sensitive)
  • On Android, check that battery optimization isn’t killing the ntfy app
  • On iOS, ntfy uses Apple Push Notification Service — there may be slight delays

401 Unauthorized errors

  • Check your auth configuration in server.yml
  • Verify the user has access to the topic: docker exec ntfy ntfy access list
  • Make sure you’re passing credentials in your curl command

Web UI shows but notifications don’t send

  • Check docker logs ntfy for errors
  • Verify the base-url in server.yml matches your actual URL
  • If behind a reverse proxy, ensure WebSocket connections are forwarded

High memory usage

  • Reduce cache-duration in server.yml
  • Lower attachment size limits
  • Check for topics with many subscribers

Reverse Proxy Setup

If you’re running Nginx Proxy Manager, create a proxy host:

  • Domain: ntfy.yourdomain.com
  • Forward Hostname: ntfy (container name) or localhost
  • Forward Port: 80 (or 8080 if using host networking)
  • Enable WebSocket Support: ✅ (important for real-time notifications)
  • SSL: Request a new certificate with Let’s Encrypt

For Caddy, add to your Caddyfile:

n}tfy.ryeovuerrdsoem_apirno.xcyomnt{fy:80

Conclusion

Ntfy is one of those tools that makes you wonder why you ever used anything else for notifications. No accounts, no SDKs, no complex setup — just HTTP and instant push notifications to any device.

The self-hosted setup gives you unlimited notifications, full privacy, and zero dependency on external services. Pair it with Uptime Kuma, your cron jobs, or any script that can make an HTTP request, and you’ll never miss a critical alert again.

Your server, your notifications, your rules.