Self-Hosted Monitoring: Building a Stack from Open-Source Tools

A step-by-step guide to deploying a monitoring stack: Prometheus, Grafana, Loki, Node Exporter, and Uptime Kuma.

You need to keep an eye on your servers, services, and websites. But paying Datadog $15 per host per month is painful, especially when you have a handful of VPS instances and a dozen self-hosted applications. The good news: an open-source monitoring stack can be assembled in an hour and covers 90% of an average project’s needs.

In this guide, we’ll assemble a stack: Prometheus (metrics), Grafana (dashboards), Loki (logs), Node Exporter (system metrics), and Uptime Kuma (website status). Everything in Docker Compose, everything with persistence, everything with alerts.

Step 1: Docker Compose

Create a monitoring directory and a docker-compose.yml file:

version: "3.8"
services:
  prometheus:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"
      - "--storage.tsdb.path=/prometheus"
      - "--storage.tsdb.retention.time=30d"
    ports:
      - "9090:9090"
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - "--path.procfs=/host/proc"
      - "--path.sysfs=/host/sys"
      - "--path.rootfs=/rootfs"
    ports:
      - "9100:9100"
    restart: unless-stopped

  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
    volumes:
      - grafana_data:/var/lib/grafana
    ports:
      - "3000:3000"
    restart: unless-stopped

volumes:
  prometheus_data:
  grafana_data:

Step 2: Prometheus — Metrics Collection

Create prometheus.yml:

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "node"
    static_configs:
      - targets: ["node-exporter:9100"]

  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

Node Exporter collects CPU, RAM, disk, network — all system-level data. To monitor multiple servers, run Node Exporter on each and add their IPs to targets.

Step 3: Grafana — Visualization

Go to http://your-server:3000 (login: admin, password: changeme). Add Prometheus as a data source (URL http://prometheus:9090).

Import dashboard 1860 (Node Exporter Full) — this is the most popular template for system metrics. After importing, you’ll see beautiful charts.

Step 4: Loki — Logs

Add Loki and Promtail to docker-compose.yml:

  loki:
    image: grafana/loki:latest
    volumes:
      - loki_data:/loki
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    restart: unless-stopped

  promtail:
    image: grafana/promtail:latest
    volumes:
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./promtail.yml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
    restart: unless-stopped

In Grafana, add Loki as a data source and use Explore to search logs. All container logs and system logs — in one place, with search and filtering.

Step 5: Uptime Kuma — Website Status

To monitor the availability of websites and APIs, run Uptime Kuma as a separate container:

  uptime-kuma:
    image: louislam/uptime-kuma:latest
    volumes:
      - uptime_kuma_data:/app/data
    ports:
      - "3001:3001"
    restart: unless-stopped

Uptime Kuma can ping, check HTTP statuses, SSL certificates, TCP ports, and DNS records. Set up notifications to Telegram, Discord, or Email.

Step 6: Alerts

Prometheus supports Alertmanager for notifications. Minimal setup — a rule in prometheus.yml:

rule_files:
  - "alert.rules.yml"

And in alert.rules.yml:

groups:
  - name: node
    rules:
      - alert: HighCPU
        expr: 100 - (avg by(instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "CPU above 80% on {{ $labels.instance }}"

Alertmanager can be connected to Telegram, Email, or Slack.

How Much Does It Cost in Resources

The entire stack on a VPS with 2 GB RAM:

  • Prometheus: ~200 MB RAM + disk for data (depends on retention)
  • Grafana: ~150 MB RAM
  • Loki: ~200 MB RAM
  • Node Exporter: ~50 MB RAM
  • Promtail: ~100 MB RAM
  • Uptime Kuma: ~100 MB RAM

Total: about 800 MB RAM for the complete monitoring stack. The remaining 1.2 GB is enough for your applications.

What’s Next

With this stack, you get:

  • Real-time metrics for all servers
  • Centralized logs with search
  • Beautiful dashboards in Grafana
  • Alerts about issues
  • Website availability monitoring

As your project grows, you can add: cAdvisor (container metrics), Blackbox Exporter (HTTP/DNS/TCP checks), and Grafana Tempo (tracing).

No $15 per host. Only your data on your server.

Official Prometheus Website | Official Grafana Website | Uptime Kuma on GitHub