Documentation navigation
CNPG Cluster Backup Stale (> 48h)
Runbook for a CNPG cluster whose last successful backup is older than 48h. This is the "backups used to work, now they don't" signal — distinct from 01-cnpg-backup-failure.md (which fires when a cluster has never backed up, timestamp == 0).
Meaning
cnpg_collector_last_available_backup_timestamp is the Unix epoch of the last successful base backup (CNPG postgres_exporter sidecar, scraped via the cnpg-cluster PodMonitor). The alert fires when (time() - last_backup) / 3600 > 48 — i.e. the last backup is more than 48h old — and the timestamp is > 0 (gated, so this alert does not duplicate CnpgClusterNoBackup which fires at == 0). for: 1h rides out a transient scrape gap.
Backups are scheduled daily (ScheduledBackup CR, default 0 0 0 * * *), so a 48h+ gap means at least two consecutive scheduled runs have failed. RPO has silently grown past the expected 24h.
The and on (job, k8s_cluster_name, environment) join keeps the alert from firing on the == 0 case (handled by CnpgClusterNoBackup); the max by (...) collapses to the primary's value (replicas report 0). The metric is already scraped into Mimir — no chart PR is needed.
Impact
- A restore from a 48h+ stale backup loses up to 48h+ of writes — RPO has silently grown past the expected 24h.
- If left unresolved this progresses to the
CnpgClusterNoBackupfailure mode (the timestamp eventually ages out / a recreated cluster resets to 0). - The cluster is still serving traffic — this is a backup-pipeline problem, not a service outage.
Diagnosis
The alert labels carry k8s_cluster_name, environment, namespace, job (the CNPG cluster name). From the alert, note all of them.
The backup pipeline, failure modes, and remediation are identical to 01-cnpg-backup-failure.md. Use its Diagnosis section end-to-end:
- Step 1 — identify the CNPG cluster and its backup config (
kubectl get cluster,scheduledbackup,backup). - Step 2 — check the
BackupCR status forphase: failedor stuckrunning; inspect the CNPG operator logs. - Step 3 — check WAL archive health (
first_recoverability_pointadvancing via Mimir); if absent,CnpgClusterNoWalRecoveryPoint(03-cnpg-no-wal-recovery-point.md) is also firing. - Step 4 — check egress (the most common root cause — IPv6
::/0missing for Scaleway S3, or the Azure Blob endpoint for AKS).
Confirm the staleness directly via Mimir (port-forward synapse-server-mimir-gateway in monitoring-system, header X-Scope-OrgID: corti):
QUERY='(time() - max by (job, k8s_cluster_name, environment, namespace) (cnpg_collector_last_available_backup_timestamp{job="<job>", k8s_cluster_name="<cluster>"})) / 3600'
curl -s -G -H "X-Scope-OrgID: corti" "http://localhost:9090/prometheus/api/v1/query" --data-urlencode "query=$QUERY" | jq -r '.data.result[0].value[1]'
A value > 48 confirms the alert (hours since last backup).
Mitigation
Step 1 — Activate PIM
Activate the required Azure Privileged Identity Management roles before touching production (MultiTenantEuOnlyAccessPimRoles / DevOpsPimRoles / Platform Team (privileged)), as in 03-kafka/01-consumer-lag.md.
Step 2 — Fix the backup pipeline
Follow 01-cnpg-backup-failure.md Mitigation step 2 — the root causes are the same (egress blocked, stuck Backup CR, WAL archiving broken). The durable fix is a chart PR (cnpg-cluster egress NetworkPolicy), not a kubectl patch (ArgoCD self-heal reverts it).
After the fix, watch for a new Backup CR to reach phase: completed and the Mimir timestamp metric to advance.
Verify
- The alert clears once
time() - last_available_backup_timestampis under 48h sustained for thefor: 1hwindow. kubectl -n <namespace> get backup -l cnpg.io/cluster=<cluster>shows a recentphase: completedBackup.- Re-run the Mimir query from Diagnosis to confirm the age is under 48h.
Notes
- Relationship to
01-cnpg-backup-failure.md— that runbook fires whentimestamp == 0(never backed up); this one fires whentimestamp > 0but stale (> 48h). The backup pipeline, diagnosis, and mitigation are shared. - Production-only — matches the chart alert. Dev/staging clusters are routinely recreated without backups.
- Metric already collected —
cnpg_collector_last_available_backup_timestampis scraped from the CNPGpostgres_exportersidecar. No chart PR required.