The most important reframe: self-hosted and cloud relay are not competing choices — they are layers. The common production homelab stack in 2026 is Apprise [1] as a stateless fan-out router sitting above ntfy [2] as the delivery server, with one or two cloud sinks (Pushover, Telegram) wired in as Apprise targets for mobile reach. The “vs” framing collapses once you see Apprise’s ntfy://topic/ and pushover://token@user/ as equally valid URLs in the same config file.
iOS is the critical architectural constraint that every other decision flows from. Gotify has no iOS app and no credible path to one [3]. ntfy ships its own iOS app but must relay through ntfy’s cloud APNs bridge — your message transits ntfy.sh’s servers for a moment [4]. The only way to keep iOS delivery fully self-contained is Pushover ($4.99 one-time) [5] or a Telegram bot (free, but bot traffic is never end-to-end encrypted [6]). This single constraint explains why most “pure self-host” deployments quietly add Pushover as the iOS sink.
The Docker wiring is effectively solved. Five composable patterns — named bridge network for same-host service groups, an external proxy network shared with Traefik, a socket proxy to avoid bind-mounting the Docker daemon socket, depends_on: condition: service_healthy for startup ordering, and _FILE env-var secrets — cover the overwhelming majority of notification stack configurations [7][8]. Apprise reaching ntfy by service name (http://ntfy:80/topic) with no published port is the canonical example of why this matters: zero host-IP coupling, zero firewall rules.
Maintenance diverges sharply at 12 months. ntfy and Gotify are single Go binaries with minimal runtime dependencies; a docker pull and restart is the entire upgrade path. Apprise is a Python package with ~143 upstream service connectors [9]: every time Slack, Discord, or Telegram changes an API, a connector may drift. The practical implication is that Apprise upgrades need testing against your active sinks, not just a blind pull. Pushbullet compounds this: it is in active decline with the thinnest tooling support of any evaluated service [10] and should be excluded from new stacks.
WhatsApp and web push are edge cases with hard cloud floors. Meta deprecated its on-premise Business API in October 2025 [11] — there is no longer a self-hosted free path to WhatsApp’s official network. Evolution API with a Baileys backend works but is brittle and terms-of-service grey. Web push requires HTTPS infrastructure, service workers, and browser opt-in; it is only worth the overhead for lab setups that already expose a browser-based dashboard to end users [12].
Recommendation by use-case profile
| Profile | Primary stack | iOS path | Cost |
|---|---|---|---|
| Pure self-host | ntfy + Apprise | ntfy APNs relay (data hops ntfy.sh) [4] | Free |
| Hybrid (best balance) | ntfy + Apprise → Pushover for iOS alerts | Pushover dedicated app [5] | $5 once |
| No-ops / low maintenance | Gotify (Android/web) + Telegram bot (mobile) | Telegram app [6] | Free |
| Team lab (shared workspace) | Apprise → Slack webhook + ntfy for personal alerts | Slack app (deprecation risk [13]) | Free tier |
| Maximum channel breadth | Apprise as sole dispatch layer, all sinks as Apprise URLs | Target-dependent | Free |
The open question this expedition leaves: as ntfy’s feature set (ACLs, priorities, action buttons, attachments, scheduled delivery [14]) continues to expand and its GitHub star count clears 30k [2], at what point does Apprise’s Python overhead become unnecessary for single-sink homelab deployments — and does ntfy eventually absorb the routing layer entirely?