Skip to content

Proxmox Backup Monitoring with a vzdump Hook Script

Updated 4 November 2026 · Tested with simulated vzdump hook calls, not on a Proxmox node

To monitor Proxmox VE backups, add a vzdump hook script that pings a heartbeat monitor when a backup job starts and ends. The script below sends a failure if any guest in the job failed, and a success only when every guest was backed up. Once the check has received its first success ping, a job that never runs, because the schedule was changed or the node is down, means no ping arrives and Watchgoose alerts you when the backup is overdue.

Table of contents

Why the built-in email is not enough

Proxmox VE can email a report after each backup job. That tells you about jobs that ran and failed, but it cannot tell you about a job that never ran. A disabled job, a broken schedule, a node that was off at the backup time or a mail server that stopped delivering all look the same: no email. A heartbeat check turns silence into an alert.

The job-end trap

vzdump calls the hook script at each phase of a backup job. The obvious approach is to ping "start" on job-start and "success" on job-end. That is wrong in a subtle way: vzdump calls job-end even when some guests failed. If one guest's backup fails, vzdump calls backup-abort for that guest, carries on with the rest, and still finishes with job-end. job-abort is only used when the whole job dies.

So the script below remembers every backup-abort in a small state file and sends a failure at job-end if anything failed.

Step 1: Create one check per backup job and node

Sign up and add a check for each backup job on each node, for example "pve1 nightly backup". Set its schedule to the job's schedule, for example 0 1 * * * for a nightly job at 01:00, and a grace time longer than the job usually takes. Copy the ping URL, which looks like https://watchgoose.com/ping/your-uuid-here.

Use a separate check for every job and every node. If two jobs pinged the same check, one job finishing would hide the other job never running.

Step 2: Install the hook script

Save this as /usr/local/bin/watchgoose-vzdump-nightly.sh on the Proxmox node and replace the URL with that node's check URL for this job:

#!/bin/sh
# vzdump hook script: report one backup job to Watchgoose.
# Use one copy of this script, with its own check URL, per backup job.

# vzdump runs hooks with an almost empty environment: set PATH explicitly
# instead of relying on the shell's default.
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH

URL="https://watchgoose.com/ping/your-uuid-here"
STATE="/run/$(basename "$0").failed"

ping_wg() {
    # Never let a monitoring problem fail the backup.
    curl -fsS -m 10 --retry 3 -o /dev/null "$@" || true
}

case "$1" in
    job-start)
        # job-start runs once vzdump holds the node's backup lock. Do not reset
        # on job-init: that phase runs before the lock, so a queued job would
        # wipe the record of a job that is still running.
        rm -f "$STATE"
        ping_wg "$URL/start"
        ;;
    backup-abort)
        # $2 = mode, $3 = guest ID. vzdump carries on with the next guest.
        echo "Backup of $VMTYPE $3 failed on $(hostname)" >> "$STATE"
        ;;
    job-end)
        # job-end also runs when some guests failed, so check our own record.
        if [ -s "$STATE" ]; then
            ping_wg --data-binary @"$STATE" "$URL/fail"
        else
            ping_wg --data-raw "vzdump job finished on $(hostname)" "$URL"
        fi
        rm -f "$STATE"
        ;;
    job-abort)
        ping_wg --data-raw "vzdump job aborted on $(hostname)" "$URL/fail"
        rm -f "$STATE"
        ;;
esac

exit 0

Make it executable:

chmod +x /usr/local/bin/watchgoose-vzdump-nightly.sh

Why the script looks the way it does:

  • vzdump runs hooks with an almost empty environment. Debian's /bin/sh falls back to a default PATH, but setting it explicitly keeps the script independent of that. If the hook still reports curl: not found, check that curl is installed (apt install curl) and executable.
  • It always exits 0. A non-zero exit from a hook in a job phase fails the backup job.
  • The failure ping carries the list of failed guests, so the alert says which VM or container failed.
  • It starts its record at job-start, not job-init. vzdump runs job-init before it takes the node's backup lock, so a second job waiting for the lock would otherwise erase the first job's failures.
  • Each copy keeps its own state file in /run, named after the script.

Step 3: Enable it for the backup job

Set the script on the backup job as root with pvesh. pvesh get /cluster/backup lists your jobs and their IDs:

pvesh set /cluster/backup/backup-1a2b3c4d-5e6f --script /usr/local/bin/watchgoose-vzdump-nightly.sh

The job's entry in /etc/pve/jobs.cfg then contains an indented script property, without a colon:

vzdump: backup-1a2b3c4d-5e6f
    schedule 01:00
    script /usr/local/bin/watchgoose-vzdump-nightly.sh

For another backup job, make another copy of the script with that job's check URL, for example watchgoose-vzdump-weekly.sh, and set it on that job in the same way.

/etc/vzdump.conf also accepts script: /usr/local/bin/watchgoose-vzdump-nightly.sh (with a colon), but that applies the script to every backup on the node. Use it only when the node runs a single backup job. For a manual run, pass the script on the command line:

vzdump 100 --script /usr/local/bin/watchgoose-vzdump-nightly.sh

Then run the job once by hand from Datacenter › Backup › Run now and check that Watchgoose shows a start ping followed by a success ping, and that the check's status changes from New to Up. A new check doesn't alert about missed runs until its first success ping arrives, so this first run is what arms it. A failure ping alerts right away, and after a start ping Watchgoose alerts if the job runs longer than the grace time.

Notes for clusters and Proxmox Backup Server

In a cluster, each node runs its own part of a backup job and calls the hook script at the same path on that node. Install the script at that path on every node the job covers, and put that node's own check URL in each copy, so you can see which node failed or did not run.

The script does not depend on the storage type, so it works the same whether the target is local storage, NFS or a Proxmox Backup Server datastore. If you test it on PBS storage or in a cluster, we would like to hear how it went.

Frequently asked questions

Does vzdump call job-end when a guest backup fails?

Yes. When one guest's backup fails, vzdump calls backup-abort for that guest, continues with the rest, and still finishes with job-end. job-abort is only used when the whole job fails. A hook script has to remember backup-abort to report a partly failed job.

Why does curl fail in my vzdump hook script?

First check that curl is installed and executable on the node (apt install curl). vzdump runs hook scripts with an almost empty environment, so set PATH at the top of the script instead of relying on the shell's default.

How do I get an alert when a Proxmox backup job did not run?

Proxmox's email report only covers jobs that ran. A heartbeat check with the job's schedule alerts you when the expected ping does not arrive, which also covers disabled jobs and nodes that were down.

References

Get an alert when this job fails or doesn't run

Watchgoose watches your scheduled jobs from the outside. Start free with 10 checks, no credit card.

Create a free account