ProxmoxSelf-Hosting

How to Monitor Proxmox with Grafana and Prometheus: A Beginner-Friendly Guide That Actually Helps

Learn how to monitor Proxmox with Prometheus and Grafana using a beginner-friendly setup with the Proxmox exporter, real commands, and troubleshooting steps.

AU

Author

David Okonkwo

FTC disclosure: This article contains affiliate links. If you purchase through these links, we may earn a commission at no additional cost to you.

Key Takeaways

  • Proxmox monitoring gets much easier when you split the stack into four parts: Proxmox API, exporter, Prometheus, and Grafana.
  • The clean beginner path is to run Grafana, Prometheus, and the Proxmox exporter in Docker, then scrape your Proxmox node through a read-only API token.
  • The PVEAuditor role is enough for metrics collection in most homelab setups. You do not need to give your monitoring stack broad admin access.
  • If Grafana shows no data, the problem is usually one of three things: the API token, the Prometheus scrape target, or the wrong datasource on the imported dashboard.
  • Historical metrics matter because the Proxmox web UI is good at showing what is happening right now, but not always why the host was struggling six hours ago.

If you have ever watched a Proxmox node feel slow, then opened the web UI only to find everything looking vaguely normal, this guide is for you. That is one of the most frustrating homelab problems because the failure is real, but the evidence has already wandered off.

The good news is that Proxmox monitoring does not need to turn into a full observability side quest. We are going to build a small stack with Prometheus and Grafana that gives you real history for CPU, memory, disk, storage, and guest activity without making the setup feel like enterprise punishment.

Why this matters before you touch anything

The Proxmox dashboard is useful, but it is mostly a live window. That works for obvious failures. It is much less helpful when your backup window overlaps with storage IO spikes, a noisy VM starts chewing CPU at 3 a.m., or a node quietly runs hot for days before you notice.

This is where Prometheus and Grafana help. Think of Proxmox as the machine room, the exporter as the translator, Prometheus as the notebook, and Grafana as the control panel on the wall. Once those four pieces are talking to each other, you stop guessing.

If you are still building the rest of your Proxmox environment, my guides on Proxmox networking, Proxmox templates, and Proxmox cluster setup are a good next layer after this one.

What we are building

We will use:

  1. A read-only Proxmox monitoring user and API token
  2. prometheus-pve-exporter to collect metrics from the Proxmox API
  3. Prometheus to scrape and store those metrics
  4. Grafana to visualize everything with a Proxmox dashboard

This guide assumes you already have:

  • a working Proxmox node or cluster
  • Docker and Docker Compose on a Linux box or VM
  • network access from that box to your Proxmox host on port 8006

You can run the monitoring stack on a dedicated VM, on a small Docker host, or on an existing observability box. For beginners, I prefer a separate VM because it keeps the moving parts easy to reason about.

What you will need

A few practical gear picks if you are building or expanding your setup:

Do not overthink the hardware. Monitoring is one of those jobs that sounds fancy but is usually light. You are not training models here. You are collecting numbers and drawing graphs.

Step 1 - Create a read-only Proxmox monitoring user

Why this matters

This is the first place beginners often make an avoidable security mistake. It is tempting to point monitoring at your existing Proxmox admin account because it is already there. Please do not do that.

Monitoring should have read-only access. If the monitoring box gets compromised, you want the blast radius to be boring.

Create the user and token

SSH into one Proxmox node as root and create a dedicated user:

pveum user add prometheus@pve --comment "Prometheus metrics collector"
pveum aclmod / -user prometheus@pve -role PVEAuditor
pveum user token add prometheus@pve monitoring -privsep 0

That last command prints the token value once. Save it immediately in your password manager or a local secrets file that is not committed anywhere.

Your final credential set will look like this:

  • User: prometheus@pve
  • Token name: monitoring
  • Token value: your-long-token-value

Verify the idea, not just the command

The goal here is simple:

  • the user has the PVEAuditor role
  • the permission path is /
  • the token exists and can read cluster information

If you are newer to Proxmox permissions, this is the same principle I recommend in my SSH hardening guide: give services only the access they need, then stop.

Step 2 - Create the monitoring stack files

Why this matters

Before you run containers, it helps to make the stack layout predictable. Otherwise, a week from now you will be staring at a folder called monitoring-final-v2-actual and wondering what past-you was trying to prove.

Create a working directory on your Docker host:

mkdir -p ~/proxmox-monitoring/prometheus
mkdir -p ~/proxmox-monitoring/grafana/provisioning/datasources
mkdir -p ~/proxmox-monitoring/pve
cd ~/proxmox-monitoring

Now create this docker-compose.yml file:

services:
  pve-exporter:
    image: prompve/prometheus-pve-exporter:latest
    container_name: pve-exporter
    restart: unless-stopped
    ports:
      - "127.0.0.1:9221:9221"
    volumes:
      - ./pve/pve.yml:/etc/prometheus/pve.yml:ro

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    command:
      - --config.file=/etc/prometheus/prometheus.yml
      - --storage.tsdb.path=/prometheus
      - --web.enable-lifecycle
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    depends_on:
      - pve-exporter

  grafana:
    image: grafana/grafana-oss:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources:ro
    depends_on:
      - prometheus

volumes:
  prometheus_data:
  grafana_data:

A couple of notes:

  • I bind the exporter to 127.0.0.1:9221 so it is not exposed to the whole LAN by default.
  • Prometheus and Grafana can be exposed to your LAN, but I still recommend putting them behind a reverse proxy later if you want remote access. If you need that piece, my Nginx Proxy Manager guide walks through the basics.

Step 3 - Configure the Proxmox exporter

Why this matters

The exporter is the translator between Prometheus and Proxmox. Prometheus cannot scrape the Proxmox API directly in the format Grafana expects for these dashboards. The exporter handles that translation for you.

Create ~/proxmox-monitoring/pve/pve.yml:

default:
  user: prometheus@pve
  token_name: monitoring
  token_value: YOUR_LONG_TOKEN_VALUE
  verify_ssl: false

Replace YOUR_LONG_TOKEN_VALUE with the actual token value from Step 1.

What could go wrong

If your Proxmox node uses a self-signed certificate, verify_ssl: false is usually the quickest beginner path. Long term, I prefer trusted certificates, but there is no point pretending every homelab starts there.

If you already use valid certificates, set this instead:

verify_ssl: true

Step 4 - Configure Prometheus scraping

Why this matters

Prometheus needs to know where metrics live, how often to collect them, and how to label them. This is where a lot of "Grafana shows nothing" problems actually start.

Create ~/proxmox-monitoring/prometheus/prometheus.yml:

global:
  scrape_interval: 30s
  evaluation_interval: 30s

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets:
          - localhost:9090

  - job_name: pve
    static_configs:
      - targets:
          - 192.168.1.10
    metrics_path: /pve
    params:
      module: [default]
      cluster: ['1']
      node: ['1']
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: pve-exporter:9221

Replace 192.168.1.10 with the IP or hostname of your Proxmox node.

If you have a cluster, list each Proxmox node under targets:

      - targets:
          - 192.168.1.10
          - 192.168.1.11
          - 192.168.1.12

This configuration follows the exporter project pattern where Prometheus sends the Proxmox node as the target parameter and the exporter does the API call on its behalf.

Step 5 - Provision the Grafana datasource

Why this matters

You can add the datasource by hand in the UI. I still prefer provisioning it in a file because it makes rebuilds painless. If you redeploy the stack later, Grafana comes back with the datasource ready instead of making you click through the same screens again.

Create ~/proxmox-monitoring/grafana/provisioning/datasources/prometheus.yml:

apiVersion: 1

datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    editable: true

Step 6 - Start the stack

Why this matters

Now we move from configuration to proof. The point is not just to start containers. The point is to confirm each layer works before you pile the next layer on top.

Launch the stack:

cd ~/proxmox-monitoring
docker compose up -d

Check container status:

docker compose ps

Then follow the logs if something looks off:

docker compose logs -f pve-exporter
docker compose logs -f prometheus
docker compose logs -f grafana

Verify the exporter first

From the Docker host, test the exporter endpoint:

curl -s http://127.0.0.1:9221/metrics | head

You should see exporter self-metrics. Then test the Proxmox-backed endpoint through Prometheus later, but this quick check confirms the container is alive.

Verify Prometheus targets

Open Prometheus at http://YOUR-DOCKER-HOST:9090/targets.

You want to see the pve target as UP. If it is DOWN, do not move to Grafana yet. Fix the scrape first.

This step saves time because Grafana is just a mirror here. If Prometheus has nothing, Grafana will faithfully visualize nothing (which is a very honest failure mode, to be fair).

Step 7 - Import the Proxmox Grafana dashboard

Why this matters

Grafana without a dashboard is just a very polished blank page. The easiest starting point is the public Proxmox dashboard with ID 10347 from Grafana Labs.

Open Grafana at http://YOUR-DOCKER-HOST:3000.

Default login:

  • Username: admin
  • Password: admin

Grafana will ask you to change the password on first login.

Then import dashboard ID 10347:

  1. Go to Dashboards
  2. Click New -> Import
  3. Enter 10347
  4. Choose the Prometheus datasource
  5. Finish the import

That dashboard is a good beginner starting point because it covers:

  • node CPU and memory
  • storage allocation and usage
  • guest CPU, memory, disk IO, and network IO
  • historical trends that the Proxmox UI does not keep front and center

Step 8 - Check that the graphs mean something

Why this matters

A dashboard full of moving lines feels impressive, but the real question is whether it helps you catch useful problems. I like to do a quick sanity test right after setup.

Try one or two controlled changes:

# On a busy VM or container host
apt update

# Or generate a little disk activity on a test VM
fio --name=randread --rw=randread --bs=4k --size=256M --runtime=60 --time_based

Then watch whether:

  • CPU changes on the correct node
  • IO appears where you expect
  • guest graphs line up with your test workload

If the graphs move in the wrong place, you probably have a target or instance-label mismatch.

Common mistakes

1. Using an admin account instead of a monitoring account

You do not need full admin rights for metrics. Use PVEAuditor and keep it boring.

2. Importing the dashboard before Prometheus targets are up

This wastes time. Always verify http://YOUR-HOST:9090/targets first.

3. Forgetting to replace the Proxmox target IP in prometheus.yml

I have seen people copy a config, stand up the stack, and then wonder why Grafana is still blank. It turns out they were scraping somebody else's example IP. Computers are ruthless about details.

4. Leaving the exporter open on all interfaces

Bind it to 127.0.0.1 unless you have a good reason not to. Metrics endpoints should not be casually exposed.

5. Blaming Grafana for a Prometheus problem

If Prometheus does not scrape the exporter successfully, Grafana cannot rescue the situation. Check the stack in this order:

  1. Proxmox token and permissions
  2. exporter container logs
  3. Prometheus target status
  4. Grafana datasource
  5. imported dashboard settings

Troubleshooting checklist

If the dashboard shows no data, walk through these checks in order:

# Is the exporter container running?
docker compose ps

# Does the exporter respond locally?
curl -s http://127.0.0.1:9221/metrics | head

# Can Prometheus see the config you expect?
docker compose exec prometheus cat /etc/prometheus/prometheus.yml

# Reload Prometheus after config changes if needed
curl -X POST http://localhost:9090/-/reload

Then confirm in the UI:

  • Prometheus -> Status -> Targets -> pve is UP
  • Grafana -> Connections -> Data sources -> Prometheus is healthy
  • Imported dashboard is using the same datasource you configured

If you want a broader monitoring foundation after this, my article on homelab monitoring with Prometheus and Grafana expands the stack beyond Proxmox specifically. And if you are building Docker-based services next, these Docker Compose best practices will save you some future cleanup.

FAQ

Do I need to run Grafana and Prometheus on the Proxmox host itself?

No. In most homelabs, I prefer a separate VM or Docker host. It keeps the monitoring stack isolated and makes maintenance easier.

Can I use a password instead of an API token?

Yes, but I do not recommend it if token auth is available. Tokens are cleaner, easier to rotate, and generally a better habit.

Should I monitor every Proxmox node separately?

Yes. If you have multiple nodes, add each node as a target in Prometheus so you get complete node-level visibility.

What if my Proxmox node uses a self-signed certificate?

Use verify_ssl: false in pve.yml as the short-term fix. Long term, trusted certificates are the better answer.

Is this enough for alerting too?

It is enough to get started with visibility. Once the data is flowing, you can add Prometheus alert rules or an Alertmanager stack later.

What to learn next

Once you have Proxmox metrics working, the next useful improvements are:

  • add node exporter on important Linux VMs for OS-level metrics
  • monitor your backup jobs and storage pool health
  • put Grafana behind HTTPS and SSO
  • build a few simple alerts for disk exhaustion, host memory pressure, and guest downtime

For official references, keep these handy:

If you came into this feeling overwhelmed by the terminology, that is normal. The trick is to stop treating monitoring like one giant product and start treating it like a small chain of jobs. Proxmox exposes data. The exporter translates it. Prometheus stores it. Grafana shows it. Once that clicks, the setup becomes much easier to manage.

And once you have historical graphs for your cluster, a whole category of weird homelab problems stops feeling mysterious. Which is nice, because the homelab will always invent new ways to be weird.