Home monitoring dashboard with real-time metrics and graphs

Build Home Monitoring System

I started self-hosting because I wanted control, but quickly realized I was flying blind. My NAS would fill up without warning. My reverse proxy would choke under load and I'd have no idea why. That's when I built a proper monitoring stack, and it transformed how I manage my homelab.

In this guide, I'll walk you through deploying Prometheus and Grafana—the industry-standard combination for infrastructure monitoring. You'll get real-time visibility into CPU, memory, disk, network, and application metrics across every machine in your homelab.

Why Prometheus and Grafana?

I considered many options: Zabbix, InfluxDB with Telegraf, even Netdata. But Prometheus + Grafana won because they're lightweight, have zero licensing costs, and the ecosystem is massive. Every self-hosted app I care about ships with Prometheus exporters: Docker, Nginx, PostgreSQL, Redis, you name it.

Prometheus scrapes metrics on an interval and stores them. Grafana queries Prometheus and turns those metrics into beautiful dashboards. Together, they give you visibility that rivals enterprise monitoring tools but running on a €10/month VPS or your home server.

Architecture Overview

Here's what we're building:

All three run in Docker, orchestrated with Docker Compose. If you're running on a VPS, I recommend RackNerd's KVM VPS—their entry-level plans have enough headroom for Prometheus + Grafana without breaking the bank.

Deploy with Docker Compose

Create a directory for your monitoring stack:

mkdir -p ~/monitoring && cd ~/monitoring

Here's my Docker Compose setup. I use a custom bridge network so all services can talk to each other:

version: '3.8'

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

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
      - GF_INSTALL_PLUGINS=grafana-piechart-panel
    volumes:
      - ./grafana_data:/var/lib/grafana
    networks:
      - monitoring
    depends_on:
      - prometheus
    restart: unless-stopped

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring
    restart: unless-stopped

networks:
  monitoring:
    driver: bridge

Save this as docker-compose.yml, then create your Prometheus configuration file:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'docker'
    static_configs:
      - targets: ['localhost:9323']

Save as prometheus.yml. Now deploy:

docker compose up -d

Check logs to ensure everything started:

docker compose logs -f
Tip: Change that default Grafana password immediately. Log in at http://localhost:3000 with admin / changeme, then go to Settings → Admin → Users and update it.

Add Your Data Source

Once Grafana is running, go to http://your-ip:3000 and add Prometheus as a data source:

  1. Click the gear icon (Settings) in the left sidebar
  2. Select "Data Sources"
  3. Click "Add data source"
  4. Choose Prometheus
  5. Set URL to http://prometheus:9090
  6. Click "Save & Test"

Monitor Remote Hosts

Want to monitor your NAS, router, or another server? Install Node Exporter on each target, then add it to your Prometheus config.

On a remote machine (Ubuntu/Debian):

sudo apt update
sudo apt install -y prometheus-node-exporter
sudo systemctl start prometheus-node-exporter
sudo systemctl enable prometheus-node-exporter

It listens on port 9100 by default. Update your prometheus.yml to scrape it:

  - job_name: 'nas'
    static_configs:
      - targets: ['192.168.1.50:9100']  # Your NAS IP

Then restart Prometheus:

docker compose restart prometheus
Watch out: Node Exporter exposes detailed system information. Don't expose port 9100 directly to the internet. Use a reverse proxy with authentication (Caddy, nginx) or keep it on your local network only. Better yet, use Tailscale or WireGuard to access monitoring remotely without exposing ports.

Import Pre-Built Dashboards

You don't need to build dashboards from scratch. Grafana has a massive library of community dashboards. My go-to is the "Node Exporter for Prometheus" dashboard (ID: 1860).

In Grafana:

  1. Click the plus icon (+) → "Import"
  2. Paste ID 1860 (or search by name)
  3. Select Prometheus as your data source
  4. Click "Import"

You'll instantly see CPU, memory, disk, and network graphs for all your monitored hosts. It's beautiful.

Other dashboards worth importing:

Set Up Alerts

Monitoring is only useful if you know when things break. I use Prometheus alert rules to notify me when disk space is low or CPU usage spikes.

Create alert-rules.yml in your monitoring directory:

groups:
  - name: node-alerts
    interval: 30s
    rules:
      - alert: HighCPUUsage
        expr: (100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)) > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High CPU usage on {{ $labels.instance }}"
          description: "CPU usage is {{ $value }}%"

      - alert: LowDiskSpace
        expr: (node_filesystem_avail_bytes{fstype!~"tmpfs|fuse.lxcfs|squashfs|vfat"} / node_filesystem_size_bytes) * 100 < 10
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Low disk space on {{ $labels.instance }}"
          description: "Only {{ $value }}% free on {{ $labels.device }}"

      - alert: HighMemoryUsage
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage on {{ $labels.instance }}"
          description: "Memory usage is {{ $value }}%"

Update your prometheus.yml to include these rules:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "alert-rules.yml"

scrape_configs:
  # ... rest of your config

Restart Prometheus and visit http://localhost:9090/alerts to see your alert rules in action.

Persistence and Backups

Both Prometheus and Grafana store data in Docker volumes. These are backed by the filesystem on your host. I recommend backing them up weekly:

#!/bin/bash
BACKUP_DIR="/mnt/backups/monitoring"
mkdir -p $BACKUP_DIR

docker run --rm -v prometheus_data:/data -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/prometheus_$(date +%Y%m%d).tar.gz -C /data .

docker run --rm -v grafana_data:/data -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/grafana_$(date +%Y%m%d).tar.gz -C /data .

echo "Backups complete at $BACKUP_DIR"

Put this in a cron job to run daily, or use a proper backup strategy.

Next Steps

Once your monitoring stack is stable, consider adding:

Spend a few evenings with this setup. Create dashboards that matter to you. Once you see a disk-space warning before your NAS fills up, or catch a memory leak in a container before it crashes, you'll understand why monitoring is the foundation of reliable self-hosting.

```