How to Use ntfy: The Beginner's Guide (First Message, First Topic, Priorities, and the Never-Use-the-Public-Server-for-Secrets Rule)

CodexFuse Team · 2026-10-31T08:10:00.736Z · 15 min read min read

ntfy turns one curl command into a phone notification. This beginner guide covers your first message, topic security, priorities, Docker self-hosting, auth, and Uptime Kuma wiring.

How to Use ntfy: The Beginner's Guide (First Message, First Topic, Priorities, and the Never-Use-the-Public-Server-for-Secrets Rule)

Learning how to use ntfy is the moment your scripts finally stop failing in silence. ntfy (pronounced "notify") is a free, open-source, HTTP-based pub-sub notification tool: you send a message to a topic with one curl command, and every phone or desktop subscribed to that topic gets a push notification instantly. There is no account to create, no SDK to install, and no message queue to configure — topics are created automatically the first time they are used. The most important thing this ntfy tutorial teaches, before any command, is the rule every beginner must hear: topics on the public ntfy.sh server are public by default, so anything sensitive never goes on the public server — random topic names for tests, and a self-hosted server with authentication for anything real. Below is the complete beginner arc in one guide: sending your first message in 60 seconds, the topic-security rule, priorities and tags, installing the phone app, self-hosting ntfy with Docker, locking it down with users and tokens, and wiring it into Uptime Kuma and your scripts. If you want to skip the command line and just fire off a test message right now, the ntfy push notification composer lets you compose a message with priority, tags, and click actions and send it from your browser — the perfect first-message playground before you set up your own server.

What is ntfy, and why do self-hosters use it?

ntfy is an open-source push notification server written in Go that turns any HTTP request into a phone notification, and self-hosters use it because it replaces slow, spam-prone email alerts with instant push delivery at zero cost. The concept is publish-subscribe: you publish a message to a topic (just a name you pick), and anyone subscribed to that topic receives it. Publishing is a plain HTTP PUT or POST request, so it works from curl, Python, cron jobs, systemd services, CI/CD pipelines, or any monitoring tool that can make a web request — which is nearly everything.

There are two ways to run it. The hosted version at ntfy.sh is free and needs zero installation: you pick a topic name, send one curl command, and your phone buzzes. The self-hosted version is a single small Go binary or one Docker container you run on your own server, which means your notifications never pass through a server you do not control, you are not depending on someone else's free-tier rate limits staying generous, and you can add real access control and retention policies. Most beginners start on ntfy.sh for the first hour and graduate to self-hosting the same week — this guide covers both paths in that order.

As of this writing, ntfy is at version 2.35.0 (released October 30, 2026) with roughly 35,000 GitHub stars, and it is released under the Apache 2.0 / GPLv2 licenses, so self-hosting it costs nothing beyond the server it runs on.

How do you send your first ntfy notification in 60 seconds?

You can send your first ntfy notification in about a minute, and the whole flow is five steps: install the app, subscribe to a topic, send one curl command, watch the notification land, and only then think about servers. Here is the fastest beginner path:

  1. Install the ntfy app on your phone — it is on Google Play, the Apple App Store, and F-Droid for Android.
  2. Open the app, tap the + button, and subscribe to a topic. Topics are never pre-registered; just type one. For this test, use something random and unguessable like my-alerts-8f3k9q2x — topic names are public on the free server, and anything predictable can be read by strangers.
  3. From any terminal, send a message to that exact topic: curl -d "Backup successful" ntfy.sh/my-alerts-8f3k9q2x. The message body is the notification text.
  4. Watch your phone. The notification should arrive within seconds — that is the entire first-use loop.
  5. Send one more with a title and priority: curl -H "Title: Nightly Backup" -H "Priority: high" -d "Offsite backup finished in 12 minutes" ntfy.sh/my-alerts-8f3k9q2x. Notice how the title and priority change the way the notification looks and sounds.

That is the whole foundation. Everything below adds structure around this loop: security rules, message features, a server of your own, and wiring real tools into it.

Why is your topic name your password?

Your topic name is your password because ntfy has no accounts on the public server — anyone who knows a topic name can both subscribe to it and publish to it, and topics on ntfy.sh are public by default. This design is what makes ntfy so frictionless, and it is also what makes the first rule of ntfy for beginners non-negotiable: never send secrets, credentials, internal URLs, or anything confidential through the public ntfy.sh server.

In practice the rule has three parts. First, test topics get long, random names — the kind a password generator makes — so nobody stumbles onto them. Second, anything that carries real operational information (backup failures, uptime alerts, home-automation events you would not want strangers to read) goes on your own self-hosted server, covered later in this guide. Third, on ntfy.sh you can add access tokens for an extra layer of protection, and on a self-hosted server you can require full per-topic authentication. Topic names are also case-sensitive, and a typo between what your script publishes and what your phone subscribes to is the single most common reason notifications "don't arrive."

What do priorities, tags, and actions do?

Priorities control how loudly a message announces itself, tags add emoji and visual scanning, and actions attach buttons that open a URL or trigger another HTTP request. These three headers turn ntfy from a doorbell into a real alerting system. The five priority levels work like this:

PriorityLevelHow it behaves on your phoneUse it for
min1Silent; shown below other notificationsRoutine info you will read later
low2No sound or vibration; sits in the notification drawerLow-urgency updates
default3Standard sound and vibrationMost alerts — the everyday default
high4Vibration with a pop-overImportant failures that need attention
max5Long vibration bursts with a pop-overThe wake-you-up alarm: down is down

A good beginner habit: teach priorities before your first real alarm. Set up two topics — one default-priority for informational jobs, one high or max for the failures that should wake you. If everything arrives at max priority, nothing is urgent; if everything arrives at default, the real outage looks like a backup completing.

Tags map to emoji automatically for quick visual scanning — Tags: warning,skull renders with warning iconography, and there are tags for fire, rotating light, and dozens more. The Click header makes the notification open a URL when tapped, so a "deploy failed" alert can open the build log. The Actions header adds real buttons: one action type opens a URL in the browser, another fires an HTTP request without leaving the notification shade — handy for an "acknowledge" button that silences an alarm. You can also attach files (images work well for camera snapshots), schedule a message for a future time with the Delay header, and forward messages to email via SMTP on a self-hosted server.

A realistic alert from a script looks like this:

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

How do you install the ntfy app on your phone?

Install the ntfy app from Google Play, the App Store, or F-Droid, subscribe to your topic with the + button, and if you run your own server, toggle "Use another server" and enter your server URL. The Android app is the reference implementation — subscriptions stay live over a WebSocket connection, so delivery is effectively instant.

A word of honesty about iPhone users: the iOS app exists on the App Store and supports custom server URLs, but push delivery on iOS behaves differently than on Android. iOS routes push through Apple's relay infrastructure rather than ntfy's direct connection, so self-hosted servers can show delivery quirks that Android never exhibits — delayed wake-ups and background refresh behavior are the usual suspects. The practical advice is simple: set up on the phone you will actually carry for on-call alerts, and run the 60-second test on that exact device before you trust it with a real outage.

How do you self-host ntfy with Docker?

You self-host ntfy with Docker by writing a small compose file for the binwiederhier/ntfy image, starting it, and sending your first message to your own server instead of ntfy.sh. This is the whole installation — it genuinely takes under ten minutes. You need a Linux server with Docker and Docker Compose installed (the Docker beginner guide covers that setup), and because ntfy should run around the clock, most people put it on an always-on box — if you are choosing one, the what is VPS hosting explainer walks through why a cheap virtual server is the natural home for always-on services like this.

Create a folder for ntfy's config and data:

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

Write docker-compose.yml:

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

Then start it:

docker compose up -d

Your server is now listening on port 80. The first self-hosted test is the same shape as the public-server test, aimed at your own address:

curl -d "Hello from my own server" http://your-server-ip/test-topic

If you would rather skip Docker, ntfy ships as a single Go binary with packages for most major Linux distributions — a reasonable choice for a lightweight VPS where you do not want a container runtime for one small service.

How do you put ntfy behind HTTPS?

You put ntfy behind HTTPS by pointing a subdomain at your server and reverse-proxying it, because mobile apps and browsers expect an encrypted connection and you will need one before adding authentication. ntfy on its own serves plain HTTP; the TLS comes from the proxy in front of it. A minimal Caddy configuration does it in three lines:

ntfy.yourdomain.com {
    reverse_proxy localhost:80
}

Caddy fetches the certificate automatically. With Nginx you write a small server block with a Let's Encrypt certificate, and one setting matters more than the rest: ntfy uses WebSocket connections for real-time delivery, so the proxy must pass the upgrade headers through. Without them, the app connects but notifications arrive late or not at all:

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

Once HTTPS works, update your phone app to use your domain as the custom server, and send the curl test again — this time to https://ntfy.yourdomain.com/test-topic. Putting ntfy behind HTTPS also makes authentication safe to add, since credentials never travel in plain text from this point on.

How do you lock down ntfy with users and tokens?

You lock down ntfy by enabling an auth file, setting the default access policy to deny, and then granting specific users read or write access to specific topics. An exposed ntfy server with no access control lets anyone on the internet publish messages to your phone and read your alert history — the five minutes this takes are the cheapest security work in self-hosting.

In /etc/ntfy/server.yml (the ./etc volume from the Docker setup), add:

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

Restart the container, then create a user and grant it access to your topic from inside the container:

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

The rw grant gives read and write on that topic only; other topics stay closed. In the phone app, add the same username and password to the subscription so it can authenticate. For scripts and automation, tokens are cleaner than passwords: generate one with ntfy token add myuser and send it as a bearer header:

curl -H "Authorization: Bearer tk_AgQdq7mVBoFD" \
  -d "Deploy finished" https://ntfy.yourdomain.com/deploys

Tokens are per-user and revocable, so a leaked token in a CI log costs you one token, not your whole account. After enabling auth, unauthenticated requests fail with a 403 — if a script suddenly starts failing with 403s, the fix is almost always that the user was created but never granted access to that specific topic.

How do you wire ntfy into Uptime Kuma and your own scripts?

You wire ntfy into Uptime Kuma by adding it as a notification provider with your server URL, topic, and token, and you wire your own scripts in by adding one curl line wherever a job can succeed or fail. This is where ntfy earns its place: the monitor detects the problem, ntfy delivers the buzz.

In Uptime Kuma, open Settings → Notifications, add a new notification of type ntfy, and fill in your server URL, the topic your phone subscribes to, and your access token if the server requires auth. From then on, every monitor can push downtime alerts straight to your phone with the priority you choose — pair it with a max-priority topic for the monitors that should wake you. If you have not set up a monitor yet, the Uptime Kuma beginner guide covers the full setup; ntfy is the delivery lane that makes those checks actually reach you.

For your own scripts, the pattern is one curl line on the failure path and one on the success path. A backup script ending looks like this:

#!/bin/bash
if rsync -a /data/ /backup/; then
  curl -s -H "Priority: low" -d "Nightly backup completed" \
    https://ntfy.yourdomain.com/backups
else
  curl -s -H "Priority: max" -H "Tags: warning" -d "BACKUP FAILED — check the server" \
    https://ntfy.yourdomain.com/backups
fi

The same one-liner works from cron jobs, systemd OnFailure directives, CI/CD pipeline steps, and Home Assistant automations (which has a community ntfy integration). Because the API is just HTTP, there is no library to vendor and no agent to install on the machine that sends the alert.

ntfy vs Gotify: which one should you pick?

Pick ntfy if you want topic-based messaging that starts with a single curl command, and Gotify if you prefer per-app tokens and a WebSocket-first design — they solve the same problem with different ergonomics. Both are open-source, self-hostable push notification servers, and both have Android apps. The honest comparison:

ntfyGotify
Mental modelTopics: anyone who knows the name can publish or subscribeApps: each app gets its own token, messages stream over WebSocket
First messageOne curl command, no setup at all (public server)Install server, create an app, copy its token, then send
AuthenticationPer-topic users and tokens, optional on public topicsPer-app tokens by design
Message featuresPriorities, tags/emoji, action buttons, attachments, scheduled deliveryMessage extras via plugins, Markdown-ish formatting
iOS appYes, on the App StoreCommunity clients; iOS story is thinner
IntegrationsUptime Kuma, Grafana, Home Assistant, CI/CD, email forwardingUptime Kuma, Home Assistant, and others via plugins

Neither is a rival to the other — they are two lanes in the same notification cluster. Beginners tend to reach for ntfy first because the first message takes sixty seconds and zero setup; teams that want strict per-application credentials sometimes prefer Gotify's token model. Everything in this guide applies to the ntfy lane.

What are the 5 beginner mistakes that silence your alerts?

The five mistakes that silence ntfy alerts are topic-name typos, secrets on the public server, missing topic grants after enabling auth, a reverse proxy that strips WebSocket headers, and every alert set to max priority. Each one is avoidable once you know it exists:

MistakeWhat happensThe fix
Topic name typoScript publishes to Backup, phone subscribes to backup — topic names are case-sensitive, so the alert vanishesCopy the topic name from the app; test every new topic with a manual curl first
Secrets on ntfy.shInternal URLs, tokens, or customer data sit on a public server readable by anyone who guesses the topicPublic server for tests only; real alerts go to your authenticated self-hosted server
User created, topic never grantedAfter enabling auth, scripts fail with 403 even though the user existsntfy access myuser mytopic rw for every topic the user needs
Proxy without WebSocket upgradeApp connects but notifications arrive late or neverAdd the Upgrade/Connection headers (see the HTTPS section) and turn off proxy buffering
Everything at max priorityYour phone screams about completed backups, so you mute it — and miss the outageReserve high/max for failures that need action; routine jobs get low or default

Sources

  • ntfy official documentation, "Getting started" (docs.ntfy.sh) — first-message flow, curl examples, topic auto-creation, app availability (Google Play, App Store, F-Droid). Crawled via search snippets 2026-10-31.
  • GitHub binwiederhier/ntfy releases API — v2.35.0 released 2026-10-30, ~35,193 stars. Queried 2026-10-31.
  • xTom blog, "How to Self-Host ntfy for Mobile Push Notifications" (published 2026-08-22) — Docker Compose install, Caddy/Nginx reverse proxy configs, auth-file + deny-all lockdown, priority/tag/action headers, troubleshooting. Crawled 2026-10-31.
  • Oh Dear docs, "ntfy Push Notifications" — topic-as-password security model, five priority levels and behaviors, app subscribe flow, custom server toggle. Crawled via search snippets 2026-10-31.
  • Medium / GitHubDaily, "ntfy: One curl command to push notifications from any script to your phone" — zero-setup curl flow, Title/Priority/tags/action-button examples. Crawled via search snippets 2026-10-31.
  • Railway docs, "Deploy & Host ntfy" — feature list (attachments, click actions, SMTP email, integrations: Uptime Kuma, Grafana, Home Assistant). Crawled via search snippets 2026-10-31.

Frequently asked questions

Is ntfy really free?

Yes. ntfy is open source under the Apache 2.0 / GPLv2 licenses, the public ntfy.sh server is free to use with rate limits that keep it fair for everyone, and self-hosting costs nothing beyond the server it runs on. There is no paid tier gating the core features — priorities, tags, actions, attachments, and authentication are all included.

Do I need to self-host ntfy to use it?

No. Start on the free ntfy.sh server: install the app, pick a topic, and send a curl command — no installation and no account. Self-host when your alerts carry anything you would not want strangers to read, when you need guaranteed availability, or when you want per-topic access control that the public server cannot fully provide.

How is ntfy different from email alerts?

Speed and signal. Email alerts arrive in a crowded inbox, can be delayed or spam-filtered, and are easy to miss at 2 a.m. ntfy messages arrive as push notifications in seconds, carry priorities that change how loudly your phone announces them, and cost nothing to run. Email still works as a fallback — ntfy can even forward messages to email via SMTP on a self-hosted server.

Can ntfy send notifications to an iPhone?

Yes — the official ntfy app is on the App Store and supports custom server URLs. Just know that iOS push delivery behaves differently than Android's direct connection, so test your critical alerts on the actual iPhone you will carry before trusting it with a real outage.

What happens if someone guesses my topic name?

On the public server, they can subscribe and read your messages — that is why test topics get long random names and real alerts move to a self-hosted server with authentication. With auth enabled and a deny-all default, an unknown topic name alone gets nothing: the request fails with a 403.

Featured on Twelve Tools Launch Llama Tools Launch your MCP