pvewatch
A single container that tells you when Proxmox backups fail, VMs go down or storage fills up. No hook scripts, no Prometheus required, no writes to the cluster.
Problem
Proxmox records every backup as a task, and a failed task is one line in a log you have to go looking for. The common "back up all VMs" job is worse: it is a single task with no VM attached, so a failure inside it has no owner and no history. Grafana-based setups answer this with exporters, dashboards and alert rules that take longer to set up than the backup schedule itself.
What I built
pvewatch polls the Proxmox API with a read-only auditor token every 15 minutes, keeps per-VM backup history in SQLite by default (PostgreSQL is optional), and alerts by email or Discord with the last 20 lines of the failing task log. A read-only dashboard shows a per-VM heatmap over 3 to 30 days, storage pool usage deduplicated across nodes, and a Sunday digest. The same data feeds a JSON API and a Prometheus endpoint, so it slots into an existing stack when there is one.
If the monitor itself dies it cannot tell you, so it pings a heartbeat URL after every successful poll. The external service alerts when the pings stop, which also catches "container up, Proxmox unreachable".
How it works
One data builder runs the queries once and feeds all three outputs, so a change to how failures are counted shows up everywhere at once.
The decision
Parse the batch log instead of changing the user's backup job. A "back up all VMs" task has no VM ID. pvewatch fetches the full task log, parses the per-VM start and finish lines, and stores each result under a synthetic ID made from the batch task and the VM. The batch ID is remembered so the log is never fetched twice. Users keep the backup mode they already have, and the heatmap still fills in per VM.
Also decided, and written down in the repo: SQLite as the zero-setup default, the standard-library HTTP server over a framework, an inline template with no frontend build, and no API versioning for a self-hosted single-user tool. Each has a dated entry in DECISIONS.md with the reasoning.
Evidence
- CI
ruff, pytest with coverage, a SonarQube quality gate on merge requests and main; a public GitHub Actions pipeline lints, tests and builds the image. - Distribution
ghcr.io image for amd64 and arm64, semver tags from git tags, Kubernetes manifests included. - In production
Watches my own Proxmox host; the heartbeat goes to an external check that alerts when the pings stop.