Upstream repo: https://github.com/binwiederhier/ntfy Upstream docs: https://docs.ntfy.sh
Everything not listed in this document should behave the same as upstream ntfy. If a feature, setting, or behavior is not mentioned here, the upstream documentation is accurate and fully applicable.
NTFY is a simple, privacy-first HTTP-based pub/sub notification service. Publishers POST to a topic URL; subscribers get an instant push. Self-hosted on StartOS, all traffic stays on your infrastructure. The package ships with authentication required by default and a deny-all topic ACL — you explicitly grant access to make anything reachable.
- Image and Container Runtime
- Volume and Data Layout
- Installation and First-Run Flow
- Configuration Management
- Network Access and Interfaces
- Actions
- Base URL
- Mobile Apps and Clients
- Push Notification Limitations
- Attachment Storage
- Backups and Restore
- Health Checks
- Dependencies
- Limitations and Differences
- What Is Unchanged from Upstream
- Quick Reference for AI Consumers
| Property | Value |
|---|---|
| Image | binwiederhier/ntfy (upstream, unmodified) |
| Architectures | x86_64, aarch64 |
| Entrypoint | ntfy serve --config /data/settings.yaml (package-owned) |
| Volume | Mount (inside container) | Contents |
|---|---|---|
main |
/data |
auth.db (users + ACLs + tokens), cache.db (message cache), webpush.db, attachments/, settings.yaml |
startos |
(not mounted in daemon) | store.json — holds the admin management token minted on install |
The settings.yaml config file is owned and enforced by StartOS. Hand-editing it is pointless — fields are re-asserted to their enforced values on service restart.
- Install the service. StartOS seeds
settings.yamlwith VAPID keys and abase-urlderived from the mDNS LAN address, and creates a critical task to set the admin password. - Run the Set Admin Password critical task. An admin user is created, a never-expiring management token is minted and stored in
store.json, and the password is shown once — copy it before closing the dialog. - The service starts automatically.
- Open the web UI at your server's address and log in as
admin.
The management token in store.json lets the package's own actions (Grant User Topic Access, Provision Publisher, etc.) authenticate against ntfy's admin API without keeping the admin password around. It persists across password rotations.
StartOS-managed (enforced in settings.yaml) |
Upstream-managed (via Configure action or ntfy web UI) |
|---|---|
listen-http: :80, auth-file, cache-file, attachment-cache-dir, web-push-file, auth-default-access: deny-all, behind-proxy: true, enable-login: true, enable-metrics: true |
base-url, enable-signup, attachment-file-size-limit, attachment-total-size-limit, visitor-attachment-total-size-limit, cache-duration, web-push-email-address, log-level |
| VAPID keys (generated on install) | Per-user profile settings, tokens, subscriptions — managed in the ntfy web UI by the logged-in user |
Run Configure to change anything in the right column. The service restarts to apply changes.
| Interface | Port | Protocol | Purpose | Access |
|---|---|---|---|---|
| Web UI | 80 | HTTP | NTFY web UI and API | LAN (mDNS), Tor, StartTunnel |
StartOS terminates TLS externally; the container itself serves plain HTTP on port 80. Other StartOS packages on the same box can reach ntfy at http://ntfy.startos on the internal VLAN.
Actions are grouped in the StartOS UI by intent. "Availability" is the action's allowedStatuses. Setter actions return the relevant current state in their result, so there are no separate "view" actions — use the ntfy web UI (Account → Users & Access, admin-only) to browse state before acting.
| Action | Purpose | Availability | Notes |
|---|---|---|---|
| Set Admin Password | One-shot: create the admin user and mint the management token. Fired as a critical task on install. | Any | Hidden. Use Reset User Password → admin to rotate later. |
| Configure | Set base URL, self-registration, attachment limits, cache retention, VAPID email, log level. | Any | Leave a field blank to use ntfy's upstream default. |
Manage human accounts and their per-user topic access.
| Action | Purpose | Availability | Inputs |
|---|---|---|---|
| Create User | Create a regular user. Returns an auto-generated password shown once. | Only running | Username |
| Reset User Password | Rotate any user's password (including admin). Tokens survive. | Only running | User (dynamicSelect, excludes anonymous and pkg_*) |
| Delete User | Delete a regular user. Admins cannot be deleted. | Only running | User (dynamicSelect, excludes admins and pkg_*) |
| Grant User Topic Access | Grant or deny read-write / read-only / write-only / deny on a topic or pattern for a specific user. Replaces prior grant. Result lists the user's full grants. |
Only running | User, topic (existing / new / personal-namespace <user>_*), permission |
"Publisher" in this package is specific: a scoped, write-only automation account (username pkg_<id>), minted for a service or script that only needs to publish to a single topic. It is not a separate ntfy role — any regular user with write access can publish too. Publishers exist so you can hand credentials to automation (StartOS packages, cron jobs, external tools) without granting broader account access.
| Action | Purpose | Availability | Inputs / Outputs |
|---|---|---|---|
| Provision Publisher | Mint a scoped, write-only automation account for a service or external tool that publishes to NTFY. Returns credentials to hand to the caller. | Only running | Publisher ID, topic. Returns {publishUrl, token, topic, username: pkg_<id>}. |
| Revoke Publisher | Delete a provisioned automation account; its topic grants and tokens cascade. Does not affect regular users' ability to publish via their own grants. | Only running | Publisher (dynamicSelect of pkg_* users) |
Grant or deny unauthenticated ("everyone") access to topics. Separate from per-user access because public access is topic-scoped, not user-scoped.
| Action | Purpose | Availability | Inputs |
|---|---|---|---|
| Set Anonymous Topic Access | Grant or deny read-write / read-only / write-only / deny on a topic for anonymous clients. Result lists every topic currently with anonymous access. |
Only running | Topic (existing / new), permission |
| Action | Purpose | Availability |
|---|---|---|
| Server Stats | Active version, base URL, message counts, account counts (users and publishers), attachment storage, feature flags. | Only running |
| Server Metrics | Detailed Prometheus metrics: throughput, subscribers, attachment bytes, UnifiedPush / web push delivery counts. | Only running |
base-url is embedded by ntfy into attachment download links (the URLs a subscriber taps to download a file) and into web-push notification click targets (the URL the browser opens when a push is tapped). It does not affect subscription binding — browser web-push subscriptions are tied to the origin where the user opens the web UI, not to base-url.
Default seeded on install: the server's mDNS URL (https://<server>.local:<port>). Works on your home LAN; not reachable remotely.
Change it via Configure to an externally reachable URL if you need:
- Tap-to-download for attachments when you're off-LAN
- Web-push notifications whose click target opens outside the home network
Access methods:
| Access from… | With base-url = LAN mDNS |
With base-url = StartTunnel / Tor |
|---|---|---|
| Home LAN | Works | Works |
| Outside (StartTunnel) | Attachment links broken | Works |
| Tor | Attachment links broken | Works |
Only one base-url at a time. Pick the widest-reachable URL your users actually use.
Official NTFY apps. The Android app (F-Droid build available) and iOS app both work with self-hosted servers — set the server URL to your externally reachable address and log in with username/password or an access token.
CLI. The ntfy CLI can publish and subscribe:
ntfy publish -u admin:<pass> https://ntfy.myserver.tld/mytopic "Hello"
ntfy subscribe --token <tk_...> https://ntfy.myserver.tld/mytopicUnifiedPush. This server works as a UnifiedPush distributor for Android — other apps on your phone (Element, Mastodon, Tusky, FairEmail, etc.) can route their push through your ntfy instead of Google FCM. No extra server-side setup.
Web push in the browser. VAPID keys are generated automatically on install. Subscribe to a topic in the web UI and browser notifications arrive even when the tab is closed.
Message replay on reconnect. When a subscriber goes offline and reconnects, ntfy automatically re-delivers messages from the cache. Window = cache-duration (default 12 hours, tunable via Configure).
This package does not relay through Google Firebase (FCM) or Apple APNs. The official NTFY Android and iOS apps work, but they maintain a persistent long-polling connection to your server — they do not receive push via Google/Apple infrastructure.
Android. Works well if you disable battery optimization for the NTFY app (Settings → Apps → NTFY → Battery → Unrestricted). Without it, Android kills the background connection and you miss notifications.
iOS. Apple's background-app restrictions make the native NTFY app unreliable. The recommended iOS path is the ntfy web app via Safari:
- Open your ntfy URL in Safari on iOS 16.4 or later.
- Share → Add to Home Screen.
- Open from the home screen and subscribe.
- Allow notifications.
This uses Apple's Web Push (VAPID) infrastructure, which delivers reliably even when Safari is closed. No ntfy.sh relay or Apple Developer account needed.
- Stored under
/data/attachmentson themainvolume. - Expire automatically when their message expires (tied to
cache-duration). - When the total limit is full, new uploads are rejected with an immediate error. Text/link/action notifications are unaffected.
- ntfy does not proactively alert on approaching limits — check Server Stats and adjust via Configure.
Defaults (upstream): per-file 15 MB, total 5000 MB, per-visitor 100 MB.
Both main and startos volumes are backed up:
auth.db,cache.db,webpush.db,attachments/,settings.yaml(onmain)store.json(onstartos) — the admin management token
On restore, users, passwords, topic ACLs, attachments, and web-push subscriptions are all restored. VAPID keys are preserved, so existing browser subscriptions continue to work.
Backup size is bounded by cache-duration × upload rate, not by total historical uploads — attachments age out of the cache regardless of backup history. Lower the attachment limits or cache retention via Configure to shrink further.
Stale cache after old-backup restore: the message cache is restored with messages that may have already expired by wall-clock time. They age out on their own; no corruption.
Base URL after restore: preserved as stored. If your new server's URL differs from the restored one, update via Configure — attachment links in already-delivered notifications still point at the old URL (cannot be retroactively rewritten).
| Check | Method | Success message |
|---|---|---|
| Web Interface | GET /v1/health (HTTP 200) |
"The web interface is ready" |
None.
- No FCM / APNs push. Native mobile apps long-poll your server; see Push Notification Limitations.
- No SMTP gateway.
smtp-sender-addrand related fields are not configured — ntfy cannot send outbound emails on your behalf. Inbound email-to-topic (ntfy'ssmtp-server-*) is also not configured. - No Twilio, Matrix, or Stripe integrations. These require external accounts and are intentionally out of scope.
- No iOS instant push via upstream-base-url. The
upstream-base-urlsetting (which would forward topics through ntfy.sh for iOS FCM delivery) is not configured. - Admin role is CLI-only. The REST API can't create new admins or change an admin's password. StartOS actions hide this — Reset User Password transparently falls back to the CLI for admin targets.
base-urlmust not be a sub-path. Upstream ntfy rejectsbase-urlvalues with a URL path component.- Publishing via
ntfy.startoson-box,base-urlexternally. Internal StartOS services reach ntfy over thehttp://ntfy.startosVLAN hostname; external clients use whateverbase-urlresolves to. The two are different URLs and that's intentional.
Everything in the ntfy web UI, the ntfy REST API, the ntfy CLI, topic semantics, wildcard ACLs, message formats, priorities, tags, action buttons, click URLs, attachments, call notifications (if ever configured), per-user tokens, UnifiedPush distribution, web push, and long-poll subscription behavior all work exactly as documented at https://docs.ntfy.sh.
package_id: ntfy
image: binwiederhier/ntfy
architectures: [x86_64, aarch64]
volumes:
main: /data
startos: (not mounted in daemon; holds store.json)
ports:
ui: 80
dependencies: none
startos_managed_settings:
- listen-http
- auth-file
- cache-file
- attachment-cache-dir
- web-push-file
- auth-default-access
- behind-proxy
- enable-login
- enable-metrics
- web-push-public-key
- web-push-private-key
- attachment-expiry-duration
- web-push-email-address (seeded; user-overridable via Configure)
user_configurable_settings:
- base-url
- enable-signup
- attachment-file-size-limit
- attachment-total-size-limit
- visitor-attachment-total-size-limit
- cache-duration
- log-level
actions:
- set-admin-password # hidden; critical task on install
- configure
- create-user
- reset-user-password
- delete-user
- grant-user-topic-access
- provision-publisher
- revoke-publisher
- set-anonymous-topic-access
- server-stats
- server-metrics
health_checks:
- web-interface: GET /v1/health
backup_volumes:
- main
- startos
internal_hostname: http://ntfy.startos:80