Reading time: ~15 minutes Audience: Homelabbers building a monitoring stack from scratch
What Is Grafana + Prometheus?
Overview
Grafana + Prometheus is the de facto standard for cloud-native monitoring. Prometheus collects metrics via HTTP scraping. Grafana visualizes them. Together, they provide a complete observability platform for your homelab: server health, container performance, network traffic, and application metrics.
Stack components: - Prometheus: Time-series database and scraper - Grafana: Visualization and alerting UI - Node Exporter: Exposes Linux hardware and OS metrics - cAdvisor: Exposes Docker container metrics - Alertmanager: Routes fired alerts to notifications
Why This Stack in 2026
The Prometheus ecosystem is larger than ever. Every major homelab tool (Proxmox, TrueNAS, Pi-hole, AdGuard Home, Syncthing) has a Prometheus exporter. Grafana’s dashboard library (grafana.com/dashboards) has 10,000+ pre-built dashboards. The stack is mature, well-documented, and completely free.
Prerequisites
Hardware Requirements
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4 cores |
| RAM | 2 GB | 4 GB |
| Storage | 20 GB | 50 GB (for 15-day retention) |
| Network | 1 GbE | 1 GbE |
Software Requirements
- Docker Engine 24.0+ and Docker Compose v2+
- A Linux host (Debian, Ubuntu, or Proxmox VM/LXC)
- Basic command-line knowledge
Knowledge Prerequisites
- Docker networking concepts
- YAML syntax
- Basic Prometheus query language (PromQL)
Step 1: Create the Docker Compose Stack
Objective
Deploy Prometheus, Grafana, Node Exporter, and cAdvisor with a single docker-compose.yml.
Step-by-Step Instructions
- Create a project directory:
mkdir -p ~/monitoring/{prometheus,grafana/config}
cd ~/monitoring
- Create
docker-compose.yml:
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus:/etc/prometheus
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=15d'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
networks:
- monitoring
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=*** - GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/config/datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:latest
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
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
privileged: true
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
- /dev/disk:/dev/disk:ro
devices:
- /dev/kmsg
networks:
- monitoring
volumes:
prometheus-data:
grafana-data:
networks:
monitoring:
driver: bridge
- Create
prometheus/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: []
rule_files:
- /etc/prometheus/rules/*.yml
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']
- job_name: 'docker'
static_configs:
- targets: ['cadvisor:8080']
- Create
grafana/config/datasources.yml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
- Start the stack:
docker compose up -d
- Verify services:
docker compose ps
- Access Prometheus at
http://your-server:9090 - Access Grafana at
http://your-server:3000(admin / your password)
Step 2: Add Alerting Rules
Objective
Create Prometheus alerting rules for common homelab scenarios.
Step-by-Step Instructions
- Create
prometheus/rules/homelab.yml:
groups:
- name: homelab-alerts
interval: 30s
rules:
- alert: NodeDown
expr: up{job="node-exporter"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Node {{ $labels.instance }} is down"
description: "Prometheus cannot scrape node-exporter on {{ $labels.instance }}."
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (irate(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 above 80% for more than 5 minutes."
- alert: DiskSpaceLow
expr: (node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space low on {{ $labels.instance }}"
description: "Less than 10% free on {{ $labels.device }}."
- alert: MemoryHigh
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is above 85%."
- alert: ContainerDown
expr: absent(container_last_seen{name=~"nextcloud|jellyfin|plex|immich"})
for: 1m
labels:
severity: critical
annotations:
summary: "Critical container is down"
description: "A critical container is not running."
- Reload Prometheus configuration:
curl -X POST http://localhost:9090/-/reload
- Check rules in Prometheus UI: Status > Rules
Step 3: Import Dashboards
Objective
Import pre-built dashboards for Node Exporter and cAdvisor.
Step-by-Step Instructions
- In Grafana, click + > Import Dashboard
- Enter ID
1860(Node Exporter Full) and click Load - Select the Prometheus data source and click Import
- Repeat for ID
14282(Docker and System Monitoring)
Other useful dashboard IDs:
| Dashboard | ID | Description |
|---|---|---|
| Node Exporter Full | 1860 | Comprehensive Linux metrics |
| Docker and System | 14282 | Container + host metrics |
| Proxmox | 10347 | Proxmox VE metrics via Prometheus |
| Pi-hole | 10176 | DNS query statistics |
| AdGuard Home | 14400 | AdGuard Home metrics |
| Speedtest | 13665 | Internet speed tracking |
Step 4: Add Additional Exporters
Objective
Monitor services that do not expose Prometheus metrics natively.
Step-by-Step Instructions
Pi-hole Exporter:
pihole-exporter:
image: ekofr/pihole-exporter:latest
container_name: pihole-exporter
environment:
- PIHOLE_HOSTNAME=192.168.1.10
- PIHOLE_API_TOKEN=*** ports:
- "9617:9617"
networks:
- monitoring
Add to prometheus.yml:
- job_name: 'pihole'
static_configs:
- targets: ['pihole-exporter:9617']
Proxmox VE Exporter:
proxmox-exporter:
image: prompve/prometheus-pve-exporter:latest
container_name: proxmox-exporter
environment:
- PVE_USER=root@pam
- PVE_PASSWORD=*** - PVE_HOST=192.168.1.5
ports:
- "9221:9221"
networks:
- monitoring
Add to prometheus.yml:
- job_name: 'proxmox'
static_configs:
- targets: ['proxmox-exporter:9221']
Step 5: Secure the Stack
Objective
Expose Grafana and Prometheus securely via reverse proxy.
Step-by-Step Instructions
-
Add Traefik or Nginx Proxy Manager to the compose file, or use an existing reverse proxy.
-
Nginx Proxy Manager configuration:
- Add proxy host:
grafana.yourdomain.com→http://grafana:3000 - Add proxy host:
prometheus.yourdomain.com→http://prometheus:9090 -
Request SSL certificates via Let’s Encrypt
-
Restrict Prometheus access:
- Do not expose Prometheus directly to the internet
- Use a VPN or internal network for Prometheus access
-
Grafana can proxy Prometheus queries safely
-
Enable Grafana HTTPS:
environment:
- GF_SERVER_PROTOCOL=https
- GF_SERVER_CERT_FILE=/etc/grafana/cert.pem
- GF_SERVER_CERT_KEY=/etc/grafana/key.pem
Pro Tips
Tip 1: Use Recording Rules for Expensive Queries
Queries that aggregate over large time ranges can be slow. Pre-compute them with recording rules:
# prometheus/rules/recordings.yml
groups:
- name: recordings
interval: 60s
rules:
- record: instance:node_cpu:rate5m
expr: avg by(instance) (irate(node_cpu_seconds_total{mode!="idle"}[5m]))
Tip 2: Separate Data Retention
Prometheus defaults to 15 days. For long-term storage, use remote_write to VictoriaMetrics or InfluxDB:
# prometheus.yml
remote_write:
- url: http://victoriametrics:8428/api/v1/write
Tip 3: Backup Your Grafana Dashboards
Dashboards live in the Grafana database. Export them regularly:
# Export all dashboards
curl -s -H "Authorization: Bearer *** http://grafana:3000/api/search | \
jq -r '.[].uri' | \
while read uri; do
curl -s -H "Authorization: Bearer *** "http://grafana:3000/api/dashboards/$uri" > "${uri//\//_}.json"
done
Troubleshooting Common Issues
Problem 1: Prometheus Targets Show “DOWN”
Check: http://prometheus:9090/targets
Fix:
- Ensure the target container is on the monitoring network
- Check firewall rules on the host
- Verify the scrape URL and port are correct
Problem 2: cAdvisor Shows No Containers
Check: Run docker logs cadvisor
Fix: cAdvisor requires privileged mode and access to /var/lib/docker. On Docker rootless, cAdvisor may not work. Use a non-rootless Docker setup or switch to the Docker daemon metrics endpoint.
Problem 3: Grafana “No Data” for Some Panels
Check: Open the panel in edit mode and run the query in Explore.
Fix: The dashboard may use metric names that differ from your exporter version. Update the query or import a newer dashboard version.
Conclusion
Summary
Grafana + Prometheus is the most powerful, flexible, and well-supported monitoring stack for homelabs. With Node Exporter for hardware metrics, cAdvisor for containers, and community dashboards for every tool, you can build a professional-grade observability platform in under an hour.
Next Steps
- Deploy the stack on your Proxmox host or a dedicated monitoring VM
- Import the Node Exporter Full and Docker dashboards
- Add exporters for your critical services (Pi-hole, Proxmox, TrueNAS)
- Set up Alertmanager for Slack/Telegram notifications
- Backup your Grafana dashboards and Prometheus configuration to Git
Affiliate Opportunities
- Mini PCs for monitoring: Intel N100 with 16 GB RAM (Amazon)
- VPS: Hetzner for offsite monitoring (referral)
- Displays: Wall-mounted tablets for Grafana kiosks (Amazon)
Internal Linking Strategy
alertmanager→ guide: “prometheus-alertmanager-setup.md”dashboards→ guide: “grafana-dashboard-homelab.md”docker-monitoring→ guide: “docker-monitoring-grafana-prometheus.md”proxmox-exporter→ guide: “proxmox-monitoring-guide.md”
CTA
- [comment] What does your monitoring stack look like? Share your Grafana screenshot below.
- [newsletter] Subscribe for our monitoring stack templates and exporter updates.
- [internal_link] Ready to set up alerting? Read our Alertmanager setup guide next.