Uptime Kuma Setup - Beautiful Uptime Monitoring

Status: Active
Last Updated: 2026-08-14
Category: Observability - Phase 1: Basic Monitoring
Prerequisites: docker-basics
Time: 1-2 hours
Tags: uptime-kuma, uptime, monitoring, docker, status-page, health-checks

Summary

Hands-on guide to deploying Uptime Kuma, the self-hosted answer to "is it up?" You'll install it with Docker Compose, build HTTP and TCP checks for every service you run, monitor Docker containers directly, and publish a public status page β€” all in about an hour of work.

🎯 What You'll Learn

By the end of this article, you'll be able to:


Table of Contents

  1. What is Uptime Kuma?
  2. Installation
  3. Your First Monitor: HTTP Check
  4. TCP Checks
  5. Docker Container Monitoring
  6. Status Pages
  7. Notifications
  8. Tuning Intervals and Retries
  9. Troubleshooting
  10. Key Takeaways
  11. Next Steps

What is Uptime Kuma?

Uptime Kuma is a self-hosted uptime checker: it probes your services on a schedule and tells you when one stops answering. It deliberately answers only Rung 1 of the observability ladder ("is it alive?") but does so extremely well.

What it's great at:

What it doesn't do (that later phases add):

Think of it as the smoke detector of your infrastructure: simple, always watching, loud when needed.

Resource Requirements

Component Requirement
RAM ~100–256 MB
CPU Negligible
Disk Small β€” history is lightweight
Network Outbound to every monitored target

Installation

Directory Layout

mkdir -p ~/apps/uptime-kuma && cd ~/apps/uptime-kuma

Keeping each app in its own directory under ~/apps/ makes backups and migration trivial β€” copy the folder, restore the folder.

Docker Compose File

Create compose.yaml:

services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "3001:3001"
    volumes:
      - ./data:/app/data

Notes:

Launch It

docker compose up -d

What happens:

1. Docker pulls louislam/uptime-kuma:1 (~200 MB)
2. Creates container "uptime-kuma"
3. Mounts ./data into the container
4. Starts the Node.js server listening on :3001
5. restart: unless-stopped ensures it survives reboots

Verify:

docker compose ps        # STATUS should be "Up"
curl -I http://localhost:3001   # expect HTTP 200

Now open http://<server-ip>:3001, create the admin account, and set the timezone correctly β€” timestamps on incidents and heartbeats depend on it.

πŸ”’ Security note: The UI has no second factor by default and full control over notifications. Don't expose port 3001 to the internet without a reverse proxy + auth, or keep it LAN/VPN-only (see kb/security/wireguard-vpn).


Your First Monitor: HTTP Check

Log in β†’ Add New Monitor. The default type is HTTP(s).

Basic Website Check

Field Value Why
Friendly Name fogserv.cloud homepage Shown everywhere; be specific
URL https://fogserv.cloud The exact thing users hit
Heartbeat Interval 60 s How often to probe
Retries 2 Down only after 3 consecutive failures
Retry Interval 60 s Gap between retries

Save, and within a minute you'll see green heartbeats. Each heartbeat records status + response time + cert expiry.

What a heartbeat actually does:

every 60s:
  GET https://fogserv.cloud
  β”œβ”€ DNS resolves?
  β”œβ”€ TCP connects?
  β”œβ”€ TLS handshake OK?
  β”œβ”€ HTTP response received?
  └─ response time recorded
any failure Γ—(retries+1) β†’ mark DOWN β†’ fire notifications
back UP later β†’ send recovery notification

Level Up the Check

A 200-response isn't proof the site works. Harden it:

Keyword check β€” verify real content came back:

Expected status code β€” if your app redirects / to /login when healthy, set expected code to 302.

Certificate expiry β€” Kuma tracks TLS expiry automatically. Add a second monitor of type HTTP(s) - Keyword with "Ignore TLS/SSL error" off, or just rely on the certificate-expiry notification setting under Settings β†’ Notifications. No more surprise cert outages.

Recommended First Five Checks

For a typical self-hosted box:

  1. Main website (HTTP + keyword)
  2. API endpoint (https://api.example.com/healthz)
  3. Database (TCP β€” below)
  4. Reverse proxy / Traefik dashboard
  5. Backup completion (Push monitor β€” see Notifications)

TCP Checks

Many services have no HTTP interface: PostgreSQL on 5432, Redis on 6379, Minecraft on 25565, SSH itself. For those, use monitor type TCP Port.

Field Example value
Friendly Name postgres on db01
Hostname db01.lan
Port 5432

What happens per heartbeat:

connect(db01.lan:5432)
β”œβ”€ success (<timeout)     β†’ UP
└─ timeout/refused        β†’ count failure toward DOWN

TCP Check Caveats

Ping Monitors

Type Ping (ICMP) checks raw reachability, bypassing services entirely. Great for distinguishing "network down" from "service down": if ping db01 succeeds but TCP 5432 fails, the network is fine and Postgres is the suspect.

⚠️ Inside Docker, ping requires NET_RAW; if ping monitors fail immediately in the container, either grant the capability or use TCP checks instead.


Docker Container Monitoring

Uptime Kuma can watch Docker containers directly β€” catching the crash-restart loop that never takes a port down long enough to fail a TCP check.

Step 1: Create a Read-Only Docker User

Kuma talks to the Docker socket, which is root-equivalent. Never mount /var/run/docker.sock read-write into a monitoring tool. Instead create a socket-proxy or a restricted user.

Simplest safe option β€” a group-restricted user via Docker socket proxy (tecnativa/docker-socket-proxy):

services:
  socket-proxy:
    image: tecnativa/docker-socket-proxy
    container_name: docker-socket-proxy
    restart: unless-stopped
    environment:
      CONTAINERS: 1   # allow ONLY container listing
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - kuma-net

  uptime-kuma:
    # ... as before ...
    environment:
      - DOCKER_HOST=tcp://socket-proxy:2375
    networks:
      - kuma-net

networks:
  kuma-net:

What this buys you: the proxy exposes only the GET /containers API. Even if Uptime Kuma were compromised, the attacker cannot spawn containers or touch the daemon β€” they can list containers, nothing more.

Step 2: Add a Docker Host

In Kuma: Settings β†’ Docker Hosts β†’ Add:

Test & save.

Step 3: Add Container Monitors

Add New Monitor β†’ type: Docker Container:

The monitor flips DOWN if the container stops, and recovers automatically when it starts again. Repeat for every critical container.


Status Pages

One of Kuma's killer features: a public page showing service health, no login required.

Add New Status Page β†’ choose slug (status.example.com/s/my-status) β†’ drag monitors into groups:

🌐 Public Services
   β”œβ”€β”€ fogserv.cloud homepage
   └── API endpoint
πŸ›  Internal (optional β€” hide these)
   β”œβ”€β”€ postgres on db01
   └── backup daily push

Options worth enabling:

Why this matters: during an incident, a status page absorbs the "is it just me?" traffic that would otherwise hammer your support channels and your already-sick servers. Publish the URL in your email footer or docs before you need it.

πŸ’‘ Keep truly internal targets (databases, VPN endpoints) off the public page. Use a separate private status page for those.


Notifications

Checks are useless unless someone hears about failures. Uptime Kuma supports 90+ notification channels β€” the Phase 1 next lesson covers alert design in depth (simple-alerts); here's the quick setup for the three most common.

Setup Pattern (same for all channels)

  1. Settings β†’ Notifications β†’ Setup Notification
  2. Choose type (SMTP Email / Discord / Telegram …)
  3. Fill credentials/webhook
  4. βœ… Enable "Apply on all existing monitors"
  5. Click Test β€” confirm the message actually arrives before trusting it

SMTP Email

Field Example
Host smtp.fastmail.com
Port 465, TLS on
User/Pass mailbox credentials
To ops@yourdomain.com

Discord Webhook

Server Settings β†’ Integrations β†’ Webhooks β†’ New Webhook β†’ Copy URL β†’ paste into Kuma. Optionally set a custom username like Uptime Bot and enable the rich embed format.

Push Monitors for Cron Jobs

Reverse-direction check: instead of Kuma asking "did it happen?", your job reports "I succeeded":

  1. Add monitor of type Push, note its URL
  2. Append to your backup script:
#!/bin/bash
restic backup /data \
  && curl -fsS --max-time 10 "http://uptime-kuma:3001/api/push/AbCdEf123?msg=ok&ping=$SECONDS" \
  || echo "ALERT: backup or push failed" >&2

How this catches silent death: if the cron job never runs, no push arrives, and after the grace period Kuma marks the monitor DOWN. This is the only reliable way to detect jobs that didn't run β€” their absence produces no error anywhere else. Use it for backups, cert renewals, and report generation.


Tuning Intervals and Retries

Default settings can produce both missed events and false alarms. Tune deliberately:

Setting Too aggressive Sweet spot Too lax
Heartbeat interval ≀20 s (noisy, hammers targets) 60 s β‰₯5 min (slow MTTD)
Retries 0 (flaps on one lost packet) 2–3 5+ (masks short outages)
Retry interval same as main (blast) 60 s β€”

Effective worst-case detection time:

MTTD β‰ˆ interval + retries Γ— retry_interval
60s + 2Γ—60s = up to ~3 min from failure to notification

Three minutes is excellent for free. Going below ~20 s intervals mostly measures network jitter, not availability.

Anti-Flap Tips


Troubleshooting

Monitor shows DOWN but curl from the server works

Certificate errors on internal HTTPS

Self-signed certs fail validation. Either trust your internal CA in the container, or consciously enable "Ignore TLS/SSL error" β€” but then you lose expiry alerts for that monitor; add a separate cert-check approach.

Docker container monitor can't connect

Lost everything after redeploying

You mounted no volume (or mounted the wrong path). State lives in ./data. Restore = stop container, restore ./data, start. Automate: include ~/apps/uptime-kuma/data in your restic/borg backup set, and consider a Push monitor on that backup job too β€” monitor the monitor.

Notification never arrived on test

Check container logs (docker compose logs uptime-kuma) for the delivery attempt; most failures are wrong SMTP port/TLS mode or Discord webhook URLs copied with trailing spaces.


Key Takeaways

  1. Uptime Kuma = fast, cheap Rung-1 observability: deploy in 10 minutes, ~100 MB RAM.
  2. Persist ./data β€” it is the whole instance; back it up like any database.
  3. Harden HTTP checks with keywords/status codes so a lying 200 still fails.
  4. Use TCP checks for non-HTTP services, ping to separate network vs service faults.
  5. Never expose the raw Docker socket β€” use a read-only socket proxy.
  6. Push monitors catch jobs that silently never ran β€” apply them to backups first.
  7. Tune interval/retries for ~3-minute worst-case detection and anti-flap stability.
  8. Publish a status page before the incident, not during it.

πŸ”— Related

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse