Prometheus and Grafana
/api/v1/public/metrics is part of the public API, unlocked by the ProxCenter API Access add-on on an Enterprise edition. Without it every call on this path answers 403 Feature not licensed, and a Grafana dashboard built on it stays empty.
One scrape job covers your whole estate. ProxCenter answers with its own aggregated view of every cluster it manages, so there is nothing to install on any Proxmox node, and the response is served from ProxCenter's inventory cache rather than fanning out to the hypervisor on each scrape.
The exposition carries 53 metric families. Everything the five dashboards chart is in there, and the full list is in the series reference below.
The five dashboards
Each answers one question rather than scrolling through everything, and they link to one another from a ProxCenter dashboards menu that carries your time range and filters across.
| Dashboard | Answers |
|---|---|
| ProxCenter Fleet Overview | Is every cluster reachable, and has it been? Ceph health, high availability state, and the changes an instantaneous count hides, such as a node that bounced overnight |
| ProxCenter Node Performance | How hard are the hosts working, and what runs out first? Load average, CPU, memory headroom, I/O wait, swap, and the host root filesystem with a seven day projection |
| ProxCenter Guest Workload | What are the guests doing? Running state over time, the biggest CPU and memory consumers, and disk and network throughput |
| ProxCenter Storage | Where is the space going? Capacity per storage, with shared storages counted once for the cluster and non-shared ones broken down per node |
| ProxCenter Backup Compliance | Which guests are actually protected, how stale their backups are, and whether the Proxmox Backup Server datastores behind them have room left |
All five ship with the image and import into Grafana 11.0 or newer, using core panels only, so nothing has to be installed alongside them:
| File | Purpose |
|---|---|
/integrations/prometheus-scrape-config.yml | Scrape job to paste into your Prometheus configuration |
/integrations/grafana-dashboard-proxcenter.json | Fleet Overview |
/integrations/grafana-dashboard-proxcenter-nodes.json | Node Performance |
/integrations/grafana-dashboard-proxcenter-guests.json | Guest Workload |
/integrations/grafana-dashboard-proxcenter-storage.json | Storage |
/integrations/grafana-dashboard-proxcenter-backups.json | Backup Compliance |
The Backup Compliance dashboard counts guests that have never been backed up.
Why that count is a series of its own
That number cannot be derived from backup age alone, because a guest with no backup has no age to report, so a dashboard built on age silently leaves the least protected guests out of its own compliance figure.
The scrape job
Seeing the whole exposition takes four scopes: nodes:read, vms:read, storage:read and backups:read. Each governs its own families, and a token short of one still answers 200 without them. storage:read alone does not open the endpoint, so grant it alongside at least one of the other three:
scrape_configs:
- job_name: proxcenter
scheme: https
metrics_path: /api/v1/public/metrics
scrape_interval: 60s
scrape_timeout: 30s
authorization:
type: Bearer
credentials: pxc_REPLACE_WITH_YOUR_TOKEN
static_configs:
- targets:
- proxcenter.example.com
A token missing one of the four scopes still answers 200. The families it may not see are filtered out of the response rather than refused, so the panels that need them render empty with no error anywhere. If a whole row of a dashboard is blank, or the Storage dashboard is empty end to end, check the token's scopes first.
Keep the 60 second interval. The values come from a cache refreshed on its own schedule, so a tighter scrape buys no fresher data.
Series reference
Every series is a gauge unless marked as a counter, and every family is filtered independently by the calling token's scopes.
The four guest byte totals are cumulative since the guest started, which is why their names end in _total. Read them with rate(), never as a value: rate(proxcenter_vm_network_receive_bytes_total[5m]) gives bytes per second. Declared as counters, Prometheus handles the reset when a guest restarts instead of reading it as an enormous negative rate.
Build (no scope, any valid token)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_build_info | version | ProxCenter build information |
Clusters (scope nodes:read)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_cluster_up | connection, type | Cluster or standalone node reachability (1 online, 0 otherwise) |
proxcenter_cluster_degraded | connection | Cluster in a degraded state (1 degraded, 0 otherwise) |
proxcenter_cluster_ceph_health | connection, health | Ceph health as a state set; health is one of ok, warn, err, unknown. Clusters without Ceph emit nothing |
Nodes (scope nodes:read)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_node_online | connection, node | Node online state (1 online, 0 otherwise) |
proxcenter_node_cpu_usage_ratio | connection, node | Node CPU usage ratio (0 to 1) |
proxcenter_node_mem_usage_ratio | connection, node | Node memory usage ratio (0 to 1) |
proxcenter_node_mem_bytes | connection, node | Node memory in use, in bytes |
proxcenter_node_mem_total_bytes | connection, node | Node memory capacity, in bytes |
proxcenter_node_rootfs_usage_ratio | connection, node | Node root filesystem usage ratio (0 to 1). This is the host root filesystem, NOT cluster storage capacity |
proxcenter_node_uptime_seconds | connection, node | Node uptime in seconds |
proxcenter_node_maintenance | connection, node | Node in maintenance mode (1 in maintenance, 0 otherwise) |
proxcenter_node_load1 | connection, node | Node load average over one minute |
proxcenter_node_load5 | connection, node | Node load average over five minutes |
proxcenter_node_load15 | connection, node | Node load average over fifteen minutes |
proxcenter_node_iowait_ratio | connection, node | Share of node CPU time spent waiting on I/O (0 to 1) |
proxcenter_node_swap_bytes | connection, node | Node swap in use, in bytes |
proxcenter_node_swap_total_bytes | connection, node | Node swap capacity, in bytes; 0 when the node has no swap |
proxcenter_node_rootfs_bytes | connection, node | Node root filesystem in use, in bytes |
proxcenter_node_rootfs_total_bytes | connection, node | Node root filesystem capacity, in bytes |
proxcenter_node_cpu_cores | connection, node | CPU cores the node reports |
proxcenter_node_info | connection, node, pve_version, kernel | Node build information: Proxmox VE version and running kernel |
Guests (scope vms:read)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_vm_status | connection, node, vmid, name, type | Guest running state (1 running, 0 otherwise) |
proxcenter_vm_cpu_usage_ratio | connection, node, vmid, name, type | Guest CPU usage ratio (0 to 1) |
proxcenter_vm_agent_enabled | connection, node, vmid, name | Guest agent config flag (1 enabled, 0 otherwise) |
proxcenter_vm_mem_usage_ratio | connection, node, vmid, name, type | Guest memory usage ratio (0 to 1) |
proxcenter_vm_mem_bytes | connection, node, vmid, name, type | Guest memory in use, in bytes |
proxcenter_vm_mem_total_bytes | connection, node, vmid, name, type | Guest memory allocation, in bytes |
proxcenter_vm_uptime_seconds | connection, node, vmid, name, type | Guest uptime in seconds |
proxcenter_vm_ha_state | connection, node, vmid, name, type, state | Guest HA state as a state set; guests not managed by HA emit nothing |
proxcenter_vm_cpu_cores | connection, node, vmid, name, type | Virtual CPU cores allocated to the guest |
proxcenter_vm_mem_host_bytes | connection, node, vmid, name, type | Guest memory as the PVE 9 host accounts it, distinct from the guest-side figure; 0 for a container, which reports none |
proxcenter_vm_network_receive_bytes_total (counter) | connection, node, vmid, name, type | Bytes received by the guest since it started |
proxcenter_vm_network_transmit_bytes_total (counter) | connection, node, vmid, name, type | Bytes sent by the guest since it started |
proxcenter_vm_disk_read_bytes_total (counter) | connection, node, vmid, name, type | Bytes read from the guest's disks since it started |
proxcenter_vm_disk_written_bytes_total (counter) | connection, node, vmid, name, type | Bytes written to the guest's disks since it started |
Storage (scope storage:read)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_storage_total_bytes | connection, storage, type, shared | Storage capacity in bytes. A shared storage is reported ONCE for the cluster, never once per node |
proxcenter_storage_used_bytes | connection, storage, type, shared | Storage space in use, in bytes. A shared storage is reported ONCE for the cluster, never once per node |
proxcenter_storage_usage_ratio | connection, storage, type, shared | Storage usage ratio (0 to 1), served pre-computed so a consumer never divides by a zero capacity |
proxcenter_storage_enabled | connection, storage | Storage enabled state (1 enabled, 0 disabled); a disabled storage still reports its capacity |
proxcenter_storage_node_total_bytes | connection, storage, node | Capacity of a non-shared storage on one node. Shared storages emit nothing here, since their capacity is not per node |
proxcenter_storage_node_used_bytes | connection, storage, node | Space in use of a non-shared storage on one node |
proxcenter_storage_node_usage_ratio | connection, storage, node | Usage ratio (0 to 1) of a non-shared storage on one node, served pre-computed |
Backups (scope backups:read)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_backup_age_seconds | connection, node, vmid, name, type, datastore | Seconds since the most recent backup of this guest |
proxcenter_backup_protected | connection, node, vmid, name, type | Guest backup coverage (1 has at least one backup, 0 has none) |
proxcenter_backup_age_seconds over the guestThe metric carries node, name and type next to connection, vmid and datastore, so a stale backup can be read without a join. In Prometheus a label is part of the series identity: a guest that migrates to another node starts a new series while the old one goes stale.
Aggregate over the guest explicitly rather than over the bare metric:
max by (vmid) (proxcenter_backup_age_seconds)
max by (connection, vmid) if several connections may carry the same VMID. The same applies to any recording rule or alerting rule built on this metric.
Proxmox Backup Server (scope backups:read)
| Series | Labels | Meaning |
|---|---|---|
proxcenter_pbs_up | connection | PBS server reachability (1 online, 0 otherwise) |
proxcenter_pbs_info | connection, version | PBS server build information |
proxcenter_pbs_datastore_total_bytes | connection, datastore | Datastore capacity in bytes; 0 for a backend that does not report one, such as S3 |
proxcenter_pbs_datastore_used_bytes | connection, datastore | Datastore space in use, in bytes |
proxcenter_pbs_datastore_available_bytes | connection, datastore | Datastore space available, in bytes |
proxcenter_pbs_datastore_usage_ratio | connection, datastore | Datastore usage ratio (0 to 1), served pre-computed so a consumer never divides by a zero capacity |
proxcenter_pbs_datastore_snapshots | connection, datastore | Snapshots held in this datastore |
proxcenter_pbs_datastore_guests | connection, datastore, kind | Distinct backup sources in this datastore, by kind (vm, ct, host) |
Three families are absent rather than wrong, and every case is deliberate:
proxcenter_vm_agent_enabledis missing on most installs. ProxCenter builds its inventory from the Proxmoxcluster/resourcesendpoint, which carries no guest agent configuration flag. The flag is therefore unknown, and an unknown is omitted rather than published as a misleading0that would report a working agent as absent.proxcenter_vm_ha_statecovers only the HA estate. A guest that HA does not manage emits nothing at all, so counting this family gives you the size of the HA estate, not of the fleet.proxcenter_backup_age_secondsskips a guest that has never been backed up, because it has no age to report. That is exactly whyproxcenter_backup_protectedexists: it publishes a0for those guests so they can be counted.
Three values are surprising but correct:
- A PBS datastore capacity of
0means the storage backend does not report one, which is normal for a datastore backed by S3. The space in use is still real, which is whyproxcenter_pbs_datastore_usage_ratiois served pre-computed: a dashboard dividing used by capacity would divide by zero. - A shared storage emits no per-node series. A Ceph pool or an NFS export has one capacity for the whole cluster, and Proxmox reports it once per node. Summing the raw figures would triple an RBD pool on a three node cluster, so
proxcenter_storage_*carries shared storages once and only breaks non-shared ones down per node. proxcenter_vm_mem_host_bytesreads0for a container. It is the PVE 9 host-side accounting of a guest's memory, which containers do not report.