Monitoring Your Homelab with Grafana and Prometheus

Stop guessing what is happening inside your servers. This guide sets up Grafana and Prometheus to monitor your Proxmox nodes and Docker containers in real time.

A cartoon server detective using a magnifying glass to inspect glowing Grafana and Prometheus monitoring charts.

If you have been running a homelab for a while, you reach a point where guessing what is happening inside your servers gets old. Is that Proxmox node running hot? Which Docker container is hogging memory? Is your network link saturated? Without monitoring, you are flying blind.

Grafana and Prometheus fix that. Together they give you a proper dashboard showing real-time metrics from your Proxmox nodes, Docker containers, and anything else you care about. Once it is set up, you will wonder how you managed without it.

This guide covers a complete monitoring stack using Docker Compose, pulling metrics from Proxmox nodes via node_exporter and Docker containers via cAdvisor, then visualising everything in Grafana.

💡
This guide assumes you already have Docker running on an Ubuntu Server VM. If not, check out Setting Up Ubuntu Server VMs on Proxmox first.

How It Works

Before diving in, it helps to understand what each component does:

  • Prometheus is the database. It scrapes metrics from various exporters on a schedule and stores them as time-series data.
  • node_exporter runs on each server you want to monitor and exposes hardware and OS metrics (CPU, memory, disk, network) that Prometheus collects.
  • cAdvisor exposes per-container metrics (CPU, memory, network, disk I/O) from Docker. Prometheus scrapes this too.
  • Grafana is the dashboard. It connects to Prometheus as a data source and lets you build and import visual dashboards.

The flow is simple: exporters expose metrics, Prometheus collects them, Grafana displays them.

Step 1: Set Up the Directory Structure

On your monitoring VM, create a folder for the stack:

mkdir -p ~/monitoring/prometheus
cd ~/monitoring

You will need two files before starting: a Docker Compose file and a Prometheus configuration file.

Step 2: Create the Prometheus Config

Prometheus needs to know where to scrape metrics from. Create the config file:

nano ~/monitoring/prometheus/prometheus.yml

Paste the following, replacing the IP addresses with your actual server IPs:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

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

  - job_name: 'node_exporter'
    static_configs:
      - targets:
          - '192.168.1.101:9100'   # Proxmox node 1
          - '192.168.1.102:9100'   # Proxmox node 2
          - '192.168.1.103:9100'   # Proxmox node 3
          - '192.168.1.104:9100'   # Proxmox node 4
        labels:
          environment: 'homelab'

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']
💡
The scrape_interval of 15s is a good balance between data resolution and storage usage. You can reduce it to 5s if you want more granular data, but expect higher disk usage over time.

Step 3: Create the Docker Compose File

nano ~/monitoring/docker-compose.yml
services:

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    user: "1000:1000"
    volumes:
      - ./prometheus/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'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
    ports:
      - "9090:9090"
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    user: "1000:1000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=changeme
      - GF_USERS_ALLOW_SIGN_UP=false
    ports:
      - "3000:3000"
    networks:
      - monitoring
    depends_on:
      - prometheus

  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
    ports:
      - "8080:8080"
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:
    driver: bridge
⚠️
Change GF_SECURITY_ADMIN_PASSWORD to something strong before starting the stack. This is the Grafana admin password and the default is not secure.

Step 4: Install node_exporter on Each Proxmox Node

node_exporter runs directly on the host, not in a container. You need to install it on each Proxmox node you want to monitor. Run the following on each node via SSH:

# Download the latest node_exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.2/node_exporter-1.8.2.linux-amd64.tar.gz

# Extract it
tar xvf node_exporter-1.8.2.linux-amd64.tar.gz

# Move the binary
sudo mv node_exporter-1.8.2.linux-amd64/node_exporter /usr/local/bin/

# Clean up
rm -rf node_exporter-1.8.2.linux-amd64*

Now create a systemd service so it runs automatically on boot:

sudo nano /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
Type=simple
User=nobody
ExecStart=/usr/local/bin/node_exporter
Restart=on-failure

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable node_exporter
sudo systemctl start node_exporter

Verify it is working:

curl http://localhost:9100/metrics | head -20

You should see a wall of metrics. Repeat this on every Proxmox node you want to monitor.

💡
node_exporter exposes its metrics on port 9100 by default. Make sure this port is accessible from your monitoring VM. If you have a firewall, allow incoming connections on 9100 from your monitoring VM's IP.

Step 5: Start the Monitoring Stack

Back on your monitoring VM:

cd ~/monitoring
docker compose up -d

Check everything started correctly:

docker compose ps

All three containers (prometheus, grafana, cadvisor) should show as running. Give it 30 seconds, then check that Prometheus is scraping your targets by opening the Prometheus web interface:

http://YOUR_VM_IP:9090/targets

Each target should show State: UP. If any show DOWN, double-check the IP addresses in prometheus.yml and that node_exporter is running on those hosts.

Step 6: Set Up Grafana

Open Grafana in your browser:

http://YOUR_VM_IP:3000

Log in with the admin credentials you set in the Compose file. The first thing to do is add Prometheus as a data source.

Add Prometheus as a Data Source

  1. Go to Connections in the left sidebar
  2. Click Data sources
  3. Click Add data source
  4. Select Prometheus
  5. Set the URL to http://prometheus:9090 (using the container name since they are on the same Docker network)
  6. Click Save and test

You should see a green confirmation message. Grafana can now query Prometheus.

Import Pre-Built Dashboards

Rather than building dashboards from scratch, import community dashboards. Grafana's dashboard library has excellent ones ready to go.

For node_exporter (server hardware metrics):

  1. Go to Dashboards and click New, then Import
  2. Enter dashboard ID 1860 (Node Exporter Full) and click Load
  3. Select your Prometheus data source
  4. Click Import

For cAdvisor (Docker container metrics):

  1. Import dashboard ID 14282 (Cadvisor Exporter)
  2. Select your Prometheus data source and import

Within minutes you should have a full dashboard showing CPU, memory, disk, and network metrics for every node and container in your homelab.

Step 7: Configure Alerting (Optional)

Dashboards are useful, but alerts are what make monitoring genuinely valuable. Grafana can notify you when something goes wrong, via email, Telegram, Slack, and more.

To set up a Telegram alert:

  1. Go to Alerting in the left sidebar
  2. Click Contact points
  3. Click Add contact point
  4. Choose Telegram as the integration
  5. Enter your Telegram bot token and chat ID
  6. Click Test to verify it works, then Save

You can then create alert rules on any panel, for example alerting if CPU usage stays above 90% for more than 5 minutes, or if disk space drops below 10%.

Useful Management Commands

# View logs
docker compose logs -f prometheus
docker compose logs -f grafana

# Restart the stack
docker compose restart

# Stop everything
docker compose down

# Update images
docker compose pull
docker compose up -d

# Check Prometheus targets
curl http://localhost:9090/api/v1/targets | python3 -m json.tool

What You Should Have Now

  • Prometheus collecting metrics from all your Proxmox nodes every 15 seconds
  • cAdvisor feeding Docker container metrics into Prometheus
  • Grafana dashboards showing CPU, memory, disk, and network in real time
  • 30 days of metric history stored on disk
  • Optional Telegram alerts when things go wrong

Once you have this running, you start noticing things you never would have caught otherwise. Which container is slowly leaking memory over a week. Which Proxmox node runs 5 degrees hotter than the others. When your disk is going to fill up. That kind of visibility changes how you manage a homelab.

Next Steps

  • Add more exporters — there are exporters for almost everything. Useful ones for a homelab include pihole-exporter, Unifi exporter, and Speedtest Tracker
  • Increase retention — change --storage.tsdb.retention.time=30d to keep longer history, or add --storage.tsdb.retention.size=10GB to cap it by disk usage
  • Expose via reverse proxy — rather than accessing Grafana on port 3000, put it behind Nginx Proxy Manager with a proper domain and SSL certificate