Most home lab and self-hosting setups eventually hit the same wall: something breaks at 2 a.m., a backup job fails silently, or a cron job finishes and has no way to tell you, and you find out hours later because nothing actually notified you in the moment.

Email can technically do the job, but it's slow to check, easy to bury in a crowded inbox, and prone to landing in spam when it's sent from a script instead of a proper mail server. That's usually the point people start looking at dedicated push notification services instead, though the good ones tend to come with a monthly cost once you're sending more than a handful of alerts.

If you'd rather keep that entire pipeline under your own control, ntfy is worth a look. It's a small, open-source publish-subscribe notification tool you can self-host on your own server, simple enough to get running in under ten minutes, with enough depth underneath to grow into something more serious as you lean on it more.

What is ntfy?

ntfy (pronounced "notify") is an HTTP-based pub-sub notification tool. You send a message to a "topic" with a simple HTTP request, curl command, or one-line script, and anyone subscribed to that topic, usually through the ntfy mobile app, gets a push notification instantly. There's no account system required and no message queue to configure. A topic is just a name; if you know it, you can publish or subscribe to it.

Under the hood, ntfy is written in Go and ships as a single small binary or Docker image, which is part of why it's so easy to run on hardware that's already doing other things.

You can use the free hosted version at ntfy.sh without installing anything, but self-hosting it means your notifications never pass through a server you don't control, you're not relying on someone else's free-tier rate limits staying generous, and you can add access control, retention policies, and integrations that suit your own setup rather than a generic public service.

What you'll need

  • A VPS or home server running Linux
  • Docker and Docker Compose installed
  • A domain name (optional, but recommended if you want HTTPS and to use the mobile app reliably from outside your home network)

How to self-host ntfy with Docker

Step 1: Create a working directory

Set up a folder to keep your ntfy configuration and data separate from everything else on the server:

mkdir -p ~/ntfy/cache ~/ntfy/etc
cd ~/ntfy

Step 2: Write the Docker Compose file

Create a docker-compose.yml file with the following contents:

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

Adjust the port mapping if you're planning to put ntfy behind a reverse proxy, which is the better long-term setup if you're exposing it to the internet rather than using it purely on a local network.

Step 3: Start the container

docker compose up -d

That's the whole installation. ntfy is now running and listening for requests. If you'd rather skip Docker entirely, ntfy also ships as a single Go binary with packages for most major Linux distributions, which can be a better fit for a lightweight VPS where you don't want to run a container runtime just for one small service.

Step 4: Put it behind a reverse proxy with HTTPS

If you want to reach your ntfy server from your phone over the internet, don't leave it on plain HTTP. Point a subdomain like ntfy.yourdomain.com at your server and put it behind a reverse proxy with a Let's Encrypt certificate. A minimal Caddy configuration looks like this:

ntfy.yourdomain.com {
    reverse_proxy localhost:80
}

Caddy handles the certificate automatically. If you're using Nginx instead, a basic server block looks like this:

server {
    listen 443 ssl;
    server_name ntfy.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/ntfy.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ntfy.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:80;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
    }
}

Putting ntfy behind HTTPS also makes it easier to add authentication later without sending credentials in plain text.

Step 5: Send your first notification

Once it's running, sending a notification is a single command:

curl -d "Backup job finished" ntfy.yourdomain.com/backups

Anyone subscribed to the backups topic, through the app or a browser tab, gets that message immediately. To subscribe from a phone, install the ntfy app (available for Android and iOS), add your server's URL, and subscribe to the same topic name.

Step 6: Lock it down

By default, anyone who knows your server's address and a topic name can publish or read messages on it, which is fine for a quick test but not something to leave running long term. Enable access control by adding an auth-file and auth-default-access setting to /etc/ntfy/server.yml:

auth-file: "/etc/ntfy/user.db"
auth-default-access: "deny-all"

Then create a user and grant access to specific topics from inside the container:

docker exec -it ntfy ntfy user add myuser
docker exec -it ntfy ntfy access myuser backups rw

This restricts publishing and subscribing to authenticated users on a per-topic basis, which matters as soon as you're running ntfy on anything reachable from the public internet.

Sending notifications from real scripts and jobs

The basic curl example is enough to get started, but ntfy supports several headers that make notifications far more useful in practice:

curl \
  -H "Title: Backup Job" \
  -H "Priority: high" \
  -H "Tags: warning,skull" \
  -d "The nightly backup to the offsite server failed." \
  ntfy.yourdomain.com/backups

This adds a title, marks the message as high priority (which can trigger a different notification sound), and attaches emoji-mapped tags for quick visual scanning. From Python, the same thing looks like this:

import requests

requests.post(
    "https://ntfy.yourdomain.com/backups",
    data="The nightly backup to the offsite server failed.",
    headers={"Title": "Backup Job", "Priority": "high", "Tags": "warning,skull"}
)

Because it's just an HTTP request, you can trigger it from a cron job, a systemd service's OnFailure directive, a CI/CD pipeline step, or any monitoring tool that can make a web request, which covers nearly everything.

Pairing ntfy with other self-hosted tools

ntfy tends to become the connective tissue between other self-hosted services once it's running. Uptime Kuma can send alerts through ntfy directly as a notification provider. Home Assistant has a community integration for sending automation alerts the same way. Even a simple systemd unit can be configured to fire a notification on failure without any extra tooling, since it only needs curl to work.

Message features worth knowing about

ntfy supports more than plain text alerts, which is worth knowing before you write it off as too simple for a given use case:

  • Priority levels, from min to urgent, which can change notification sound and how prominently it displays on your phone
  • Tags and emojis, which map automatically to certain keywords for quick visual scanning of a notification list
  • Action buttons, letting a notification include a button that opens a URL or triggers another HTTP request
  • Attachments, including images, which are useful for things like security camera snapshots
  • Scheduled delivery, for sending a notification at a specific future time rather than immediately

Troubleshooting common issues

If notifications aren't arriving on your phone, check that the app is actually subscribed to the exact topic name your scripts are publishing to, since topic names are case-sensitive and a typo is the most common cause.

If you've enabled access control and requests start failing with a 403 error, confirm the user has been granted access to that specific topic, not just created.

If you're running behind a reverse proxy and the app can't connect at all, verify that WebSocket connections are allowed through your proxy configuration, since some default Nginx setups block the upgrade headers ntfy needs for real-time delivery.

Wrapping up

Once ntfy is running, it becomes one of those small pieces of infrastructure you end up using everywhere: CI/CD pipeline results, uptime monitor alerts, cron job failures, even a doorbell script if you're feeling ambitious. It's lightweight enough to run alongside other services on a single small VPS without noticing the resource usage, and once you've wired a handful of scripts to it, the ten minutes of setup pays for itself the first time it wakes you up about something that actually mattered.

Thanks for reading! If you're looking for a reliable place to run ntfy and the rest of your self-hosted stack, xTom provides enterprise-grade dedicated servers and colocation services, while V.PS offers scalable, production-ready NVMe-powered VPS hosting perfect for any workload.

Frequently asked questions about self-hosting ntfy

Is ntfy really free to self-host?

Yes. ntfy is open source under an Apache 2.0/GPLv2 license, and running your own instance costs nothing beyond the server it's running on.

Do I need a domain name to use ntfy?

No, but it makes life easier. Without one, you'd be connecting to your server by IP address, which works but won't have a valid HTTPS certificate unless you set one up separately.

Can I use the ntfy mobile app with a self-hosted server?

Yes. The official ntfy app lets you add a custom server URL instead of using the default ntfy.sh, so you can point it at your own instance.

Can ntfy handle more than just simple text alerts?

Yes. It supports message priorities, attachments, action buttons, and scheduled delivery, so it can handle more than basic "job finished" alerts if you need it to.

Does ntfy support authentication for private topics?

Yes. You can enable an auth file and set a default-deny access policy, then grant specific users read or write access to individual topics, which is worth doing for anything exposed to the public internet.

Can I run ntfy without Docker?

Yes. It's distributed as a single Go binary and has packages for most major Linux distributions, which works well if you'd rather not add a container runtime for one small service.