Velero alert runbook¶
Source rule: cluster/monitoring/velero-alerts.yaml (PrometheusRule velero-alerts, ns monitoring).
All 11 alerts in that rule are documented below. Metric names and labels are those of Velero v1.18 (pkg/metrics/metrics.go), the running version.
GitOps rule: every command in this runbook is read-only. Velero objects (Schedules, BSLs, Backups, credentials secrets) are ArgoCD-managed — fixes land as a Git change, never as a live kubectl apply/patch/edit/delete. Where a live action is genuinely the fix, it is called out explicitly.
Topology referenced by these alerts:
- BSL default → office MinIO (minio.office.svc:9000, ns office)
- BSL home → home MinIO (minio-home.media.svc:9000, ns media)
- Schedules: velero-daily-resources, velero-weekly-resources, velero-daily-arr-stack (cron 30 2 * * *, ns media, TTL 72h — source for the monthly restore-drill)
velero-backup-health¶
VeleroBackupFailure¶
Severity: warning
Fires when: increase(velero_backup_failure_total[24h]) > 0 sustained for: 1h — at least one backup reached the Failed phase in the last 24h.
Likely causes¶
- BackupStorageLocation connectivity (MinIO down, or the cross-site link degraded).
- Velero pod resource exhaustion (OOMKilled) before completion.
- Invalid resource references in the Schedule template (e.g. a deleted CRD).
Diagnose¶
kubectl get backups -n velero --sort-by=.metadata.creationTimestamp | tail -10kubectl logs deploy/velero -n velero --tail=200velero backup describe <name> -n velero(read-only)- PromQL:
increase(velero_backup_failure_total[24h]) > 0— theschedulelabel names the failing schedule.
Resolution: If the cause is configuration (excluded resources, storageLocation, credentials), fix the Schedule or sealed secret in Git and let ArgoCD sync. If it was a transient BSL blip, the next scheduled run clears the alert — do not hand-run a backup via CLI.
VeleroBackupPartialFailure¶
Severity: warning
Fires when: increase(velero_backup_partial_failure_total[24h]) > 0 sustained for: 1h — backups completed but individual items errored. Restore from such a backup may be incomplete.
Likely causes¶
- RBAC denial on specific namespaces or resource types.
- Transient API-server timeouts during item collection.
- Resources that error on read/serialization.
Diagnose¶
kubectl get backups -n velero --sort-by=.metadata.creationTimestamp | tail -5kubectl describe backup <name> -n velero— inspect the Warnings/Errors counts in status.velero backup describe <name> -n velero --details(read-only)- PromQL:
increase(velero_backup_partial_failure_total[24h])
Resolution: Requires a Git change to the Schedule template (includedNamespaces, excludedResources, itemOperationTimeout) or to the Velero ClusterRole if RBAC is the gap. Do not patch the Backup object — Backups are immutable records of a run.
VeleroBackupValidationFailure¶
Severity: warning
Fires when: increase(velero_backup_validation_failure_total[6h]) > 0 sustained for: 30m — a Backup spec was rejected at validation, before execution.
Likely causes¶
- Schedule references a non-existent
storageLocationor an invalid TTL. - Malformed backup template in the ArgoCD-managed manifest.
- Missing required fields in the generated Backup.
Diagnose¶
kubectl get schedules -n velerokubectl describe schedule <name> -n velero— the template and last-run status.- PromQL:
increase(velero_backup_validation_failure_total[6h])—schedulelabel identifies the offender.
Resolution: Always a Git change — correct the Schedule manifest in the repo. A validation failure is deterministic and will repeat every run until the spec is fixed; it will not self-clear.
velero-backup-freshness¶
VeleroDailyBackupMissing¶
Severity: critical
Fires when: time() - max(velero_backup_last_successful_timestamp{schedule="velero-daily-resources"}) > 48 * 3600 sustained for: 1h — no successful daily-resources backup in over 48h, i.e. two consecutive misses.
Likely causes¶
- Persistent BSL outage (MinIO down).
- Velero controller pod down or in CrashLoopBackOff.
- Cross-site link down, blocking the BSL in use.
Diagnose¶
kubectl get pods -n velerokubectl logs deploy/velero -n velero --since=48h | grep -iE "error|fail"kubectl get backupstoragelocations -n velero(short namebsl)- PromQL:
max(velero_backup_last_successful_timestamp{schedule="velero-daily-resources"})— exact last-success epoch.
Resolution: If Velero itself is unhealthy, treat the pod state as the symptom and find the cause (config, quota, node). If the BSL is down, restore the MinIO service. Config fixes go through Git; only genuine infrastructure recovery (MinIO service, node, link) is a live action.
VeleroWeeklyBackupMissing¶
Severity: warning
Fires when: time() - max(velero_backup_last_successful_timestamp{schedule="velero-weekly-resources"}) > 9 * 24 * 3600 sustained for: 1h — the weekly (Sun 03:00) run has not succeeded in over 9 days, i.e. a missed Sunday plus slack.
Likely causes¶
- Same as the daily alert — BSL outage or Velero unavailability across the weekly window.
- Schedule cron/timezone misalignment.
- Namespace ResourceQuota blocking pod creation during the window.
Diagnose¶
kubectl get backups -n velero | grep weekly-resourceskubectl describe schedule velero-weekly-resources -n velerokubectl get resourcequota -n velero- PromQL:
max(velero_backup_last_successful_timestamp{schedule="velero-weekly-resources"})
Resolution: Cron, quota, and schedule-template fixes are Git changes. If the miss was a one-off during an outage that has since cleared, the next Sunday run resolves the alert.
VeleroArrStackBackupStale¶
Severity: warning
Fires when: time() - max(velero_backup_last_successful_timestamp{schedule="velero-daily-arr-stack"}) > 50 * 3600 sustained for: 1h. This is the restore-drill's source backup; 50h catches a miss while a usable candidate (TTL 72h) still exists.
Why it matters: the monthly restore-drill restores the newest arr-stack backup. Stale here means the drill silently validates an old backup.
Likely causes¶
- Volume/namespace issues in ns
mediablocking the backup. - BSL unreachable at the 02:30 run time.
- Velero pod restart across the 02:30 window.
Diagnose¶
kubectl get backups -n velero | grep arr-stackkubectl logs deploy/velero -n velero --since=50h | grep arr-stackkubectl describe schedule velero-daily-arr-stack -n velero- PromQL:
max(velero_backup_last_successful_timestamp{schedule="velero-daily-arr-stack"})
Resolution: Template problems are a Git change. If the cause is a transient storage or network fault that has cleared, the 02:30 run restores freshness — do not trigger a backup by hand.
VeleroNoBackupMetrics¶
Severity: warning
Fires when: absent(velero_backup_last_successful_timestamp) sustained for: 30m.
Why it matters: this is the watchdog on the watchdog. With no series, every freshness alert above goes silent rather than firing — absent() is the only rule that still triggers.
Likely causes¶
- Velero pod down or not Ready, so the ServiceMonitor target is empty.
- ServiceMonitor / label-selector drift so Prometheus no longer selects the target.
- NetworkPolicy blocking Prometheus from scraping the metrics port (container port 8085,
http-monitoring).
Diagnose¶
kubectl get pods -n velerokubectl get svc,endpoints -n velerokubectl get servicemonitor -n velero -o yaml- PromQL:
up{namespace="velero"}— confirm the scrape target's exact job label on the Prometheus Targets page before filtering onjob=.
Resolution: ServiceMonitor labels and NetworkPolicy are ArgoCD-managed — fix in Git. Velero's liveness/readiness probes hit /metrics, so a Ready pod that exports nothing points at the scrape path, not at Velero.
velero-infra-health¶
VeleroBSLUnavailable¶
Severity: critical
Fires when: velero_backup_location_status_gauge == 0 sustained for: 10m for any BackupStorageLocation (label backup_location_name). The 10m window tolerates a transient cross-site tunnel blip but pages within minutes of a real outage.
Impact: one BSL down = off-site redundancy lost (the other still serves backup and restore). Both down = nothing writable or restorable.
Likely causes¶
- MinIO down at the corresponding site.
- Cross-site tunnel down.
- Bad credentials in the
velero-credentials/velero-credentials-homesecrets.
Diagnose¶
kubectl get backupstoragelocations -n velero— which location is Unavailable.kubectl logs deploy/velero -n velero | grep -i backupstoragelocation— validation errors.kubectl get pods -n office -l app=minioandkubectl get pods -n media -l app=minio— MinIO at both sites.- PromQL:
velero_backup_location_status_gauge == 0
Resolution: Credential fixes are a Git change (re-seal the secret; note that env secretKeyRef values snapshot at pod create, so a rollout is needed after the sealed secret syncs). MinIO or link recovery is infrastructure work. Do not patch the BSL object to force it Available.
VeleroRepoMaintenanceFailing¶
Severity: warning
Fires when: increase(velero_repo_maintenance_failure_total[6h]) > 2 sustained for: 1h — note the threshold is more than 2 failures, not any failure. Single transient failures are tolerated because retries succeed.
Why it matters: failed maintenance pods accumulate in Error state against the velero ResourceQuota (30 pods).
Likely causes¶
- Network instability to MinIO on the cross-site path.
- Bucket permission or repository state problems.
- ResourceQuota exhaustion preventing maintenance pods from starting.
Diagnose¶
kubectl get pods -n velero | grep -i maintenancekubectl get resourcequota -n velerokubectl logs deploy/velero -n velero | grep -i maintenance— therepository_namelabel identifies the repo.- PromQL:
increase(velero_repo_maintenance_failure_total[6h])
Resolution: Quota sizing, loadAffinity pinning to office, and NetworkPolicy are Git changes. Do not delete the Error pods to clear the quota — that hides the cause and the controller recreates them; fix the underlying reachability or quota first.
VeleroRestoreFailed¶
Severity: warning
Fires when: increase(velero_restore_failed_total[1h]) > 0 sustained for: 5m.
Why it matters: if this is the monthly restore-drill, the backups may not be restorable at all. That is the most serious class of signal in this runbook — investigate immediately.
Likely causes¶
- Incomplete or corrupt backup data (often downstream of a prior partial failure).
- StorageClass mismatch between the backup and the current cluster.
- Longhorn volume restore problems (missing snapshot, insufficient space).
Diagnose¶
kubectl get restores -n velero --sort-by=.metadata.creationTimestamp | tail -5kubectl describe restore <name> -n velerovelero restore describe <name> -n velero --details(read-only)kubectl get volumes.longhorn.io -n longhorn-system— check target volume state.- PromQL:
increase(velero_restore_failed_total[1h])
Resolution: Restore-drill definitions and any StorageClass mapping live in Git — fix there. Per repo policy, restore drills target sandbox namespaces only, never live data. If the backup itself is unrestorable, escalate: that invalidates the recovery path, not just this run.
VeleroBackupDeletionFailing¶
Severity: warning
Fires when: increase(velero_backup_deletion_failure_total[24h]) > 0 sustained for: 2h — expired backups are not being garbage-collected, so the office MinIO bucket grows unbounded.
Likely causes¶
- BSL write/delete permissions revoked, or object-lock on the bucket.
- Network partition preventing object deletion.
- Velero GC controller erroring against the API server.
Diagnose¶
kubectl get backups -n velero— look for accumulating Expired/Deleting entries.kubectl get deletebackuprequests -n velerokubectl logs deploy/velero -n velero | grep -iE "deletion|gc"- PromQL:
increase(velero_backup_deletion_failure_total[24h])
Resolution: Credential and BSL-permission fixes are a Git change. Do not delete Backup objects by hand to reclaim space — that orphans the objects in the bucket without releasing them. Fix the delete path, then let GC drain the backlog.