New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
ntfy delivers messages from scripts, monitoring, and home automations to subscribed browsers and mobile clients. This recipe requires login, denies anonymous topic access, stores messages and attachments locally, and provisions an administrator from a private bcrypt entry.
ntfy is a publish/subscribe notification server. A backup job, availability check, or household script sends an HTTP request to a topic; subscribed clients receive the message through the browser or a mobile app. Priorities, titles, tags, and attachments help a short notification carry enough context to act.
Use it for personal monitoring and automation when you want to operate the notification server yourself. It can cover everyday Pushover-style publish/subscribe workflows. You take responsibility for HTTPS, accounts, uptime, storage, and the delivery path a phone or browser uses; it does not turn a notification cache into a permanent archive.
This recipe runs the official pinned image as UID/GID 1000 with a read-only root filesystem. An initialization helper prepares one named volume. The server keeps SQLite message/auth databases and attachment files together, requires login, disables signup, and denies topic reads and writes without an explicit permission. The administrator comes from a private declarative bcrypt entry before requests are served.
Messages and attachments are cached for seven days. Accounts, access tokens, topic ACLs, and account subscriptions/settings persist in the same volume; the web client also keeps a local IndexedDB cache. Server backup does not copy a browser profile. A restored attachment retains its original expiration time.
Topic authentication does not make attachment URLs private. Anyone who knows an attachment URL can download its bytes without login. Treat the URL as a bearer secret, encrypt sensitive attachments before uploading, and avoid putting passwords or recovery keys in notification bodies. Data at rest is not end-to-end encrypted by this recipe.
The smoke checks a live HTTP subscriber, actual publishing, cached reads, accounts, token ACLs, attachment bytes, and recovery after deleting the volume. It also verifies that an attachment is downloadable without login when its URL is known. No physical phone or background browser delivery was tested.
The browser and official Android/iOS clients are available upstream. An open browser can receive messages directly. Android can use direct server connections; Firebase is a separate delivery path. Instant iOS notifications from a self-hosted server need upstream polling, normally through ntfy.sh, while closed-browser Web Push needs VAPID keys and browser push services. This recipe configures none of those optional relays, SMTP, phone calls, or external storage. Read their connectivity and privacy requirements before enabling them; do not assume the CPU/server smoke proves mobile background delivery.
Keep the web port on loopback or behind a trusted VPN/proxy. Publish only through HTTPS, protect .env, grant automation accounts only their required topics, and encrypt external backups. ntfy does not provide native OIDC, SAML, MFA, or trusted-user-header login in this version. Forwarded client-IP trust stays off, so proxy requests share the proxy’s rate limit.
The declarative administrator hash is authoritative on every startup. Rotate it deliberately and preserve a matching environment during restore. A regular account and token created through the native CLI/API are stored in SQLite and are included in backup.
Upstream is dual licensed under Apache-2.0 and GPLv2; the recipe is MIT. The catalog icon comes from the upstream mask asset under the Apache-2.0 option; its license is retained in ASSET-LICENSES.md.
Sources: v2.34.0, configuration and access control, publishing, subscriptions, license.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
v2.34.0v2.34.0Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-10-25.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
The recipe covers every requirement at this level.
A setup designed for predictable operations and observability.
You still need to add: separate database, external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run ntfy bundle.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psUse Ubuntu 22.04+ or Debian 12+ on amd64/arm64 with Docker Engine and Compose v2.24+. Plan for 1 CPU, 128 MB RAM (256 MB recommended), and 2 GB disk plus growing message/attachment caches and off-server backups. Only amd64 was exercised.
Copy the complete recipe, scripts, and proxy/ directory. Create .env with mode 600, choose a strong unique administrator password, and use the pinned image to hash it:
cp .env.example .env
chmod 600 .env
docker run --rm -it binwiederhier/ntfy:v2.34.0 user hash
Put the printed hash in .env as NTFY_AUTH_USERS='ntfyadmin:<bcrypt-hash>:admin'. Single quotes preserve literal $; do not double them in .env. Keep the original password in a password manager. Do not paste the hash directly into Compose, where escaping rules differ.
| Parameter | Meaning |
|---|---|
NTFY_VERSION |
Pinned app tag including v, initially v2.34.0. |
NTFY_PORT |
Host loopback port, initially 8083. |
NTFY_URL |
Exact browser origin, initially http://localhost:8083; external HTTPS URL behind a proxy. Used for attachment/client URLs. |
NTFY_AUTH_USERS |
Single-quoted name:bcrypt:role administrator entry. Authoritative on startup, not a one-time setup password. |
NTFY_DATA_VOLUME |
Named volume for cache.db, user.db and attachments/. |
TZ |
IANA time zone, default Etc/UTC. |
NTFY_BACKUP_DIR |
Optional absolute backup path; defaults to backups/ beside the recipe. |
The scripts source .env as trusted shell configuration. Do not use a file from an untrusted source. The helper initializes the volume, then ntfy runs as UID/GID 1000 with a read-only root. Start and sign in as ntfyadmin with the password you hashed:
docker compose config --quiet
docker compose pull
docker compose up -d --wait --wait-timeout 180
curl --fail http://127.0.0.1:8083/v1/health
Open NTFY_URL, sign in, and subscribe to household. Send a test notification; curl prompts for the password without putting it in the command:
curl --fail -u ntfyadmin -d 'Test notification' http://127.0.0.1:8083/household
Set NTFY_URL to the external HTTPS origin and put a host proxy in front of loopback port 8083. Permit SSH/HTTP/HTTPS only, verify login and blocked anonymous topic access, and keep an encrypted backup off the VPS. The browser UI can load before login, but protected topic data cannot be read anonymously.
Use ssh -L 8083:127.0.0.1:8083 user@server with the matching local URL, or publish through an HTTPS LAN/VPN proxy. If you deliberately bind to a private host IP, restrict it with a firewall. Keep authentication on. Attachment URLs remain bearer links even on a private topic.
Set NTFY_URL=https://notify.example.com and recreate the service. proxy/ includes host-side Caddy, Nginx and Traefik examples for 127.0.0.1:8083. Caddy manages certificates; Nginx needs certificate paths; Traefik needs websecure and a letsencrypt resolver. A containerized proxy needs a shared network and internal port 8080 instead of its own loopback. Keep websocket upgrades and streaming responses enabled; Nginx buffering is disabled and the request cap is 15 MB. Forwarded client-IP trust remains disabled, so visitors share the proxy IP’s rate limit; do not enable header trust until only the actual proxy can reach the service and client-supplied headers are sanitized.
The declarative administrator entry is reapplied on every startup. To rotate it, generate a new hash, update the single-quoted NTFY_AUTH_USERS entry, and recreate ntfy. For automation create a regular account and explicit topic permissions; run these interactive commands and create a token in that account’s UI:
docker compose exec ntfy ntfy user add backupjob
docker compose exec ntfy ntfy access backupjob backups rw
Messages and attachments are cached for seven days; attachment storage is capped at 1 GB and individual files at 15 MB. Clearing browser history does not erase the server cache. Attachment URLs can be shared, so treat them as secrets and encrypt sensitive files before upload. Web Push, iOS upstream polling, Firebase, SMTP and calls are not enabled; configure and test background delivery separately using the official documentation.
Run ./backup.sh. It stops the writer and archives the entire named volume with .env and Compose in one mode-600 backups/ntfy-
Restore replaces the current volume. Use a trusted backup, the matching pinned app version and NTFY_AUTH_USERS from archived configuration.env. Compare the saved environment manually before restore; it is not activated. On a new host initialize the matching recipe first. The script validates the payload and both database names, creates a safety backup, stops the writer and replaces data. A replacement failure leaves ntfy stopped.
docker compose up -d --wait --wait-timeout 180
./restore.sh ./backups/ntfy-TIMESTAMP.tar
curl --fail http://127.0.0.1:8083/v1/health
Sign in, read a saved message, download a sample attachment and publish a new message with a regular account/token. Confirm another topic and anonymous reads remain denied. Original retention timestamps still apply: recovery does not extend an old attachment’s lifetime.
Read upstream release notes, make and export a backup, and change NTFY_VERSION to a reviewed fixed tag. Run docker compose pull and docker compose up -d --wait --wait-timeout 180, then inspect logs, log in, and test publishing/subscribing with a non-admin account. SQLite migrations run at startup. Preserve credentials and review client/server compatibility; do not use latest.
Do not run an older server on a database migrated by a newer version. Stop ntfy, set the old fixed version and matching NTFY_AUTH_USERS in .env, pull and recreate without starting, then restore the complete pre-update backup. The safety backup reads the stopped volume before replacement. Keep the failed installation’s archive until recovery is confirmed.
docker compose stop ntfy
# Set the old pinned version and matching NTFY_AUTH_USERS in .env.
docker compose pull ntfy
docker compose create ntfy
./restore.sh ./backups/ntfy-BEFORE-UPDATE.tar
docker compose down keeps the named data volume. After checking an encrypted off-server backup, docker compose down --volumes permanently removes messages, attachments, accounts, tokens, ACLs and settings. Removing the recipe directory also removes .env and any local backups.
Sources: configuration, Docker installation, publishing, subscription clients, v2.34.0.
Tell us how it went on your server
Opens a prefilled GitHub issue. A GitHub account is required; submitting the issue makes it public.
Run docker compose config --quiet, docker compose ps, and docker compose logs --tail=100 ntfy permissions. Confirm the permissions helper exited successfully and UID/GID 1000 can write the volume. /v1/health must return healthy true. An invalid bcrypt entry prevents startup: generate it with the pinned image’s user hash command and single-quote it in .env. Do not paste plaintext or an unquoted dollar-containing hash.
Sign in as the name before the first colon in NTFY_AUTH_USERS. Its hash is declarative and reapplied at startup; changing an old plaintext password note or using a CLI password command on a provisioned user does not rotate it. Create a normal account for automation, inspect docker compose exec ntfy ntfy user list, and grant only required topics with ntfy access. A regular user’s token inherits that user’s ACLs. A topic name is not an access credential.
The server smoke proves HTTP delivery, not OS push. Check the client’s server URL, login, network reachability and notification permissions. Browser Web Push needs HTTPS/VAPID and browser push services; self-hosted iOS instant push needs upstream polling; Android direct connections have their own background/battery requirements. Those integrations are not configured by this recipe. Use the upstream client-specific setup instead of disabling authentication.
Match NTFY_URL to the exact externally reachable origin, recreate ntfy, and test a newly uploaded sample. Attachment URLs deliberately work as bearer links without login; topic ACLs do not protect a leaked link. Encrypt sensitive files before upload. Both messages and attachments expire after seven days, and restore keeps original timestamps. Check disk space and the 1 GB total / 15 MB per-file limits.
Preserve websocket upgrades and streaming HTTP responses, disable proxy buffering and use long read timeouts. With forwarded-IP trust disabled, requests share the proxy IP’s rate limit. Keep host port 8083 private; a containerized proxy cannot use its own 127.0.0.1 to reach a host service. Do not trust arbitrary X-Forwarded-For values to bypass rate limiting.
Check that the complete archive includes both SQLite databases and attachments, and compare configuration.env manually before restoring. A mismatched NTFY_AUTH_USERS entry overrides restored administrator credentials. Keep the current volume and safety backup while investigating; expired cache data does not become fresh after recovery. Re-test a message, sample attachment, regular user and scoped token.
Sources: configuration, publishing, subscriptions.
Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.
A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.