> ## Content Index
> Fetch the complete content index at: https://blog.uptime.bet/llms.txt
> Use this file to discover other available public pages before exploring further.

# ntfy 2.36: Self-Host Push Notifications for Your Homelab with Docker Compose
- URL: https://blog.uptime.bet/ntfy-self-hosted-push-notifications-docker/
- Published: 2026-11-01T08:00:00.000Z
- Updated: 2026-11-01T08:00:01.000Z
- Description: ntfy turns a simple curl request into a push notification on your phone. What is new in v2.36, and how to run a private instance with Docker Compose, users and tokens.
- Author: Alexandre Nebeling
- Tags: Trending, Self-Hosted, Open Source, Docker, Homelab, Monitoring, Notifications

Most homelabs have the same blind spot: a backup script fails at 3 a.m., a disk fills up, a container restarts in a loop, and nobody finds out until something breaks for real. Email alerts get buried, and wiring every tool into Slack or Telegram is fiddly. **ntfy** (pronounced "notify") solves this in the simplest way possible: send an HTTP request, get a push notification on your phone or desktop.

ntfy just shipped **v2.36.0** on 31 October 2026, just one day after v2.35.0, and with more than 35,000 GitHub stars it is one of the best-known self-hosted notification tools. In this post we look at what ntfy does, what is new, and how to run your own private instance with Docker Compose.

## What is ntfy?

ntfy is an HTTP-based publish/subscribe notification service written in Go and licensed under **Apache-2.0** (the repository also carries GPLv2 as a dual license option for the code). You publish a message to a *topic* with a plain `PUT` or `POST`, and every device subscribed to that topic gets it as a notification.

There is a free public instance at ntfy.sh, open-source apps for Android (Google Play and F-Droid) and iOS, a web app that can show browser notifications, and a CLI. Because the server is open source, you can run all of it yourself.

![A terminal window publishing a message to an ntfy topic with curl](https://blog.uptime.bet/content/images/2026/11/ntfy-screenshot-curl.webp)

Publishing a notification is a single curl command. [Source: ntfy repository](https://github.com/binwiederhier/ntfy?ref=blog.uptime.bet) (Apache-2.0)

## Key features

- **Dead simple publishing.** `curl -d "Backup done" https://ntfy.example.com/backups` is a complete, working notification. Any script, cron job or tool that can make an HTTP request can use it.
- **Rich messages.** Titles, five priority levels (from `min` to `max`/`urgent`), tags that turn into emojis, Markdown formatting, click actions, icons, attachments and action buttons.
- **Scheduled delivery.** Send a message now and have it delivered later.
- **Apps everywhere.** Android, iOS, a web app with Web Push for background notifications, and a CLI that can both publish and subscribe.
- **Access control.** Users with `user` and `admin` roles, per-topic read/write ACLs and access tokens, so a private instance can deny everything by default.
- **Integrations.** Many tools speak ntfy directly or through Apprise. It also works as a UnifiedPush distributor on Android, and it can receive messages by email if you enable the built-in SMTP server.
- **Lightweight.** A single Go binary with SQLite by default; PostgreSQL is supported for larger setups.

![ntfy web app with subscribed topics on the left and a notification with an image attachment](https://blog.uptime.bet/content/images/2026/11/ntfy-screenshot-web-detail.webp)

The ntfy web app showing a notification with a title, priority, tag and image attachment. [Source: ntfy repository](https://github.com/binwiederhier/ntfy?ref=blog.uptime.bet) (Apache-2.0)

## What is new in v2.35 and v2.36

The last two releases are mostly about limits, correctness and security, which is good news if you run ntfy for more than one person:

- **Daily limits survive restarts (v2.36.0).** Message, email and phone call limits are now stored in the user database instead of memory. With PostgreSQL they are shared between servers, and each visitor's day starts at its own time instead of midnight UTC.
- **Experimental admin socket (v2.36.0).** A local Unix socket (`experimental-listen-admin-unix`, off by default) serves the admin API. Note that it needs no credentials, so anyone who can open the socket is an admin. Leave it off unless you know you need it.
- **Stricter config check (v2.36.0).** When `base-url` is set, the whole config is validated again, so a broken config that used to start may now fail at startup. Check your logs after upgrading.
- **Security fix (v2.35.0).** Matrix push gateway requests with a push key that does not name a valid topic are now rejected. The release notes describe a case on SQLite where a crafted key could store messages under a protected topic.
- **Fewer random 403s (v2.35.0).** A race in the account handlers that could treat a logged-in request as anonymous (or the other way around) under concurrent requests from the same IP is fixed.

Upgrade note from v2.36.0: everyone's daily counters start over at the upgrade, and servers that share a PostgreSQL database must keep their clocks in sync.

## Why self-host it?

The public ntfy.sh server is free and works fine, but topics there are public: **the topic name is effectively the password**. That is fine for "the washing machine is done", less so for "backup of the finance server failed". Your own instance gives you:

- Topics that nobody else can read or write, thanks to `deny-all` by default.
- No rate limits other than the ones you set.
- Message history on your own disk.
- One notification hub for every tool in your lab.

## Install ntfy with Docker Compose

The official image is `binwiederhier/ntfy` (amd64, armv6, armv7 and arm64). The server listens on port 80 inside the container. The image does not ship a `server.yml`, so you either mount one or configure everything with `NTFY_*` environment variables, which is what we do here.

### 1\. Create a folder

```bash
mkdir -p /opt/ntfy && cd /opt/ntfy

```

### 2\. Create `compose.yml`

```yaml
services:
  ntfy:
    image: binwiederhier/ntfy:v2.36.0
    container_name: ntfy
    command: serve
    restart: unless-stopped
    init: true
    environment:
      TZ: Europe/Berlin
      NTFY_BASE_URL: https://ntfy.example.com
      NTFY_CACHE_FILE: /var/lib/ntfy/cache.db
      NTFY_AUTH_FILE: /var/lib/ntfy/auth.db
      NTFY_AUTH_DEFAULT_ACCESS: deny-all
      NTFY_BEHIND_PROXY: "true"
      NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments
      NTFY_ENABLE_LOGIN: "true"
      NTFY_UPSTREAM_BASE_URL: https://ntfy.sh
    volumes:
      - ./data:/var/lib/ntfy
    ports:
      - "127.0.0.1:8080:80"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"]
      interval: 60s
      timeout: 10s
      retries: 3
      start_period: 40s

```

A few notes on these settings:

- `NTFY_BASE_URL` must be the public URL your phone uses to reach the server.
- `NTFY_CACHE_FILE` keeps messages on disk, so a restart does not lose them and clients can fetch what they missed.
- `NTFY_AUTH_FILE` plus `NTFY_AUTH_DEFAULT_ACCESS: deny-all` turns your server into a private instance: nobody can read or write anything until you allow it.
- `NTFY_BEHIND_PROXY` is required behind a reverse proxy. Without it, ntfy sees every visitor as the proxy's IP and rate limits them all as one.
- `NTFY_UPSTREAM_BASE_URL` is only needed for instant notifications on **iOS**. Your server then sends a small "poll request" to ntfy.sh, which contains only a message ID and a hash of the topic URL, not your message; the iPhone then fetches the real message from your server. Remove the line if you do not use iOS.
- The port is bound to `127.0.0.1` because a reverse proxy should handle HTTPS in front of it. The healthcheck and `init: true` follow the example in the official docs.

### 3\. Start it

```bash
docker compose up -d
docker compose logs -f ntfy

```

### 4\. Create an admin user

Users are managed with the `ntfy user` command inside the container. It asks for a password interactively:

```bash
docker compose exec ntfy ntfy user add --role=admin alice

```

Admins can read and write every topic. For scripts, create a dedicated user and give it only what it needs:

```bash
docker compose exec ntfy ntfy user add backup-bot
docker compose exec ntfy ntfy access backup-bot backups write-only

```

### 5\. Create an access token for scripts

Tokens are nicer than passwords in scripts, can expire and can be revoked one by one:

```bash
docker compose exec ntfy ntfy token add --expires=90d --label="backup script" backup-bot

```

Keep in mind that a token currently grants full access to that user's account (everything except changing the password or deleting the account), which is another reason to use a low-privilege user for automation.

### 6\. Put it behind HTTPS

Point your reverse proxy (Traefik, Caddy, Nginx Proxy Manager, nginx) at `127.0.0.1:8080` for `ntfy.example.com`. ntfy keeps long-lived connections open for subscribers (HTTP streaming and WebSockets), so make sure your proxy does not cut them off with short timeouts or response buffering. The official config docs have complete nginx, Apache and Caddy examples.

## Sending your first notifications

A simple message with a token:

```bash
curl -H "Authorization: Bearer tk_yourtokenhere" \
     -d "Nightly backup finished" \
     https://ntfy.example.com/backups

```

With a title, high priority and a warning emoji tag:

```bash
curl -H "Authorization: Bearer tk_yourtokenhere" \
     -H "Title: Backup failed" \
     -H "Priority: high" \
     -H "Tags: warning" \
     -d "rsync exited with code 23 on nas01" \
     https://ntfy.example.com/backups

```

A pattern that works well in cron jobs is to notify only on failure:

```bash
/usr/local/bin/backup.sh || curl -H "Authorization: Bearer tk_yourtokenhere" \
  -H "Priority: high" -H "Tags: rotating_light" \
  -d "backup.sh failed on $(hostname)" https://ntfy.example.com/backups

```

![Android notification drawer with two ntfy alerts about a failed backup and unauthorized access](https://blog.uptime.bet/content/images/2026/11/ntfy-screenshot-phone-notification.webp)

Alerts from ntfy on an Android phone, with tags turned into emojis. [Source: ntfy repository](https://github.com/binwiederhier/ntfy?ref=blog.uptime.bet) (Apache-2.0)

## Subscribing on your phone

Install the ntfy app from Google Play, F-Droid or the App Store, add your server URL in the settings, log in with your user and subscribe to topics such as `backups` or `alerts`. On Android you can choose per topic whether to use instant delivery through a persistent connection to your server. In the web app you can also enable background notifications through Web Push, which needs extra `NTFY_WEB_PUSH_*` keys (generated with `ntfy webpush keys`), as described in the docs.

## Connecting ntfy to your other tools

This is where ntfy shines in a homelab:

- **changedetection.io** and other tools that use Apprise can send to ntfy with an `ntfy://` or `ntfys://` URL. See our [changedetection.io guide](https://blog.uptime.bet/changedetection-io-self-hosted-website-change-monitoring-docker/) for the setup.
- **Healthchecks** has a built-in ntfy integration, so missed cron pings can land directly on your phone. More in our [Healthchecks post](https://blog.uptime.bet/healthchecks-self-hosted-cron-job-monitoring-docker/).
- **Many other apps** support ntfy natively or through Apprise; the [ntfy docs keep a list of integrations](https://docs.ntfy.sh/integrations/?ref=blog.uptime.bet). ntfy also accepts publishing via a simple `GET` URL, which helps with tools that can only call a webhook.

## Tips

- **Pick topic names with care.** Even on a private server, use clear names per purpose (`backups`, `updates`, `security`) and give scripts write-only access.
- **Pin the image tag** and read the release notes before you bump it. v2.36.0 is a good example: its stricter config check can stop a server with a broken config from starting.
- **Back up the `data` folder.** It holds the user database, the message cache and attachments.
- **Keep the experimental features off** (clustering, admin socket) unless you have a specific reason to try them.

## Limitations

- **iOS needs ntfy.sh for instant delivery.** Without `upstream-base-url`, notifications still arrive on iPhones, but according to the docs they can be delayed considerably. With it, your server talks to ntfy.sh (only message IDs and topic hashes, not content).
- **Tokens are not scoped.** Granular access tokens are on the roadmap but not implemented yet, so use separate low-privilege users.
- **It is a pipe, not a monitor.** ntfy delivers messages; it does not detect problems on its own. Pair it with Healthchecks, changedetection.io or your monitoring stack.
- **Android battery.** Instant delivery on a self-hosted server keeps a connection open, which can cost some battery depending on your device.

## Alternatives in short

**Gotify** is another popular self-hosted push server; its project lists an Android app and a web UI, but no official iOS app. **Apprise** is not a server but a library and API that sends to dozens of services, including ntfy, so the two complement each other. If you want one self-hosted endpoint that every script and app can reach, ntfy is the easiest of the three to start with.

## Conclusion

ntfy is one of those tools that you set up in ten minutes and then use everywhere. A single container, a private `deny-all` config, one admin user and a token for your scripts are all you need to get backup, update and monitoring alerts straight to your phone. The latest releases tighten limits and fix security issues, so if you already run ntfy, it is worth reading the notes and upgrading.

How do you get alerts from your homelab today: ntfy, Gotify, Telegram, or good old email?

## Sources

- [ntfy on GitHub](https://github.com/binwiederhier/ntfy?ref=blog.uptime.bet)
- [ntfy v2.36.0 release notes](https://github.com/binwiederhier/ntfy/releases/tag/v2.36.0?ref=blog.uptime.bet)
- [ntfy v2.35.0 release notes](https://github.com/binwiederhier/ntfy/releases/tag/v2.35.0?ref=blog.uptime.bet)
- [ntfy installation docs (Docker)](https://docs.ntfy.sh/install/?ref=blog.uptime.bet#docker)
- [ntfy configuration docs](https://docs.ntfy.sh/config/?ref=blog.uptime.bet)
- [ntfy publishing docs](https://docs.ntfy.sh/publish/?ref=blog.uptime.bet)
- [binwiederhier/ntfy on Docker Hub](https://hub.docker.com/r/binwiederhier/ntfy?ref=blog.uptime.bet)