Deployment guide

Uptime Kuma with ntfy

Overview

This image delivers uptime monitoring and alert delivery in a single virtual machine. Uptime Kuma runs the checks (HTTP and HTTPS endpoints, TCP ports, DNS records, ping, certificate expiry and more) and shows current state and history on a dashboard.

When a check changes state, Uptime Kuma publishes the alert to ntfy, a push notification server running alongside it on the same instance. A phone app, a browser tab, a script or a chat bot can pick the alert up there.

The two halves are wired together before you ever sign in, rather than shipped side by side for you to connect. First boot creates the ntfy channel inside Uptime Kuma with this instance's own ntfy access token and marks it the default, so every monitor you create alerts through the local broker with nothing to configure.

Because the broker is local, alerts do not depend on an external notification service, an API quota or an outbound webhook. Both the monitoring data and the alert history stay on your own instance.

What you get:

  • Uptime Kuma 2.5.0 (MIT) as the monitoring engine and dashboard
  • ntfy 2.27.0 (Apache-2.0) as the local publish and subscribe notification server
  • Local SQLite storage for both, so the appliance runs standalone with no external database, broker or relay
  • A unique administrator password, a unique ntfy user, access token and topic, and a unique TLS certificate, all generated on first boot
  • The ntfy channel already registered in Uptime Kuma as the default notification, plus a working self-check monitor
  • An on-instance end-to-end test that drives a monitor to DOWN and reads the resulting alert back out of ntfy
  • uptime-kuma.service, ntfy.service and nginx.service as systemd units, enabled and active
  • 24/7 SecureAnyCloud support

Two things this image deliberately does differently from an unconfigured install:

  • Uptime Kuma's first run normally lets whoever reaches the instance first create the administrator account. On a public IP, a stranger can win that race. First boot creates the administrator itself, so the wizard is already used up before the web interface accepts a connection.
  • ntfy's shipped default is auth-default-access: read-write, meaning anybody may publish to and subscribe to any topic. This image sets deny-all and creates a single scoped user, so your alert topic is not readable by the internet.

Step 1 - Launch the VM

Deploy the image from the Azure Marketplace, or with the Azure CLI. Standard_B2s is the recommended size and is the size this image is tested on.

Open these inbound ports in the network security group:

Port Purpose

22 SSH

443 Uptime Kuma dashboard (HTTPS)

8443 ntfy: where your phone, browser or scripts subscribe (HTTPS)

80 Redirects to 443 only

Restrict port 22 to your own address. If you only ever subscribe from a known network, restrict port 8443 to that network too.

Step 2 - Confirm the appliance came up

SSH in as azureuser and check the three services. First boot generates every credential on the instance, so wait a few seconds after the VM reports running.

sudo systemctl is-active uptime-kuma.service ntfy.service nginx.service

All three report active.

The first-boot unit writes a sentinel and a bootstrap marker when it has finished. The credentials file it produces is readable only by root:

sudo ls -l /var/lib/sac/data/
sudo stat -c '%a %U:%G %n' /var/lib/sac/secrets/sac-credentials.txt

A start-time guard runs before the three services are allowed to start. It refuses to let the appliance run on anything other than credentials generated on this instance. Run it by hand as a quick health check:

sudo /usr/local/sbin/uptime-kuma-ntfy-guard.sh

It prints GUARD_OK.

Step 3 - Read your instance's credentials

Every secret on this instance was generated when it first booted. Nothing here shipped inside the image, and no two deployments share a value.

Read the credentials file:

sudo cat /var/lib/sac/secrets/sac-credentials.txt

The file contains:

Key What it is

KUMA_URL The Uptime Kuma dashboard address

KUMA_ADMIN_USERNAME / KUMA_ADMIN_PASSWORD Your Uptime Kuma administrator sign-in

NTFY_URL The ntfy server address for subscribers

NTFY_USERNAME / NTFY_PASSWORD Your ntfy sign-in, for the app and the web interface

NTFY_TOKEN The access token Uptime Kuma publishes with

NTFY_TOPIC This instance's own topic name, which is not guessable

NTFY_SUBSCRIBE_URL The full URL to point a subscriber at

TLS_CERTIFICATE The certificate generated for this instance

Store the password somewhere safe, and treat this file as a secret.

The image also records the pinned upstream versions and the checksums they were verified against:

cat /var/lib/sac/config/sac-uptime-kuma-ntfy.release

Step 4 - Sign in to the dashboard

Browse to https://<your-vm-ip>/ and sign in with KUMA_ADMIN_USERNAME and KUMA_ADMIN_PASSWORD.

The certificate is generated on your instance and is not signed by a public authority, so your browser shows a warning the first time. This is expected. Step 10 explains how to reissue the certificate for a real DNS name.

Because the administrator account already exists, you are asked to sign in rather than to create an account. Anyone else who reaches the address gets the same sign-in prompt.

After you sign in, you land on the dashboard. The image ships with one monitor already configured, a self-check against the local ntfy server, so the dashboard is populated from the start. The screenshot below is from an instance with several monitors added.

Step 5 - Check that the alert channel is already wired up

Open the profile menu, then choose Settings, then Notifications. A notification named ntfy (this appliance) already exists.

It points at the ntfy server on this instance, publishes to your instance's topic, and authenticates with your instance's access token. It is marked as the default, so it is pre-selected on every new monitor you create. You do not need to change anything.

Step 6 - Create a monitor

Select Add New Monitor, choose a monitor type, give it a name and a target, and save. Leave ntfy (this appliance) ticked under Notifications.

A monitor that cannot reach its target goes red and fires an alert. The detail page shows the check history and the events that produced notifications.

Step 7 - Subscribe to your alerts

This step makes the image a pipeline rather than two programs. Alerts land on your instance's ntfy topic, and anything that can speak HTTP can subscribe.

From a browser or the ntfy mobile app, open the value of NTFY_SUBSCRIBE_URL (that is https://<your-vm-ip>:8443/<your-topic>) and sign in with NTFY_USERNAME and NTFY_PASSWORD. The ntfy app for Android and iOS takes the same server address, topic and credentials.

From a script or the command line, use the access token. The following command reads back every alert currently cached on your topic and reports how many there are:

sudo bash -c 'TOKEN=$(sed -n "s/^NTFY_TOKEN=//p" /var/lib/sac/secrets/sac-credentials.txt); TOPIC=$(sed -n "s/^NTFY_TOPIC=//p" /var/lib/sac/secrets/sac-credentials.txt); curl -s -H "Authorization: Bearer ${TOKEN}" "http://127.0.0.1:8088/${TOPIC}/json?poll=1" | tee /tmp/alerts.json; echo "alerts currently on topic ${TOPIC}: $(grep -c . /tmp/alerts.json)"; rm -f /tmp/alerts.json'

Each line of output is one alert as JSON, with the monitor name and status in title and the reason in message, followed by a count.

On a brand-new instance, the count is 0. Nothing has gone down yet, so there is nothing to deliver. Run Step 8 below, then run this command again. The count becomes 1, and the alert names the monitor the self-test took down. That transition from nothing to something, produced by Uptime Kuma and read back out of ntfy, is the whole product working.

To stream alerts as they happen rather than polling, drop the ?poll=1 and leave the connection open.

Because the server denies access by default, an unauthenticated request to the same topic is refused:

sudo bash -c 'TOPIC=$(sed -n "s/^NTFY_TOPIC=//p" /var/lib/sac/secrets/sac-credentials.txt); curl -s -o /dev/null -w "anonymous subscribe: HTTP %{http_code}\n" "http://127.0.0.1:8088/${TOPIC}/json?poll=1"'

That prints HTTP 403.

Step 8 - Prove the whole pipeline end to end

The image ships with a self-test that exercises the real path. It signs in to Uptime Kuma with your administrator credentials and creates a temporary monitor pointing at a closed port, so the monitor must fail. It waits for the monitor to go DOWN, then subscribes to your ntfy topic and reads the resulting alert back. It removes the temporary monitor afterwards.

Run the self-test:

sudo /usr/local/sbin/sac-uptime-kuma-e2e-probe.sh

It finishes with E2E_PIPELINE_OK and prints the alert it read back, including the topic, the title and the message.

Run the self-test after any change to the appliance. If it fails, the two halves are not talking and the alerting you rely on is not working.

Step 9 - The listener and authentication model

Neither application process is reachable from the network. Uptime Kuma listens on 127.0.0.1:3001 and ntfy on 127.0.0.1:8088. nginx terminates TLS and is the only service on a routable address.

List the listeners:

sudo ss -ltnp | grep -E '3001|8088|:80 |:443|:8443'

Each application keeps its own authentication as the gate, so no shared password protects anything that you cannot rotate:

  • Port 443 is the Uptime Kuma dashboard. Uptime Kuma authenticates every session itself with your administrator credentials.
  • Port 8443 is ntfy. ntfy denies all access by default and only your instance's user and token are authorised, scoped to your topic.

The settings enable-signup and enable-reservations are off, so nobody can use the exposed ntfy endpoint to register an account or claim a topic. No upstream relay is configured, so no notification leaves the instance unless you configure one.

Step 10 - Reissue the TLS certificate for a real name

The certificate generated at first boot covers loopback, the hostname and the instance's private address, and its public address where Azure exposes one. If you reach the appliance by a public IP or a DNS name, reissue the certificate with that name:

sudo /usr/local/sbin/sac-uptime-kuma-tls-reissue.sh status.example.com

Pass several names at once if you need them. The command replaces the key material, keeps loopback and the private address so the on-instance checks keep working, and reloads nginx.

To use a certificate from a public authority instead, place the certificate and key at /etc/ssl/sac/uptime-kuma-ntfy.crt and /etc/ssl/sac/uptime-kuma-ntfy.key, keep the key readable by the www-data group, and run sudo systemctl reload nginx.

Step 11 - Maintenance

Where the data lives. Uptime Kuma's database is /var/lib/uptime-kuma/kuma.db. ntfy's message cache and user database are in /var/lib/ntfy/. Back up both directories:

sudo systemctl stop uptime-kuma.service
sudo tar -czf /root/uptime-kuma-backup.tar.gz /var/lib/uptime-kuma /var/lib/ntfy
sudo systemctl start uptime-kuma.service

Message retention. ntfy caches messages for 12 hours by default. For a longer alert history on the server, change cache-duration in /etc/ntfy/server.yml and restart ntfy.service.

Adding more ntfy users. Give a colleague their own credentials rather than sharing yours:

sudo bash -c 'NTFY_CONFIG_FILE=/etc/ntfy/server.yml NTFY_PASSWORD=<set-at-first-boot> ntfy user add --role=user colleague'
sudo bash -c 'TOPIC=$(sed -n "s/^NTFY_TOPIC=//p" /var/lib/sac/secrets/sac-credentials.txt); NTFY_CONFIG_FILE=/etc/ntfy/server.yml ntfy access colleague "${TOPIC}" ro'

These commands grant read-only access to your alert topic. Use rw if the colleague also needs to publish.

Operating system updates. Unattended upgrades are enabled, so security updates apply automatically. Reboot when a new kernel is installed.

Logs. Read the recent log lines for both services:

sudo journalctl -u uptime-kuma.service -n 50 --no-pager sudo journalctl -u ntfy.service -n 50 --no-pager

If a service will not start, run the guard. It names the exact reason rather than failing silently:

sudo /usr/local/sbin/uptime-kuma-ntfy-guard.sh

Support

Every SecureAnyCloud image comes with 24/7 support. Contact us at sales@secureanycloud.com.

Deploy on Azure

Launch Uptime Kuma with ntfy on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure