Skip to content

Add Velero backup/restore for cert-manager secrets across cluster rebuilds - #20

Merged
nbrieussel merged 2 commits into
mainfrom
feature/velero-backup
Jul 29, 2026
Merged

Add Velero backup/restore for cert-manager secrets across cluster rebuilds#20
nbrieussel merged 2 commits into
mainfrom
feature/velero-backup

Conversation

@nbrieussel

Copy link
Copy Markdown
Contributor

Summary

  • Velero (CNCF incubating, Apache-2.0 — chosen over AppsCode's KubeStash to avoid any paid-tier ceiling), scoped narrowly to backing up cert-manager/gateway namespace Secrets: this cluster is otherwise 100% declarative from git, so the only thing worth backing up is the handful of Secrets Kubernetes controllers mint at runtime — specifically cert-manager's issued wildcard TLS cert and ACME account key, which get wiped on every full cluster rebuild and force a brand new Let's Encrypt order (what hit the prod rate limit on 2026-07-25).
  • Own dedicated Scaleway bucket + IAM identity, not OpenBao's own backup bucket — sharing it broke OpenBao's snapshot script's retention cleanup (companion infra repo PR).
  • Auto-restore on rebuild: a PreSync hook on gateway-config waits for a completed Velero backup to sync, restores the secrets, then lets gateway-config's own sync proceed to create the ClusterIssuers/Gateway that would otherwise trigger a fresh ACME order. This is a hook on gateway-config itself rather than a separate wave-ordered Application — ArgoCD's sync-wave ordering across sibling Applications turned out not to be a hard blocking guarantee (confirmed live across several full cluster rebuilds), while a PreSync hook is a real guarantee scoped to that Application's own sync lifecycle.
  • 30-minute wait budget in the hook, not 10: the timeout exits 0 (success) by design so a genuinely-empty-bucket first boot doesn't block forever, but that same success path means there's no retry-based recovery once cert-manager issues a fresh cert off the back of it — a cold OpenBao → ESO → Velero credential chain measured ~15min end to end in testing.
  • existingResourcePolicy deliberately left at its safe default (skip-if-exists), not update — confirmed live that update would silently overwrite a live, possibly-renewed certificate if this object were ever reprocessed against an already-running cluster.

Test plan

  • helm lint/helm template clean on velero-init and gateway-config
  • kubectl apply --dry-run=client clean against the real scaleway-homelab cluster
  • Manual Velero backup + restore verified live
  • Full end-to-end verified across multiple real cluster destroy/recreate cycles — final run confirmed: Restore: Completed, letsencrypt-prod-account-key/scalepack-fr-wildcard-tls both created within the same second (signature of a restore, not a fresh ACME issuance), zero CertificateRequest/Order/Challenge objects created

🤖 Generated with Claude Code

https://claude.ai/code/session_01CCgyeSfKWH6m5mmJEg6t43

Nicolas Brieussel and others added 2 commits July 29, 2026 00:30
CNCF-incubating, Apache-2.0 (chosen over AppsCode's KubeStash to avoid
any paid-tier ceiling). Scope is deliberately narrow: this cluster is
otherwise 100% declarative from git, so the only thing worth backing
up is the handful of Secrets Kubernetes controllers mint at runtime —
specifically cert-manager's issued wildcard TLS cert and ACME account
key, which get wiped on every full cluster rebuild and force a brand
new Let's Encrypt order (what hit the prod rate limit on 2026-07-25).

Own dedicated bucket + IAM identity (backup-velero-dev-id), not
OpenBao's own backup-dev-id — sharing it broke OpenBao's snapshot
script's retention cleanup. openbao namespace is excluded by omission
(includedNamespaces is an allow-list). No volume snapshots — nothing
in scope here lives on a PV, only in Secrets.

apps/velero-init materializes Velero's S3 credentials from OpenBao via
ESO, mirroring apps/external-dns-init's pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCgyeSfKWH6m5mmJEg6t43
A PreSync hook on gateway-config: waits for a completed Velero backup
to sync, restores cert-manager's ACME account key + the gateway's
wildcard TLS secret, then lets gateway-config's own sync proceed to
create the ClusterIssuers/Gateway that would otherwise trigger a fresh
ACME order.

This is a hook on gateway-config itself rather than a separate
wave-ordered Application, and deliberately so: ArgoCD's sync-wave
ordering across sibling Applications is not a hard blocking guarantee
(confirmed live across several full cluster rebuilds — a sibling
Application stuck retrying didn't stop the next wave from proceeding
regardless). A PreSync hook is a real guarantee, scoped to this
Application's own sync lifecycle: the Gateway/ClusterIssuers cannot
apply until the hook Job exits 0.

The hook's wait budget is ~30min, not the ~10min first tried — the
timeout exits 0 (success) by design so a genuinely-empty-bucket first
boot doesn't block forever, but that same success path means there's
no retry-based recovery once cert-manager issues a fresh cert off the
back of it. A cold OpenBao -> ESO -> Velero credential chain measured
~15min end to end in testing, so a shorter budget couldn't tell
"nothing to restore yet" apart from "not ready yet".

existingResourcePolicy is deliberately left at its safe default
(skip-if-exists) rather than "update": the ordering above already
guarantees the target Secrets don't exist on a genuine fresh rebuild,
so "update" would only ever matter for reprocessing against an
already-running cluster — silently overwriting a live, possibly-
renewed certificate with a stale one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCgyeSfKWH6m5mmJEg6t43
@nbrieussel
nbrieussel merged commit 3032714 into main Jul 29, 2026
2 checks passed
@nbrieussel
nbrieussel deleted the feature/velero-backup branch July 29, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant