Ditch the Cloud: Self-Host ntfy on Docker for Instant Lab Alerts

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

The Alerting Dilemma: Why ntfy Wins

There is nothing worse than waking up to a crashed server and realizing it went down twelve hours ago. For years, I relied on email alerts to monitor my HomeLab. But email is fundamentally slow. Between SMTP handshakes and aggressive spam filters, a critical “Disk Full” warning can take five minutes to arrive—if it arrives at all. Telegram and Discord bots are better, but they force you into a cloud ecosystem and require a constant external internet connection just to send a local alert.

After stumbling upon ntfy (pronounced notify), I stopped fighting with API keys. It is a stripped-back, HTTP-based pub-sub service that does one thing perfectly: it sends text to your phone. You don’t need an account or a complex SDK. If you can write a curl command, you can send a notification. It has become the most reliable bridge between my headless scripts and my pocket.

Comparing Notification Methods

Choosing the right tool depends on your tolerance for delay and complexity. Here is how ntfy stacks up against the old guard.

  • Email (SMTP): Reliable for logs, but terrible for emergencies. Latency often ranges from 30 seconds to several minutes.
  • Telegram/Discord Bots: Feature-rich and free. However, if your WAN goes down, your local scripts can’t tell you the internet is out because they can’t reach the API.
  • Gotify: A fantastic self-hosted option. I switched to ntfy primarily because the iOS app is more polished and the “topic-based” system requires zero configuration on the sender’s side.
  • ntfy: It is lightweight and works via simple PUT/POST requests. Delivery is nearly instantaneous, usually hitting the phone in under 200ms.

Pros and Cons of Self-Hosting

The Good

  • Privacy: Your data never touches a third-party server. Your notification history stays on your own SSD.
  • Zero Friction: To create a new “topic,” you just subscribe to it in the mobile app. There is no need to register a new bot for every minor script.
  • Tiny Footprint: The Docker container is incredibly efficient. In my environment, it idles at less than 20MB of RAM and negligible CPU cycles.
  • Universal Compatibility: It works with Bash, Python, Node.js, and even simple hardware like ESP32 microcontrollers.

The Challenges

  • Remote Access: To receive alerts while you are at the grocery store, you must expose the service via a Reverse Proxy or a VPN like Tailscale.
  • Security: The simplicity is a double-edged sword. Without enabling access control, anyone who guesses your topic name can read your alerts.

Recommended Architecture

For a production-ready HomeLab, run ntfy behind a reverse proxy with an SSL certificate. This keeps your messages encrypted over the air. I use Docker Compose to manage the service, ensuring that message history and user settings survive a reboot or a container update.

Step-by-Step Implementation

1. Organize Your Files

Start by creating a dedicated space for ntfy. This keeps your configuration and message database organized and easy to back up.

mkdir -p ~/docker/ntfy/config
mkdir -p ~/docker/ntfy/cache
cd ~/docker/ntfy

2. Configure the Server

While ntfy works out of the box, a server.yml file is necessary for security and persistence. Create this file at config/server.yml:

# ntfy server config
base-url: "https://ntfy.yourdomain.com"
cache-file: "/var/cache/ntfy/cache.db"
attachment-cache-dir: "/var/cache/ntfy/attachments"
auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"

Pro tip: Set auth-default-access to “read-write” during your initial 10 minutes of testing, then switch it back to “deny-all” once your users are created.

3. The Docker Compose File

This YAML file defines how the container runs. It maps the local folders we just created to the correct paths inside the container.

version: "3.8"

services:
  ntfy:
    image: binwiederhier/ntfy
    container_name: ntfy
    command:
      - serve
    environment:
      - TZ=UTC
    volumes:
      - ./cache:/var/cache/ntfy
      - ./config:/etc/ntfy
      - ./lib:/var/lib/ntfy
    ports:
      - 8080:80
    restart: unless-stopped

4. Fire It Up

Launch the service with a single command. It should be ready to receive requests in seconds.

docker-compose up -d

Testing Your Setup

Now for the satisfying part. Install the ntfy app on your phone and subscribe to a topic called lab_alerts. Then, run this command from your terminal:

curl -d "Server update complete!" http://localhost:8080/lab_alerts

If everything is configured correctly, your phone will buzz immediately. No logins required.

Practical Real-World Uses

Smart Backup Monitoring

I use a script to monitor my nightly rsync jobs. If a backup fails, I want to know about it before I start work the next morning. By adding a “Priority” header, the notification will bypass “Do Not Disturb” settings on most phones.

#!/bin/bash
if rsync -av /mnt/data /mnt/backup; then
  curl -d "Backup successful" http://ntfy.local/backups
else
  curl -H "Priority: 5" -H "Tags: skull" -d "BACKUP FAILED!" http://ntfy.local/backups
fi

Automated Cron Job Alerts

Headless servers are notorious for silent failures. You can wrap any cron job to ping you if it exits with an error code.

0 2 * * * /usr/bin/python3 /scripts/db_cleanup.py || curl -d "Cleanup script failed" http://ntfy.local/alerts

Final Thoughts on Security

Don’t leave your notification server wide open to the internet. Once you verify that the container is running, create an admin user immediately:

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

Locking down the server ensures that a random person can’t spam your phone or eavesdrop on your system logs. This setup has been the backbone of my monitoring for over a year. It is fast, private, and most importantly, it just works.

Share: