I installed Uptime Kuma after clients told me about downtime first

I installed Uptime Kuma after clients told me about downtime first

I was tired of learning a site was down from a client email. Uptime Kuma on Ubuntu 24.04 looked like a thirty-minute Docker job until port 3001 was already taken. Here's the wall and the Compose file that got monitors and Telegram alerts running.

 Uptime Kuma monitoring platform

Caption: Uptime Kuma on my VPS — HTTP, TCP, and Docker checks without a SaaS uptime bill.

 Uptime Kuma infrastructure overview

Caption: Kuma sits on the VPS and checks websites, APIs, and services I actually run.

Introduction

I wanted to hear about downtime from a dashboard, not from a client. Uptime Kuma is the small Docker box I put on Ubuntu 24.04 for HTTP(s), TCP, ping, DNS, Docker, and a pile of other checks — plus Telegram when something flips red.

On a lab VPS this install is famous for failing to start because port 3001 is already taken. I got bind: address already in use from Compose, moved the host port, and the UI came up. After that I spent more time on false reds (Cloudflare treating the VPS like a bot) than on the container itself.

I keep it behind HTTPS before I expose the admin UI. The data stays on my disk in ./data.

Why I picked Uptime Kuma

  • Check data stays on my VPS — I am not paying Pingdom to watch my own sites.
  • HTTP(s), keyword, TCP, ping, DNS, Docker, Postgres, MySQL, Redis — one UI.
  • Response-time graphs and a public status page I can send to a client.
  • Telegram, Discord, Slack, email, Gotify, and a long list of other notifiers.
  • It fits on a 1 GB RAM box next to other containers if I do not go crazy on check count.

Prerequisites

Hardware (comfortable minimum):

  • 1+ CPU core (2 if this box also runs apps)
  • 1 GB RAM (2 GB+ if I add a lot of monitors)
  • 5+ GB free disk
  • Ubuntu 24.04 LTS

Software & accounts:

  • Root or sudo
  • A domain (optional, but I want HTTPS)
  • Docker Engine 24+ and the Compose plugin
  • Outbound internet so checks can reach targets

Security notes:

  • SSH keys; password login off on production.
  • HTTPS before the UI is public.
  • Strong admin password; extra reverse-proxy auth if the VPS is noisy.

Update first:

sudo apt update && sudo apt upgrade -y
sudo apt install curl wget git ca-certificates -y

Installation Guide

Official Docker image — that is the path I use.

1. Install Docker Engine

curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker

Verify Docker is running:

docker --version
docker compose version

2. Create Persistent Directories

sudo mkdir -p /opt/uptime-kuma
cd /opt/uptime-kuma

3. Create docker-compose.yml

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

Save the file, then start the stack:

docker compose up -d
docker compose ps

4. Initial Setup via Web UI

Open http://YOUR_SERVER_IP:3001 in the browser.

  1. Create the first admin account (this becomes the owner).
  2. Log in to the dashboard.
  3. Click Add New Monitor for the first HTTP check.

 Uptime Kuma dashboard

Caption: Monitor cards and response-time overview after the first checks are in.

5. Enable HTTPS with a Reverse Proxy (Recommended)

Option A — Caddy (automatic Let's Encrypt):

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy -y

Create /etc/caddy/Caddyfile entry:

monitor.yourdomain.com {
    reverse_proxy localhost:3001
}

Reload Caddy:

sudo systemctl reload caddy

Option B — Nginx with Certbot if Nginx is already on the box.

Configuration

Monitor Examples

HTTP(s) website check:

  • Monitor Type: HTTP(s)
  • URL: https://www.nzian.xyz
  • Heartbeat Interval: 60 seconds
  • Retries: 3
  • Accepted Status Codes: 200-299

TCP port check (e.g. MySQL):

  • Monitor Type: Port
  • Hostname: db.internal.example.com
  • Port: 3306

Keyword check (verify page content):

  • Monitor Type: HTTP(s) — Keyword
  • URL: https://your-app.com/health
  • Keyword: "ok"

Notification Channels

  1. Go to Settings → Notifications.
  2. Click Setup Notification.
  3. Choose a provider (Telegram and Discord are what I use for client boxes).
  4. Attach the notification to each monitor under Notifications.

Example Telegram setup:

  • Create a bot via @BotFather.
  • Paste the bot token into Uptime Kuma.
  • Send /start to the bot, then use Get chat ID in Uptime Kuma.

Status Page

  1. Go to Status Pages → New Status Page.
  2. Add monitor groups (e.g. "Production APIs", "Client Sites").
  3. Set a slug like status and enable public access.
  4. Share https://monitor.yourdomain.com/status/your-slug.

Docker Socket Monitoring (Optional)

To watch Docker containers on the same host, mount the socket (I only do this on a dedicated monitoring VPS):

volumes:
  - ./data:/app/data
  - /var/run/docker.sock:/var/run/docker.sock:ro

Usage

First-login checklist:

  1. Unique strong password.
  2. Monitors for every production URL I care about.
  3. At least two notification channels (Telegram + email).
  4. A status page if a client needs to see green bars.
  5. Test alerts by pausing a monitor or stopping a test container.

Daily:

  • Glance at the dashboard.
  • Read incident history when something goes red.
  • 60s heartbeats for APIs I get paged on; 5m for internal tools.

Worth turning on later:

  • Prometheus metrics endpoint (Grafana)
  • Maintenance windows before deploys
  • Tags by client or project

Where it broke

On a lab Ubuntu 24.04 box this Compose file is famous for dying on port 3001.

1. bind: address already in use

docker compose up -d failed. Logs from docker compose logs -f showed the host port already taken — leftover Node app, another Kuma attempt, or something else on 3001.

Fix: change the host side in docker-compose.yml, for example "3002:3001", then:

docker compose up -d
docker compose ps

If Caddy/Nginx still points at 3001, I update the reverse proxy to match.

2. Monitors flip red while the site is fine

That was not a Compose bug. Cloudflare bot fight, fail2ban, or a timeout that was too tight. I bumped retries and timeout, and allowlisted the VPS IP where I could. Self-signed targets need Ignore TLS/SSL errors per monitor — I only do that for lab boxes I control.

Notifications that never fire are usually a bad chat ID / webhook, or the monitor was never attached to the notification. I re-test the provider before I blame Kuma.

Troubleshooting

  • High CPU on a small VPS: fewer monitors, or longer intervals on the boring checks.
  • Empty dashboard after a rebuild: ./data was not mounted. I never run this image without that volume.
  • Upgrade: from /opt/uptime-kuma, docker compose pull && docker compose up -d.

Deeper issues: GitHub discussions and release notes before a major bump.

Scaling, Securing, and Next Steps

  • Backup: copy /opt/uptime-kuma/data — that is config and history.
  • Separate VPS: if the app box dies, I still want alerts. Kuma belongs on a different machine when I can afford it.
  • Reverse-proxy auth in front of the admin UI.
  • A second Kuma elsewhere that watches the first instance.
  • Grafana if I want longer trends than the built-in charts.
  • Keep docker-compose.yml in Git.

Conclusion

Uptime Kuma is running in Docker on my Ubuntu box, data under /opt/uptime-kuma/data, HTTPS on the domain, Telegram on the monitors I actually care about. I hear about a red check in seconds instead of hours.

Next I would move it off the same VPS as the apps, and restore that data directory once on a spare box so I trust the backup. Port 3001 was the install wall; Cloudflare false reds were the week-two wall.

Did you hit the same wall?

I got stuck on port 3001 already in use (bind: address already in use). Did you hit the same thing, or a different one — Cloudflare false positives, notifications that never fired, a missing ./data volume? Tell me in the comments. I read them.

Need this done on your server?

I deploy and harden Laravel/CodeCanyon apps on cPanel or VPS, and offer monthly Server Watch retainers. Hire for deploy · Care plan

References

Share:

Get new posts in your inbox

No spam. One short email per new article — practical PHP, Laravel, devops, and AI-assisted workflows.

Comments

Powered by GitHub Discussions via Giscus. A free GitHub account is required.