ntfy: The Self-Hosted Push Notification Server That Stops Routing Your Alerts Through Google
Your phone buzzed because a server in California decided it should. That is true whether the alert came from your bank, your doorbell, or the cron job on your own home server — and it is a strange thing to accept when the entire point of the alert was that something on your machine happened.

Push notifications are one of those layers of modern computing that everybody uses and nobody inspects. They feel like a direct line from an application to your pocket. They are not. On Android, delivery is mediated by Google's Firebase Cloud Messaging. On iOS, every single one goes through Apple's Push Notification service. There is no third option, no self-hosted path, no way to opt out — the operating system decides who carries your alerts, and in doing so it gets to see that they happened, when, and roughly how urgent they were.
For most alerts this is a shrug. For the specific category of alerts that a self-hoster generates — "someone logged into your server at 3 a.m.", "the backup completed", "motion detected in the garage", "this page you're watching changed" — it is a quiet contradiction. You went to the trouble of running your own everything so that nobody else holds your data, and then you hand the notification layer to a company that logs metadata about every alert you receive.
ntfy is the most practical answer to that contradiction, and it has become one of the most widely deployed pieces of self-hosted infrastructure in existence: roughly
33,400 GitHub stars, dual-licensed
Apache-2.0 / GPLv2, released as
v2.28.0 in early September 2026, and maintained by one primary author with a substantial contributor base. It is a single Go binary that turns an HTTP request into a push notification.
This is a deep-dive on what it actually does, what it costs, where your data goes, and — importantly — where the "self-hosted" claim quietly breaks.
1. What ntfy Actually Is
Strip away the marketing and ntfy is a
pub-sub notification service that speaks plain HTTP.
You publish a message to a topic. Anyone subscribed to that topic receives it. That is the entire model.
``
bash
curl -d "Backup completed" ntfy.sh/my-backups-topic
`
That is it. No account, no SDK, no API key, no client library. A topic is created the moment anyone publishes to it or subscribes to it. There is no registration step, no schema, no queue declaration. If you can make an HTTP request — from curl
, from a shell script, from a cron job, from a Python one-liner at the end of a long data job — you can send yourself a push notification.
The receiving side is equally unceremonious. The Android app subscribes to a topic by name. The web app does the same in a browser tab and turns incoming messages into desktop notifications. The CLI can subscribe and pipe messages into a script. That symmetry — publish with HTTP, receive on any device — is the whole design, and it is why ntfy has been adopted so broadly by people who would never configure a message broker.
What ntfy is not matters just as much, because the confusion is common. It is not a message queue. There are no consumer groups, no acknowledgements, no durable replay, no ordering guarantees across restarts. Messages are cached so that a device reconnecting shortly after a message was published can still receive it, but that cache is a convenience, not a log. If nobody is listening, the message is effectively a broadcast into an empty room.
2. How It Works, Concretely
The mechanics are simple enough to hold in your head, which is the point.
Server. One Go binary. It holds topics in memory, persists messages into a small SQLite cache, and keeps open connections from subscribers.
Publishing. PUT
or POST
to /<topic>
. Various headers control behaviour:
`
bash
curl \
-H "Title: Backup failed" \
-H "Priority: urgent" \
-H "Tags: warning,skull" \
-d "Exit code 2 on /mnt/data" \
ntfy.sh/server-alerts
`
There are headers for title, priority, tags, click actions, action buttons, attachment URLs, scheduled delivery, and icons. Every one of them is optional — the plain -d "message"
form works and is what most people use most of the time.
Subscribing. Clients hold a long-lived HTTP connection (EventSource or WebSocket) against /<topic>/json
or the equivalent, and receive a JSON event per message. The official apps do this and handle reconnection, background delivery, and local notification rendering.
Delivery to phones. Here is where the operating systems intrude. On Android, the app maintains its own connection to your server, so a fully self-hosted setup is genuinely self-contained. On iOS, the app cannot maintain that connection in the background, so delivery is brokered — and this is the subject of section 4.
Attachments. Files can be attached to notifications, up to a configured size limit. This is quietly one of the most useful features: a security camera can POST a snapshot, a backup script can attach a log file, and it arrives on your phone as a notification you can open.
3. Why Push Notifications Are a Bigger Privacy Hole Than They Look
This is the argument for self-hosting ntfy, and it deserves to be made properly rather than asserted.
A push notification payload, in the normal case, contains a title and a body. If you are using a commercial notification service — Pushover, Pushbullet, a Telegram bot, even a matrix bridge — that title and body pass through someone else's infrastructure. For "your nightly backup finished," that is mildly uninteresting. For the categories that follow, it is not:
- Security alerts. "Failed SSH login: 14 attempts from 203.0.113.x." You have now told a third party that your server exists, that it is being probed, and what your response threshold is.
- Financial triggers. "BTC below $60,000." You have disclosed your holdings' shape and your attention to a market.
- Health and home. "Motion detected in the nursery at 02:14." You have disclosed occupancy patterns for a specific room in your house.
- Monitored pages. "The page you're watching changed." Combined with which pages you watch, that is a profile of your commercial or legal interests — and it is exactly the data that a page-monitoring SaaS would find valuable.
Metadata is the sharper problem. Even when a payload is encrypted, the fact and timing of notifications reveal a rhythm: when you sleep, when you leave the house, how often your monitoring fires, whether you are at your desk. Over months, that rhythm is a behavioural fingerprint, and it sits with a company whose business model does not require your consent to use it.
Running your own ntfy server removes the third party from the notification path on Android and desktop entirely. Your device talks to your server. Nothing else needs to know that anything happened. That is a real and achievable win, and it is the reason ntfy sits in so many self-hosted stacks.
It does not, however, fully remove the third party from iOS — and pretending otherwise would make this article another one of those self-hosting write-ups that quietly overstates the result.
4. The iOS Problem, Stated Honestly
This is the limitation that most ntfy coverage glosses over, and it should be the first thing an iPhone user reads.
Android maintains a persistent connection to your ntfy server. Self-hosted means self-hosted. You can firewall the server to your tailnet, never expose it to the public internet, and push notifications still arrive.
iOS does not allow that. Apple requires that all push notifications be delivered through Apple's Push Notification service. An app cannot hold an arbitrary background socket to your own server and render notifications from it. So when you self-host ntfy and use the iOS app, there are two possible delivery paths:
1. The app polls your server periodically. This works, is entirely private, and is slow — notifications can be delayed by minutes, or longer if the OS decides to be aggressive about background execution.
2. Your server forwards delivery requests to the upstream ntfy.sh
service, which then pushes through APNs so the notification arrives instantly. Private in content if you encrypt it, but you have now introduced an external dependency and told a third party that something was delivered to someone at some time.
The second option is what most people end up choosing, because delayed notifications defeat much of the purpose. And it means that the most private possible iPhone setup is not fully private in the way the Android setup is. The content can be protected — ntfy supports end-to-end encryption, though it is still flagged experimental — but the metadata leaks to Apple by design, and the delivery request leaks to ntfy.sh by configuration.
That is not a reason to avoid ntfy on iOS. It is a reason to know what you have actually bought. If your threat model is "I don't want a notification vendor building a profile of my alerts," self-hosting ntfy still delivers that, because the upstream sees an opaque delivery trigger rather than a readable stream. If your threat model is "nothing leaves my network," you should be on Android, or accept polling, or accept delay.
One more iOS wrinkle worth knowing: the iOS app has historically been distributed through TestFlight, which has a hard tester cap. Availability is not always immediate.
5. Deployment Walkthrough
ntfy is among the easiest things you will ever self-host. A minimal Docker Compose setup is genuinely this short:
`
yaml
services:
ntfy:
image: binwiederhier/ntfy:v2.28.0
container_name: ntfy
command: serve
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./cache:/var/cache/ntfy
- ./config:/etc/ntfy
environment:
TZ: UTC
`
Start it, open http://your-host:8080
, and you have a working notification server. Publish to it:
`
bash
curl -d "hello from my own server" http://your-host:8080/test
`
Subscribe in the Android app by adding your server URL and the topic name. Done.
For anything beyond a home-lab experiment, you will want a server.yml
in the mounted config directory. The two settings that matter most:
`
yaml
base-url: https://ntfy.example.com
cache-file: /var/cache/ntfy/cache.db
`
base-url
must be correct, or attachment links and click actions will point at the wrong host — a classic first-run confusion where notifications arrive but links are broken. cache-file
determines where the message cache lives; point it at the persistent volume, or you lose cached messages on every container restart.
Resource usage is negligible: a Go binary that idles in the tens of megabytes. It runs on a Raspberry Pi without complaint, and it will comfortably sit alongside a dozen other services you are already running.
6. Authentication: The Part Everyone Gets Wrong
Out of the box, a self-hosted ntfy server is open. Anyone who can reach it can publish and subscribe to any topic. That is fine on a tailnet. It is not fine if you expose it to the internet, and the default surprises people.
The core thing to understand: a topic name is a password. There is no separate secret. If your topic is backups
, anyone who guesses it can read your backup alerts and, worse, publish fake ones. The project's own documentation is blunt about this — pick topic names that are long and unguessable:
`
server-alerts-8f3a2b91c7d4
`
Not alerts
. Not backups
. Treat it like the credential it is.
For real access control, ntfy supports an auth file with users, passwords, and per-topic permissions in a read / write / read-write / deny matrix:
`
yaml
users:
- name: admin
pass: "$2a$10$..." # bcrypt hash
role: admin
- name: homeassistant
pass: "$2a$10$..."
permissions:
- topic: home-
permission: rw
- name: viewer
pass: "$2a$10$..."
permissions:
- topic: home-
permission: r
`
This is the shape you want for a multi-user or multi-service setup: one credential per producer, scoped to the topics that producer is allowed to touch, and no shared secret across everything. Combined with tokens (generated per user, revocable, usable in headers) this is adequate for small deployments.
What it is not is a full identity system. There is no OIDC, no SAML, no group management, no SSO. If you want notifications gated behind your existing identity provider, you do that at the reverse proxy layer — the same pattern that applies to the other self-hosted applications in this series.
7. Priorities, Actions, and What You Can Actually Send
The feature surface is small and well chosen. The things worth knowing:
Priorities. Five levels, from min to urgent. They map to different sounds, vibration patterns, and — importantly — whether the notification interrupts Do Not Disturb. urgent
notifications on Android can break through DND, which is exactly what you want for "the sump pump failed" and exactly what you do not want for "a new episode downloaded." Pick deliberately; the most common self-inflicted problem is everything being urgent, after which nothing is.
Tags and emojis. Short tags render as emoji and help you classify at a glance — warning
, skull
, white_check_mark
, rotating_light
. Small thing, real usability gain when you have twelve sources feeding one topic.
Action buttons. A notification can carry buttons that open a URL or make an HTTP call. This turns ntfy from a broadcast channel into a lightweight remote control:
`
bash
curl \
-H "Actions: http, Open Garage, https://home.example.com/api/garage/open, clear=true" \
-d "Delivery arrived" \
ntfy.sh/home
`
Which is genuinely useful and also genuinely a security consideration — a button that performs an action, delivered to whatever device holds the topic, with no second factor. Scope those endpoints tightly.
Scheduled delivery. A Delay
header sends the message at a future time. Useful for reminders from scripts.
Attachments. Files up to a configured limit, delivered with the notification. Camera snapshots, log tails, generated reports.
End-to-end encryption. Supported, still marked experimental. In practice this means: usable if you need it, not something you should assume is bulletproof.
8. Where It Fits in a Real Setup
ntfy's value compounds with everything else you self-host, because it is the layer that makes all the other layers tell you things. Concretely:
Uptime monitoring. Point your monitor's webhook at an ntfy topic instead of at an email address. Alerts arrive in seconds on your phone rather than sitting in an inbox you check hourly.
Page-change monitoring. The same for watched pages — and here the privacy argument is sharpest, because a third-party notification would tell someone exactly which pages you are watching.
Home automation. Motion, doors, leaks, temperature thresholds. The picture-attachment feature makes this dramatically more useful than a text alert.
Cron and long-running jobs. The canonical use:
`
bash
0 3 /usr/local/bin/backup.sh && \
curl -H "Priority: low" -d "Backup OK $(date +%F)" https://ntfy.example.com/backups-8f3a || \
curl -H "Priority: urgent" -H "Tags: warning" -d "BACKUP FAILED $(date +%F)" https://ntfy.example.com/backups-8f3a
`
That one line is the reason a lot of people install ntfy, and it is worth ten minutes of anyone's time.
CI pipelines. A build finishing, a deploy succeeding, a test flaking — posted to a topic the team subscribes to.
Security events. Failed logins, new device registrations, certificate expiry warnings.
Manual "ping me later." at now + 20 minutes <<< 'curl -d "Take it out" https://ntfy.example.com/reminders-2b91'
. Low-tech and effective.
The pattern in every case: anything that can make an HTTP request can now reach you. That is a remarkably low bar, and it is why ntfy ends up wired into more things than you expect when you install it.
9. The Honest Limitations
iOS delivery is not fully self-contained. Covered above; it is the biggest one, and it should be a conscious decision rather than a surprise.
It is not a message queue. No consumer groups, no acknowledgements, no durable replay, no guaranteed delivery. If you need at-least-once semantics or a replayable event log, use NATS, RabbitMQ, or Kafka. ntfy is a notification channel, and treating it as infrastructure plumbing will end badly.
Topics are the only secret by default. Get this wrong and your alerts are public. See section 6.
Rate limiting has been a moving target. The Rate-Topics
header was removed in v2.9.0 after it was found to introduce a denial-of-service risk when subscriber rate limiting was enabled. Abuse controls are version-sensitive and security-relevant — read release notes before upgrading, and do not assume a rate-limiting config from a 2023 blog post still behaves the way you expect.
Message IDs are opaque by design. They changed length in a past release to avoid collisions. Do not build consumer logic that parses them or assumes a fixed format.
Attachments are stored on your server. A large attachment limit plus an open server is a disk-usage problem waiting to happen.
Bus factor is effectively two. The project has hundreds of contributors but a very small core. It is healthy and well maintained today; it is still a concentrated dependency for something you may wire into your security alerting.
End-to-end encryption is experimental. Real, implemented, not yet something to stake a threat model on.
No native desktop app. The web app covers it, but it is a browser tab rather than a first-class client.
10. What It Costs
| Item | Cost |
|---|---|
| ntfy license | $0 (Apache-2.0 / GPLv2) |
| Server (on hardware you own) | $0 |
| Domain + TLS (if public) | ~$10–15/year, or free via your reverse proxy |
| Tailnet for private access | $0 if you run your own control plane |
| Hosted ntfy.sh (optional) | $0 free tier, $5–20/month paid tiers |
| Total for self-hosted | $0 marginal |
The comparison that matters is against the commercial alternatives. Pushover is a one-time $5 per platform for the app with message limits. Pushbullet, Telegram bots, and Slack webhooks are free but route your data through someone. Hosted notification SaaS products charge per seat and per message, which is a strange thing to pay for alerts about your own infrastructure.
For a home server, the marginal cost of ntfy is genuinely zero — it fits in the RAM you are already not using.
11. Where Your Data Lives
Run it yourself and the message cache sits in a SQLite file on your disk. Message content, topics, timestamps, attachment files: all local. Nothing is sent anywhere unless you configure it — with two exceptions to know about:
Upstream forwarding for iOS. If you enable it, delivery requests leave your network. Metadata, not content, but metadata.
External attachment URLs. If you publish an attachment by URL rather than by upload, the client fetches it from wherever it lives, which may not be you.
Beyond that, the privacy story is as good as it gets for a notification system: your server, your topics, your phone. The only party that sees anything is your own infrastructure — plus, on iOS, Apple, unavoidably.
12. ntfy vs Pushover vs Gotify vs Telegram
ntfy vs Pushover. Pushover is polished, has excellent clients, and costs $5 once. It is also a company reading every notification you send. ntfy is free, self-hostable, and has a slightly rougher client experience. If you want zero maintenance and are not sending sensitive alerts, Pushover is a reasonable choice. If you are sending security alerts about your own infrastructure, routing them through someone else's server undermines the point.
ntfy vs Gotify. The closest comparison — both are self-hosted push servers. Gotify is Java-based, heavier, and its mobile story is weaker. ntfy won this category on simplicity and on the quality of its Android app. Gotify has its adherents, but ntfy's enormous lead in adoption reflects a real difference.
ntfy vs a Telegram bot. The bot approach is popular and terrible for privacy: every alert passes through Telegram's servers in plaintext to them, tied to your account. It works. It is the opposite of self-hosting.
ntfy vs Matrix. Matrix is a full chat protocol — encrypted, federated, self-hostable — and it can deliver notifications. It is also dramatically heavier to run. If you already run a Matrix homeserver for chat, pushing alerts into a room is sensible. Installing Synapse purely for push notifications is a lot of infrastructure for a small job.
13. Who Should Not Use ntfy
- Anyone who needs guaranteed delivery. It is best-effort by design.
- iPhone-only households needing instant, fully-private alerts. You will have to choose between delay and an upstream dependency.
- Anyone who needs audit trails on notifications. The cache is not a log.
- Teams needing SSO and centralized identity. Do it at the proxy, or pick something else.
- Anyone who will not lock down access. An open ntfy server on the public internet is a spam vector and a data leak.
14. Getting Started Sensibly
The sequence that avoids the common mistakes:
1. Deploy it on your tailnet first. Do not expose it publicly on day one. You will learn the tool faster without worrying about access control, and a private server needs almost none.
2. Pick long, random topic names immediately. Do this before you wire anything up, because renaming topics later means updating every producer and every device subscription.
3. Start with one high-value alert. A cron job, a backup result, an SSH login. One. Live with it for a week before adding twelve more.
4. Set priorities deliberately. Default almost everything to low or default. Reserve high and urgent for things you would want to wake you.
5. Then decide about public exposure. If you need to reach it from outside your network, put it behind a reverse proxy with TLS, and enable the auth file with per-service credentials.
6. Subscribe on desktop too. The web app is a genuinely good way to see alerts while working, and it costs nothing.
15. Security Hardening
If you expose ntfy beyond your own network, these are non-negotiable:
Enable the auth file with per-topic permissions. One credential per producer, scoped as narrowly as the tool allows. No shared admin token in twelve scripts.
Use unguessable topic names even with auth enabled. Defence in depth: if auth is misconfigured, the topic name is still a barrier.
Put it behind TLS. Alerts reveal a lot; do not send them in clear text across the internet.
Constrain the attachment size and total cache. An open or semi-open server with a generous attachment limit is a disk-exhaustion target.
Review the action-button endpoints. Any action button performs an HTTP call from whatever device received it, with no additional authentication. Scope those endpoints to the minimum, and never expose a destructive action without a second step.
Watch release notes for rate-limit and abuse-control changes. This is the area with documented security churn.
Log and review. ntfy's own logs tell you who is publishing to which topic. On a shared server, glance at them occasionally.
16. Troubleshooting
Notifications arrive on Android but not iOS (or are delayed). The iOS delivery path. Either enable upstream forwarding for instant delivery, or accept polling delay. There is no third option.
Notifications arrive with broken links. base-url
in server.yml
is wrong or unset. It must be the externally reachable URL, including scheme.
Messages vanish after a restart. The cache file is not on a persistent volume.
A subscriber sees nothing at all. Check the topic name character by character, and check that the auth file grants read on that topic. Case matters.
Attachments fail. Size limit, or disk space on the cache volume.
Everything published is visible to everyone. You are on the default open configuration. Fix this before anything else.
High CPU or memory. Almost always a very large message cache or a subscriber storm. Check the cache duration setting.
17. Scaling and Operational Reality
For personal and small-team use, ntfy will never be your bottleneck. A single instance handles thousands of messages per second on modest hardware, and the realistic constraint is your upstream bandwidth rather than the server.
Where it starts to matter:
Many subscribers on one topic. Each is an open connection. Thousands of concurrent subscribers on one instance is where you would start thinking about resources, and it is not a scenario most self-hosters hit.
Large attachments, many of them. Disk, not CPU.
Message cache duration. Longer retention means a bigger SQLite file and more to back up. For a notification system, days — not months — is the right instinct. Retention exists to bridge a device being briefly offline, not to be a log.
Backups. The cache file and the config file (including your auth file) are what you need. Both are small. Include them in whatever backup routine you already run, and do not overthink it.
The operational burden is close to zero, which is the honest summary. It is a single container that has never once needed my attention after configuration.
18. Three Setups Worth Copying
The single-user home server. ntfy in a container on the same box as everything else, reachable only over your tailnet, one unguessable topic per alert source. Your backup script, your UPS monitor, your router's WAN-change hook, and your home automation all post to their own topics. You subscribe to a wildcard where the client supports it, or to the three topics you actually care about. Nothing is exposed to the internet, no auth file is needed, and the whole thing took twenty minutes. This is what most people should build.
The household. The same server, but with per-person topics and a shared one. home-alerts
goes to everyone, admin-security
goes only to you, kids-dinner
goes to the family. Everyone installs the app and subscribes to their own set. This is where priorities start to matter — a shared server with everything set to urgent is a household that learns to hate notifications within a week.
The small team on-call rotation. ntfy behind TLS with an auth file, one credential per monitoring system, a topic per service, and a separate high-priority topic for genuinely paging-worthy events. Add an action button that acknowledges the alert by calling back into your monitoring system. Keep the attachment limit modest and the cache retention short. This works well up to a handful of people; beyond that you want an incident management tool with rotations and escalation, and ntfy becomes the transport rather than the system of record.
What all three share: the topic naming was decided up front and written down, priorities were chosen deliberately, and the server is not publicly reachable unless it has to be.
19. Migrating From Whatever You Use Now
The transition is easier than most infrastructure migrations because ntfy can sit alongside* your existing notification path for as long as you like.
From Pushover or Pushbullet. Run both for two weeks. Every script that posts to Pushover also posts to ntfy — usually a second curl` line. Compare which alerts you actually wanted, then drop the commercial one. The thing you will notice is that Pushover's client is marginally nicer and ntfy's is yours.
From email-based alerts. This is the biggest quality-of-life jump. Find the webhook or notification setting in your monitoring tool and replace the SMTP path with an HTTP POST. Most tools that can send email can also call a webhook; if yours cannot, a five-line script bridge handles it. Email alerts for infrastructure events go from "noticed during the morning inbox sweep" to "noticed in four seconds."
From a Telegram bot. Delete it. Not because Telegram is bad, but because a bot token plus your chat ID in a dozen scripts means every alert you send is readable by a company that also has your phone number and your contact graph. The migration takes an afternoon and the privacy improvement is immediate.
From Slack or Discord webhooks for personal alerts. Keep them for team channels if that is where your team lives. For personal alerting about your own machines, move them to ntfy — a corporate chat platform is a strange place to send "motion detected in your hallway."
The pattern that works in every case: add ntfy as an additional destination first, live with it, then remove the old one once you have confirmed nothing is missed. Notification systems are cheap to run in parallel and expensive to get wrong during a cutover.
20. The Verdict
ntfy is one of those rare tools where the self-hosted version is not a compromise — it is the better product. You are not giving up features to own it; you are giving up a third party that has no business seeing your alerts. The HTTP interface is so simple that integrating it takes seconds, the Android experience is genuinely good, and the resource cost is so low that it is effectively free if you already run a server.
The honest caveats are real but narrow. iOS cannot be fully self-contained, and that will not change because it is an Apple policy rather than an ntfy limitation. It is a notification channel, not a message broker, and treating it as one will disappoint you. The default configuration is open, and you have to close it.
None of those offset the core value: your monitoring, your automations, and your security alerts can reach your phone without a company in the middle recording that they happened.
Related
Comments (0)
No comments yet. Be the first to comment!