I wanted to know when my media stack grabbed something — without routing my notifications through Discord or a third-party push service. The answer was ntfy, self-hosted.

ntfy in a container

ntfy is a dead-simple pub/sub notification server: publish to a topic over HTTP, subscribe from a phone or browser app. Self-hosted, it's one container behind Traefik. I set it to deny-all by default with a single user scoped to one topic, so it isn't an open relay.

Wiring up the *arr apps

Sonarr, Radarr, Lidarr and Readarr all have a native ntfy connection under Settings → Connect. Point it at the server, set the topic, pick your events (grab / import / upgrade / health), done.

The gotcha that cost me an hour

Creating the connection via the API kept failing with:

Unable to send test message: Value cannot be null. (Parameter 'source')

The *arr ntfy connector calls .Select() on its Tags field, and if you omit that field it's null — so the whole request throws. The fix: include an empty tags array in the payload even if you don't use tags. Once I did, every test message went straight to my phone.

No Discord, no cloud, no monthly anything — just my own little notification bus.