Files
PVE-Backup-Report/README.md
T

8.8 KiB

📄 PVE Backup Report

Version Python License Docker Security

Python tool to generate a daily backup report for Proxmox VE against Proxmox Backup Server.

Documentation

Features

  • 📄 Timestamped daily PDF report, without overwriting previous reports.
  • 🧭 Inventory of PBS storages declared in Proxmox VE.
  • Coverage analysis for QEMU VMs and LXC containers.
  • 📝 PVE VM/CT notes included in the report.
  • 🕒 Retrieval of the latest known backups via PVE tasks and vzdump logs.
  • 🗄️ Optional collection of PBS datastores, namespaces, prune jobs, snapshots and storage usage.
  • 🔐 Audit of PBS backup accounts and effective permissions.
  • 📊 PBS retention with expected version counts, delta and PVE state for VM/CT entries.
  • ⚠️ Anomalies section for errors or partial collection data.
  • 🐳 Recommended execution with Docker, or direct CLI execution.

🔐 Prerequisites

Create dedicated API users or tokens for this reporting application. They must be limited to audit/read-only permissions:

  • Proxmox VE: assign only the PVEAuditor role to the user or API token used by PVE_API_TOKEN_ID.
  • Proxmox Backup Server: assign only the Audit role to the user or API token used by each PBS<number>_API_TOKEN_ID.

Do not use administrator or write-enabled accounts. The application only needs to read inventory, backup jobs, tasks, datastores, namespaces, snapshots and retention data.

🐳 Usage with Docker

Docker is the recommended execution mode. The image includes the Python dependencies and system libraries required by WeasyPrint.

📦 Setup

cp .env.example .env
mkdir -p reports

Then edit .env with your PVE cluster and PBS connection details.

In Docker, keep:

REPORT_OUTPUT_DIR=/reports

The compose.yaml file mounts the local ./reports directory into the container at /reports. Generated PDFs are therefore visible on the host in ./reports/.

⚙️ Minimal configuration

PVE_API_URL=https://pve.example.invalid:8006
PVE_API_TOKEN_ID=backup-report@pve!report
PVE_API_TOKEN_SECRET=change-me
REPORT_OUTPUT_DIR=/reports
REPORT_TIMEZONE=Europe/Paris
REPORT_LANGUAGE=fr
PVE_VERIFY_TLS=true

To enable PBS collection, provide the URL, token ID and secret for the relevant PBS. An incomplete configuration does not enable PBS collection.

Example with one PBS:

PBS01_NAME=nom-affiche
PBS01_API_URL=https://backup.example.invalid:8007
PBS01_API_TOKEN_ID=
PBS01_API_TOKEN_SECRET=

One or more PBS servers can be configured. To add a server, duplicate the block and increment the prefix number: PBS02_*, PBS03_*, PBS04_*, etc. The application automatically detects all PBS<number>_* blocks present in the environment.

🏗️ Build

docker compose build

Verification

docker compose run --rm pve-backup-report --check-config
docker compose run --rm pve-backup-report --check-api

--check-api tests the main PVE endpoints. If /cluster/backup returns HTTP 403 - Permission check failed (/, Sys.Audit), the token works but is missing the Sys.Audit privilege on /.

📄 Generating the report

docker compose run --rm pve-backup-report --generate-pdf

The PDF report is written to ./reports/ with a timestamped filename and never overwrites a previous report.

🧪 Docker diagnostic commands

docker compose run --rm pve-backup-report --dump-inventory
docker compose run --rm pve-backup-report --dump-coverage
docker compose run --rm pve-backup-report --dump-report-data
docker compose run --rm pve-backup-report --dump-pbs-storage-usages
docker compose run --rm pve-backup-report --dump-pbs-users
docker compose run --rm pve-backup-report --debug-last-backup-vmid <VMID>

Running the container without arguments only executes the default pve-backup-report command. To generate a PDF, use --generate-pdf explicitly.

🕒 Scheduling with cron and Docker

Example crontab on the host to run the report every day at 02:00:

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

0 2 * * * cd /srv/pve-backup-report && /usr/bin/flock -n /tmp/pve-backup-report.lock /usr/bin/docker compose run --rm pve-backup-report --generate-pdf >> /var/log/pve-backup-report.log 2>&1

Replace /srv/pve-backup-report with the actual path to the repository. The cd is important: Docker Compose finds compose.yaml there and the application loads .env from it.

💻 Direct command-line usage

This mode is useful for development or diagnostics outside a container. The host must have Python, the project's Python dependencies and the system libraries required by WeasyPrint.

📦 Local installation

python3 -m venv .venv
. .venv/bin/activate
python -m ensurepip --upgrade
pip install -r requirements.txt
pip install -e .

To run the tests:

pip install -e ".[dev]"
pytest

🧰 Installed commands

pve-backup-report --check-config
pve-backup-report --check-api
pve-backup-report --dump-inventory
pve-backup-report --dump-coverage
pve-backup-report --dump-report-data
pve-backup-report --dump-pbs-storage-usages
pve-backup-report --dump-pbs-users
pve-backup-report --generate-pdf
pve-backup-report --debug-last-backup-vmid <VMID>

Without an editable install, from the repository:

PYTHONPATH=src python3 -m pve_backup_report --check-config
PYTHONPATH=src python3 -m pve_backup_report --check-api
PYTHONPATH=src python3 -m pve_backup_report --dump-inventory
PYTHONPATH=src python3 -m pve_backup_report --dump-coverage
PYTHONPATH=src python3 -m pve_backup_report --dump-report-data
PYTHONPATH=src python3 -m pve_backup_report --dump-pbs-storage-usages
PYTHONPATH=src python3 -m pve_backup_report --dump-pbs-users
PYTHONPATH=src python3 -m pve_backup_report --generate-pdf
PYTHONPATH=src python3 -m pve_backup_report --debug-last-backup-vmid <VMID>

In direct local execution, REPORT_OUTPUT_DIR can remain reports/ or point to any other directory writable by the current user.

The same .env file can be used in Docker and in local execution. If REPORT_OUTPUT_DIR=/reports is kept outside Docker and /reports is not accessible, the application automatically falls back to the local reports/ directory and logs the fallback.

🕒 Scheduling with cron without Docker

Example using the project's virtual environment:

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

0 2 * * * cd /srv/pve-backup-report && /usr/bin/flock -n /tmp/pve-backup-report.lock /srv/pve-backup-report/.venv/bin/pve-backup-report --generate-pdf >> /var/log/pve-backup-report.log 2>&1

The user running the crontab must have read access to .env and write access to REPORT_OUTPUT_DIR.

🔧 Key variables

Variable Description
PVE_API_URL URL of a reachable PVE node, e.g. https://pve.example.invalid:8006.
PVE_API_TOKEN_ID Full token identifier, e.g. backup-report@pve!report.
PVE_API_TOKEN_SECRET Token secret.
REPORT_LANGUAGE PDF report language, fr or en. Default: fr.
PVE_VERIFY_TLS Keep true in production; use PVE_CA_BUNDLE for an internal CA.
PVE_TASK_HISTORY_LIMIT Number of recent PVE tasks inspected to find the latest backup.
PVE_TASK_LOG_LIMIT Number of lines retrieved per vzdump log to extract per-VM/CT detail.
PBS_HOSTNAMES Optional manual mapping of PBS servers as address=display-name,address2=display-name2.
PBS<number>_* Optional PBS API configurations, e.g. PBS01_*, PBS02_*, PBS10_*.
REPORT_FILENAME_PREFIX Prefix for the generated PDF filename.

📄 PDF output

--generate-pdf generates a timestamped PDF in REPORT_OUTPUT_DIR. The Last backup column shows the status, date, time and duration when PVE provides that information. The PBS VM/CT backup retention <PBS> tables are split by namespace and show the datastore, the number of PBS versions, and the oldest and most recent backup visible on each configured PBS.

🤖 AI Usage

The application was entirely coded using Codex and Claude Code. I would never have had enough time to build the application on my own in such a short period of time.

⚖️ License

This project is distributed under the Apache License 2.0. See the LICENSE file.