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:
- Prometheus server: Central metrics database. Scrapes targets, stores time-series data, handles alerting rules.
- Grafana: Visualization and dashboards. Queries Prometheus, renders graphs, sends notifications.
- Node Exporter: Runs on each machine you want to monitor. Exposes CPU, memory, disk, network stats.
- cAdvisor (optional): Container monitoring. Tracks CPU/memory per container if you run Docker.
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
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:
- Click the gear icon (Settings) in the left sidebar
- Select "Data Sources"
- Click "Add data source"
- Choose Prometheus
- Set URL to
http://prometheus:9090 - 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
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:
- Click the plus icon (+) → "Import"
- Paste ID
1860(or search by name) - Select Prometheus as your data source
- Click "Import"
You'll instantly see CPU, memory, disk, and network graphs for all your monitored hosts. It's beautiful.
Other dashboards worth importing:
- 1860: Node Exporter Dashboard (general server stats)
- 3662: Prometheus Stats (monitor Prometheus itself)
- 11074: Node Exporter for Prometheus (alternative, more detailed)
- 1815: Prometheus (simple but effective)
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:
- Alertmanager: Route alerts to Slack, email, or PagerDuty
- More exporters: MySQL, PostgreSQL, Redis, Nginx, Docker stats
- Log aggregation: Pair with Loki for centralized logging
- Custom metrics: Export metrics from your own applications
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.