← all builds

From-Scratch Build · Self-Hosted Alerts

Self-Hosted Push Notifications

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.

ntfyHTTP pub/subDocker curlWebhooks

What it is

A notification you can curl

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

Tools under the hood

A single container does the job. Here is what each piece contributes.

server

ntfy

The notification server itself. It accepts published messages over HTTP and streams them to every subscriber of the matching topic.

protocol

HTTP pub/sub

Publish with a POST, subscribe with a streaming GET. No proprietary protocol — just requests any tool already understands.

packaging

Docker Compose

Runs the server as one container, mapping an internal port to the host and mounting cache and config directories for persistence.

health

Healthcheck

A compose healthcheck polls the server's /v1/health endpoint so the container reports unhealthy the moment the service stops responding.

clients

Browser + mobile app

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.

integrations

curl / scripts

Official Python and JavaScript libraries exist, but anything that can make an HTTP call — shell, cron, CI, a sensor — can publish.

How it works

Topic, subscribe, publish

The mental model has exactly three moves, and once they click the whole service makes sense:

  1. Create a topic

    A topic is just a name like alerts or backups. Picking the name is creating it — the server makes the route on first use.

  2. Subscribe

    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.

  3. Publish

    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.

  4. Receive instantly

    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

One container, persistent state

Deployment is a single service in Docker Compose, designed to survive restarts:

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

What rebuilding it taught me