A backup you have never tested is a guess, not a backup. Proxmox Backup Server (PBS) deduplicates and stores your VM and container backups as content-addressed chunks on disk, and like any data sitting on a physical drive, those chunks can degrade over time from bit rot, failing sectors, filesystem corruption, or a bad RAID rebuild. The failure mode is quiet: nothing alerts you until the day you actually need to restore, and by then it is too late to fix. Verify Jobs are PBS's built-in answer to this problem. They periodically re-read stored backup data, recompute checksums, and flag anything that no longer matches what was originally written — catching corruption while you still have time to re-run a fresh backup, instead of finding out during a disaster recovery.
This guide covers what Verify Jobs actually check, how they differ from pruning and garbage collection, and how to configure them through both the PBS web interface and the command line, including scheduling, namespace scoping, thread tuning, and how to read the results.
What a Verify Job Actually Does
Every backup snapshot stored in PBS is broken into fixed- or variable-size chunks, each identified by a SHA-256 hash of its contents. When you run a verify job, PBS walks through the selected snapshots, reads every referenced chunk back off disk, recomputes its hash, and compares that hash against the one recorded in the snapshot's manifest. If they match, the chunk is intact. If they don't — because of a flipped bit, a truncated file, or a disk read error — the chunk is marked corrupt and the snapshot is flagged as failed verification.
This is fundamentally different from what happens during a normal backup or restore. A backup write and a restore read only touch the specific chunks involved in that operation; they don't proactively scan data that isn't currently being used. A chunk that was written six months ago and hasn't been touched since could be silently corrupted on disk, and you would have no way of knowing until you tried to restore from a snapshot that references it — unless a verify job had already read it and told you.
Verification vs. Pruning vs. Garbage Collection
These three maintenance operations are easy to conflate because they all run against the same datastore, but they solve completely different problems:
- Prune jobs remove old backup snapshots according to a retention policy (keep-last, keep-daily, keep-weekly, and so on). Pruning only touches metadata — it decides which snapshots should no longer exist, but it does not free any disk space or check data integrity.
- Garbage collection (GC) runs after pruning and physically deletes chunks on disk that are no longer referenced by any remaining snapshot, reclaiming storage space. GC also has a safety mechanism that avoids removing chunks currently in use, but it does not validate the correctness of chunks that are kept.
- Verify jobs don't remove or reclaim anything. Their only job is to confirm that the chunks backing your existing snapshots are byte-for-byte what they should be, and to record a pass/fail verification state against each snapshot.
You need all three working together: prune to enforce retention, GC to reclaim space from pruned data, and verify to make sure the data you're keeping is actually trustworthy. A datastore that only prunes and garbage-collects can silently accumulate corrupted "successful" backups for months without anyone noticing.
Prerequisites
- A working Proxmox Backup Server instance with at least one datastore that already has backup snapshots in it.
- Administrative access to the PBS web UI, or root/API-token shell access for the CLI method.
- Enough spare I/O headroom to tolerate a full read pass over your datastore — verification is read-intensive and will compete with concurrent backup jobs for disk throughput.
If you haven't yet connected PBS to your Proxmox VE cluster as a storage target, or haven't configured retention with prune and garbage-collection jobs, set those up first — verify jobs are meant to complement that pipeline, not replace it.
Creating a Verify Job from the Web UI
- Log in to the PBS web interface and select your datastore from the left-hand tree.
- Open the Verify Jobs tab. This is a dedicated tab, separate from Prune & GC and from Sync Jobs — don't confuse the three.
- Click Add to open the new job dialog.
- Fill in the fields:
- Datastore — pre-filled with the datastore you're currently viewing.
- Namespace — leave blank to verify the root namespace, or pick a specific namespace if you use PBS's namespace feature to separate backups by tenant, environment, or Proxmox VE cluster.
- Max Depth — how many namespace levels deep the job should recurse (0–7, default 7, which covers everything).
- Skip Verified (maps to
ignore-verified) — when enabled, snapshots that already passed verification are skipped on subsequent runs, saving I/O. - Re-Verify After (maps to
outdated-after) — the number of days after which a previously-verified snapshot is treated as "outdated" and re-checked anyway, even with Skip Verified enabled. This is what lets you catch bit rot on data that hasn't changed. - Schedule — a calendar-event string controlling when the job runs automatically, for example
dailyorsun 03:00.
- Click Create. The job now appears in the Verify Jobs list, where you can see its next scheduled run, edit its settings, or trigger it manually with the Run now button.
You can also verify data without creating a recurring job at all. From the datastore's Content tab, the Verify All button at the top runs a one-off verification pass over every snapshot currently shown, and each individual snapshot row has its own verify action in the row's icon menu if you only want to spot-check one backup.
Creating a Verify Job from the CLI
Everything configurable in the GUI has a CLI equivalent through proxmox-backup-manager, which is useful for scripting datastore setup or managing PBS entirely over SSH.
To create a scheduled verify job:
proxmox-backup-manager verify-job create verify-daily \
--store mydatastore \
--schedule "daily" \
--ignore-verified true \
--outdated-after 30
This creates a job named verify-daily against the mydatastore datastore, running once a day, skipping snapshots that were successfully verified within the last 30 days and re-checking anything older than that.
To list existing verify jobs and confirm the one you just created:
proxmox-backup-manager verify-job list
To inspect a single job's configuration:
proxmox-backup-manager verify-job show verify-daily
To change a setting on an existing job — for example tightening the re-verification window to two weeks:
proxmox-backup-manager verify-job update verify-daily --outdated-after 14
To remove a job you no longer need:
proxmox-backup-manager verify-job remove verify-daily
If you just want to run an immediate, unscheduled verification pass against a datastore without creating a persistent job at all, use the direct verify subcommand instead:
proxmox-backup-manager verify mydatastore \
--ignore-verified false \
--read-threads 2 \
--verify-threads 4
Setting --ignore-verified false here forces a full re-check of every snapshot regardless of prior verification state — useful for an on-demand integrity audit, for example right after suspecting a hardware issue on the storage backing the datastore.
The verification.cfg File
Like most PBS job types, verify jobs are ultimately stored as plain-text entries in a configuration file at /etc/proxmox-backup/verification.cfg. You won't normally need to hand-edit this file — the GUI and CLI both write to it — but knowing its format is useful for auditing a PBS host's configuration or replicating settings across multiple servers. A single job entry looks like this:
verification: verify-daily
store mydatastore
schedule daily
ignore-verified true
outdated-after 30
The full set of properties available in this file:
| Property | Type | Default | Purpose |
|---|---|---|---|
store | string (required) | – | The datastore this job verifies. |
schedule | calendar-event | – | When the job runs automatically. Omit for a manual-only job. |
ignore-verified | boolean | true | Skip snapshots already marked as verified. |
outdated-after | integer (days) | – | Re-verify snapshots older than this, even if ignore-verified is true. |
ns | string | root | Restrict the job to a specific namespace. |
max-depth | integer (0–7) | 7 | How many namespace levels to recurse into. |
read-threads | integer (1–32) | 1 | Parallel chunk-reading threads. |
verify-threads | integer (1–32) | 4 | Parallel checksum-verification threads. |
comment | string | – | Free-text description for your own reference. |
Choosing a Schedule: The Two-Tier Approach
A single verify job configuration rarely fits every situation well, because there's a real tradeoff between thoroughness and I/O load. The Proxmox documentation and community practice both converge on the same pattern: run two separate jobs with different scopes.
- A frequent, narrow job — daily or even hourly, with
ignore-verified trueand nooutdated-afterset (or a long one). This catches newly written backups and expiring snapshots quickly, without re-reading data that was already confirmed good. - An infrequent, exhaustive job — weekly or monthly, with a short
outdated-afterwindow (orignore-verified falsefor a full sweep) that forces re-verification of everything, including data that hasn't changed in months. This is what actually catches bit rot: silent degradation of chunks that no backup or restore operation has touched recently.
In practice, something like this pair of CLI commands gives you both layers:
proxmox-backup-manager verify-job create verify-new \
--store mydatastore --schedule "daily" --ignore-verified true
proxmox-backup-manager verify-job create verify-full-sweep \
--store mydatastore --schedule "sun 02:00" --outdated-after 7
The second job's outdated-after 7 means that even if the daily job already verified a snapshot earlier in the week, the Sunday sweep treats anything older than seven days as due for re-checking, giving you a rolling full-datastore audit roughly once a week.
Namespace Scoping for Multi-Tenant or Multi-Cluster Datastores
If you use PBS namespaces to separate backups — for example one namespace per Proxmox VE cluster, per customer, or per environment (production vs. staging) — you don't have to verify the entire datastore in one job. Setting the ns property scopes a job to a specific namespace tree, and max-depth controls how far into sub-namespaces it recurses:
proxmox-backup-manager verify-job create verify-prod-only \
--store mydatastore --ns customer-a/production --max-depth 1 \
--schedule "daily"
This is useful when different namespaces have different criticality or different SLAs — you might want production backups re-verified nightly while a rarely-touched archive namespace only gets checked monthly, all against the same physical datastore.
Tuning Thread Counts
The read-threads and verify-threads parameters control how much of the verification workload runs in parallel. Reading chunks off disk and computing SHA-256 hashes are different kinds of work — one is I/O-bound, the other is CPU-bound — which is why PBS lets you tune them independently.
- On datastores backed by spinning disks, raising
read-threadsmuch beyond 1–2 usually doesn't help and can hurt, since a single HDD (or even a RAID array of HDDs) has limited random-read throughput and extra concurrent readers just cause more seeking. - On NVMe or SSD-backed datastores, which handle concurrent random reads well, increasing
read-threadsto 4–8 can noticeably speed up a full verification pass. verify-threads(checksum computation) scales more directly with available CPU cores and is generally safe to raise on any multi-core host, since it's just recomputing hashes on data already in memory.
If a verify job is competing too heavily with active backup or restore traffic, the safer lever to pull first is usually the schedule — running the exhaustive weekly sweep during an off-hours maintenance window — rather than reducing thread counts, since a job that runs less often but at full efficiency is often preferable to one that's perpetually throttled.
Reading Verification Results
After a verify job runs, its outcome is visible in a few places:
- Task Log — under the datastore's Verify Jobs tab, or the global Task History, each run shows as a task with a status of OK or an error. Opening the task shows a per-snapshot breakdown of what was checked and, if applicable, exactly which chunk(s) failed.
- Content tab — each snapshot listed in the datastore's Content tab shows a verification status icon (a checkmark for verified-OK, a warning symbol for failed) with the timestamp of the last check.
- Notifications — if you've configured PBS's notification targets and matchers (email, Gotify, or a generic webhook), a failed verify job triggers the same notification pipeline as any other failed maintenance task, so you don't have to log in and check manually. If you haven't set this up yet, it's worth doing alongside your verify jobs specifically so integrity failures reach you immediately rather than being discovered on your next login.
What to Do When Verification Fails
A failed verify job means at least one chunk referenced by a snapshot no longer matches its recorded checksum. This is bad news for that specific snapshot, but the correct response depends on scope:
- Check whether other snapshots share the corrupt chunk. Because PBS deduplicates at the chunk level, a single corrupted chunk can affect every snapshot that happened to reference it — potentially several backups across different dates if the underlying data didn't change between them. The task log identifies which snapshots were affected.
- Do not delete the corrupted snapshot immediately if it's your only copy. A partially corrupted backup may still be partially restorable, and depending on what changed, an earlier or later snapshot might still have a good copy of the same chunk. Assess before you prune.
- Investigate the underlying storage. A single bad chunk can be a one-off bit flip, but if verify jobs start reporting multiple corrupted chunks across unrelated snapshots, treat it as a signal of failing disks, a degraded RAID array, or a filesystem problem on the datastore's backing storage, and check
smartctloutput and array health before it gets worse. - Re-run a fresh backup as soon as possible for any VM or container whose most recent good snapshot is now further in the past than your recovery point objective allows.
- Run garbage collection afterward. GC has logic to avoid removing chunks that are still referenced, but after resolving a corruption issue and pruning bad snapshots, running GC ensures the datastore's chunk store reflects only what's actually needed.
Performance and Cost Considerations
Because verification reads every byte of every chunk it checks, a full sweep over a large datastore is not free — it consumes disk I/O, CPU for hashing, and time. On multi-terabyte datastores this can run for hours. A few practical guidelines:
- Schedule exhaustive sweeps during low-activity windows (overnight, weekends) rather than competing with your primary backup window.
- Use
ignore-verified truewith a reasonableoutdated-aftervalue rather thanfalsefor routine jobs — forcing a full re-check on every single run wastes I/O re-verifying data that hasn't changed and was already confirmed good recently. - If PBS is running on a VM or LXC container itself with limited CPU allocation, watch host-level CPU usage during a verify run and adjust
verify-threadsdown if it's starving other workloads on the same node. - Remote datastores (verified over a network mount rather than local disk) will be considerably slower to verify than local storage — factor this into how aggressive a schedule is realistic.
Frequently Asked Questions
Does a successful backup mean the data is already verified?
No. A backup that completes without error only confirms the write succeeded at the time it was written. It says nothing about whether the data is still intact weeks or months later. Verify jobs are the only mechanism that re-checks data at rest after the fact.
Do verify jobs modify or repair corrupted data?
No. Verification is read-only and diagnostic — it identifies corruption but does not attempt to repair it, since there's nothing to repair from (PBS doesn't keep redundant copies of each chunk within a single datastore). Recovery means restoring from a different, still-good snapshot or re-running a fresh backup.
Will running a verify job interfere with scheduled backups?
They can run concurrently, but since both are I/O-intensive, running a large verification sweep at the same time as your nightly backup window can slow both down. Staggering the schedules avoids this.
Can I verify backups on a remote sync target as well as the primary datastore?
Yes — if you replicate backups to a second PBS instance using sync jobs, that target datastore is a separate PBS datastore in its own right and should have its own independent verify job configuration. Verifying only the source and assuming the replicated copy is fine defeats the purpose of having an offsite copy.
What's a sensible default if I don't want to think about tuning this?
A daily job with ignore-verified true and no aggressive re-check window, plus a weekly job with outdated-after 7, covers the two failure modes that matter most: catching problems in new backups quickly, and catching slow bit rot in older ones on a rolling weekly basis. Adjust the numbers up or down based on how large your datastore is and how much I/O headroom you actually have.
Conclusion
Prune and garbage collection jobs keep a PBS datastore tidy, but neither of them tells you whether the backups you're keeping are actually good. Verify jobs close that gap by treating "backed up" and "verified intact" as two separate guarantees, and checking the second one on a schedule instead of leaving it to chance. Setting up even a basic daily-plus-weekly pair of verify jobs, combined with notifications routed to somewhere you'll actually see them, is a small amount of configuration that turns "I think my backups are fine" into something you can actually demonstrate.