Restore CRD
The Restore Custom Resource Definition defines a restore operation from a backup. You can restore the full backup or selectively restore specific resources.
Overview
- API Group:
retour.thebackup.company/v1 - Kind:
Restore - Scope: Namespaced
- Identification: Provide exactly one of
backupName,backupJobUUID, orbackupPath.
Spec
Identification (exactly one required)
| Field | Type | Description |
|---|---|---|
backupName |
string | Name of the Backup CR to restore from. Use when the backup's CR exists in this cluster. |
backupJobUUID |
string | Job UUID of the backup, as recorded in the Backup CR's status.jobUUID. Use for programmatic restore. |
backupPath |
string | Explicit path on the storage backend. Use when no Backup CR exists (e.g., restoring from another cluster, or after the CR was deleted). |
Optional Fields (with defaults)
| Field | Type | Default | Description |
|---|---|---|---|
storageBackend |
string | — | Storage backend to restore from. Required when using backupPath. |
resourcesToRestore |
array | [] |
Selective restore: list of resources (kind, name, namespace). Omit for full restore. |
isTemplate |
bool | false |
If true, store as template only — no execution. |
templateRef |
string | — | Reference another Restore with isTemplate: true as source. |
name |
string | — | Deprecated. Use metadata.name instead. |
ResourceReference (resourcesToRestore)
| Field | Type | Description |
|---|---|---|
kind |
string | Resource kind (e.g., Deployment, PersistentVolumeClaim). |
name |
string | Resource name. |
namespace |
string | Resource namespace. Use "" for cluster-scoped resources. |
Where Things Land
| Field | Type | Default | Description |
|---|---|---|---|
namespaceMapping |
map | {} |
Restore namespace <key> into namespace <value>. Also how a restore with no local Backup CR tells the operator which namespaces to mint RBAC for. |
storageClassMapping |
map | {} |
A claim backed up with storage class <key> comes back with class <value>. |
imageRewrites |
array | [] |
Replace container image prefixes in restored workloads, for a registry that moved. First matching rule wins. |
resourceModifiers |
array | [] |
Selector-scoped RFC 6902 JSON patches or RFC 7386 merge patches applied to manifests before they are created. A patch that tries to change a manifest's apiVersion or kind is refused. |
existingResourcePolicy |
string | Keep |
What to do with an object that already exists. Keep leaves it alone, Update overwrites it and reports immutable-field conflicts per resource, Replace deletes and recreates it. PVCs are exempt from all three; targetPVC is how data goes into an existing claim. |
preserveNamespaceUIDRange |
bool | false |
Keep the source namespace's OpenShift SCC UID and supplemental-group ranges. The default drops them so the target cluster allocates its own, which is what a cross-cluster restore wants. |
Data
| Field | Type | Default | Description |
|---|---|---|---|
pathFilters |
[]string | [] |
Restore individual files and directories instead of whole volumes (rclone filter dialect: a bare pattern includes, - pattern excludes). Setting this makes the restore data-only, with no workload manifests recreated. |
targetPVC |
object | nil |
Restore the selected files into an existing claim (claimName, namespace) instead of creating claim-restore-* PVCs. Needs the restore to resolve to exactly one data operation, and implies data-only. |
secrets |
object | nil |
secrets.mode: Restore decrypts the backup's encrypted Secrets stream. Same admin gate and same dedicated passphrase as the backup side. |
skipVerification |
bool | false |
Turns off the post-restore check of the restored volume against the backup it came from. Verification is on by default and a failure fails the restore rather than reporting Completed over incomplete data. Kopia-written operations verify themselves and are unaffected. |
scaleWorkloadsToZero |
bool | false |
Recreate Deployments and StatefulSets with zero replicas and CronJobs suspended, so the application cannot write to the volumes that were just restored. Drills set it; so should any restore you plan to inspect before starting. |
Rehearsal and Import
| Field | Type | Default | Description |
|---|---|---|---|
dryRun |
bool | false |
Render what the restore would create into status.plannedObjects without applying anything. Under Update or Replace the plan also names the live objects that would be touched. |
veleroImport |
object | nil |
Restore Kubernetes manifests out of an existing Velero backup (backendRef, path), and its volume data too for backups kopia wrote (volumeData.enabled, plus the source cluster's repository password). Mutually exclusive with backupName, backupJobUUID and backupPath. |
hooks |
object | nil |
hooks.post runs commands in the restored application's containers after the data is back. Refused together with scaleWorkloadsToZero, which would leave nothing to exec into. |
Status
The worker pod writes status as the restore runs; the operator only writes a terminal phase if the worker dies without reporting (condition reason OperatorFallback).
| Field | Type | Description |
|---|---|---|
phase |
string | Pending, Running, Completed, or Failed. |
resolvedBackupPath |
string | Backend path the restore was resolved to, whichever of backupName, backupJobUUID, or backupPath was given. |
startTime |
string | When restore started. |
completionTime |
string | When restore finished. |
restoreSize |
int64 | Total size restored in bytes. |
progress |
object | Per-PVC progress (totalPVCs, completedPVCs, pvcDetails). |
error |
string | Error message if failed. |
conditions |
array | Standard Kubernetes conditions (e.g., Complete). |
existingResources |
array | What happened to each object that already existed: kept, updated, replaced, or a conflict with the reason. |
resourcesModified |
int | How many manifests a resource modifier changed. |
plannedObjects |
array | The dry-run plan: what would be created, patched or replaced. |
secretsRestored |
int | How many Secrets came back from the encrypted stream. |
veleroImport |
object | Import counts: sensitive objects skipped by the deny-list, unsupported kinds skipped, PVCs recreated without data, and per-claim volume restore outcomes. |
databaseRecoveryHints |
array | For delegated CloudNativePG clusters, a ready-to-apply bootstrap.recovery stanza per cluster. Retour never bootstraps recovery on its own. |
hookStatuses |
array | Per-hook outcome for post-restore hooks. |
Examples
Full Restore (by Backup Name)
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: full-restore
spec:
backupName: my-backup
storageBackend: s3-production
Selective Restore
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: selective-restore
spec:
backupName: my-backup
storageBackend: s3-production
resourcesToRestore:
- kind: Deployment
name: webapp
namespace: production
- kind: PersistentVolumeClaim
name: webapp-data
namespace: production
Restore from External Path
Use when no Backup CR exists — restoring from another cluster, or after the CR was deleted:
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: external-restore
spec:
backupPath: /backups/2024-01-15/my-backup
storageBackend: s3-production
Self-contained backups
Every backup carries its own metadata documents on the storage backend, so a backupPath restore needs nothing else — no CR, no database. Point the path at the backup's directory (the status.backendPath of the original Backup). See Disaster Recovery.
Restore Template (No Execution)
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: restore-template
spec:
isTemplate: true
backupName: my-backup
storageBackend: s3-production
resourcesToRestore:
- kind: Deployment
name: webapp
namespace: production
Using a Template
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: restore-from-template
spec:
templateRef: restore-template
Restore into Another Namespace
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: clone-to-staging
spec:
backupName: production-nightly
storageBackend: s3-production
namespaceMapping:
production: staging
storageClassMapping:
ceph-rbd: standard
scaleWorkloadsToZero: true
Dry Run First
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: recovery-plan
spec:
backupName: production-nightly
storageBackend: s3-production
existingResourcePolicy: Update
dryRun: true
Read status.plannedObjects, then re-apply the same manifest with dryRun
removed.
File-Level Restore into an Existing Claim
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: recover-config
spec:
backupName: production-nightly
storageBackend: s3-production
pathFilters:
- /etc/app/config.yaml
targetPVC:
claimName: webapp-data
namespace: production
Patch Manifests on the Way In
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: restore-with-modifiers
spec:
backupName: production-nightly
storageBackend: s3-production
resourceModifiers:
- groupKind: Deployment.apps
namespaces: [production]
mergePatch: |
{"spec":{"replicas":1}}
Import from Velero
apiVersion: retour.thebackup.company/v1
kind: Restore
metadata:
name: import-legacy-app
spec:
veleroImport:
backendRef: velero-bucket
path: backups/daily-20260830020000
volumeData:
enabled: true
repoPassword:
name: velero-repo-credentials
namespaceMapping:
legacy-app: legacy-app
dryRun: true
See Storage Backends for the backend that reaches the Velero bucket, and the Capability Reference for what the import does and does not carry.