Uptime Kuma with ntfy 2.5.6 on AWS User Guide
Overview
This guide covers launching and verifying Uptime Kuma with ntfy 2.5.6 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other products in the listing are described on the listing's guide hub.
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.
On this image the two are 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, so 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.
The image runs Uptime Kuma 2.5.6 from the official 2.5.6 release with its production dependencies updated to releases that fix every known advisory reachable at run time, and ntfy 2.32.0 compiled by cloudimg from the official 2.32.0 source with Go 1.27.3. The upstream ntfy 2.32.0 release binary was built with Go 1.27.1, against which govulncheck lists 13 known Go standard-library advisories; this build has none that govulncheck finds reachable. Node.js 22.23.4 runs Uptime Kuma. The base is a fully patched Ubuntu 24.04 LTS, and unattended security upgrades stay on for your instance.
What is on the image:
- Uptime Kuma 2.5.6 (
uptime-kuma.service) running as the unprivilegedkumauser on127.0.0.1:3001only - ntfy 2.32.0 (
ntfy.service) running as the unprivilegedntfyuser on127.0.0.1:8088only - 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
adminaccount, an ntfycloudimguser, 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, andcloudimg-uptime-kuma-e2e-probe, an end-to-end test of the alert pipeline- The build record (
BUILD-INFO.txt), the dependency lockfiles, thenpm auditandgovulncheckresults and the licences in/usr/share/doc/cloudimg-uptime-kuma-ntfy/
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; on a public address that is a race you can lose. On this image first boot creates the database and the
adminaccount 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: only thecloudimguser 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); 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, and cross-site form posts and WebSocket connections are refused by nginx (and by Uptime Kuma's own origin check).
- Real client addresses. nginx passes the connecting address to Uptime Kuma and ntfy and ignores any
X-Forwarded-Forheader 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, it polls
uptime.kuma.pet) is off. ntfy has noupstream-base-url(the ntfy.sh relay used for instant iOS delivery), nofirebase-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.serviceandnginx.serviceare 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 thekumagroup only. - No scheduled firmware-metadata refresh. The
fwupdfirmware updater stays installed, but its LVFS metadata refresh (fwupd-refresh.timerand.service) is masked, so an LVFS outage never marks your instance unhealthy at boot; runsudo fwupdmgr refreshyourself 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 auditon the upstream 2.5.6 lockfile lists 25 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,lodashandglob. What remains is the SQLite driver's install-time build tooling (tar,node-gypand 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 full result ships inkuma-npm-audit-production.json. Novite(a development tool) is installed. - ntfy.
govulncheckreports no vulnerability reachable from ntfy. It lists one advisory at module level only, GO-2026-5932: thegolang.org/x/crypto/openpgppackage is deprecated and has no fixed version. That package is not compiled into ntfy; the build fails if it ever is. The full output ships inntfy-govulncheck-binary.txtandntfy-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/docspage instead links to the documentation at docs.ntfy.sh.
Prerequisites
- 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 a single Go process; a smaller burstable type also works for a few dozen monitors.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| Uptime Kuma with ntfy 2.5.6 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once 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.6 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Security group: 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/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 (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this 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, and the VPC, subnet, key pair and CIDR with your own:
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: the AMI already makes IMDSv2 required.
Step 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. Your credentials are in a 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=cloudimg
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://www.cloudimg.co.uk/guides/uptime-kuma-with-ntfy-2-5-6-on-ubuntu-24-04-aws/
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, and ntfy refuses it to anyone without your credentials anyway.
Step 3: Confirm the services are running
First boot, Uptime Kuma, ntfy and nginx are active, the two applications run as their own unprivileged users, ntfy reports healthy, and the start guard (which runs before every start of the three services) passes:
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.
These are 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).
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/cloudimg-uptime-kuma-ntfy/BUILD-INFO.txt
ntfy version 2.32.0
v22.23.4
Uptime Kuma 2.5.6 with ntfy 2.32.0 - cloudimg build (d-320, d-304, d-094)
Uptime Kuma 2.5.6 (MIT, uptime-kuma-LICENSE)
source https://github.com/louislam/uptime-kuma/archive/refs/tags/2.5.6.tar.gz
ntfy 2.32.0 (Apache-2.0 / GPL-2.0 dual licence, ntfy-LICENSE + ntfy-LICENSE.GPLv2)
source https://github.com/binwiederhier/ntfy/archive/refs/tags/v2.32.0.tar.gz
binary sha256 43ca315eee3c45e518701f74ea3dcc624bfac255edd581d1623bed8db7e21999
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)
Step 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 it, or install your own certificate (Step 9). Because the administrator already exists, Uptime Kuma asks you to sign in rather than to create 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 confirm the first-run pages are closed from the command line too. 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/cloudimg-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/cloudimg/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
Step 5: The alert channel is already wired up
In Uptime Kuma, open the profile menu, then Settings, then Notifications. The channel ntfy (this instance) is already there:

It publishes to the ntfy server on this instance with your access token and 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
Step 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 it needs a network path to them, and the security group must allow nothing inbound for monitoring to work.
Step 7: Subscribe to your alerts
Alerts land on your instance's ntfy topic on port 8443, and 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 for phones install your own certificate first (Step 9). Note that without an upstream relay the iOS app delivers notifications when it polls rather than 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, 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/cloudimg-ukn-host.conf)
T=$(sudo sed -n 's/^NTFY_TOPIC=//p' /root/uptime-kuma-ntfy-credentials.txt)
C=(--cacert /etc/ssl/cloudimg/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), or use /<topic>/sse or /<topic>/ws. From another machine, use your instance's address and the same Authorization: Bearer header.
Step 8: Prove the whole pipeline end to end
The image ships a self-test of the real path: it signs in to Uptime Kuma through nginx with your administrator credentials, creates a temporary monitor pointing at a closed port so it must go down, waits for the alert, and reads it 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/cloudimg-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 = cloudimg 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.
Step 9: HTTPS certificate and host name
The certificate was created on your instance's first boot 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:
sudo openssl x509 -in /etc/ssl/cloudimg/uptime-kuma-ntfy.crt -noout -subject -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/ssl/cloudimg/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 = cloudimg
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/cloudimg/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 (for example status.example.com, pointed at this instance), run the helper. It records the name, creates a new self-signed certificate for 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 (recommended before you subscribe phones), replace /etc/ssl/cloudimg/uptime-kuma-ntfy.key (keep it 0600 root) and /etc/ssl/cloudimg/uptime-kuma-ntfy.crt, then run sudo systemctl reload nginx.
Step 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 counts upstream-base-url, firebase-key-file, web-push-public-key and smtp-sender-addr entries: none). The three services cannot reach the instance metadata service, while the Amazon Time Sync Service at 169.254.169.123 keeps working, and the firmware-metadata refresh is masked:
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
If you want features that need an outside service, configure them yourself in /etc/ntfy/server.yml and restart ntfy (sudo systemctl restart ntfy):
- Instant delivery to the iOS app needs
upstream-base-url: "https://ntfy.sh", which 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.
Uptime Kuma's own notification providers (email, Slack, Teams, Telegram and around 90 others) can be added 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.txtor delete it. If you lose it, reset it from a shell on the instance with Uptime Kuma's own tool (it 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. To change the
cloudimgpassword, or to give a colleague read-only access to the topic:
sudo ntfy user change-pass cloudimg
sudo ntfy user add --role=user colleague
sudo ntfy access colleague <your-topic> read-only
sudo ntfy token add --label=my-script cloudimg
- Backups: Uptime Kuma's database is in
/var/lib/uptime-kumaand ntfy's users, tokens and message cache in/var/lib/ntfy. Back up both directories with the services stopped, and keep the backup as safe as the credentials: the databases hold your monitors' secrets and the ntfy tokens. - Message retention: ntfy keeps messages for 12 hours (
cache-durationin/etc/ntfy/server.yml). - Upgrades: to move to a newer release, launch the newer version from the listing when it is published and restore your backups.
- Logs:
sudo journalctl -u uptime-kuma -u ntfy -n 100 --no-pager. - Security patches: unattended-upgrades remains 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/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; nginx is what keeps forwarded headers honest and refuses cross-site requests.
- Give each subscriber its own ntfy user or token with only the access it needs, rather than sharing the
cloudimgpassword.
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 it under the Apache License 2.0, and both texts ship with it. ntfy's web app includes notification sounds under the Mixkit Free License and Creative Commons Attribution terms, as in every upstream build. Node.js is distributed under the MIT License. This image ships Uptime Kuma 2.5.6 with updated dependencies and an ntfy binary that cloudimg compiled from the unmodified 2.32.0 source with Go 1.27.3; the build record and licences are in /usr/share/doc/cloudimg-uptime-kuma-ntfy/. cloudimg provides the packaging, the hardening, first-boot HTTPS and per-instance credentials, and 24/7 support.
cloudimg 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 support@cloudimg.co.uk or visit cloudimg support.