From-Scratch Build · Self-Hosted Alerts
A private notification service that pushes alerts to my phone and desktop from a single HTTP request — no app to write, no cloud account, no third-party backend. Built from scratch around the ntfy server, running in Docker.
What it is
This is a self-hosted push-notification service. The whole premise is delightfully simple: to send yourself an alert, you make an HTTP request. To receive alerts, you subscribe to a named topic — in a browser tab, in a mobile app, or with a script. A build finishes, a sensor trips a threshold, a backup completes — any program that can send an HTTP request can now ping you.
I built this to learn how push notifications work without depending on Apple, Google or any SaaS dashboard. A topic is just a name. Publishing is a POST. Subscribing is a long-lived GET. That radical simplicity is the entire point — and it's what makes the service trivial to glue into anything.
The core idea I wanted to learn: notifications don't need an SDK. If the server speaks plain HTTP, then a one-line curl from a cron job, a CI pipeline or a microcontroller is enough to reach my pocket.
The stack
A single container does the job. Here is what each piece contributes.
The notification server itself. It accepts published messages over HTTP and streams them to every subscriber of the matching topic.
Publish with a POST, subscribe with a streaming GET. No proprietary protocol — just requests any tool already understands.
Runs the server as one container, mapping an internal port to the host and mounting cache and config directories for persistence.
A compose healthcheck polls the server's /v1/health endpoint so the container reports unhealthy the moment the service stops responding.
Subscribe by opening the topic URL in a tab, or install the companion mobile app and subscribe to the same topic name for real push alerts.
Official Python and JavaScript libraries exist, but anything that can make an HTTP call — shell, cron, CI, a sensor — can publish.
How it works
The mental model has exactly three moves, and once they click the whole service makes sense:
A topic is just a name like alerts or backups. Picking the name is creating it — the server makes the route on first use.
Open the topic's URL in a browser tab to stream updates live, or subscribe to the same name in the mobile app to get real push notifications on your phone.
Send a message body to the topic URL with a POST. For a quick test that's one line: curl -d "Hi" http://host:1337/alerts.
Every subscriber to that topic gets the message in real time. The same message reaches the browser tab and the phone at once.
How it runs
Deployment is a single service in Docker Compose, designed to survive restarts:
serve command, exposing its HTTP API on a host port./v1/health on an interval; restart: unless-stopped brings the service back automatically if it ever falls over.TZ environment variable keeps notification timestamps aligned with local time.In my rebuild I focused on the round trip: bring up the container, subscribe to a topic in one tab, curl a message from a terminal, and watch it appear instantly.
Reflection