The Ultimate Self-Hosted Observability Stack: Prometheus + Grafana + Loki + Uptime Kuma
Vendor lock-in on APM and monitoring platforms like Datadog, New Relic, and Dynatrace is notorious for unpredictable billing spikes. Adding custom tags, ingestion volume, or retained log indices can quickly turn a $200/mo budget into a $2,000/mo surprise invoice.
With this open-source Docker Compose observability pipeline, you get:
- System Metrics & Dashboards: Prometheus + Node Exporter + cAdvisor visualized in Grafana.
- Structured Log Aggregation: Promtail shipping container logs to Loki without heavy ElasticSearch overhead.
- External Uptime & HTTP Checks: Uptime Kuma with instant Telegram, Discord, and webhook incident alerts.
- Resource Footprint: Runs reliably on a modest VPS (4GB–8GB RAM) for dozens of microservices.
High-Level Architecture
[ Docker Containers & Linux Host ]
|
+---> Node Exporter (CPU, RAM, Disk, Network) ----+
| |
+---> cAdvisor (Container CPU, Memory, I/O) ------+---> Prometheus (Metrics Storage)
| | |
+---> Promtail (Container Docker Logs) ---------> Loki v
|---> Grafana (Unified Dashboard)
|
[ External Web Services / APIs ] ------------------------------> Uptime Kuma (Status & Alerts)
1. Production Docker Compose (docker-compose.yml)
version: "3.8"
networks:
monitoring-net:
driver: bridge
volumes:
prometheus-data:
grafana-data:
loki-data:
uptime-kuma-data:
caddy-data:
caddy-config:
services:
# --- Reverse Proxy with Auto HTTPS ---
caddy:
image: caddy:2-alpine
container_name: monitor-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- monitoring-net
depends_on:
- grafana
- uptime-kuma
# --- Metrics Scraper & Time-Series DB ---
prometheus:
image: prom/prometheus:v2.51.0
container_name: prometheus
restart: unless-stopped
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=30d"
- "--web.enable-lifecycle"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
networks:
- monitoring-net
# --- Host Metrics Collector ---
node-exporter:
image: prom/node-exporter:v1.7.0
container_name: node-exporter
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- "--path.procfs=/host/proc"
- "--path.rootfs=/rootfs"
- "--path.sysfs=/host/sys"
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
networks:
- monitoring-net
# --- Docker Container Metrics ---
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
container_name: cadvisor
restart: unless-stopped
privileged: true
devices:
- /dev/kmsg
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
networks:
- monitoring-net
# --- Log Aggregation Engine ---
loki:
image: grafana/loki:3.0.0
container_name: loki
restart: unless-stopped
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml:ro
- loki-data:/loki
networks:
- monitoring-net
# --- Log Shipper (Promtail) ---
promtail:
image: grafana/promtail:3.0.0
container_name: promtail
restart: unless-stopped
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yaml:/etc/promtail/config.yml:ro
command: -config.file=/etc/promtail/config.yml
networks:
- monitoring-net
depends_on:
- loki
# --- Visualization Dashboard ---
grafana:
image: grafana/grafana:10.4.0
container_name: grafana
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=ChangeThisSecurePassword!
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
networks:
- monitoring-net
# --- Synthetic Uptime & Public Status Page ---
uptime-kuma:
image: louislam/uptime-kuma:1.23.13-alpine
container_name: uptime-kuma
restart: unless-stopped
volumes:
- uptime-kuma-data:/app/data
networks:
- monitoring-net
2. Configuration Files
prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node-exporter"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]
loki-config.yaml
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
retention_period: 30d
Caddyfile
grafana.yourdomain.com {
encode gzip zstd
reverse_proxy grafana:3000
}
status.yourdomain.com {
encode gzip zstd
reverse_proxy uptime-kuma:3001
}
3. Recommended Grafana Dashboards to Import
Once Grafana is online, go to Dashboards -> Import and paste these official Community Dashboard IDs:
-
Node Exporter Full (ID:
1860): Complete server stats (CPU utilization, RAM saturation, disk I/O, network bandwidth). -
cAdvisor Docker Monitoring (ID:
14282): Per-container memory consumption, CPU limits, network drops. -
Loki Docker Logs (ID:
13639): Live log streaming with real-time regex search.
4. Setting Up Alerting in Uptime Kuma
- Open
https://status.yourdomain.com. - Go to Settings -> Notifications -> Add Notification.
- Select Telegram / Discord / Slack / Webhook.
- Set check interval to 30s and retry count to 2.
- Create a public status page to display uptime SLA to your clients with zero extra subscription fees.
Summary & Cost Comparison
- Cloud APM & Log SaaS (Datadog / New Relic): $250 - $2,500/month.
- Self-Hosted Stack: $10 - $20/month on a Hetzner CPX31 (4 vCPU, 8GB RAM).
- Annual Savings: $2,800 to $29,000+ per year.
Production Stacks & Blueprints
Want curated Docker Compose configs, security hardening, automated backups, and reverse proxy blueprints for 30+ top open-source tools?
👉 Check out SelfHostStack or grab the turnkey Self-Hosted Starter Stack Pack ($29).
Top comments (0)