feat(kopiur): alert on backup freshness and enable weekly verification - #4305
Conversation
Backups ran hourly for 18d with LAST-VERIFIED empty on all 18 policies, and nothing watched freshness -- a repo that stops being restorable reads healthy until a restore is attempted. Group is kopiur.freshness.rules and the freshness alert is KopiurBackupOverdue: the chart ships its own kopiur.rules group with a KopiurBackupStale on the same metric at the same severity, which would have produced identical alert fingerprints.
|
Warning Review limit reached
Next review available in: 40 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Comment |
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/ai/hermes
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/ai/odysseus
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/ai/opencode
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/default/changedetection
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/default/dumbassets
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/default/karakeep
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/default/nextcloud
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/bazarr
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/brrpolice
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/fileflows
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/jellyfin
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/prowlarr
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/qbittorrent
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/qui
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/radarr
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/seerr
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/sonarr
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ spec @@
# kopiur.home-operations.com/v1alpha1/SnapshotPolicy/media/wizarr
! + one map entry added:
+ verification:
+ quick:
+ schedule:
+ cron: "0 3 * * 0"
+ jitter: 3h
@@ (root level) @@
# monitoring.coreos.com/v1/PrometheusRule/kopiur-system/kopiur-rules
! + one document added:
+ apiVersion: monitoring.coreos.com/v1
+ kind: PrometheusRule
+ metadata:
+ name: kopiur-rules
+ namespace: kopiur-system
+ labels:
+ kustomize.toolkit.fluxcd.io/name: kopiur
+ kustomize.toolkit.fluxcd.io/namespace: kopiur-system
+ spec:
+ groups:
+ - name: kopiur.freshness.rules
+ rules:
+ - alert: KopiurBackupOverdue
+ annotations:
+ summary: "kopiur {{ $labels.namespace }}/{{ $labels.policy }} has no successful backup for {{ $value | humanizeDuration }} — restore point is aging"
+ expr: "time() - kopiur_policy_last_backup_success_timestamp_seconds > 6 * 60 * 60"
+ for: 30m
+ labels:
+ severity: warning
+ - alert: KopiurBackupCritical
+ annotations:
+ summary: "kopiur {{ $labels.namespace }}/{{ $labels.policy }} has not backed up successfully in {{ $value | humanizeDuration }}"
+ expr: "time() - kopiur_policy_last_backup_success_timestamp_seconds > 24 * 60 * 60"
+ for: 30m
+ labels:
+ severity: critical
+ - alert: KopiurPolicyMetricsAbsent
+ annotations:
+ summary: "No kopiur policy backup metrics at all — the controller is down or every policy stopped reporting; backup state is unknown, not healthy"
+ expr: absent(kopiur_policy_last_backup_success_timestamp_seconds)
+ for: 30m
+ labels:
+ severity: critical
+ - alert: KopiurRepoEmpty
+ annotations:
+ summary: "kopiur repository {{ $labels.name }} reports zero snapshots — the repo is empty or unreadable"
+ expr: "kopiur_repo_snapshot_count == 0"
+ for: 1h
+ labels:
+ severity: critical
+ - alert: KopiurControllerNotLeader
+ annotations:
+ summary: "No kopiur controller holds the leader lease — nothing is scheduling backups"
+ expr: "max(kopiur_leader_is_leader) == 0"
+ for: 30m
+ labels:
+ severity: critical
|
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| JavaScript | Aug 2, 2026 3:02p.m. | Review ↗ | |
| Shell | Aug 2, 2026 3:02p.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
AI Automated ReviewAnalysis engine: qwen-3.6-fast@http://litellm.ai.svc.cluster.local/v1 (openai) RecommendationApprove. The PR adds well-reasoned PrometheusRule alerts for backup freshness, controller health, and repo integrity, plus enables weekly snapshot verification with jitter. All changes are internally consistent, follow repository conventions, and address the documented gaps from past incidents. Change-by-Change Findings
|
| Check | Status |
|---|---|
| Validate manifest against target cluster version | PrometheusRule uses monitoring.coreos.com/v1 (standard Prometheus Operator CRD). SnapshotPolicy uses kopiur.home-operations.com/v1alpha1. Both APIs are custom/operator-managed and validated by their respective controllers at reconcile time. No version pinning mismatch detected in the diff. |
| Check for resource quota / limit changes | No resource requests, limits, quotas, or HPA changes in this PR. Not applicable. |
Unknowns or Needs Verification
- The schema URL
https://k8s-schemas.home-operations.com/was not fetchable (not on the allowlist). This is an info-level gap; it does not affect correctness since the manifests follow established repo patterns and the PR author states they validated withkustomize buildandkubectl apply --dry-run=server. - The inhibit rule between
KopiurBackupOverdue(warning) andKopiurBackupCritical(critical) is acknowledged as intentionally absent by the author ("arguably deserves the noise"). This is a conscious trade-off, not a defect.
Backups ran hourly for 18d with
LAST-VERIFIEDempty on all 18 policies, and nothing watched freshness. A repo that stops being restorable reads identical to a healthy one until a restore is attempted.Alerts
KopiurBackupOverdue(6h),KopiurBackupCritical(24h),KopiurPolicyMetricsAbsent,KopiurRepoEmpty,KopiurControllerNotLeader.Name collision avoided
The chart ships its own
kopiur.rulesgroup (viamonitoring.prometheusRule.enabled) containing aKopiurBackupStaleon the same metric at the same severity with the samefor:. Identical result labels mean identical alert fingerprints, which Alertmanager cannot distinguish — and crossing the threshold downward would make one rule resolve while the other kept firing.Hence group
kopiur.freshness.rulesand alertKopiurBackupOverdue. The chart's other 8 alerts are not duplicated; freshness, metric-absence, empty-repo and never-leader are genuine gaps.Verification
spec.verificationis opt-in and nobody had opted in. Enables thequick(blob-level) tier weekly with 3h jitter so 18 policies do not hit the NAS at once. Concrete cron rather than theHplaceholder.Validation
kustomize build,kubectl apply --dry-run=serveron the rule and an envsubst'd SnapshotPolicy, every expression run against live VictoriaMetrics. All 18 policies are currently under 1h old, so these are silent on merge.Known, not addressed
KopiurBackupCriticalat 24h double-notifies withKopiurBackupOverdueat 6h — the inhibit rule matches onalertname, which differs. A 24h-dead backup arguably deserves the noise.