Skip to content

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, or backupPath.

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.