Uptime Kuma: Server and Website Monitoring
Uptime Kuma is a self-hosted, open-source alternative to UptimeRobot with a beautiful interface, Telegram notifications, and support for HTTP, TCP, DNS, Docker, and other monitor types.
Installing via Docker
docker run -d --name uptime-kuma -p 3001:3001 -v uptime-kuma:/app/data --restart unless-stopped louislam/uptime-kuma:latestOpen http://your-server:3001 for the initial setup.
Docker Compose
version: '3.8'
services:
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
ports:
- "3001:3001"
volumes:
- ./uptime-kuma-data:/app/data
restart: unless-stoppedNginx Reverse Proxy + SSL
server {
listen 443 ssl;
server_name status.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/status.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/status.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}Configuring Monitors
- HTTP(S) — website check, expected status 200, keyword match
- TCP Port — port availability check (MySQL 3306, SSH 22)
- DNS — DNS record monitoring
- Docker Container — container status
- Ping — ICMP availability check
Telegram Notifications
In the settings, add Notification → Telegram:
Bot Token: 123456:ABCdefGHIjklMNO
Chat ID: -1001234567890 # for a group
# Get the Chat ID:
curl "https://api.telegram.org/bot{TOKEN}/getUpdates"Status Page (Public Page)
Uptime Kuma supports creating beautiful public Status Pages — show service status to your customers without giving them access to the admin panel.
Real Errors Explained
Three situations cover almost every issue during initial Uptime Kuma setup: volume permissions, a wrong Telegram Chat ID, and a self-signed certificate on an internal service. All three are easy to reproduce and just as easy to fix once you understand the root cause instead of simply restarting the container.
The Database Cannot Be Written
Error: EACCES: permission denied, open '/app/data/kuma.db'The Uptime Kuma container runs as an unprivileged user inside the image, while the mounted ./uptime-kuma-data directory on the host is owned by root. Fix it with chown -R 1000:1000 ./uptime-kuma-data — you can find the process's actual UID inside the container with docker exec uptime-kuma id.
Telegram Notifications Do Not Arrive
Error: Bad Request: chat not foundThe Chat ID is wrong — a common cause is that Telegram uses negative IDs for groups (for example -1001234567890), and supergroups start with -100. If the bot is not a member of the group, or has been blocked by the user in a private chat, you get the exact same error.
Uptime Kuma vs. UptimeRobot vs. Healthchecks.io
| Criterion | Uptime Kuma | UptimeRobot | Healthchecks.io |
|---|---|---|---|
| Hosting | Self-hosted | SaaS | SaaS or self-hosted |
| Cost | Free (your own VPS) | Free up to 50 monitors, then paid | Free up to 20 checks |
| Checks from multiple regions | No, a single server | Yes | Yes |
| Public Status Page | Yes, built in | Yes | Limited |
When Uptime Kuma Is Not a Good Fit
- You need monitoring from several geographic regions at once — a self-hosted install only checks from a single VPS, and a local network blip triggers a false alarm.
- Customers need an independent third-party availability report — a self-hosted status page is not considered neutral proof in SLA disputes.
- You need complex on-call escalation chains — PagerDuty or Opsgenie is the more honest fit here.
- You are planning several thousand monitors — SQLite by default is not built for that load without extra tuning.
- You need a long multi-year metrics history for SLA reporting — Uptime Kuma keeps aggregates for a limited period.
Verifying the Result
# The container is up and listening on the port
docker ps | grep uptime-kuma
curl -I http://localhost:3001
# The status API for a specific monitor (public if Status Page is enabled)
curl https://status.yourdomain.com/api/status-page/heartbeat/default
# Container logs when something looks off
docker logs uptime-kuma --tail 50An empty response from curl -I, or a code other than 200/301, means the nginx reverse proxy could not reach the container — check that proxy_pass points to the right port and that the container is actually running. After the initial check, set up the monitoring server itself using the checklist from the article on configuring a cloud server, and it is convenient to run Uptime Kuma's database backups with rclone.