Did the latest run complete?
Start with result and age
A pass needs a recent timestamp. An old pass is stale evidence, not current recovery coverage.
The scheduled job rotates through a recovery manifest, restores a protected guest from backup into a fenced scratch network, boots it, runs operating-system and service assertions, removes the temporary copy, and confirms that the copy is gone. The report and dashboard below show the outcome without exposing the systems being recovered.
The path took twelve reruns to reach the intended assertion set. The failures were not variations of one bug. The job bypassed its scoped credential, the runbook assumed missing preconditions, effective permissions collapsed to an empty intersection, TLS verification targeted the wrong identity, and a bare boolean destroyed the reason a restore assertion failed.
last run 2026-09-20 · PASS · 181 seconds
Built from the weekly restore design, Jenkins pipeline, restore verifier, dated live-fire result, failure saga, backup digest template, and public recovery dashboard.
Proxmox Backup Server can verify that stored chunks and indexes are readable. That is necessary, but it does not show that Proxmox can reconstruct a guest, that the guest boots, or that the application inside it can perform its job.
The scheduled restore pipeline closes that gap. It chooses the next protected guest, chooses a backup source, creates a temporary guest under a reserved scratch identity, attaches it to a network with no production uplink, and boots it. Common assertions inspect the operating system. A service profile adds the checks that matter for the recovered workload.
The temporary machine is destroyed after success or failure. Cleanup then asks the platform whether the guest is absent. The delete command returning zero is supporting evidence; the absence check is the teardown result.
The fenced network is a bridge with no uplink to production. A restored directory server, secrets server, or application can therefore use its original configuration without answering on the live address or contacting production peers.
A hypervisor can mark a guest running while the operating system is stuck, the filesystem is damaged, the guest agent is absent, or the application service cannot read its data. The verifier divides checks into a common set and a registered service profile.
| Assertion group | What it checks | Failure it catches |
|---|---|---|
| Hypervisor state | The restored guest exists, starts, and reaches the expected runtime state. | Restore or boot failure before the operating system responds. |
| Guest agent | The agent answers through the platform control channel. | A running guest that cannot report internal state. |
| Operating system | Expected family, disks, filesystems, and protected paths are present. | The wrong backup, incomplete disk, or damaged filesystem. |
| Identity | The recovered guest reports the expected system class without joining live peers. | A restored image that does not match the selected manifest entry. |
| Service profile | Registered processes, ports, files, or data checks succeed inside the fence. | A machine that boots but cannot perform the workload’s job. |
| Teardown | The reserved scratch guest is absent after deletion. | A leaked duplicate left behind by a successful command. |
The verifier also records whether each planned assertion executed. That audit catches a conditional branch that silently skipped its check while leaving the surrounding stage green.
The email names the selected recovery class, backup source class, result, duration, assertion summary, and teardown result. It carries enough context to decide whether the run was quiet, whether one assertion failed, or whether the pipeline stopped before it created anything.

A separate dead-man monitor watches the age of the latest successful digest. If the report disappears, silence does not read as a healthy recovery week.
The recovery path did not turn green by fixing one restore command. Each correction exposed the next assumption.
The repository defined a narrow recovery token, but the command that performed the restore still ran through a broader administrative path. The job could pass while bypassing the permission boundary it claimed to test. The execution code was changed to use the scoped identity.
The first live walk found identity creation ordered after the rule that referenced it, a missing custom role, and an endpoint check that assumed failover had already been established. The runbook was rewritten in the order the platform actually requires.
The platform calculates a token’s rights from the intersection of user, group, token, path, and role permissions. The configuration looked granted in individual places while the effective result was no permission. The verifier now queries effective access instead of reading one ACL in isolation.
The client reached a cluster endpoint whose certificate names did not satisfy the original assertion. The correction aligned the test with the endpoint identity and trusted chain the recovery client actually uses.
The restore assertion returned only a boolean. Jenkins could report failure but not the provider response that explained it. The verifier now carries a typed result with the failed step, response detail, and safe context.
A missing guest-agent permission stopped internal checks. One operating-system assertion asked for a fact that could never appear and waited through the full retry window. Cleanup trusted command success without verifying absence. Each became a named check with bounded retry and a separate teardown query.
One final development pass recorded 197 of 200 assertions passing. The remaining three stayed in the report as tracked gaps. That number belongs to that dated run and is not presented as a current fleet count.
The public dashboard shows freshness, result, duration, restore coverage, and active failure state under stable service classes. Guest names, infrastructure identifiers, addresses, backup paths, job parameters, and management links are removed before the public data source.
Live public Grafana view
Open full screenChecking the live backup dashboard.
The live backup dashboard is unavailable. This recovery write-up remains available, and no cached backup state is presented as current.
Did the latest run complete?
A pass needs a recent timestamp. An old pass is stale evidence, not current recovery coverage.
Which protection layer is behind?
Freshness by datastore shows whether the primary and mirrored paths both contain current recovery material.
Did recovery slow down?
A longer restore can reveal storage, network, or guest-initialization change before it becomes a timeout.
Did the job clean up?
The run is incomplete while the scratch guest still exists, even when every application assertion passed.
| When | What runs | What happens next |
|---|---|---|
| On the weekly schedule | Jenkins selects the next manifest entry and eligible backup source. | Preflight must pass before a scratch guest is created. |
| On restore | PBS supplies the snapshot and Proxmox creates the isolated guest. | Boot and assertion phases begin under the scoped identity. |
| On an assertion failure | The verifier records the failed check and safe reason. | The run is red and still enters cleanup. |
| On cleanup | Proxmox deletes the scratch guest; the verifier queries for absence. | Notification distinguishes verified teardown from leaked state. |
| On rerun | Preflight checks that no previous scratch guest remains. | The same manifest entry can be exercised without manual state repair. |
| Between runs | Prometheus evaluates freshness, outcome, duration, coverage, and missing heartbeat. | An old or absent result cannot remain green. |