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.