# prometheus-pve-exporter reads Proxmox over its API and turns it into gauges

> The Prometheus PVE exporter is a Python program that scrapes a Proxmox VE node through its API and exposes node, VM, container, storage, backup and replication state as Prometheus metrics, with per-collector switches and a multi-target URL scheme. It fits an operator who wants Proxmox in their existing Prometheus stack, and it does not fit a deployment that cannot give the exporter API credentials.

**prometheus-pve/prometheus-pve-exporter** — Exposes information gathered from Proxmox VE cluster for use by the Prometheus monitoring system

- Repository: https://github.com/prometheus-pve/prometheus-pve-exporter
- Stars: 1,477 · Forks: 135
- Language: Python
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/prometheus-pve-prometheus-pve-exporter

## It is a multi-target exporter, and the URL carries the target

The Prometheus PVE exporter exposes information gathered from a Proxmox VE node for use by the Prometheus monitoring system. The design is a multi-target exporter, and the mechanism is in the request URL rather than in a configuration file.

The example is http://localhost:9221/pve?target=1.2.3.4&cluster=1&node=1, where 1.2.3.4 is the IP of the Proxmox VE node to get metrics from. The target request parameter defaults to localhost, so if pve_exporter is deployed directly on the Proxmox host, target can be omitted. And the module request parameter selects which module to use from the config file.

That shape is the standard multi-target pattern, and it is what lets one exporter instance serve a whole cluster. The exporter is a single process that Prometheus scrapes many times, once per node, with the target in the query string. The node and cluster parameters are switches rather than configuration: cluster collectors are run if the url parameter cluster=1 is set and skipped if it is 0, and the same is true of node.

The default listen address is [::]:9221, and the README notes that using [::] binds to both IPv6 and IPv4 sockets on dual stacked machines.

One thing to be clear about is that the exporter does not push. Prometheus scrapes it, and the exporter answers by calling the Proxmox API for the node named in the target parameter. The metrics you get are a view of the API at scrape time.

## Collectors are switches, and the config one scales badly

The collector list is the feature set, and every entry has a matching --collector.X and --no-collector.X flag so you can enable or disable it at startup.

The cluster collectors are status, which exposes Node/VM/CT-Status, version for PVE version info, node for PVE node info, cluster for PVE cluster info, resources for PVE resources info, backup-info for information about guests which are not covered by any backup job, and qdevice for PVE QDevice connection state.

The node collectors are config, which exposes PVE onboot status, replication for PVE replication info, and subscription for PVE subscription info.

The scrape collectors are about the exporter itself: pve-api-metrics exposes the duration of PVE API calls, and target-metrics exposes the duration of scrapes by target. Those are available from the /metric path rather than the /pve path, which is the usual Prometheus convention for exporter self-instrumentation.

The performance warning is specific and worth heeding. The config collector results in one API call per guest VM or CT, and the README therefore recommends disabling this collector using the --no-collector.config flag on big deployments. That is a real design constraint rather than a tuning hint, because on a host running hundreds of guests the config collector alone will dominate the scrape duration and the number of API calls, and Proxmox's API is not a free resource.

The backup-info collector answers a question most monitoring setups do not ask: which guests are not covered by any backup job. That is an operational metric rather than an infrastructure one, and it is the kind of thing that turns a monitoring tool into a safety net.

## The metrics are gauges keyed by node, guest or storage

The exported metric names are simple, and the id label carries the object type, which is the design decision to understand.

pve_up is a gauge for whether a node, VM or CT is online or running, and the ids in the example are node/proxmox, qemu/100, and storage/proxmox/local, storage/proxmox/local-lvm and storage/proxmox/vms. So one metric covers nodes, QEMU virtual machines and storages, distinguished by the prefix of the id label rather than by separate metric names.

pve_disk_size_bytes reports the storage size in bytes for the type storage, and the root image size for VMs, for the types qemu and lxc. pve_disk_usage_bytes reports the used disk space in bytes for storage, and the used root image space for VMs.

That dual meaning is convenient for alerting, because a single recording rule covers both a full filesystem and a virtual disk, but it is also a trap for anyone writing a query. A panel that sums pve_disk_usage_bytes across all ids mixes node root images with guest root images and with filesystem usage, and the result is not a meaningful number. The type prefix in the id label is the only way to separate them, which means every meaningful query starts with a label filter.

The naming is otherwise conventional Prometheus style, with the pve_ prefix, HELP and TYPE lines, and gauge as the type for values that go up and down.

## Installing is pip or a container, and the container runs as a non-root user

The installation section gives two routes, and the README notes that Python 3.9 or better is required.

With pip:

```shell
python3 -m pip install prometheus-pve-exporter
pve_exporter --help
```

With Docker, the image is pulled and the container run:

```shell
docker pull prompve/prometheus-pve-exporter
```

```shell
docker run -it --rm prompve/prometheus-pve-exporter --help
```

And the example that shows a real deployment mounts a configuration file and publishes the port:

```shell
docker run --init --name prometheus-pve-exporter -d -p 127.0.0.1:9221:9221 -v /path/to/pve.yml:/etc/prometheus/pve.yml prompve/prometheus-pve-exporter
```

The exporter is then reachable at http://localhost:9221/.

Two details in that command are deliberate. The port mapping binds to 127.0.0.1 rather than 0.0.0.0, and the README explains why: the HTTP endpoints provided by Prometheus components should not be exposed to publicly accessible networks like the internet, unless you know what you are doing and have taken appropriate measures. The exporter is subject to the threats outlined in the Prometheus security model.

The Dockerfile does the same thing inside the image. The final stage creates a group and a user with uid and gid 101, both named prometheus, switches to that user with USER prometheus, and exposes 9221. The entrypoint is the venv's pve_exporter.

The build stage is more elaborate than it needs to be for a Python package, and the reason is in the wheel command. It builds with --no-binary cffi --no-binary pyyaml, so the two packages with native extensions are compiled rather than pulled as wheels. The requirements file is generated by pip-compile with --generate-hashes --strip-extras, which means every dependency is pinned with hashes, a supply chain practice worth copying into any container build.

## Configuration lives in a YAML file, and the CLI mirrors Prometheus conventions

The config file defaults to /etc/prometheus/pve.yml, and the --config.file option points somewhere else. The repository ships a pve.yml at the root, and the module request parameter selects which module from that file to use, which is how a single exporter can present different credential sets or different scopes per target.

The command-line flags follow the naming conventions that Prometheus exporters have standardised on, and that convention is worth understanding. The --web.listen-address flag follows the pattern of web.listen-address, the --server.keyfile and --server.certfile flags follow server.keyfile and server.certfile, and the --collector.X pattern is the community convention for enabling and disabling collectors by name. The net result is that a config file for this exporter looks like a config file for any other Prometheus component, which matters when you are managing a fleet of exporters.

SSL is supported through the keyfile and certfile pair, so the exporter's own metrics endpoint can be served over TLS rather than plaintext.

The packaging metadata says requires-python >= 3.11, which is higher than the Python 3.9 or better mentioned in the installation text. The pyproject lists the development status as 3 - Alpha, and the build system requires setuptools and setuptools-scm, with the version and dependencies read dynamically from requirements.in and the readme from README.rst. The console script is pve_exporter, mapped to pve_exporter.cli:main.

## Where the exporter is the wrong tool

The first wrong-tool case is a Proxmox deployment where the exporter cannot hold API credentials. Every collector here works by calling the Proxmox API, so the exporter needs a token or an API user with rights to read node, guest, storage and cluster state, including the rights to see the guest configurations for the onboot status and the backup job list. A locked-down Proxmox installation that will not issue credentials to a monitoring system has to find another route to the same data, and that route is usually another exporter or a pushgateway.

The second is a monitoring system that is not Prometheus. The whole interface, the multi-target URL, the collectors, the gauges, the self-instrumentation on the /metric path, is Prometheus-shaped. The data is easy to scrape, but there is no push mode, no OpenMetrics exposition for another system, and no other collector protocol described in the documentation.

The third is granularity. This is a node-level and guest-level exporter. It tells you that a guest is up, how much disk a storage has, whether a guest is covered by a backup job, and what the replication state is. It does not tell you the CPU or memory a guest is consuming inside Proxmox, because that is not what the Proxmox API reports, and it does not replace node_exporter for the physical host. A monitoring plan needs both, and the dashboards are where people discover the difference.

The fourth is the config collector cost, which is not a limitation of the design but a limit in practice. On a cluster with many guests, leaving it enabled turns a scrape into a burst of API calls proportional to guest count, and the README's own recommendation is to disable it on big deployments. That means a deliberate trade: you lose the onboot visibility for most guests, or you accept a slow scrape, and a team should decide which rather than discover it during an incident.

## Conclusion

prometheus-pve-exporter fits a team already running Prometheus that wants Proxmox node, guest, storage and replication state in the same dashboards, and it does not fit a Proxmox deployment where the exporter cannot hold API credentials. Before deploying, read the collector list and turn off the ones you do not need, because the README warns that the config collector makes one API call per guest VM or container and recommends disabling it on big deployments.

## FAQ

### How do I install the Prometheus PVE exporter?

With pip, using python3 -m pip install prometheus-pve-exporter, or with Docker by pulling prompve/prometheus-pve-exporter. The README states that Python 3.9 or better is required for the installation, while the package metadata declares requires-python >= 3.11.

### How do I run the Prometheus PVE exporter in Docker?

Mount a configuration file and publish the port with docker run --init --name prometheus-pve-exporter -d -p 127.0.0.1:9221:9221 -v /path/to/pve.yml:/etc/prometheus/pve.yml prompve/prometheus-pve-exporter. The exporter is then reachable at http://localhost:9221/.

### Which collectors should I disable on a large Proxmox cluster?

The README states that the config collector results in one API call per guest VM or CT, so it is recommended to disable it with the --no-collector.config flag on big deployments. Every collector has a matching --collector.X and --no-collector.X flag.

### How does the exporter know which Proxmox node to scrape?

Through the target request parameter, for example http://localhost:9221/pve?target=1.2.3.4&cluster=1&node=1. The target defaults to localhost, so it can be omitted when the exporter runs on the Proxmox host, and the module request parameter selects which module to use from the config file.

## Sources

- [Issues](https://github.com/prometheus-pve/prometheus-pve-exporter/issues)
- [License: Apache-2.0](https://github.com/prometheus-pve/prometheus-pve-exporter/blob/main/LICENSE)
- [prometheus-pve/prometheus-pve-exporter on GitHub](https://github.com/prometheus-pve/prometheus-pve-exporter)
- [README](https://github.com/prometheus-pve/prometheus-pve-exporter/blob/main/README.md)
- [Releases](https://github.com/prometheus-pve/prometheus-pve-exporter/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/prometheus-pve-prometheus-pve-exporter
