Deployment guide

Uptime Kuma with ntfy 2.5.7

Overview

Uptime Kuma is a self-hosted uptime monitor: HTTP and HTTPS endpoints, keywords, TCP ports, ping, DNS records, certificate expiry, databases and more, with a dashboard, history and status pages. ntfy is a self-hosted push-notification server: anything that can make an HTTP request can publish to a topic, and the ntfy app for Android and iOS, a browser or a script can subscribe to it.

This guide covers launching and verifying Uptime Kuma with ntfy 2.5.7 on Ubuntu 24.04 from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace, which you select as the software version when you subscribe and launch. The other products in the listing are described on the listing's guide hub.

What you get:

  • One alert pipeline, not two programs installed side by side. First boot creates Uptime Kuma's default notification channel, ntfy (this instance), pointing at the ntfy server on the same instance with the instance's own access token. Every monitor you create alerts through it with nothing to configure. You subscribe your phone, browser or scripts to the instance's own topic. Alerts never depend on an outside notification service, and both the monitoring data and the alert history stay on your instance.
  • Uptime Kuma 2.5.7 (uptime-kuma.service) running as the unprivileged kuma user on 127.0.0.1:3001 only.
  • ntfy 2.34.0 (ntfy.service) running as the unprivileged ntfy user on 127.0.0.1:8088 only.
  • nginx publishing Uptime Kuma over HTTPS on port 443 and ntfy over HTTPS on port 8443, with a certificate created on your instance's first boot. Port 80 redirects to HTTPS.
  • An Uptime Kuma admin account, an ntfy SecureAnyCloud user, an ntfy access token and an ntfy topic, all unique to your instance and created on first boot.
  • The default ntfy (this instance) notification channel and a self-check monitor of the ntfy server.
  • uptime-kuma-ntfy-set-hostname, a helper to serve both under your own DNS name, and sac-uptime-kuma-e2e-probe, an end-to-end test of the alert pipeline.
  • Patched, source-pinned builds. Uptime Kuma 2.5.7 comes from the official 2.5.7 release, with its production dependencies updated to releases that fix every known advisory reachable at run time. SecureAnyCloud compiles ntfy 2.34.0 from the official 2.34.0 source with Go 1.27.3. Building from source pins the Go toolchain, ntfy's build tags and its link against the system C library, and govulncheck finds no vulnerability reachable in this build. Node.js 22.23.4 runs Uptime Kuma.
  • The build record (BUILD-INFO.txt), the dependency lockfiles, the npm audit and govulncheck results and the licences in /usr/share/doc/sac-uptime-kuma-ntfy/.
  • A fully patched Ubuntu 24.04 LTS base, with unattended security upgrades staying on for your instance.
  • Default user: ubuntu
  • Architecture: x86_64
  • Root volume: 8 GiB gp3

Security posture

  • No setup wizard for strangers. A fresh Uptime Kuma 2.x install shows a database chooser and then lets the first visitor create the administrator account (admin). On a public address, that is a race you can lose. On this image, first boot creates the database and the admin account before the web server is allowed to start, so both first-run pages are already closed when the instance first answers on 443. A check before every start refuses to run Uptime Kuma, ntfy or nginx until first boot has finished.
  • ntfy denies anonymous access. ntfy's upstream default lets anyone publish to and read any topic. This image runs auth-default-access: deny-all, so only the SecureAnyCloud user and its access token can publish to or read your instance's topic, and only that topic. Self-registration and topic reservations are off, and the web app requires a login.
  • Per-instance secrets. The Uptime Kuma admin password, Uptime Kuma's session-signing (JWT) secret, the ntfy password, the ntfy access token, the ntfy topic and the TLS key are all generated on your instance's first boot. No database, password, token or key ships in the image. Your credentials are in /root/uptime-kuma-ntfy-credentials.txt (mode 0600, root only), and the applications store passwords only as bcrypt hashes.
  • Loopback only. Uptime Kuma and ntfy listen on 127.0.0.1. nginx on 443 and 8443 is the only way in.
  • HTTPS on first boot. nginx serves both with a TLS key and self-signed certificate created on your instance (TLS 1.2 and 1.3 only). Port 80 only redirects to HTTPS.
  • Fixed host name. Both are served under a name taken from the instance metadata at first boot (or one you set), never from the request. Requests for any other host name are dropped. nginx refuses cross-site form posts and WebSocket connections, and so does Uptime Kuma's own origin check.
  • Real client addresses. nginx passes the connecting address to Uptime Kuma and ntfy and ignores any X-Forwarded-For header a client sends, so their logs and ntfy's rate limits see who really connected.
  • Nothing leaves the instance unless you configure it. Uptime Kuma's update check (on by default upstream, where it polls uptime.kuma.pet) is off. ntfy has no upstream-base-url (the ntfy.sh relay used for instant iOS delivery), no firebase-key-file (Firebase Cloud Messaging), no web-push keys and no SMTP server configured, so alerts are delivered only to subscribers of this instance. The monitors you create are, of course, the outbound checks you ask for.
  • No metadata access. Uptime Kuma checks whatever URLs and hosts you enter, so uptime-kuma.service, ntfy.service and nginx.service are blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). Amazon Time Sync keeps working.
  • Least privilege. Both services run with no Linux capabilities, a read-only system (ProtectSystem=strict) except their own data directory, and private /tmp. Uptime Kuma's ping monitors use unprivileged ICMP sockets that the image allows for the kuma group only.
  • No scheduled firmware-metadata refresh. The fwupd firmware updater stays installed, but its LVFS metadata refresh (fwupd-refresh.timer and .service) is masked, so an LVFS outage never marks your instance unhealthy at boot. Run sudo fwupdmgr refresh yourself if you use it.
  • IMDSv2 required. The AMI is registered with ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests.
  • Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.

Known advisories and how the build handles them

  • Uptime Kuma dependencies. npm audit on the upstream 2.5.7 lockfile lists 27 advisories in the production tree. This build updates, within their own major release lines, every affected library that Uptime Kuma uses at run time: axios, nodemailer (to the 10.x line upstream's next release already uses), ws, protobufjs, @grpc/grpc-js, liquidjs, mysql2, follow-redirects, http-cache-semantics, lodash, glob, body-parser, undici and ip-address. Two items remain. The first is the SQLite driver's install-time build tooling (tar, node-gyp and their helpers), which only runs when the driver is compiled from source; at run time the driver loads its prebuilt binary and never loads that code, which the build verifies. The second is @azure/msal-node (GHSA-xcx6-jx8p-x3h5), used through @azure/identity by the SQL Server monitor's Azure sign-in options; its fix is only in a later major release that no @azure/identity release uses yet. The advisory affects the OAuth authorization-code sign-in flow, which neither Uptime Kuma nor its SQL Server driver ever uses (they use client-secret, managed-identity, username/password and default credentials only), and the build verifies that too. The full result ships in kuma-npm-audit-production.json. No vite (a development tool) is installed.
  • ntfy. govulncheck reports no vulnerability reachable from ntfy. It lists one advisory at module level only, GO-2026-5932. The golang.org/x/crypto/openpgp package is deprecated and has no fixed version. That package is not compiled into ntfy, and the build fails if it ever is. The full output ships in ntfy-govulncheck-binary.txt and ntfy-govulncheck-source.txt. ntfy's web app is built from its own lockfile (npm audit: 0 vulnerabilities). The ntfy binary links the system C library, so Ubuntu's security updates to it apply.
  • ntfy documentation. Upstream's binary embeds a copy of the ntfy documentation at /docs. Its build installs unpinned Python packages, so this image's /docs page instead links to the documentation at docs.ntfy.sh.

Prerequisites

You need:

  • An AWS account and permission to launch EC2 instances.
  • An EC2 key pair in the Region you launch in.
  • A subnet that gives the instance a public IPv4 address (or private connectivity from where you will use the dashboard), and network access from the instance to the endpoints you want to monitor.

Recommended instance type: m5.large. Uptime Kuma is a single Node.js process with SQLite storage, and ntfy is a single Go process, so a smaller burstable type also works for a few dozen monitors.

Connecting to your instance

Connect over SSH as ubuntu, the SSH user for Uptime Kuma with ntfy 2.5.7 on Ubuntu 24.04 (Ubuntu 24.04 LTS).

1

Launch the instance

Option A: AWS Marketplace console

  • Open the SecureAnyCloud Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace. Choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  • When the subscription is active, choose Launch your software.
  • Choose One-click launch from AWS Marketplace, or Launch from EC2 Console for the full launch wizard. In Version, choose Uptime Kuma with ntfy 2.5.7 on Ubuntu 24.04, then choose your Region.
  • Pick an instance type and your key pair. m5.large is recommended, and the software charge is $0.04 per vCPU per hour.
  • Set the security group to allow SSH (22), HTTP (80), HTTPS (443) and 8443 (ntfy, where your phone, browser or scripts subscribe). Restrict SSH to your own IP address, 80 and 443 to the people who use the dashboard, and 8443 to the devices that subscribe.
  • Choose your VPC and a subnet that gives the instance a public IP. Leave the metadata defaults, because the image already requires IMDSv2. Then choose Launch.

Option B: AWS CLI

Run this command on your own workstation. It creates a security group that allows SSH, the dashboard and ntfy from your management address only, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page. Replace the VPC, subnet, key pair and CIDR with your own values.

SG=$(aws ec2 create-security-group --group-name uptime-kuma-ntfy \
  --description "Uptime Kuma with ntfy" --vpc-id vpc-0123456789abcdef0 \
  --query GroupId --output text) for p in 22 80 443 8443; do aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port "$p" --cidr <your-mgmt-cidr> done aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type m5.large \
  --key-name my-key-pair \
  --security-group-ids "$SG" \
  --subnet-id subnet-0123456789abcdef0 \
  --associate-public-ip-address \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=uptime-kuma-ntfy}]'

You do not need --metadata-options, because the AMI already makes IMDSv2 required.

2

Connect and read your credentials

Connect as ubuntu with the key pair you chose.

ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>

First boot finishes within a minute of launch. It creates the certificate, both applications' databases and accounts, and the ntfy token and topic, then lets the services start. Read your credentials from the root-only file.

sudo cat /root/uptime-kuma-ntfy-credentials.txt
# Uptime Kuma with ntfy - generated on this instance's first boot by uptime-kuma-ntfy-firstboot.service. # Unique to this instance; nothing here shipped in the image. Store it somewhere safe. KUMA_URL=https://ec2-35-174-111-13.compute-1.amazonaws.com/ KUMA_ADMIN_USERNAME=admin KUMA_ADMIN_PASSWORD=<KUMA_ADMIN_PASSWORD> NTFY_URL=https://ec2-35-174-111-13.compute-1.amazonaws.com:8443/ NTFY_USERNAME=sac NTFY_PASSWORD=<NTFY_PASSWORD> NTFY_TOKEN=<NTFY_TOKEN> NTFY_TOPIC=<NTFY_TOPIC> NTFY_SUBSCRIBE_URL=https://ec2-35-174-111-13.compute-1.amazonaws.com:8443/<NTFY_TOPIC> # # Uptime Kuma: open KUMA_URL and sign in as admin (self-signed certificate minted on this instance; port 80 redirects). # Every monitor you create alerts through the default "ntfy (this instance)" channel to NTFY_TOPIC. # ntfy: subscribe to NTFY_SUBSCRIBE_URL in the ntfy app or a browser as NTFY_USERNAME / NTFY_PASSWORD, or with # curl -H "Authorization: Bearer <NTFY_TOKEN>" https://<host>:8443/<NTFY_TOPIC>/json # Change the name both are served as:
sudo uptime-kuma-ntfy-set-hostname <your-dns-name>
# Restrict 80/443/8443 in the security group to the people and devices that need them.
# Guide: https://secureanycloud.com/stacks/uptime-kuma-with-ntfy-2-5-7/

KUMA_ADMIN_PASSWORD, NTFY_PASSWORD, NTFY_TOKEN and NTFY_TOPIC hold your instance's own values. The topic name is long and random, so it cannot be guessed. ntfy also refuses the topic to anyone without your credentials.

3

Confirm the services are running

Check that first boot, Uptime Kuma, ntfy and nginx are active. Confirm that the two applications run as their own unprivileged users, that ntfy reports healthy, and that the start guard passes. The guard runs before every start of the three services.

systemctl is-active uptime-kuma-ntfy-firstboot.service uptime-kuma.service ntfy.service nginx.service ps -o user=,comm= -p "$(systemctl show -p MainPID --value uptime-kuma.service),$(systemctl show -p MainPID --value ntfy.service)" curl -sf http://127.0.0.1:8088/v1/health | jq -e '.healthy == true'
sudo /usr/local/sbin/uptime-kuma-ntfy-guard
active active active active ntfy ntfy kuma uptime-kuma true GUARD_OK

If a service ever refuses to start, run the guard. It names the exact reason.

List the TCP ports the instance listens on.

sudo ss -Hltn | awk '{print $4}' | sort -u
0.0.0.0:22 0.0.0.0:443 0.0.0.0:80 0.0.0.0:8443 127.0.0.1:3001 127.0.0.1:8088 127.0.0.53%lo:53 127.0.0.54:53 [::]:22 [::]:443 [::]:80 [::]:8443

Uptime Kuma (3001) and ntfy (8088) listen on loopback only; nginx publishes them on 443 and 8443. The loopback port 53 entries are Ubuntu's local stub resolver (systemd-resolved).

Check the versions and the build record.

/opt/ntfy/bin/ntfy --version node --version grep -E '^(Uptime Kuma|ntfy|Node.js| source| binary sha256| govulncheck)' /usr/share/doc/sac-uptime-kuma-ntfy/BUILD-INFO.txt
ntfy version 2.34.0 v22.23.4 Uptime Kuma 2.5.7 with ntfy 2.34.0 - sac build (d-320, d-304, d-094) Uptime Kuma 2.5.7 (MIT, uptime-kuma-LICENSE) source https://github.com/louislam/uptime-kuma/archive/refs/tags/2.5.7.tar.gz ntfy 2.34.0 (Apache-2.0 / GPL-2.0 dual licence, ntfy-LICENSE + ntfy-LICENSE.GPLv2) source https://github.com/binwiederhier/ntfy/archive/refs/tags/v2.34.0.tar.gz binary sha256 96092dc8fc817e8b3f64643725b8a73c086a373fc3fb73c012ebea646dcdbd2f govulncheck v1.9.0 -mode=binary and source mode: 0 reachable vulnerabilities (DB updated: 2026-10-21 00:27:29 +0000 UTC) Node.js v22.23.4 (MIT, nodejs-LICENSE)
4

Sign in to Uptime Kuma

Browse to the KUMA_URL from Step 2, for example https://ec2-35-174-111-13.compute-1.amazonaws.com/. The certificate is self-signed, so your browser warns once. Accept the warning, or install your own certificate (Step 9). Because the administrator already exists, Uptime Kuma asks you to sign in instead of creating an account.

Sign in as admin with KUMA_ADMIN_PASSWORD. The dashboard lists your monitors. A new instance has one, a self-check of the local ntfy server. The screenshot below is from an instance with two more monitors added, one of them pointing at a closed port so it is down.

You can also confirm from the command line that the first-run pages are closed. Port 80 only redirects to HTTPS. Uptime Kuma reports that neither the database setup nor the account setup is needed, and the account table holds only your admin. Uptime Kuma's update check is off, it reads client addresses from the header nginx writes, and its public URL is your instance's name. The block stops with an error if any answer is not the expected one.

H=$(sudo sed -n 's/^set $ukn_host "\(.*\)";$/\1/p' /etc/nginx/sac-ukn-host.conf)
R=$(curl -s -o /dev/null -w '%{http_code} %{redirect_url}' --resolve "$H:80:127.0.0.1" "http://$H/"); echo "$R"
S=$(curl -s --cacert /etc/ssl/sac/uptime-kuma-ntfy.crt --resolve "$H:443:127.0.0.1" "https://$H/setup-database-info"); echo "$S"
U=$(sudo sqlite3 "file:/var/lib/uptime-kuma/kuma.db?mode=ro" "SELECT username FROM user;"); echo "$U"
sudo sqlite3 "file:/var/lib/uptime-kuma/kuma.db?mode=ro" "SELECT key, value FROM setting WHERE key IN ('checkUpdate','trustProxy','primaryBaseURL') ORDER BY key;" [ "$R" = "301 https://$H/" ] && [ "$S" = '{"runningSetup":false,"needSetup":false}' ] && [ "$U" = admin ]
301 https://ec2-35-174-111-13.compute-1.amazonaws.com/ {"runningSetup":false,"needSetup":false} admin checkUpdate|false primaryBaseURL|"https://ec2-35-174-111-13.compute-1.amazonaws.com" trustProxy|true
5

The alert channel is already wired up

In Uptime Kuma, open the profile menu, then choose Settings, then Notifications. The channel ntfy (this instance) is already there.

It publishes to the ntfy server on this instance with your access token. It is marked as the default, so it is pre-selected on every new monitor.

CH=$(sudo sqlite3 "file:/var/lib/uptime-kuma/kuma.db?mode=ro" \
  "SELECT name, is_default, json_extract(config,'$.ntfyserverurl'), json_extract(config,'$.ntfyAuthenticationMethod') FROM notification;")
echo "$CH" [ "$CH" = 'ntfy (this instance)|1|http://127.0.0.1:8088|accessToken' ]
ntfy (this instance)|1|http://127.0.0.1:8088|accessToken
6

Create a monitor

Choose Add New Monitor. Pick a monitor type (HTTP(s), keyword, TCP port, ping, DNS and many more), give it a name and a target, and save. Leave ntfy (this instance) ticked under Notifications. When a monitor cannot reach its target, it goes red and fires an alert. Its page shows the check history.

Uptime Kuma checks your endpoints from this instance, so the instance needs a network path to them. Monitoring requires no inbound rules in the security group.

7

Subscribe to your alerts

Alerts land on your instance's ntfy topic on port 8443. Anything that speaks HTTP can subscribe.

From a browser, open NTFY_URL and sign in with NTFY_USERNAME and NTFY_PASSWORD. Then subscribe to NTFY_TOPIC.

From the ntfy app for Android or iOS, add a subscription with Use another server. Enter NTFY_URL and your topic, and sign in with the same user. Your phone must trust the instance's certificate, so install your own certificate first (Step 9). Without an upstream relay, the iOS app delivers notifications when it polls instead of instantly (see Step 10).

From a script, use the access token. This block publishes a test message with the token and reads it back from the topic. It then shows that a request without credentials is refused. The token is passed to curl as a header file, never on the command line.

H=$(sudo sed -n 's/^set $ukn_host "\(.*\)";$/\1/p' /etc/nginx/sac-ukn-host.conf)
T=$(sudo sed -n 's/^NTFY_TOPIC=//p' /root/uptime-kuma-ntfy-credentials.txt) C=(--cacert /etc/ssl/sac/uptime-kuma-ntfy.crt --resolve "$H:8443:127.0.0.1") M="test from $(hostname) at $(date -u +%H:%M:%S)"
P=$(curl -s "${C[@]}" -H @<(sudo sed -n 's/^NTFY_TOKEN=/Authorization: Bearer /p' /root/uptime-kuma-ntfy-credentials.txt) -d "$M" "https://$H:8443/$T" | jq -r .event)
G=$(curl -s "${C[@]}" -H @<(sudo sed -n 's/^NTFY_TOKEN=/Authorization: Bearer /p' /root/uptime-kuma-ntfy-credentials.txt) "https://$H:8443/$T/json?poll=1" | jq -r 'select(.event == "message") | .message' | grep -cxF "$M")
A=$(curl -s "${C[@]}" -o /dev/null -w '%{http_code}' "https://$H:8443/$T/json?poll=1")
N=$(curl -s "${C[@]}" -o /dev/null -w '%{http_code}' -d 'anonymous publish' "https://$H:8443/$T")
echo "publish with token : $P";
echo "read back with token : $G message";
echo "anonymous subscribe : $A";
echo "anonymous publish : $N" [ "$P" = message ] && [ "$G" = 1 ] && [ "$A" = 403 ] && [ "$N" = 403 ]
publish with token : message read back with token : 1 message anonymous subscribe : 403 anonymous publish : 403

To stream alerts as they arrive, drop ?poll=1 and keep the connection open (https://<host>:8443/<topic>/json). You can also use /<topic>/sse or /<topic>/ws. From another machine, use your instance's address and the same Authorization: Bearer header.

8

Prove the whole pipeline end to end

Run the self-test that ships with the image. It exercises the real path. It signs in to Uptime Kuma through nginx with your administrator credentials and creates a temporary monitor pointing at a closed port, so the monitor must go down. It waits for the alert, then reads the alert back from your ntfy topic through nginx on 8443 with your access token, as a subscriber would. It deletes the temporary monitor afterwards.

OUT=$(sudo /usr/local/sbin/sac-uptime-kuma-e2e-probe 2>&1) || { echo "$OUT"; exit 1; } echo "$OUT" | grep -E '^(probe: (signed in|default ntfy channel|ALERT| title| message)|E2E_PIPELINE_OK)' grep -qx 'E2E_PIPELINE_OK' <<<"$OUT"
probe: signed in to Uptime Kuma as admin (JWT issued) probe: default ntfy channel present (id 1) -> topic <NTFY_TOPIC> probe: ALERT DELIVERED to an authenticated subscriber of https://ec2-35-174-111-13.compute-1.amazonaws.com:8443/<NTFY_TOPIC> probe: title = sac e2e probe 1792610335769 Down [Uptime-Kuma] probe: message = connect ECONNREFUSED 127.0.0.1:1 E2E_PIPELINE_OK

Run it after any change to the instance. If it fails, alerts are not reaching your subscribers.

9

HTTPS certificate and host name

First boot created the certificate for the instance's public DNS name, public IP, private IP and localhost. The key, the credentials file and both data directories are readable only by their owners. Inspect the certificate with this command.

sudo openssl x509 -in /etc/ssl/sac/uptime-kuma-ntfy.crt -noout -subject -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/ssl/sac/uptime-kuma-ntfy.key /root/uptime-kuma-ntfy-credentials.txt /var/lib/uptime-kuma /var/lib/ntfy /etc/ntfy/server.yml
subject=CN = ec2-35-174-111-13.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-35-174-111-13.compute-1.amazonaws.com, IP Address:35.174.111.13, IP Address:172.31.91.150, IP Address:127.0.0.1, DNS:localhost 600 root:root /etc/ssl/sac/uptime-kuma-ntfy.key 600 root:root /root/uptime-kuma-ntfy-credentials.txt 750 kuma:kuma /var/lib/uptime-kuma 750 ntfy:ntfy /var/lib/ntfy 640 root:ntfy /etc/ntfy/server.yml

To serve both under your own DNS name, run the helper. An example name is status.example.com, pointed at this instance. The helper records the name and creates a new self-signed certificate for it. It updates nginx, ntfy's base URL and Uptime Kuma's Primary Base URL (used in alert links), restarts what is running and rewrites the URLs in the credentials file.

sudo uptime-kuma-ntfy-set-hostname status.example.com

If the instance's public address changes after a stop and start, run the helper again.

To use a certificate from your own certificate authority, replace /etc/ssl/sac/uptime-kuma-ntfy.key (keep it 0600 root) and /etc/ssl/sac/uptime-kuma-ntfy.crt. Then run sudo systemctl reload nginx. This is recommended before you subscribe phones.

10

Outbound connections and metadata

ntfy listens on loopback, denies anonymous access, allows no self-registration, and has no relay, push service or mail server configured. The last line of the output counts upstream-base-url, firebase-key-file, web-push-public-key and smtp-sender-addr entries, and the count is none. The three services cannot reach the instance metadata service, while the Amazon Time Sync Service at 169.254.169.123 keeps working. The firmware-metadata refresh is masked. Run this block to check each setting.

sudo grep -E '^(listen-http|auth-default-access|enable-signup|enable-reservations|require-login|behind-proxy)' /etc/ntfy/server.yml
OUTBOUND=$(sudo grep -cE '^(upstream-base-url|firebase-key-file|web-push-public-key|smtp-sender-addr):' /etc/ntfy/server.yml || true); echo "$OUTBOUND" systemctl show -p IPAddressDeny uptime-kuma.service ntfy.service nginx.service chronyc -n sources | grep 169.254
FW=$(systemctl is-enabled fwupd-refresh.timer fwupd-refresh.service || true); echo "$FW" [ "$OUTBOUND" = 0 ] && [ "$(sort -u <<<"$FW")" = masked ] && [ "$(systemctl show -p IPAddressDeny --value uptime-kuma.service ntfy.service nginx.service | grep -c 169.254.169.254)" = 3 ]
listen-http: "127.0.0.1:8088" auth-default-access: "deny-all" enable-signup: false require-login: true enable-reservations: false behind-proxy: true 0 IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 IPAddressDeny=168.63.129.16/32 fd00:ec2::254/128 169.254.169.254/32 ^* 169.254.169.123 3 4 377 7 +373ns[ +410ns] +/- 482us masked masked

To use features that need an outside service, configure them yourself in /etc/ntfy/server.yml. Then restart ntfy with sudo systemctl restart ntfy.

  • Instant delivery to the iOS app needs upstream-base-url: "https://ntfy.sh". It forwards a poll request (not the message) for each notification to ntfy.sh.
  • Email notifications need an smtp-sender-* mail server.
  • Firebase (firebase-key-file) and web push (web-push-* keys) need their own credentials.

You can add Uptime Kuma's own notification providers (email, Slack, Teams, Telegram and around 90 others) in Settings → Notifications, next to the ntfy channel. To turn Uptime Kuma's update check back on, use Settings → About.

Maintenance

  • Uptime Kuma password: change it in Settings → Security, then update /root/uptime-kuma-ntfy-credentials.txt or delete the file. If you lose the password, reset it from a shell on the instance with Uptime Kuma's own tool, which asks for the new password interactively.
sudo systemctl stop uptime-kuma cd /opt/uptime-kuma && sudo -u kuma env DATA_DIR=/var/lib/uptime-kuma/ /opt/node/bin/node extra/reset-password.js
sudo systemctl start uptime-kuma
  • ntfy password and users: the ntfy command reads passwords from standard input, so they never appear on a command line. Use these commands to change the SecureAnyCloud password or to give a colleague read-only access to the topic.
sudo ntfy user change-pass sac
sudo ntfy user add --role=user colleague
sudo ntfy access colleague <your-topic> read-only
sudo ntfy token add --label=my-script sac
  • Backups: Uptime Kuma's database is in /var/lib/uptime-kuma, and ntfy's users, tokens and message cache are in /var/lib/ntfy. Back up both directories with the services stopped. Keep the backup as safe as the credentials, because the databases hold your monitors' secrets and the ntfy tokens.
  • Message retention: ntfy keeps messages for 12 hours (cache-duration in /etc/ntfy/server.yml).
  • Upgrades: to move to a newer release, launch the newer version from the listing when it is published, then restore your backups.
  • Logs: sudo journalctl -u uptime-kuma -u ntfy -n 100 --no-pager.
  • Security patches: unattended-upgrades stays enabled, so the OS, including the C library ntfy uses, keeps receiving security updates.

Security recommendations

  • Keep the security group tight. Open 22 only to your management address, 80 and 443 only to the people who use the dashboard, and 8443 only to the devices and scripts that subscribe.
  • Use your own certificate before you subscribe phones or other people sign in, so they trust it without exceptions.
  • Keep both applications on loopback. Reach them through nginx, which keeps forwarded headers honest and refuses cross-site requests.
  • Give each subscriber its own ntfy user or token with only the access it needs, instead of sharing the SecureAnyCloud password.

Support and licensing

Uptime Kuma is developed by Louis Lam and contributors and distributed under the MIT License. ntfy is developed by Philipp C. Heckel and contributors and dual-licensed under the Apache License 2.0 and the GPLv2. This image distributes ntfy under the Apache License 2.0, and both texts ship with it. Node.js is distributed under the MIT License.

ntfy's web app includes notification sounds under the Mixkit Free License and Creative Commons Attribution terms, as in every upstream build.

This image ships Uptime Kuma 2.5.7 with updated dependencies and an ntfy binary that SecureAnyCloud compiled from the unmodified 2.34.0 source with Go 1.27.3. The build record and licences are in /usr/share/doc/sac-uptime-kuma-ntfy/. SecureAnyCloud provides the packaging, the hardening, first-boot HTTPS and per-instance credentials, and 24/7 support.

SecureAnyCloud is not affiliated with or endorsed by the Uptime Kuma or ntfy projects or their authors. Product names are marks of their owners and are used here only to identify the software.

For help with deployment or configuration, email sales@secureanycloud.com or visit SecureAnyCloud support.

Deploy on AWS

Launch Uptime Kuma with ntfy 2.5.7 on Ubuntu 24.04 on AWS, with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS