Home / Stories / Backup health
Story · Reliability Live

A backup you have never restored is a hope. A failing one is a finding.

Backups fail quietly. The nightly Velero run reports PartiallyFailed because one volume could not be snapshotted; the database backup has been failing since a credential rotated. Nobody looks until the day a restore is needed. The assistant checks every few hours and opens one issue per broken schedule, with the evidence and the likely cause.

Starts fromVelero Backup objects and CloudNativePG Backup objects in the cluster.
Looks atBackup phase and errors, the Velero Schedule or CNPG Cluster behind them, and the matching Schedule, BackupStorageLocation or Cluster manifests in git.
You getOne issue per failing schedule or database: what failed, since when, the probable cause, and the steps to fix and verify it.
01 · Every six hours

Read the backups, not the dashboards.

The backup scan lists Velero Backup objects and CloudNativePG Backup objects created in the last 48 hours (both configurable), straight from the cluster API, read-only.

Here: 14 Velero runs and 8 database backups in the window.

02 · Failed and partially failed

"Partially" failed is still failed.

Velero runs in Failed or PartiallyFailed count. A partial failure often means the Kubernetes objects were saved but a volume was not: exactly the part you need for a restore.

Runs are grouped by their Schedule, so seven failed nights become one finding, not seven.

03 · Gather the evidence

Errors, the schedule, and what git says it should be.

For each failing schedule the scan collects the error messages of the most recent runs, the Schedule's spec and status from the cluster, and the Schedule and BackupStorageLocation manifests from your repo.

The error usually names the layer: storage credentials, bucket, snapshot permissions, a node agent.

04 · Databases too

CloudNativePG backups, grouped per database cluster.

Failed CNPG backups are grouped by the Cluster that owns them. The Cluster's status and its spec.backup block (object store, credentials reference, retention) are attached as context.

05 · Diagnose

A probable cause and concrete next steps.

The model reads the evidence and writes a short diagnosis: the most likely cause, what to check first, and how to confirm the fix. If the analysis is unavailable, the issue is still opened with the raw phase and error, never dropped.

06 · An issue, not an MR

Backup fixes are too context-dependent to guess.

Backup findings are opened as issues, labelled reliability, high risk. The fix is often outside git (a cloud permission, a bucket policy, a rotated key), and a wrong storage change can make things worse.

That caution is earned: a storage-location prefix removed to fix one error once made the whole location invalid, because the bucket was shared. The full issue is below.

07 · When nothing fails Roadmap

No failed backup can also mean no backup.

If the BackupStorageLocation is Unavailable, Velero attempts nothing, so there is nothing failed to find. Planned: check the storage location itself, and the age of the newest successful backup per schedule, so silence is not read as health.

Also planned: flag runs whose volume snapshot list is empty when the schedule includes volumes.

08 · From finding to fix Roadmap

MRs where the fix does live in git.

Planned: draft MRs for schedule and retention changes, and for a missing BackupStorageLocation, grounded in the bucket's actual layout before touching a prefix.

And when an upgrade bumps a backup plugin, compare the cloud permissions each version documents, so a new required permission is in the upgrade MR instead of in next month's failed restore.

velero.io/v1 Backuplast 48 h · 14 runs
backup scanevery 6 h · read-only
postgresql.cnpg.io/v1 Backuplast 48 h · 8 runs
schedule "0 */6 * * *" · lookback 48h · no write access to anything
velero backups · last 48 h
  • hourly-config-20260923… ×12 Completed
  • daily-full-20260923040012 PartiallyFailed · 3 errors
  • daily-full-20260922040009 PartiallyFailed · 3 errors
evidence · schedule daily-full
phase: PartiallyFailed
errors: 3
error: snapshot shop/data-orders-db-0: 403 permission denied
       (create snapshot not allowed for the backup identity)
volumeSnapshotsAttempted: 3   completed: 0
# Schedule (cluster)
spec.template: { includedNamespaces: [shop, payments], snapshotVolumes: true, ttl: 720h }
status.lastBackup: 2026-09-23T04:00:12Z
# git: infrastructure/velero/schedule_daily-full.yaml · backupstoragelocation_default.yaml
cnpg · Cluster payments/billing-pg
  • billing-pg-20260923000000 failed
  • billing-pg-20260922000000 failed
spec.backup:
  barmanObjectStore:
    destinationPath: s3://acme-backups/billing-pg
    s3Credentials: { accessKeyId: { name: billing-pg-s3, key: ACCESS_KEY_ID } }
  retentionPolicy: 30d
status.lastFailedBackup: 2026-09-23T00:00:04Z
G
gitops-assistant backup analysis
The Kubernetes objects are backed up; every volume snapshot fails with 403. The backup identity can read disks but may not create snapshots.
Check first: the snapshot permission on the backup identity's cloud role. Nothing in the Schedule or storage location changed.
G
gitops-assistant
Confirm with a one-off backup of shop and check that volumeSnapshotsCompleted equals volumeSnapshotsAttempted.
Velero backup failing: daily-full
issue · 2 runs PartiallyFailed · volume snapshots 0 / 3
category:reliabilitysource:backuprisk:high
↓ the full issue is below
planned checks · silence is not success
  • BackupStorageLocation default Unavailable · no backup attempted
  • newest Completed backup · daily-full 9 days old · schedule is daily
  • volume snapshots completed 0, schedule includes 3 volumes
0 failed backups in window · and still no usable backup
planned · infrastructure/velero/schedule_daily-full.yaml
  spec:
    schedule: "0 4 * * *"
    template:
-     ttl: 72h0m0s
+     ttl: 720h0m0s        # match the 30-day retention the runbook promises
      snapshotVolumes: true
planned · upgrade-time permission diff
backup plugin v1.7 → v1.9, documented cloud permissions
+ one permission added in v1.9, missing from the current role

The issue you get.

Illustrative, shaped like the real backup issue: same title format, same labels, same evidence sections. Backup findings are issues today, not merge requests.

Issue

Velero backup failing: daily-full

opened by gitops-assistant · cluster prod · namespace velero
category:reliabilitysource:backuprisk:high
DiagnosisEvidenceNext steps
Summary

The Velero schedule daily-full has produced PartiallyFailed backups on the last 2 runs in the 48-hour window. Kubernetes objects are saved; no volume snapshot completed (0 of 3), so persistent data in shop and payments is not restorable from these backups.

Probable cause

Every snapshot error is a 403 on snapshot creation. The backup identity can list disks but lacks permission to snapshot them. The Schedule and BackupStorageLocation in git are unchanged in this period, which points at the cloud role rather than the manifests.

Blast radius

Velero schedule daily-full: 2 backup runs failed. Restores from these runs would bring back objects without their data.

RunPhaseError
daily-full-20260923040012PartiallyFailedsnapshot shop/data-orders-db-0: 403 permission denied
daily-full-20260922040009PartiallyFailedsnapshot shop/data-orders-db-0: 403 permission denied
Related manifests in git
  • infrastructure/velero/schedule_daily-full.yaml
  • infrastructure/velero/backupstoragelocation_default.yaml
  • Check the cloud role bound to the Velero identity for the snapshot-create permission your backup plugin version documents.
  • Grant the missing permission (outside this repo), then run a one-off backup: velero backup create verify-snapshots --include-namespaces shop.
  • Confirm volumeSnapshotsCompleted equals volumeSnapshotsAttempted in velero backup describe.
  • Leave the next scheduled run to confirm; this issue will not re-open while runs complete.

What keeps it honest.

A backup alert that cries wolf gets muted, and a muted backup alert is how data gets lost.

One finding per scheduleFailed runs are grouped per Velero Schedule or CNPG Cluster, and an open finding is not re-raised on every scan.
Partial countsPartiallyFailed is treated as a failure, because the missing part is usually the data.
Never droppedIf the analysis cannot be produced, the issue still opens with the raw phase and error.
No guessed storage editsStorage locations, prefixes and credentials are not rewritten by a model. The issue explains; you change.
Read-onlyThe scan lists backups, schedules and clusters. It cannot create, delete or restore anything.
Evidence, not a verdictThe raw errors of up to five recent runs are attached, so you can check the diagnosis instead of trusting it.

Where this stands.

Live
  • Velero Failed and PartiallyFailed backups, grouped per Schedule
  • CloudNativePG failed backups, grouped per database cluster
  • Evidence from the cluster and git, diagnosis and next steps, as issues
  • Velero and backup-plugin chart upgrades via the upgrade lane
In development
  • Nothing in flight right now: the roadmap items are designed and queued
Roadmap
  • Stale-backup check: age of the newest successful backup per schedule
  • BackupStorageLocation availability and empty-snapshot detection
  • Draft MRs for schedule, retention and missing storage locations
  • Grounding storage-location prefix changes in the bucket's real layout
  • Cloud permission diff when a backup plugin is upgraded
← Previous storySecrets hygiene