LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

Uptime Kuma: Self-Hosted Status Pages and Alerting Done Right

uptime-kumamonitoringstatus-pagealertinghomelabself-hostingdockerobservability

Your Gitea instance went down at 11pm on a Tuesday. Nobody noticed until the developer trying to push code at 9am Wednesday morning hit a connection refused error. Eight hours of undetected downtime, and the fix was a simple container restart.

That’s the problem Uptime Kuma solves. It sits inside your network, pings every service you run on a configurable interval, and fires a notification the moment something stops responding — to Telegram, Slack, Discord, email, Gotify, ntfy, PagerDuty, or any of 90+ other destinations. It also generates a status page you can share with users or teammates, complete with 90-day uptime history and incident announcements.

It’s free, MIT-licensed, runs in a single Docker container, and takes about 15 minutes to deploy from zero to functional. This guide covers everything: installation, monitor types, notification setup, status pages, advanced patterns like push monitors for backup verification, and how Uptime Kuma fits alongside Prometheus and Grafana in a real homelab stack.


What Is Uptime Kuma?

Uptime Kuma is a self-hosted uptime monitoring tool created by Louis Lam and maintained by an active open source community on GitHub. The project launched in 2021 and rapidly became one of the most-starred self-hosting projects in the ecosystem — it fills a gap that every homelab operator eventually runs into.

The gap: you run a dozen services — Jellyfin, Nextcloud, Gitea, Vaultwarden, Home Assistant, Proxmox, your reverse proxy — and you have no systematic way to know when any of them go down. You find out when you try to use the service, or when someone else complains.

The commercial alternatives are expensive or limited. UptimeRobot’s free tier restricts you to 50 monitors and 5-minute check intervals. StatusCake and Pingdom charge serious money for features that are table stakes. And both monitor from the outside — they cannot reach services that live entirely inside your home network, behind a VPN, or on a non-routed subnet.

Uptime Kuma runs inside your network. It checks your services from the same network perspective your users have. It sees the internal hostname, the real response time, the actual TLS certificate. And it costs nothing except the disk space for a Docker volume.

What Uptime Kuma does well:

  • 20+ monitor types: HTTP, TCP, ping, DNS, Docker containers, TLS cert expiry, databases, and more
  • Status pages with 90-day uptime history, incident management, and custom branding
  • 90+ notification integrations — Telegram, Slack, Discord, email, Gotify, ntfy, PagerDuty, webhooks, Apprise, Matrix, and many more
  • Push monitors (dead man’s switch) for verifying cron jobs and backup scripts
  • Maintenance windows to suppress alerts during planned downtime
  • Monitor tags, groups, and dependencies for organized multi-service setups
  • Lightweight: roughly 100MB RAM for a typical homelab deployment

What Uptime Kuma does not do:

Uptime Kuma is a blackbox availability monitor. It does not collect internal application metrics (that’s Prometheus), analyze log streams (that’s Loki), or trace requests through distributed systems (that’s Tempo/Jaeger). If you want to know whether your service is up and responding, Uptime Kuma is the right tool. If you want to know why it’s slow, you need metrics. The two approaches complement each other — run both.


Installation

The simplest and most maintainable deployment is a single Docker Compose file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# docker-compose.yml
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "3001:3001"
    volumes:
      - uptime-kuma:/app/data

volumes:
  uptime-kuma:

Deploy it:

1
docker compose up -d

Open your browser to http://your-host:3001. On first visit, you’ll create an admin account. Pick a strong password — this UI controls your entire monitoring configuration and has access to your notification credentials.

With Traefik for HTTPS Access

If you run Traefik as your reverse proxy (and you should — see the Traefik Complete Guide), add labels to expose Uptime Kuma over HTTPS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    networks:
      - traefik_proxy
    volumes:
      - uptime-kuma:/app/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.uptime-kuma.rule=Host(`uptime.yourdomain.com`)"
      - "traefik.http.routers.uptime-kuma.entrypoints=websecure"
      - "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
      - "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"

networks:
  traefik_proxy:
    external: true

volumes:
  uptime-kuma:

With this configuration, https://uptime.yourdomain.com routes through Traefik with automatic Let’s Encrypt TLS. Remove the ports mapping — you don’t want direct port access once Traefik is handling it.

Node.js Direct Install

For environments where Docker is unavailable, Uptime Kuma runs directly on Node.js 18+:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Install Node.js 18+ if not present
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs

# Clone the repository
git clone https://github.com/louislam/uptime-kuma.git
cd uptime-kuma

# Install dependencies
npm run setup

# Start (foreground for testing)
node server/server.js

# For production, create a systemd service
sudo npm install pm2 -g
pm2 start server/server.js --name uptime-kuma
pm2 startup
pm2 save

Persistent Data and Backups

All Uptime Kuma data lives in /app/data inside the container. With the named volume approach, this is stored in Docker’s volume directory (typically /var/lib/docker/volumes/uptime-kuma/_data). What’s in there:

  • kuma.db — the SQLite database containing all monitor configurations, status history, and notification settings
  • data/upload/ — any images uploaded for status pages (logos, etc.)
  • data/ssl/ — certificates if you use Uptime Kuma’s built-in HTTPS (not needed with Traefik)

For easier backup management, use a bind mount instead of a named volume:

1
2
volumes:
  - /opt/uptime-kuma/data:/app/data

Then back up the directory with Restic or rsync:

1
restic backup /opt/uptime-kuma/data

Updating Uptime Kuma:

1
2
docker compose pull
docker compose up -d

That’s it. The image tag louislam/uptime-kuma:1 tracks the latest 1.x release. Uptime Kuma migrates the database schema automatically on startup.


Monitor Types: What You Can Watch

HTTP(s) Monitors

The workhorse. Sends an HTTP request to a URL and checks the response:

  • Status code check: expects 2xx by default, configurable to any code or range
  • Keyword monitoring: verifies the response body contains a specific string — useful for confirming an application rendered correctly rather than just returning a 200 OK from an error page
  • JSON path monitoring: evaluates a JSONPath expression against the response body: $.status == "healthy" — ideal for health check endpoints that return structured data
  • Certificate expiry check: can be enabled alongside any HTTPS monitor to alert before the TLS cert expires

TCP Port Monitor

Opens a TCP connection to a host:port and checks that it succeeds. Use this for:

  • Databases that don’t expose an HTTP interface (MySQL on 3306, PostgreSQL on 5432, Redis on 6379)
  • SSH availability (port 22)
  • Any custom service that listens on a TCP port

Note: TCP monitoring only confirms the port is open and accepting connections. It does not authenticate or verify the service is functional. For deeper checks, use a database-specific monitor or an HTTP health endpoint.

Ping (ICMP)

Sends ICMP echo requests and measures round-trip time. Best for:

  • Network infrastructure: routers, switches, access points
  • Hosts that don’t run HTTP services
  • Basic reachability checks for VMs and bare-metal servers

DNS Monitor

Resolves a DNS record and verifies the result matches an expected value. Supports A, AAAA, CNAME, MX, NS, SOA, and TXT record types. Useful for:

  • Confirming your dynamic DNS updates are propagating correctly
  • Detecting DNS hijacking or misconfiguration
  • Monitoring split-horizon DNS to ensure internal and external records are correct

Docker Container Monitor

Checks whether a named Docker container is in a running state. Requires mounting the Docker socket read-only:

1
2
3
volumes:
  - uptime-kuma:/app/data
  - /var/run/docker.sock:/var/run/docker.sock:ro

Once the socket is mounted, add a Docker container monitor and specify the container name. If the container exits unexpectedly, crashes into a restart loop, or is manually stopped, the monitor transitions to down and fires your configured notifications.

Certificate Expiry Monitor

Checks the TLS certificate on any domain and alerts when expiry is within a configurable threshold (default 14 days, recommended 30 days for homelab use). Add every domain you run — catching a certificate expiry before it happens prevents the embarrassing “your site isn’t secure” conversations.

Push Monitor (Dead Man’s Switch)

This is one of Uptime Kuma’s most valuable and underappreciated features. Instead of Uptime Kuma polling a target, you create a Push monitor and receive a unique ping URL. Your script or cron job calls that URL at the end of a successful run. If Uptime Kuma doesn’t receive a ping within the configured interval, it assumes the job failed and fires an alert.

Use cases:

  • Backup jobs: alert if the backup didn’t run
  • Database dump crons: alert if the dump failed or was skipped
  • Batch data processing: alert if the pipeline didn’t complete
  • Long-running background workers: alert if the worker died silently

The push URL looks like: https://uptime.yourdomain.com/api/push/AbCdEfGhIj?status=up&msg=OK

Database Monitors

Native connection checks for MySQL, MariaDB, PostgreSQL, MongoDB, and Redis. Configure with a connection string, and Uptime Kuma opens an actual database connection to verify the service is accepting queries — not just that the port is open.

Real Browser Monitor (Playwright)

Full browser-based monitoring using Playwright for scenarios that require JavaScript execution or login flows. This feature requires additional setup (the Playwright container or a compatible headless Chrome installation) but enables checking single-page applications, login pages, and multi-step user journeys.


Setting Up Your First Monitors

Adding an HTTP Monitor Step by Step

  1. Click Add New Monitor in the left sidebar
  2. Monitor Type: HTTP(s)
  3. Friendly Name: Something human-readable — “Gitea”, “Jellyfin”, “Vaultwarden”
  4. URL: The full URL including scheme — https://gitea.yourdomain.com
  5. Heartbeat Interval: 60 seconds is right for most homelab services. Drop to 30 seconds for critical services. Use 300 seconds for batch jobs.
  6. Retries: Set to 3. This means Uptime Kuma will attempt 3 consecutive failed checks before marking the monitor as down and firing notifications. This prevents alert storms from transient network blips.
  7. Heartbeat Retry Interval: 20 seconds. After a failed check, wait this long before the retry attempt.
  8. Request Timeout: 48 seconds default. Reduce to 10-15 seconds for services that should respond quickly.
  9. Accepted Status Codes: Default is 200-299. Some services return 301, 302, 401, or 204 as their healthy state — adjust accordingly.
  10. Certificate Expiry: Enable this toggle for any HTTPS URL to get automatic cert expiry warnings.
  11. Tags: Color-coded labels for filtering. Create tags like “critical”, “media”, “networking”, “backup”.

Click Save and the monitor appears in your dashboard, running its first check immediately.

Monitor Groups and Organization

As your monitor count grows past 10-15, organization becomes important. Uptime Kuma supports two levels of organization:

Tags: Filterable color-coded labels. A single monitor can have multiple tags. Use these for cross-cutting concerns: critical, external, homelab, docker, database.

Monitor Groups: Create a Group monitor and then drag other monitors into it. Groups display as collapsible sections in the dashboard and on status pages. Organize by service category, environment, or team:

  • Infrastructure (router, NAS, Proxmox, pfSense)
  • Media (Jellyfin, Sonarr, Radarr, Prowlarr)
  • Productivity (Nextcloud, Gitea, Vaultwarden, Paperless)
  • Databases (PostgreSQL, Redis, MariaDB)

Maintenance Windows

Maintenance windows suppress alerts during planned downtime. Navigate to Maintenance in the left sidebar:

  • One-time maintenance: Schedule a specific start and end time for a single planned event (server upgrades, migrations)
  • Recurring maintenance: Set a weekly or daily recurring window — useful for backup jobs that restart services, weekly reboots, or off-hours batch processing

During an active maintenance window, affected monitors display a wrench icon rather than their normal status, and notifications are suppressed. Monitors still check — you’ll see the status history accurately reflects the downtime — but your phone doesn’t ring.

Monitor Dependencies

Under Advanced settings on any monitor, you can set parent monitors. If a parent monitor is down, Uptime Kuma suppresses alerts for dependent monitors. This prevents alert floods when a single upstream failure cascades:

  • Your Traefik reverse proxy goes down → suppresses alerts for all services behind Traefik
  • Your NAS goes down → suppresses alerts for Jellyfin, Nextcloud, and other NAS-dependent services
  • Your VPN concentrator goes down → suppresses alerts for all VPN-routed monitors

Notification Integrations

Uptime Kuma ships with 90+ notification integrations. Here are the most useful ones for homelab and DevOps use, with setup instructions.

Telegram (Easiest for Personal Use)

  1. Open Telegram and message @BotFather
  2. Send /newbot, follow the prompts, receive your bot token
  3. Start a conversation with your new bot (this creates a chat)
  4. Get your chat ID: visit https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates and look for "chat":{"id":...}
  5. In Uptime Kuma: Settings → Notifications → Add Notification → Telegram
  6. Enter bot token and chat ID, click Test

Telegram is near-instant, works globally, and the bot API is reliable. This is the default recommendation for personal homelab setups.

Slack and Discord

Both use incoming webhooks:

Slack: Create an app at api.slack.com/apps, add the “Incoming Webhooks” feature, create a webhook for your chosen channel, copy the URL.

Discord: In your server, open channel settings → Integrations → Webhooks → New Webhook. Copy the webhook URL. In Uptime Kuma, use the Slack integration type but append /slack to the Discord webhook URL (Discord natively speaks the Slack message format): https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN/slack

Or use the dedicated Discord integration type for native Discord formatting with embed support.

Email (SMTP)

For Gmail, use an app password (not your main account password) with 2FA enabled:

SMTP Host:    smtp.gmail.com
SMTP Port:    587
Security:     STARTTLS
Username:     your@gmail.com
Password:     your-app-password
From Address: your@gmail.com
To Address:   alerts@yourdomain.com

For Fastmail, use smtp.fastmail.com:587 with your Fastmail app password. For a self-hosted SMTP relay (Postfix, Maddy), point to your internal SMTP server.

Gotify (Self-Hosted Push Notifications)

Gotify is a simple self-hosted push notification server with Android and web clients. Run it alongside Uptime Kuma:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
services:
  gotify:
    image: gotify/server
    container_name: gotify
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - gotify-data:/app/data
    environment:
      - GOTIFY_DEFAULTUSER_PASS=changeme

volumes:
  gotify-data:

In Gotify’s web UI, create an application and copy its token. In Uptime Kuma, add a Gotify notification with your server URL and application token.

ntfy.sh (Zero-Config Push Notifications)

ntfy is even simpler than Gotify. You can use the public ntfy.sh service or self-host it. No account required for public use:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Self-hosted ntfy
services:
  ntfy:
    image: binwiederhier/ntfy
    container_name: ntfy
    restart: unless-stopped
    ports:
      - "8090:80"
    command: serve
    volumes:
      - ntfy-data:/var/cache/ntfy
      - /etc/ntfy:/etc/ntfy

volumes:
  ntfy-data:

In Uptime Kuma, add an ntfy notification:

  • ntfy URL: https://ntfy.yourdomain.com (or https://ntfy.sh for the public service)
  • Topic: A unique string like homelab-alerts or uptime-kuma-yourname

Subscribe to the topic in the ntfy mobile app and you’ll receive push notifications.

Apprise

Apprise is a Python library that speaks 50+ notification services with a single unified URL format. If Uptime Kuma’s built-in integrations don’t cover your preferred service, check Apprise. One Apprise URL can fan out to multiple services simultaneously.

PagerDuty and OpsGenie

For professional on-call rotation and escalation, both PagerDuty and OpsGenie have dedicated integrations. Create a service in PagerDuty, generate an integration key, and paste it into Uptime Kuma. Alerts from Uptime Kuma will appear as incidents in your on-call tool and follow your escalation policies.

Webhooks

The catch-all integration. Uptime Kuma sends a JSON POST to any URL you specify when a monitor changes state. Use this to integrate with:

  • Custom internal alerting systems
  • n8n or Zapier automation flows
  • Home Assistant automations (blink a light when a service goes down)
  • Logging pipelines

Notification Best Practices

Always click Test before relying on a notification channel. A misconfigured bot token or wrong webhook URL will silently discard alerts.

Notification scheduling: In the notification settings, you can configure time windows for when alerts are sent. For homelab services that are “nice to have” but not critical, set quiet hours so you don’t get paged at 3am for a media server that went down. For genuinely critical services (VPN, router, primary services you use for work), leave notifications always-on.

Multiple channels: Configure a fast channel (Telegram/ntfy) for immediate awareness and email as a backup/audit trail. That way if one channel has issues, you still get the alert.


Status Pages: Your Public Service Dashboard

A status page is a public-facing (or password-protected) web page that shows the current and historical health of your services. Uptime Kuma can generate as many status pages as you want, each showing a curated subset of your monitors.

Creating a Status Page

  1. Navigate to Status Page in the left sidebar
  2. Click New Status Page
  3. Set a slug — this becomes the URL path: status
  4. Configure the page title and description
  5. Add monitors to display by clicking Add Group and dragging monitors into groups
  6. Customize the logo (upload your own), theme color, and whether the page is public or password-protected
  7. Click Save

Access your status page at http://your-host:3001/status/your-slug. If you’ve configured a custom domain through Traefik or Nginx, the status page is available there too.

Status Page Features

90-day uptime history: Each monitor on the status page displays a bar chart of daily uptime over the past 90 days. Green bars are fully up, yellow are degraded, red are outages.

Current status indicators: Color-coded indicators (operational, degraded, major outage) for each service. The indicator is derived from the current monitor status.

Incident history: You can manually post incidents to the status page — useful for communicating outages, planned maintenance, and resolutions to users or team members.

Maintenance announcements: Active maintenance windows you’ve created in Uptime Kuma automatically appear on the status page with their scheduled duration.

Custom Domain Setup

Point status.yourdomain.com at Uptime Kuma using Traefik labels:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
labels:
  - "traefik.enable=true"
  # Main Uptime Kuma UI
  - "traefik.http.routers.uptime-kuma.rule=Host(`uptime.yourdomain.com`)"
  - "traefik.http.routers.uptime-kuma.entrypoints=websecure"
  - "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
  # Status page on its own domain
  - "traefik.http.routers.status-page.rule=Host(`status.yourdomain.com`)"
  - "traefik.http.routers.status-page.entrypoints=websecure"
  - "traefik.http.routers.status-page.tls.certresolver=letsencrypt"
  - "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"

Uptime Kuma serves both the admin UI and status pages from the same port. Traefik routes both hostnames to the same container.

Status Badges

Uptime Kuma generates embeddable status badges for any monitor. Find the badge URL in the monitor’s settings or via the API:

# Uptime percentage badge
![Uptime](https://status.example.com/api/badge/1/uptime)

# Current status badge
![Status](https://status.example.com/api/badge/1/status)

# Response time badge
![Response Time](https://status.example.com/api/badge/1/avg-response)

Embed these in your GitHub README, internal wikis, or project documentation pages. The badge number (/1/) is the monitor ID, visible in the URL when you open a monitor in the Uptime Kuma UI.


Advanced Monitoring Patterns

Push Monitor for Backup Verification

The push monitor is the single most underused feature in uptime monitoring tools. Here’s a concrete implementation with a Restic backup script:

First, create a Push monitor in Uptime Kuma:

  1. Add New Monitor → Push
  2. Name it “Daily Restic Backup — nas-data”
  3. Set the heartbeat interval to 1440 minutes (24 hours) plus a buffer — 1500 or 1560 minutes works well
  4. Copy the generated push URL

Now add the push call at the end of your backup script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env bash
set -euo pipefail

# Your push URL from Uptime Kuma
PUSH_URL="https://uptime.yourdomain.com/api/push/AbCdEfGhIj"

# Restic configuration
export RESTIC_REPOSITORY="s3:s3.amazonaws.com/your-backup-bucket/nas-data"
export RESTIC_PASSWORD_FILE="/etc/restic/password"
export AWS_ACCESS_KEY_ID="your-key-id"
export AWS_SECRET_ACCESS_KEY="your-secret-key"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

log "Starting Restic backup..."

# Run the backup
if restic backup /data/important --verbose 2>&1; then
    log "Backup completed successfully"

    # Prune old snapshots
    restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

    # Notify Uptime Kuma: backup succeeded
    curl -fsS "${PUSH_URL}?status=up&msg=Backup+OK" > /dev/null
    log "Uptime Kuma notified: success"
else
    log "ERROR: Backup failed"
    # Notify Uptime Kuma: backup explicitly failed
    curl -fsS "${PUSH_URL}?status=down&msg=Backup+FAILED" > /dev/null || true
    exit 1
fi

Schedule this with cron or a systemd timer. If the script runs and fails before reaching the curl line, or if it doesn’t run at all (cron misconfiguration, machine offline, script killed), Uptime Kuma will fire an alert after 1500 minutes of silence. You get alerted both for explicit failures and for jobs that silently stopped running.

Certificate Expiry Monitoring for All Domains

Add a dedicated Certificate Expiry monitor for every domain you own, even if you already have an HTTP monitor for the service:

  • Set threshold to 30 days — gives you time to renew before the certificate expires
  • Include wildcard cert domains like *.yourdomain.com
  • Monitor externally-issued certificates for services you don’t control (vendor portals, partner APIs)

The separate cert expiry monitor catches cases where your ACME renewal automation has silently broken, or where a certificate was replaced with one from a different CA that your internal systems don’t trust.

Docker Container Monitoring

Mount the Docker socket and add container monitors for every critical container:

1
2
3
volumes:
  - uptime-kuma:/app/data
  - /var/run/docker.sock:/var/run/docker.sock:ro

Configure monitors for containers that don’t expose an HTTP health endpoint — database containers, background workers, queue processors. Pair container monitoring with HTTP monitoring for full coverage: the HTTP monitor verifies the service responds correctly, the container monitor catches crash-looping or zombie containers that appear to be running but are stuck.

Database Health Checks

Configure database monitors with actual connection strings:

# PostgreSQL
postgresql://username:password@postgres-host:5432/database_name

# MySQL / MariaDB
mysql://username:password@mysql-host:3306/database_name

# Redis (no auth)
redis://redis-host:6379

# Redis (with auth)
redis://:password@redis-host:6379

These monitors open a real connection and execute a simple query to verify the database is accepting connections and responding to queries — a meaningfully stronger check than a TCP port monitor.

Internal Service Monitoring

Because Uptime Kuma runs inside your network, it can reach services that are never exposed to the public internet:

  • Proxmox web UI: https://proxmox.internal:8006
  • pfSense/OPNsense: https://pfsense.internal
  • Managed switch web UI: http://switch.internal
  • Internal Gitea: https://gitea.internal
  • Home Assistant: http://homeassistant.local:8123
  • Internal APIs: anything reachable by hostname or IP on your LAN

Set up a VPN monitor to check your WireGuard or OpenVPN endpoint’s reachability from inside the network — if the VPN service crashes, the monitor catches it before you try to connect from outside.


Uptime Kuma in a Homelab Stack

What to Monitor in a Typical Homelab

Here’s a comprehensive monitoring checklist for a standard homelab:

Infrastructure layer:

  • Router/firewall (ping by IP)
  • NAS (ping + HTTP for the web management UI)
  • Proxmox VE (HTTPS status code — https://proxmox:8006)
  • Network switch (ping or HTTP web UI)
  • UPS management card (HTTP or ping)

Network services:

  • External DNS resolution (DNS monitor — verify your public domain resolves correctly)
  • Internal DNS resolver (DNS monitor — verify internal hostnames resolve)
  • Your WAN IP / DynDNS hostname (DNS or HTTP monitor)

Reverse proxy and certificates:

  • Traefik dashboard (HTTP — https://traefik.yourdomain.com/dashboard/)
  • TLS certificate expiry for every public domain (Certificate Expiry monitors)
  • TLS certificate for your wildcard cert if using one

Docker services (HTTP keyword or status check):

  • Gitea / Forgejo
  • Nextcloud
  • Vaultwarden / Bitwarden
  • Home Assistant
  • Jellyfin / Plex
  • Paperless-ngx
  • Grafana
  • Loki / Promtail stack
  • Any custom applications

Databases:

  • PostgreSQL (database monitor)
  • MySQL/MariaDB (database monitor)
  • Redis (database monitor)

Backup verification (push monitors):

  • Daily NAS backup job
  • Database dump cron (one push monitor per database)
  • Config/secrets backup job
  • Off-site backup sync job

External availability (if you serve public traffic):

  • Your public domain landing page (HTTP from Uptime Kuma — confirms routing, DNS, and TLS all work end-to-end)

Complete Monitoring Stack Docker Compose

A production-ready Compose file combining Uptime Kuma with Gotify for push notifications, all behind Traefik:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    networks:
      - traefik_proxy
    volumes:
      - uptime-kuma:/app/data
      - /var/run/docker.sock:/var/run/docker.sock:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.uptime-kuma.rule=Host(`uptime.yourdomain.com`)"
      - "traefik.http.routers.uptime-kuma.entrypoints=websecure"
      - "traefik.http.routers.uptime-kuma.tls.certresolver=letsencrypt"
      - "traefik.http.routers.status-page.rule=Host(`status.yourdomain.com`)"
      - "traefik.http.routers.status-page.entrypoints=websecure"
      - "traefik.http.routers.status-page.tls.certresolver=letsencrypt"
      - "traefik.http.services.uptime-kuma.loadbalancer.server.port=3001"

  gotify:
    image: gotify/server
    container_name: gotify
    restart: unless-stopped
    networks:
      - traefik_proxy
    volumes:
      - gotify-data:/app/data
    environment:
      - GOTIFY_DEFAULTUSER_NAME=admin
      - GOTIFY_DEFAULTUSER_PASS=changeme-use-secrets
      - GOTIFY_SERVER_PORT=80
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gotify.rule=Host(`push.yourdomain.com`)"
      - "traefik.http.routers.gotify.entrypoints=websecure"
      - "traefik.http.routers.gotify.tls.certresolver=letsencrypt"
      - "traefik.http.services.gotify.loadbalancer.server.port=80"

networks:
  traefik_proxy:
    external: true

volumes:
  uptime-kuma:
  gotify-data:

After deploying:

  1. Log into Gotify at https://push.yourdomain.com, create an application, copy the token
  2. Log into Uptime Kuma at https://uptime.yourdomain.com, add a Gotify notification using https://push.yourdomain.com as the server URL
  3. Test the notification, then start adding monitors

How Uptime Kuma Complements Prometheus and Grafana

Uptime Kuma and Prometheus are not competing tools — they occupy different layers of the observability stack.

Uptime Kuma handles blackbox monitoring: it knows whether your service is up or down, how fast it responds, and whether the TLS certificate is valid. It doesn’t know why the service is slow or what’s happening inside it.

Prometheus handles whitebox monitoring: it scrapes internal metrics from running services — CPU, memory, request rate, error rate, queue depth, database connection pool utilization. It knows the internals but relies on the service being up enough to expose its /metrics endpoint.

The combination is powerful: Uptime Kuma catches the “is it up?” question immediately with simple configuration. Prometheus answers “why is it slow?” with rich query capability. Run both. Use Uptime Kuma for your status page and initial alerting. Use Prometheus alerting rules for capacity and performance issues. Use Grafana to visualize both.


Backup and Data Management

What’s in /app/data

/app/data/
├── kuma.db           # SQLite database — all config, monitor history, notification settings
├── kuma.db-shm       # SQLite shared memory file (safe to exclude from backup while running)
├── kuma.db-wal       # SQLite write-ahead log (include in backup or checkpoint first)
└── upload/           # Uploaded images for status pages

The SQLite database is the single source of truth. Back it up and you can restore your entire Uptime Kuma installation.

Backup Approaches

Bind mount with Restic:

1
2
volumes:
  - /opt/uptime-kuma/data:/app/data
1
2
3
4
5
# /etc/cron.daily/uptime-kuma-backup
#!/bin/bash
restic -r s3:s3.amazonaws.com/your-bucket/uptime-kuma backup /opt/uptime-kuma/data \
  --password-file /etc/restic/password \
  --tag uptime-kuma

Export via the UI: Settings → Backup → Export. This generates a JSON export of your monitor configurations (not the history). Useful for migrating between instances or version-controlling your monitor config.

SQLite dump for a portable copy:

1
sqlite3 /opt/uptime-kuma/data/kuma.db ".dump" > uptime-kuma-backup-$(date +%Y%m%d).sql

Restoring

  1. Stop the container: docker compose down
  2. Copy your backed-up data directory to the bind mount location (or restore the volume)
  3. Start the container: docker compose up -d

Uptime Kuma will start with your full configuration, all monitor history, and all notification settings intact.

Querying the Database Directly

The SQLite database is readable with sqlite3 for custom queries:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
sqlite3 /opt/uptime-kuma/data/kuma.db

# List all monitors
SELECT id, name, type, url, active FROM monitor;

# Check recent downtime events
SELECT m.name, h.status, datetime(h.time/1000, 'unixepoch') as time, h.msg
FROM heartbeat h
JOIN monitor m ON h.monitor_id = m.id
WHERE h.status = 0
ORDER BY h.time DESC
LIMIT 20;

Tips, Tricks, and Gotchas

Interval tuning matters. 60 seconds is the right default for most homelab services. Use 30 seconds for critical infrastructure (router, DNS, reverse proxy). Use 300 seconds for batch jobs and services that are intentionally offline sometimes. Over-polling wastes resources and generates excessive history without providing additional value.

Set retries to 3. A single failed check is not a reliable signal — it might be a transient DNS hiccup, a momentary high-load spike, or a network packet drop. Three consecutive failures indicates a genuine problem worth waking up for.

The flapping problem. If a monitor constantly oscillates between up and down, you have two options: increase the retry count and interval to reduce sensitivity, or investigate the service. Flapping monitors cause alert fatigue and eventually get ignored. Fix the underlying instability rather than suppressing the symptom.

Accepted status codes need thought. Some services return non-200 codes for legitimate healthy states. APIs that require authentication return 401 for unauthenticated requests — which is correct behavior, not a failure. APIs returning 204 for successful operations, 301 for canonical redirects, or 202 for accepted async operations are all healthy. Configure accepted status codes based on what your specific endpoint actually returns when healthy.

Request headers for authenticated endpoints. Under Advanced settings, you can add arbitrary HTTP headers to your monitor’s request. Add an Authorization: Bearer your-token header to monitor protected API health endpoints without exposing the full service publicly.

HTTP to HTTPS redirects. Uptime Kuma follows redirects by default. If you configure a monitor with http:// but the service redirects to https://, the monitor follows the redirect and checks the final destination. This is usually what you want, but be aware that if your redirect is broken, the monitor will catch the redirect failure rather than the SSL failure.

Resource usage. A fully configured Uptime Kuma instance with 50 monitors and 90-second intervals uses approximately 100-150MB of RAM and negligible CPU. It’s one of the lightest services you’ll run. The SQLite database grows at roughly 1-5MB per month depending on monitor count and check frequency — disk usage is not a concern for years.

API access. Uptime Kuma exposes an unofficial REST API (and an official API in v2) for automation. You can create monitors programmatically, query status, and trigger pushes via the API. The push URL is also accessible without authentication, which is intentional — it needs to be callable from scripts without credential management.

Browser-based monitoring setup. The Playwright/real browser monitor type requires either the louislam/uptime-kuma:1-alpine image built with Chromium, or a separate Playwright service. For most homelab use cases, HTTP keyword monitoring is sufficient and much lighter. Reserve browser monitoring for scenarios that genuinely require JavaScript execution.


Wrapping Up

Uptime Kuma earns its place in every homelab and small-to-medium production environment. It’s the answer to “I don’t know when my services go down” — simple to deploy, visually clear, and genuinely useful without requiring deep configuration or ongoing maintenance.

The sweet spot is clear: deploy it in 15 minutes, add monitors for everything you run, wire up a Telegram bot or ntfy topic, set up push monitors for your backup jobs, and create a status page for anything user-facing. The immediate payoff is knowing when something breaks before users complain. The longer-term payoff is the historical uptime data — 90 days of uptime bars that tell you which services are reliable and which ones need attention.

For deeper observability — understanding why a service was slow, what resources it consumed, what errors it logged — pair Uptime Kuma with Prometheus, Grafana, and Loki. The three tools cover blackbox availability, internal metrics, and log analysis respectively. Together they answer every important question about your infrastructure. Separately, each one answers a specific question well.

Start with Uptime Kuma. It’s the easiest observability win you’ll get today.

Comments