Skip to content

RestoreSync CRD

The RestoreSync Custom Resource Definition keeps a standby cluster warm. It polls a storage backend the primary cluster writes to, works out what is new from the backup metadata alone, and creates ordinary Restore CRs from a template. The standby never connects to the primary.

Overview

  • API Group: retour.thebackup.company/v1
  • Kind: RestoreSync
  • Scope: Namespaced
  • Discovery: a carrier index job reads the metadata documents under the backend's path and prints one entry per backup. No Backup CRs, no database, no cross-cluster connection.

Spec

Required Fields

Field Type Description
storageBackendRef string StorageBackend on this cluster pointing at the same destination as the primary.
restoreTemplate object A Restore spec minus the backup reference. Copied verbatim onto every created Restore, and validated by the same checks a Restore reconcile applies.

Optional Fields (with defaults)

Field Type Default Description
pollInterval duration 15m How often to look for new backups. The floor is 1m.
mode string Latest Latest replays only the newest qualifying backup per poll. Continuous replays every new one, oldest first.
selector object nil Which backups qualify: namespaces, namePrefix, or scheduleName (matched through the <schedule>-<timestamp> naming the primary uses).
allowSameNamespace bool false Required for Continuous with no namespaceMapping. See below.
suspend bool false Stop polling. Set it before promoting the standby, so a late poll does not restore over the now-live cluster.

The Restore-Storm Guard

mode: Continuous with a template that carries no namespaceMapping restores every new backup into its original namespaces. On a standby that is the point. Applied to the primary by mistake, it would overwrite live workloads with their own backups, every poll, forever. So that combination is refused unless the CR says allowSameNamespace: true. With an explicit mapping the guard does not apply, because the author has already said where restores land.

Idempotency

A backup is never restored twice. Created Restores carry an ownership label, the backup's job UUID, and a deterministic name, and the controller checks its status watermark and the live labelled Restores before creating anything. Status alone would be lossy across conflict retries.

Created Restores are owned by the RestoreSync, so deleting the sync cascades to them. A failed Restore flips Degraded naming the Restore, and never blocks later syncs.

Recovery Point

The standby lags by the backup cadence plus the poll interval, plus however long the restore takes. A 03:00 nightly backup with a 15-minute poll is on the standby shortly after 03:15. Tighten the schedule first: the poll only discovers what the primary has written.

Status

Field Type Description
lastPolledAt string When the backend was last listed.
lastSyncedBackup string Job UUID of the newest backup replayed.
restoresCreated int How many Restores this sync has created.
recentRestores array The most recent created Restores and their outcomes.
conditions array Includes Degraded with reason TemplateInvalid or RestoreFailed.

Examples

Warm Standby for One Namespace

apiVersion: retour.thebackup.company/v1
kind: RestoreSync
metadata:
  name: shop-dr
  namespace: retour
spec:
  storageBackendRef: shared-dr-bucket
  pollInterval: 15m
  mode: Latest
  selector:
    scheduleName: nightly
    namespaces: [shop]
  restoreTemplate:
    namespaceMapping:
      shop: shop
    scaleWorkloadsToZero: true

Every Backup, into Mapped Namespaces

apiVersion: retour.thebackup.company/v1
kind: RestoreSync
metadata:
  name: shop-dr-continuous
  namespace: retour
spec:
  storageBackendRef: shared-dr-bucket
  mode: Continuous
  selector:
    namePrefix: shop-
  restoreTemplate:
    namespaceMapping:
      shop: shop-standby

Scope

RestoreSync replays data. It is not a failover product: DNS and traffic switching, promoting the standby, fencing the failed primary, and failing back are all yours. Pair it with whatever entrypoint switch your platform uses.