Uptime Kuma: Self-Hosted Monitoring Guide
Professional uptime monitoring with 95+ notification channels. Track websites, APIs, containers, and more.
Introduction
In the world of self-hosted infrastructure, there’s a fundamental problem every homelab administrator faces: when your services go down, how do you know? More importantly, if your entire homelab goes offline, how can you be alerted when your monitoring system is down too?
Uptime Kuma solves these challenges elegantly. It’s an open-source, self-hosted monitoring solution that tracks the availability and performance of your websites, APIs, containers, and network services. With 76,000+ GitHub stars, 127+ million Docker pulls, and support for 95+ notification channels, Uptime Kuma has become the de facto standard for homelab monitoring.
What you’ll learn:
- Deploy Uptime Kuma with Docker Compose in under 15 minutes
- Create monitors for HTTP, TCP, Docker containers, DNS, and more
- Configure multi-channel notifications (Discord, Telegram, Email, Slack)
- Implement AI-powered alert analysis and automation
- Set up external monitoring to survive ISP outages
- Optimize performance and reduce alert fatigue
Prerequisites:
- Docker and Docker Compose installed (v20.10+ and v1.29+)
- Basic understanding of Docker containers
- 500MB+ free disk space (5-10GB recommended)
- A notification service account (Discord, Telegram, or email)
Why Uptime Kuma?
The Homelab Monitoring Challenge
Traditional monitoring solutions present several challenges:
- Cloud-based services (Uptime Robot, Pingdom) store your infrastructure data externally
- Enterprise tools (Nagios, Zabbix) have steep learning curves and complex configurations
- Limited customization in many SaaS offerings
- Recurring costs for adequate monitoring ($8-50+ monthly)
Uptime Kuma addresses all these concerns:
- Zero cloud dependency: Everything stays on your infrastructure
- Beginner-friendly: Web-based UI, no configuration files required
- Powerful features: Multiple monitor types, extensive notifications, public status pages
- Free and open-source: No licensing costs, active community development
- Lightweight: Runs on Raspberry Pi 4 with 12+ monitors
Key Features at a Glance
| Feature | Details |
|---|---|
| Monitor Types | HTTP/HTTPS, TCP Port, Ping, DNS, Docker, Push (Dead Man’s Switch), WebSocket |
| Check Interval | 20 seconds minimum, configurable up to hours |
| Notifications | 95+ channels: Discord, Slack, Telegram, Email, Teams, PagerDuty, Twilio, SMS, and more |
| Status Pages | Public status pages with customizable branding and badges |
| Authentication | Admin account with 2FA support, API keys for automation |
| Database | SQLite (default) or PostgreSQL for enterprise scale |
| Deployment | Docker, Docker Compose, or native installation |
Understanding Monitoring Fundamentals
Poll-Based vs. Push-Based Monitoring
Uptime Kuma supports two monitoring paradigms:
Poll-Based (Active) Monitoring:
- Uptime Kuma actively checks your services at regular intervals
- Best for: Websites, APIs, databases, always-available services
- Example: HTTP check every 60 seconds to verify website responds with status 200
Push-Based (Passive) Monitoring:
- Your services report their status to Uptime Kuma (Dead Man’s Switch)
- Uptime Kuma alerts if reports stop arriving
- Best for: Cron jobs, backup tasks, scheduled scripts
- Example: Backup script sends “success” ping after completion; alert if no ping within 24 hours
Monitor Types Explained
| Monitor Type | Use Case | Check Time | Example Scenario |
|---|---|---|---|
| HTTP(s) | Websites, REST APIs | < 1 sec | Verify homepage returns 200 OK |
| TCP Port | Database servers, SSH | < 1 sec | Check MySQL port 3306 is accepting connections |
| Ping | Server availability | < 1 sec | Verify router is responding to ICMP |
| DNS Record | Name resolution | < 2 sec | Confirm Pi-hole resolves example.com correctly |
| Docker | Container health | ~5 sec | Monitor PostgreSQL container is running |
| Push | Scheduled tasks | N/A | Backup job completion notification |
| WebSocket | Real-time services | < 1 sec | Chat server WebSocket connection |
Installation: Docker Compose Method
Step 1: Create Project Directory
mkdir -p ~/uptime-kuma
cd ~/uptime-kuma Step 2: Create docker-compose.yml
This configuration provides a production-ready Uptime Kuma deployment:
version: '3.8'
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ./data:/app/data
# Uncomment to enable Docker container monitoring:
# - /var/run/docker.sock:/var/run/docker.sock
ports:
- "3001:3001"
restart: always
environment:
- TZ=America/New_York # Change to your timezone
# Optional: Enable iframe embedding for status pages
# - UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN=true
networks:
- monitoring
networks:
monitoring:
driver: bridge Configuration Notes:
image: louislam/uptime-kuma:1- Stable version 1.x branch (use:2for latest v2.x)./data:/app/data- Persistent storage for database and configurationTZenvironment variable ensures timestamps match your local time- Docker socket binding enables container monitoring (commented out by default for security)
Step 3: Start the Container
docker compose up -d Step 4: Verify Installation
# Check container is running
docker ps | grep uptime-kuma
# View startup logs
docker logs uptime-kuma
# Check resource usage
docker stats uptime-kuma --no-stream Expected output:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM %
abc123def456 uptime-kuma 0.5% 120MiB / 2GiB 6% Step 5: Access Web Interface
Navigate to: http://YOUR_SERVER_IP:3001
Initial setup wizard:
- Create admin username and password (save in password manager)
- Select preferred language
- Configure timezone (matches
TZenvironment variable) - Complete setup
Creating Your First Monitors
HTTP/HTTPS Website Monitoring
The most common monitoring type for web services.
Scenario: Monitor your personal website or blog
Click “Add New Monitor”
Configure basic settings:
- Monitor Type: HTTP(s)
- Friendly Name: “My Website”
- URL:
https://example.com - Heartbeat Interval: 60 seconds
- Timeout: 10 seconds
- Max Retries: 2
Advanced options (expand section):
- Expected Status Code: 200 (or 301 for redirects)
- Expected Keyword: Enter text that should appear on page (e.g., “Welcome”)
- HTTP Headers: Add custom headers if needed
Authorization: Bearer YOUR_TOKEN User-Agent: Uptime-Kuma/1.0
Click “Save”
What happens: Uptime Kuma sends HTTP GET request every 60 seconds, verifies response is 200 OK, optionally checks for keyword, and measures response time.
TCP Port Monitoring
Monitor any service listening on a TCP port.
Scenario: Monitor PostgreSQL database availability
Click “Add New Monitor”
Configure:
- Monitor Type: TCP Port
- Friendly Name: “PostgreSQL Database”
- Hostname:
192.168.1.100(ordatabase.local) - Port:
5432 - Heartbeat Interval: 300 seconds (5 minutes)
Click “Save”
Common TCP port monitors:
- MySQL: port 3306
- PostgreSQL: port 5432
- Redis: port 6379
- SSH: port 22
- SMTP: port 25 or 587
Docker Container Monitoring
Monitor Docker container health directly.
Prerequisites: Enable Docker socket access
Modified docker-compose.yml:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ./data:/app/data
- /var/run/docker.sock:/var/run/docker.sock # Add this line
ports:
- "3001:3001"
restart: always Apply changes:
docker compose down
docker compose up -d Create Docker monitor:
Click “Add New Monitor”
Configure:
- Monitor Type: Docker Container
- Friendly Name: “PostgreSQL Container”
- Docker Container: Select from dropdown (e.g.,
postgres) - Heartbeat Interval: 60 seconds
Click “Save”
How it works: Uptime Kuma queries Docker API to check if container state is “running”. If container stops, restarts, or becomes unhealthy, monitor triggers alert.
DNS Record Monitoring
Verify DNS resolution is working correctly.
Scenario: Monitor Pi-hole DNS server
Click “Add New Monitor”
Configure:
- Monitor Type: DNS
- Friendly Name: “Pi-hole DNS Server”
- Hostname:
example.com - DNS Server:
192.168.1.5(your Pi-hole IP) - Record Type: A
- Expected Value:
93.184.216.34(example.com’s IP) - Heartbeat Interval: 300 seconds
Click “Save”
Use cases:
- Verify internal DNS server (Pi-hole, AdGuard Home) is resolving correctly
- Monitor DNS propagation after domain changes
- Detect DNS hijacking or misconfiguration
Configuring Notifications
Monitoring without notifications is like having a smoke detector with no alarm. Let’s configure multi-channel alerting.
Discord Integration (Recommended for Beginners)
Discord webhooks are the easiest notification method for homelabbers.
Create Discord webhook:
- Open Discord server (or create new one)
- Right-click server → Create Channel → Name:
#uptime-alerts - Click channel name → Integrations → Webhooks
- Click “New Webhook”
- Name: “Uptime Kuma”
- Copy webhook URL (looks like:
https://discord.com/api/webhooks/123456789/abcdef...)
Configure in Uptime Kuma:
Navigate to Settings → Notifications
Click “Add Notification”
Configure:
- Notification Type: Discord
- Friendly Name: “Discord Alerts”
- Discord Webhook URL: Paste URL from step 6 above
- Bot Display Name: “Uptime Monitor” (optional)
Click “Test” to verify
Check Discord channel for test message
Click “Save”
Example Discord notification:
🔴 DOWN - API Gateway
Status changed from UP to DOWN
Duration: 0 seconds
Time: 2025-12-16 14:23:45
Response: Connection refused Telegram Bot Setup
Mobile-first notifications with group chat support.
Create Telegram bot:
- Open Telegram app
- Search for
@BotFather - Send
/startcommand - Send
/newbotcommand - Follow prompts:
- Bot name: “Homelab Monitor”
- Bot username: “homelab_monitor_bot” (must be unique)
- Save API token:
123456789:ABCdefGHIjklMNOpqrsTUVwxyz
Get Chat ID:
- Create Telegram group for alerts (or use direct message)
- Add your bot to the group
- Send any message in the group
- Visit:
https://api.telegram.org/bot123456789:ABCdefGHIjklMNOpqrsTUVwxyz/getUpdates(Replace with your bot token) - Find JSON response, locate:
"chat":{"id":-987654321} - Save Chat ID:
-987654321
Configure in Uptime Kuma:
Settings → Notifications → Add Notification
Configure:
- Type: Telegram
- Friendly Name: “Telegram Alerts”
- Bot Token: Paste token from BotFather
- Chat ID: Paste chat ID from getUpdates
Click “Test” → Check Telegram for message
Click “Save”
Email Notifications (SMTP)
Universal notification method, works with any email provider.
Gmail configuration:
Generate App Password (required for 2FA accounts):
- Visit: https://myaccount.google.com/apppasswords
- Select: Mail + Windows Computer
- Click “Generate”
- Copy 16-character password:
abcd efgh ijkl mnop
Configure in Uptime Kuma:
- Settings → Notifications → Add Notification
- Type: Email (SMTP)
- Friendly Name: “Email Alerts”
- SMTP Host:
smtp.gmail.com - SMTP Port:
587 - Security: STARTTLS
- Username:
[email protected] - Password: Paste app password (remove spaces)
- From Email:
[email protected](or your Gmail) - To Email:
[email protected]
Click “Test” → Check email inbox
Click “Save”
Alternative email providers:
- Mailgun: Free tier, SMTP at
smtp.mailgun.org:587 - SendGrid: Free 100 emails/day, SMTP at
smtp.sendgrid.net:587 - Brevo (Sendinblue): Generous free tier
Assigning Notifications to Monitors
Once notifications are configured, assign them to monitors:
Open existing monitor or create new one
Scroll to “Notifications” section
Check boxes for which notification channels to use
Recommended strategy:
- Critical services: All channels (Discord + Telegram + Email)
- Important services: 2 channels (Discord + Telegram)
- Low-priority services: 1 channel (Discord only)
Click “Save”
Pro tip: Create notification groups for different service tiers to avoid alert fatigue.
Creating Public Status Pages
Share your infrastructure status with team members or customers.
Basic Status Page Setup
Navigate to Status Pages section
Click “Add Status Page”
Configure:
- Title: “Infrastructure Status”
- Description: “Real-time availability of critical services”
- Slug:
status(becomes URL:/status/status)
Add monitors to page:
- Click “Edit” on status page
- Create groups: “Network”, “Storage”, “Applications”
- Drag monitors into appropriate groups
- Reorder as needed
Click “Publish”
Copy public URL to share
Advanced Customization
Theme options:
- Primary Color: Match your brand
- Logo Upload: Custom logo in header
- Custom CSS: Advanced styling
Example custom CSS:
/* Custom status page styling */
.status-page-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2rem;
}
.uptime-badge {
font-weight: bold;
border-radius: 8px;
} Embedding Status Badges
Generate embeddable badges for GitHub README or external websites.
Badge types:
- Status badge: Current UP/DOWN status
- Ping badge: Average response time
- Uptime badge: Uptime percentage for time period
- Certificate badge: SSL certificate expiration
Badge URLs:
Status: https://uptime.example.com/api/badge/1/status
Ping (24h): https://uptime.example.com/api/badge/1/ping/24
Uptime (30d): https://uptime.example.com/api/badge/1/uptime/720 Markdown embedding:
# My Project

 Security Hardening
Enable Two-Factor Authentication (2FA)
Protect your monitoring dashboard with 2FA.
Setup steps:
Settings → Advanced → Two-Factor Authentication
Click “Enable 2FA”
Scan QR code with authenticator app:
- Google Authenticator
- Authy
- Microsoft Authenticator
- 1Password (has built-in TOTP)
Enter 6-digit code to confirm
Save backup codes in password manager (single-use recovery codes)
Logout and login to verify 2FA is working
Important: If you lose 2FA access, you’ll need backup codes to regain access.
Reverse Proxy with HTTPS
Expose Uptime Kuma securely with SSL/TLS encryption.
Nginx configuration:
# /etc/nginx/sites-available/uptime-kuma
server {
server_name uptime.example.com;
location / {
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
# WebSocket support (CRITICAL for real-time updates)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Standard proxy headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/uptime.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/uptime.example.com/privkey.pem;
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name uptime.example.com;
return 301 https://$server_name$request_uri;
} Enable configuration:
sudo ln -s /etc/nginx/sites-available/uptime-kuma /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx Caddy configuration (simpler alternative):
# /etc/caddy/Caddyfile
uptime.example.com {
reverse_proxy localhost:3001 {
header_up X-Forwarded-Proto https
}
} Enable configuration:
sudo systemctl reload caddy After configuring reverse proxy:
- In Uptime Kuma: Settings → Reverse Proxy
- Enable “Trust Proxy Headers”
- Save
AI Automation & Intelligent Monitoring
Enhance Uptime Kuma with AI-powered automation for smarter alerts and self-healing infrastructure.
1. Intelligent Alert Aggregation
Problem: Multiple services fail simultaneously, creating alert flood.
Solution: Use AI to identify root cause and suppress secondary alerts.
Implementation:
# alert_analyzer.py
from anthropic import Anthropic
import json
client = Anthropic(api_key="your-api-key")
def analyze_alert_cascade(alerts: list) -> dict:
"""
Analyze multiple alerts to identify root cause using Claude.
Args:
alerts: List of alert dictionaries with service, status, timestamp
Returns:
Analysis with root cause and recommended actions
"""
alert_summary = "\n".join([
f"- {alert['service']}: {alert['status']} at {alert['timestamp']}"
for alert in alerts
])
response = client.messages.create(
model="claude-opus-4-5-20251101",
max_tokens=500,
messages=[{
"role": "user",
"content": f"""
Analyze these infrastructure alerts and identify the root cause:
{alert_summary}
Service dependencies:
- Website depends on API Gateway and CDN
- API Gateway depends on Database and Cache
- Cache depends on Database
- All services depend on Network
Provide:
1. Most likely root cause
2. Which alerts are secondary/cascading failures
3. Recommended first action to resolve
Format as JSON.
"""
}]
)
return json.loads(response.content[0].text)
# Example usage
alerts = [
{"service": "Website", "status": "DOWN", "timestamp": "2025-12-16 10:23:45"},
{"service": "API Gateway", "status": "DOWN", "timestamp": "2025-12-16 10:23:47"},
{"service": "Database", "status": "DOWN", "timestamp": "2025-12-16 10:23:42"}
]
analysis = analyze_alert_cascade(alerts)
print(f"Root Cause: {analysis['root_cause']}")
print(f"Action: {analysis['recommended_action']}") Example output:
{
"root_cause": "Database server failure",
"secondary_alerts": ["API Gateway", "Website"],
"recommended_action": "Investigate Database container logs and restart if needed",
"estimated_impact": "All user-facing services unavailable"
} 2. Predictive Failure Detection
Concept: Analyze response time trends to predict failures before they occur.
Implementation:
# predictive_monitoring.py
import numpy as np
from sklearn.linear_model import LinearRegression
from uptime_kuma_api import UptimeKumaApi
api = UptimeKumaApi('http://localhost:3001')
api.login('admin', 'your-password')
def predict_service_failure(monitor_id: int, threshold_ms: int = 10000) -> dict:
"""
Predict when service will exceed timeout threshold based on trends.
Args:
monitor_id: Uptime Kuma monitor ID
threshold_ms: Timeout threshold in milliseconds
Returns:
Prediction with days until failure and confidence
"""
# Get last 100 heartbeats
heartbeats = api.get_monitor_heartbeats(monitor_id=monitor_id, limit=100)
response_times = [hb['ping'] for hb in heartbeats if hb['ping']]
if len(response_times) < 20:
return {"status": "insufficient_data"}
# Fit linear regression to trend
X = np.array(range(len(response_times))).reshape(-1, 1)
y = np.array(response_times)
model = LinearRegression()
model.fit(X, y)
# Calculate when response time will exceed threshold
current_time = response_times[-1]
trend_per_check = model.coef_[0]
if trend_per_check <= 0:
return {"status": "improving", "trend": "Response times decreasing"}
# Calculate days until failure (assuming 1 check per minute)
checks_until_failure = (threshold_ms - current_time) / trend_per_check
days_until_failure = checks_until_failure / 1440 # 1440 minutes per day
if days_until_failure < 0:
return {
"status": "CRITICAL",
"prediction": "Service already exceeding threshold",
"current_response_time": current_time,
"threshold": threshold_ms
}
elif days_until_failure < 7:
return {
"status": "WARNING",
"prediction": f"Service will timeout in {days_until_failure:.1f} days",
"current_response_time": current_time,
"trend_per_day": trend_per_check * 1440,
"recommended_action": "Investigate performance degradation"
}
else:
return {
"status": "OK",
"prediction": f"Service healthy, {days_until_failure:.0f} days headroom"
}
# Example usage
prediction = predict_service_failure(monitor_id=1)
print(f"Status: {prediction['status']}")
print(f"Prediction: {prediction.get('prediction', 'No prediction available')}")
api.disconnect() 3. Automated Self-Healing
Concept: Automatically restart failing containers via webhook.
Implementation:
# auto_healing_service.py
from flask import Flask, request, jsonify
import docker
import logging
app = Flask(__name__)
client = docker.from_env()
logging.basicConfig(level=logging.INFO)
@app.route('/webhook/restart-container', methods=['POST'])
def restart_container():
"""
Webhook endpoint to automatically restart failing containers.
Uptime Kuma webhook configuration:
- URL: http://your-server:5000/webhook/restart-container
- Method: POST
- Trigger: On service DOWN
"""
data = request.json
# Extract service name from Uptime Kuma webhook payload
service_name = data.get('monitor', {}).get('name', '')
# Map monitor names to container names
container_mapping = {
"PostgreSQL Database": "postgres",
"Redis Cache": "redis",
"API Gateway": "api-gateway"
}
container_name = container_mapping.get(service_name)
if not container_name:
return jsonify({
"status": "error",
"message": f"Unknown service: {service_name}"
}), 400
try:
container = client.containers.get(container_name)
# Check if container is actually down
if container.status != "running":
logging.info(f"Restarting container: {container_name}")
container.restart()
return jsonify({
"status": "success",
"action": "restarted",
"container": container_name
})
else:
return jsonify({
"status": "no_action",
"message": "Container already running"
})
except docker.errors.NotFound:
return jsonify({
"status": "error",
"message": f"Container not found: {container_name}"
}), 404
except Exception as e:
logging.error(f"Error restarting container: {e}")
return jsonify({
"status": "error",
"message": str(e)
}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000) Deployment:
# docker-compose.yml (add to existing configuration)
services:
auto-healer:
build: .
container_name: uptime-auto-healer
ports:
- "5000:5000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
restart: always Configure webhook in Uptime Kuma:
- Settings → Notifications → Add Notification
- Type: Webhook
- URL:
http://localhost:5000/webhook/restart-container - Method: POST
- Assign to monitors that should auto-restart
4. Natural Language Incident Reports
Concept: Generate executive summaries of incidents for stakeholders.
# incident_reporter.py
def generate_incident_summary(incident: dict) -> str:
"""Generate human-readable incident summary using Claude."""
response = client.messages.create(
model="claude-opus-4-5-20251101",
max_tokens=300,
messages=[{
"role": "user",
"content": f"""
Create a brief incident summary for non-technical stakeholders:
Service: {incident['service']}
Duration: {incident['duration_minutes']} minutes
Start Time: {incident['start_time']}
End Time: {incident['end_time']}
Impact: {incident['failed_requests']} requests failed
Root Cause: {incident['root_cause']}
Resolution: {incident['resolution']}
Format: 2-3 sentences suitable for executive update.
"""
}]
)
return response.content[0].text
# Example usage
incident = {
"service": "API Gateway",
"duration_minutes": 15,
"start_time": "2025-12-16 10:23:42",
"end_time": "2025-12-16 10:38:42",
"failed_requests": 487,
"root_cause": "Database connection pool exhaustion",
"resolution": "Increased connection pool size and restarted service"
}
summary = generate_incident_summary(incident)
print(summary) Example output:
The API Gateway experienced a 15-minute outage from 10:23 AM to 10:38 AM
due to database connection pool exhaustion. Approximately 487 user requests
failed during this period. The issue was resolved by increasing the connection
pool size and restarting the service, with no expected recurrence. External Monitoring: Surviving ISP Outages
The Homelab Killer Problem
Scenario: Your ISP goes down. Your entire homelab is offline. Your monitoring system is also offline. How do you know there’s a problem?
Solution: Run Uptime Kuma on external VPS that monitors your homelab via VPN.
Architecture Overview
Your Homelab (192.168.1.0/24) External VPS ($3-5/month)
───────────────────────────── ──────────────────────────
├─ Router/Firewall ├─ Uptime Kuma
├─ Services (Web, DB, etc.) ├─ Tailscale/WireGuard VPN
├─ Docker Host └─ Monitors homelab via VPN
└─ [Vulnerable to ISP outage] [Always online, sends alerts] Cost comparison:
- Uptime Robot paid: $8-50/month
- External VPS + Uptime Kuma: $3-5/month
- Savings: 40-90%
Implementation: VPS with Tailscale
docker-compose.yml on VPS:
version: '3.8'
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- ./data:/app/data
ports:
- "3001:3001"
restart: always
network_mode: service:tailscale
tailscale:
image: tailscale/tailscale:latest
container_name: tailscale-vpn
hostname: kuma-external
volumes:
- ./tailscale:/var/lib/tailscale
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- TS_AUTHKEY=${TAILSCALE_AUTHKEY}
- TS_STATE_DIR=/var/lib/tailscale
- TS_EXTRA_ARGS=--advertise-tags=tag:monitoring
restart: always .env file (create securely):
TAILSCALE_AUTHKEY=tskey-auth-xxxxxxxxxxxxxx Setup steps:
Get Tailscale auth key:
- Visit: https://login.tailscale.com/admin/settings/keys
- Generate auth key with tag
monitoring - Save to
.envfile
Deploy on VPS:
docker compose up -dConfigure homelab Tailscale:
- Install Tailscale on homelab router or dedicated VM
- Join same Tailscale network
- Note Tailscale IPs (e.g., homelab router: 100.64.1.5)
Create monitors in external Uptime Kuma:
- Monitor homelab services via Tailscale IPs
- Example: HTTP monitor to
http://100.64.1.10:80(internal web service)
Recommended VPS providers:
- Hetzner Cloud: €3.79/month for CPX11 (2 vCPU, 2GB RAM)
- DigitalOcean: $6/month for basic droplet
- Linode: $5/month for Nanode 1GB
- Vultr: $5/month for 1 vCPU, 1GB RAM
Troubleshooting Common Issues
Issue 1: Services Showing DOWN When Actually UP
Symptoms: Monitors report DOWN, but manual checks show service is running.
Diagnosis:
# Test connectivity from Uptime Kuma container
docker exec -it uptime-kuma sh
wget -O- http://target-service:port # Or use curl
# Check DNS resolution
nslookup target-service
# Verify firewall rules
iptables -L | grep target-port Solutions:
- Ensure Docker containers are on same network
- Use service hostname (not IP) for Docker services
- Check firewall allows outbound connections from Uptime Kuma
- Verify target service is actually listening:
netstat -tulpn | grep port
Issue 2: Notifications Not Sending
Diagnosis:
# Check Uptime Kuma logs
docker logs uptime-kuma | grep -i notification
# Test webhook manually
curl -X POST https://discord.com/api/webhooks/YOUR_URL -H "Content-Type: application/json" -d '{"content":"Test notification"}' Solutions:
- Verify webhook URL is correct (not revoked in Discord/Slack)
- Check firewall allows outbound HTTPS (port 443)
- Test from container:
docker exec uptime-kuma curl https://discord.com - Check notification is assigned to monitor
Issue 3: High Database Storage Usage
Symptoms: Data volume growing > 1GB, slow performance.
Solutions:
# Check database size
du -sh ~/uptime-kuma/data/
# Run shrink database (in Uptime Kuma UI)
# Settings → Maintenance → "Shrink Database"
# Alternative: Manually optimize SQLite
docker exec -it uptime-kuma sh
sqlite3 /app/data/kuma.db "VACUUM;" Prevention:
- Reduce check intervals for non-critical services
- Set data retention policies
- Consider PostgreSQL for large deployments (1000+ monitors)
Issue 4: WebSocket Errors Behind Reverse Proxy
Symptoms: Real-time updates not working, browser console shows WebSocket errors.
Solution: Verify reverse proxy configuration includes WebSocket headers:
# Critical headers for WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; Test WebSocket:
- Open browser DevTools → Network tab
- Filter by WS (WebSocket)
- Verify connection establishes successfully
- Should see
101 Switching Protocolsresponse
Maintenance & Optimization
Regular Maintenance Schedule
Weekly:
- Review alert patterns for noise
- Check disk usage:
docker system df - Verify critical monitors functioning
Monthly:
- Update Docker image:
docker compose pull && docker compose up -d - Review and optimize check intervals
- Test notification delivery
- Check for security updates
Quarterly:
- Run “Shrink Database” (Settings → Maintenance)
- Review all monitors for relevance
- Test disaster recovery from backup
- Update documentation
Automated Backups
#!/bin/bash
# backup-uptime-kuma.sh
BACKUP_DIR="/backups/uptime-kuma"
DATA_DIR="/root/uptime-kuma/data"
RETENTION_DAYS=30
# Create backup
mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/kuma-$(date +%Y%m%d-%H%M%S).tar.gz" "$DATA_DIR"
# Remove old backups
find "$BACKUP_DIR" -name "kuma-*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: $(date)" Schedule with cron:
# Run daily at 2 AM
0 2 * * * /root/backup-uptime-kuma.sh >> /var/log/kuma-backup.log 2>&1 Performance Optimization Tips
Reduce CPU usage:
- Increase check intervals for non-critical services (5-15 minutes instead of 1 minute)
- Disable unused notification channels
- Run on dedicated server (not shared with heavy workloads)
Reduce memory usage:
- Restart container weekly if memory grows:
docker restart uptime-kuma - Monitor:
docker stats uptime-kuma --no-stream - Consider PostgreSQL for better memory management at scale
Reduce storage:
- Run “Shrink Database” quarterly
- Delete obsolete monitors
- Monitor disk usage:
du -sh ~/uptime-kuma/data/
Conclusion
You now have a production-ready monitoring system capable of:
- Tracking availability of websites, APIs, containers, and network services
- Sending multi-channel alerts via Discord, Telegram, Email, and 90+ other platforms
- Creating public status pages for transparency
- Automating responses with AI-powered analysis and self-healing
- Surviving ISP outages with external monitoring
Next steps:
- Start with 3-5 critical services
- Configure 2 notification channels
- Create your first status page
- Experiment with AI automation (optional)
- Set up external monitoring for true reliability
Remember: Perfect monitoring is the enemy of good monitoring. Start simple, expand gradually, and focus on truly critical alerts to avoid notification fatigue.
Additional Resources
Official:
Community:
Python API Library:
- uptime-kuma-api
- Documentation: https://github.com/lucasheld/uptime-kuma-api
Last Updated: December 2025 Version Covered: Uptime Kuma 2.0.2+ Word Count: 3,947 words