The HYEHOST bear in a Halloween cape beside glowing server pumpkinsThe HYEHOST bear enjoying Bonfire Night with fireworks and a glowing server cabinet
A little extra this HalloweenLight up your balance

Your Halloween credit treatYour Bonfire Night boost

+15% extra account credit

Add $5–$100 USD to your HYEHOST account and receive 15% extra credit automatically.

Add $5
$5.75account credit
Add $25
$28.75account credit
Add $100
$115account credit
  1. Log in to your HYEHOST account.
  2. Open Billing and add $5–$100 credit.
  3. Your 15% bonus is added automatically. No code needed.

Valid for qualifying $5–$100 USD top-ups on 31 October 2026, until 23:59 UK time.Valid for qualifying $5–$100 USD top-ups from 1–5 November 2026, ending at 23:59 UK time on 5 November.

Log in and add credit
HYEHOST

Self-Host ntfy on a VPS: Docker, HTTPS and Private Alerts

Self-host ntfy on a VPS with Docker Compose, HTTPS and private topics. Set up authenticated push notifications for backups, uptime checks and automation.

Start the Docker setupSet private permissions
HYEHOST bear mascot receiving a private push notification on a phone beside a VPS

The 31 October release makes this a timely moment to look at ntfy: version 2.36.0 keeps daily usage limits across restarts and includes changes to how those limits are shared with PostgreSQL. For a first deployment, a single server with persistent SQLite storage is a simpler starting point.

Below, a script account can publish to one topic, a reader account can receive it, and anonymous clients are denied. You will also see where self-hosting ends: mobile push delivery, recovery and independent availability checks still need deliberate setup.

What is ntfy, and where does it fit?

ntfy is an open-source notification service. An application publishes an HTTP request to a named topic, and subscribers receive the message. That makes it useful when a tool can call an HTTP endpoint but does not have a dedicated mobile notification system.

Backup results

Send a short success or failure message after a backup job finishes. Include a job name and the result, with a link to protected logs when useful.

Uptime alerts

Have a separate monitor publish when an endpoint fails or recovers. Put the checker somewhere that can still run if the application server is unavailable.

Automation updates

Receive deployment, scheduled-task or bot-job results without repeatedly opening a terminal to inspect routine output.

Keep notification payloads short. “Nightly backup failed on worker-2” is more useful on a lock screen than a raw log containing customer data, credentials or internal connection strings.

1. Prepare a VPS, a domain and Docker

Use a Linux VPS with SSH access, Docker Engine and the Compose plugin. Our Ubuntu Docker installation guide covers that foundation. Check that the host has spare memory and disk for its existing workloads as well as the notification service; monitor actual use after deployment.

  • Choose a hostname such as alerts.example.com and point its DNS A record to the VPS. Add an AAAA record only when IPv6 reaches the same service correctly.
  • Allow inbound TCP ports 80 and 443 for the Caddy setup below. UDP 443 is optional for HTTP/3. Keep your existing SSH access available.
  • Use a fresh deployment or adapt an existing reverse proxy. Another service already listening on 80 or 443 will conflict with this example.
  • Run the commands from one project directory using an account authorised to manage Docker.

HYEHOST Cloud VPS provides root access in Wolverhampton, Ashburn and Auckland, so you can install and maintain this stack. Choose a region around your users and the systems sending alerts.

mkdir -p ~/ntfy-alerts
cd ~/ntfy-alerts
docker compose version

Create the following three files in that directory. Replace alerts.example.com in both configuration files with your real hostname.

2. Create the Docker Compose stack

Save this as compose.yaml. It uses the official ntfy image with Caddy as the HTTPS entry point:

services:
  ntfy:
    image: binwiederhier/ntfy:v2.36.0
    command: ["serve"]
    restart: unless-stopped
    volumes:
      - ./server.yml:/etc/ntfy/server.yml:ro
      - ntfy_state:/var/lib/ntfy

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - ntfy

volumes:
  ntfy_state:
  caddy_data:
  caddy_config:

Only Caddy publishes ports on the host. It reaches ntfy using the service name ntfy on the project's Docker network. Do not add a public ntfy port that bypasses this HTTPS entry point.

The ntfy image is pinned to the release checked for this article. The Caddy example follows its 2.x Alpine tag; pin tested image digests as well if your deployment requires reproducible builds. Review newer releases before reusing the configuration later.

3. Enable persistent storage and private access

Save this as server.yml:

base-url: "https://alerts.example.com"
listen-http: ":80"
behind-proxy: true

auth-file: "/var/lib/ntfy/user.db"
auth-default-access: "deny-all"
enable-login: true
enable-signup: false

cache-file: "/var/lib/ntfy/cache.db"
cache-duration: "24h"

The authentication database and message cache live in the persistent ntfy_state volume. Anonymous access is explicitly denied. Login is enabled, and public self-service registration is disabled. The versioned configuration reference explains those settings.

The 24-hour cache is a delivery buffer for clients that miss a message. It is not a permanent audit trail or a backup of the system that sent the alert. Use your normal logging and recovery tools for those jobs.

behind-proxy: true lets ntfy use proxy-supplied client information for rate limiting. This topology keeps ntfy behind Caddy on the project network; review that trust boundary if you add another proxy or attach other containers to it.

4. Configure HTTPS with Caddy

Save this as Caddyfile:

alerts.example.com {
    reverse_proxy ntfy:80 {
        flush_interval -1
    }
}

Caddy can obtain and renew a public certificate when the hostname and reachable ports meet its HTTPS requirements. Its data volume preserves certificate state between container replacements.

The streaming setting avoids delaying streamed responses through buffering. Keep the hostname consistent with ntfy's base-url.

Validate the Compose model, then start the services:

docker compose config --quiet
docker compose up -d
docker compose ps
docker compose logs --tail=60 ntfy caddy

Open https://alerts.example.com and check the certificate. If certificate issuance fails, fix DNS and reachability before creating an exception in your browser or sending credentials.

5. Separate the publisher from the reader

Create two regular users from the server. Each user-add command prompts for a password and confirmation, so the password does not have to appear in the command itself:

docker compose exec ntfy ntfy user add alert-reader
docker compose exec ntfy ntfy access alert-reader backups read-only

docker compose exec ntfy ntfy user add backup-agent
docker compose exec ntfy ntfy access backup-agent backups write-only

The first account can read the backups topic. The second can publish to it. Neither is an all-topic administrator. Local management commands operate on the configured user database; they do not require an HTTP admin login. The tagged user-command implementation and access-command implementation document the relevant behaviour.

Check the resulting account permissions:

docker compose exec ntfy ntfy access

Give each application its own publisher identity when you add more integrations. That lets you change or revoke one application's credentials without changing every phone and script.

6. Send a message and test the access rules

Publish a first notification from a machine with curl:

curl --fail-with-body --user backup-agent \
  -H 'Title: Backup test' \
  -H 'Tags: white_check_mark' \
  --data-binary 'The notification path is working.' \
  https://alerts.example.com/backups

curl prompts for the publisher's password. A successful response contains the accepted message. These fields follow the publishing API.

Retrieve the latest cached message using the reader account:

curl --fail-with-body --user alert-reader \
  'https://alerts.example.com/backups/json?poll=1&since=latest'

The poll option makes this a one-shot request. A normal streaming subscription stays open to receive new messages.

Now repeat a publish request without credentials:

curl --silent --show-error --write-out '\nHTTP %{http_code}\n' \
  --data-binary 'Anonymous access probe' \
  https://alerts.example.com/backups

It must be rejected. Also check that the reader cannot publish, the publisher cannot read, and neither account can access an unrelated topic. A working notification alone does not prove that permissions are correct.

7. Subscribe on a phone or desktop

In the ntfy mobile app, add your server's full HTTPS URL and the backups topic, then provide the alert-reader credentials in the server's account settings. Send another test while the app is open and another with the phone locked.

Android

Allow the app to receive notifications and check its background/battery settings. Test the self-hosted server while the screen is off.

iPhone

Prompt delivery through the standard iOS app requires an upstream push relay. Configure it if timely background alerts matter to your setup.

Web browser

The web UI is useful for reading topics. Background Web Push needs separate server configuration and browser permission.

For iOS push delivery, add the following to server.yml and restart ntfy:

upstream-base-url: "https://ntfy.sh"
docker compose restart ntfy

The documented iOS flow sends a poll-request message ID and a hash of the topic URL upstream; the phone then retrieves the content from your server. Metadata still passes through an external service. Without the relay, background delivery can be delayed.

HTTPS protects the connection to your server. Do not treat this configuration as end-to-end encryption that hides messages from the server administrator. Choose alert content accordingly and review the project's privacy explanation for the mobile services you use.

8. Connect backups and uptime checks

For a backup job, send the success notification only after the backup command has succeeded. Handle backup failure and notification failure separately: a failed HTTP request does not prove the backup failed, and silence does not prove it worked.

The interactive password examples above are for setup. An unattended job needs credentials from your secret-management system or a permission-restricted client configuration. Keep the publisher account limited to its topic and avoid putting passwords into a cron command, public repository or job log.

ntfy's integration examples include Uptime Kuma. Create a separate topic and publisher for uptime events, grant a reader access, then use the notification provider settings supported by your installed monitor. Our Uptime Kuma guide covers the monitor itself.

Run the checker independently of the server being monitored. For important alerts, consider a second delivery channel too: if the ntfy VPS or the phone's connection is down, this notification path is unavailable.

Keep the notification service recoverable

Back up compose.yaml, server.yml, Caddyfile and the persistent volumes needed for recovery. The user database contains sensitive authentication material, and cached notifications may contain private operational information, so protect the copies.

For a straightforward SQLite backup, stop ntfy, copy the complete ntfy_state volume using a volume-backup procedure, then start it again. Include any database sidecar files. Copying a live database as if it were an ordinary text file can produce an inconsistent result; SQLite describes the backup risks.

Keep an encrypted off-server copy and test a restoration on a separate instance. HYEHOST Storage Box can hold those recovery files; our rclone and SFTP guide covers encrypted backup storage.

Before updates, save a working configuration and backup, review the release notes and test account permissions again afterwards. Our Compose update guide explains the pull, recreate and recovery process.

Common ntfy self-hosting problems

SymptomCheck firstUseful next step
Certificate failureDNS records and public TCP 80/443 reachabilityCheck Caddy logs and remove an incorrect AAAA record
Unauthorised requestAccount credentials, exact topic and read/write permissionInspect ntfy access output; do not enable anonymous access to hide the error
Messages arrive latePhone background settings, iOS relay and proxy bufferingCompare an open-app test with a locked-phone test
Users disappear after recreationThe mounted state volume and auth-file pathCheck the Compose project name and recover the correct volume

A server health check confirms only part of the path. Include a periodic end-to-end test that reaches a real subscriber, and investigate missed alerts rather than relying solely on container status.

Self-hosted ntfy FAQ

Is ntfy free to self-host?

The ntfy server is open-source software that you can run on your own VPS. You still pay for the server, domain and any optional external services you choose.

Are ntfy topics private by default?

Do not assume so. This guide explicitly enables authentication, denies anonymous topic access and grants separate read-only and write-only permissions for the backups topic. Test that an anonymous publish request is rejected.

Which ports does this Docker setup expose?

Only Caddy publishes host ports: 80 and 443 over TCP, plus 443 over UDP for HTTP/3. ntfy has no published host port and is reached by Caddy over the project's Docker network.

Does self-hosted ntfy work with iPhone notifications?

Yes, but prompt notifications through the standard iOS app need the optional upstream push relay. The upstream receives poll-request metadata, while the phone retrieves message content from your server. Without that configuration, background delivery may be delayed.

Is opening the web UI enough for browser background notifications?

No. The web interface can display topics, but Web Push needs additional server configuration and browser permission. This guide's basic configuration does not enable Web Push.

Can ntfy replace an uptime monitor or a backup?

No. ntfy delivers notifications. Use a separate checker to detect outages and a backup system to preserve data. An alert server cannot send an outage message while it is itself unavailable.