Uptime Kuma is a self-hosted uptime monitor and status-page application. This guide builds a small, production-minded Uptime Kuma VPS on Ubuntu 24.04 LTS, keeps its native port off the public internet, and publishes it through Caddy with an automatically managed TLS certificate.
The monitor should normally live away from the systems it checks. If Uptime Kuma runs beside the website it monitors, a failure of that VPS can take out both the service and the alert. A separate LayerOne VPS gives the monitor a different failure boundary, but it still does not replace checks from a second provider or geographic region when that distinction matters.
Uptime Kuma VPS requirements and LayerOne cost
| Item | Practical starting point |
|---|---|
| Minimum RAM | 1 GB for the OS, Docker, Caddy, and a small monitor set |
| Recommended RAM | 2 GB for update headroom, longer history, and dozens of checks |
| Expected CPU usage | About 1–3% of one vCPU while idle for a small set; brief 10–30% bursts when many checks run together |
| Storage requirement | 10 GB minimum; plan for 20 GB or more as heartbeat history and logs grow |
| Exact LayerOne SKU | gc.micro — catalog slug layerone-starter, 1 vCPU, 2 GB RAM, 40 GB disk |
| Expected monthly cost | $5.00/month equivalent, metered at $0.0068/hour while the VPS exists |
Those CPU figures are planning estimates, not limits or guarantees. Check type,
interval, retained history, notification providers, DNS latency, and monitor
count all change resource use. A 10-second interval creates six times as many
checks as a 60-second interval. Watch docker stats, disk use, and the
VPS monitoring guide after importing a real
monitor set.
LayerOne meters the VPS hourly; a short calendar month, deletion before 730 hours, extra transfer, or a future catalog change can make the invoice differ from the monthly equivalent. Confirm the current catalog on Pricing before ordering.
Before you install
This walkthrough assumes:
- a fresh Ubuntu 24.04 LTS server with a public IPv4 address;
- a DNS
Arecord such asstatus.example.compointing to that address; - Docker Engine and the Compose plugin installed with Running Docker; and
- an SSH key, a non-root sudo user, and current security updates. Complete Your first hour on a new server first.
Uptime Kuma v2 requires storage that supports POSIX file locks. Use the local
VPS disk for /app/data; do not place its SQLite data directory on NFS.
1. Apply both firewall layers
On the LayerOne server's Networking tab, set inbound policy to DROP and
outbound policy to ACCEPT. Allow TCP 22 only from your administration IP or
network, then allow TCP 80 and 443 from 0.0.0.0/0. See
The cloud firewall.
Apply the same intended policy with UFW inside Ubuntu. Keep the browser console open until a second SSH connection succeeds:
sudo apt update
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from YOUR.ADMIN.IP.ADDRESS to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Docker can insert forwarding rules ahead of UFW. The LayerOne cloud firewall is therefore the authoritative upstream boundary for Docker-published ports. This stack intentionally publishes only 80 and 443; port 3001 remains on the private Compose network.
2. Create the Uptime Kuma stack
Create the project and local data directory:
sudo mkdir -p /opt/uptime-kuma/data
sudo chown -R "$USER":"$USER" /opt/uptime-kuma
cd /opt/uptime-kuma
Create /opt/uptime-kuma/compose.yaml:
services:
uptime-kuma:
image: louislam/uptime-kuma:2
restart: unless-stopped
volumes:
- ./data:/app/data
expose:
- "3001"
caddy:
image: caddy:2-alpine
restart: unless-stopped
depends_on:
- uptime-kuma
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
The 2 image tag follows the supported Uptime Kuma v2 channel. It receives v2
updates without silently crossing to a future major version.
Create /opt/uptime-kuma/Caddyfile, replacing the example hostname:
status.example.com {
encode zstd gzip
reverse_proxy uptime-kuma:3001
}
Caddy supports the WebSocket connection Uptime Kuma needs. Uptime Kuma does not
support being mounted reliably under a path such as /kuma; give it a hostname
or subdomain instead.
Validate the effective Compose configuration before starting it:
cd /opt/uptime-kuma
docker compose config --quiet
docker compose up -d
docker compose ps
docker compose logs --tail=100 uptime-kuma caddy
3. Complete first-run setup and verify it
Open https://status.example.com, create the administrator, and use a unique
password. Then enable two-factor authentication under Uptime Kuma's security
settings. Do this immediately; the first-run screen is not an access control
boundary.
Verify DNS, TLS, the private application port, and container health:
curl -fsSI https://status.example.com
sudo ss -lntp | grep -E ':(80|443|3001)[[:space:]]'
docker compose ps
The socket check should show public listeners for 80 and 443 but not a
public 0.0.0.0:3001 listener. Create one HTTPS monitor and one deliberately
failing test monitor. Confirm the failure alert and the recovery alert reach an
off-server destination. A green dashboard does not prove notification delivery.
If all access is through Caddy, Uptime Kuma's official proxy guide recommends enabling trusted proxy headers in Settings → Reverse Proxy → HTTP Headers so client addresses are interpreted correctly. Do not enable that setting if untrusted clients can bypass the proxy.
4. Back up and restore Uptime Kuma
Uptime Kuma v2 supports backing up the entire data directory, not the removed JSON backup feature. Stop the application briefly so SQLite and its auxiliary files form a consistent set:
cd /opt/uptime-kuma
docker compose stop uptime-kuma
sudo tar -C /opt/uptime-kuma -czf "/root/uptime-kuma-$(date -u +%Y%m%dT%H%M%SZ).tar.gz" data compose.yaml Caddyfile
docker compose start uptime-kuma
Copy that archive to encrypted storage outside the VPS. LayerOne does not take snapshots or backups; read Backups and snapshots.
To restore onto a compatible fresh stack, stop Uptime Kuma, move the current
data directory aside, extract the archive under /opt/uptime-kuma, correct
ownership if necessary, and start the service. Keep the old directory until the
dashboard, monitors, notification providers, and history have been verified.
5. Update safely
Read the Uptime Kuma release notes before every major migration. For updates within v2:
cd /opt/uptime-kuma
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 uptime-kuma
Take an off-server backup first. Do not interrupt a database migration. Confirm the version, dashboard login, a real monitor, and a test notification after the containers return.
Security and operational pitfalls
- Never mount
/var/run/docker.sockjust to monitor containers unless you accept that a compromise of Uptime Kuma can become control of the Docker host. - Do not put secrets, private hostnames, raw error bodies, or internal ports on a public status page.
- Keep authentication enabled even if only status pages are public.
- Monitoring this VPS from itself cannot report loss of the server or its network path. Add an independent outside check.
- Review retention and
du -sh /opt/uptime-kuma/dataregularly. A full root filesystem can corrupt databases and stop alerts.