Skip to content

Architecture

Retour splits the work across four components: the operator reconciles CRs and sets up RBAC, the worker orchestrates each backup or restore, the carrier moves the data, and an optional UI wraps the same flows. State lives in exactly two places — the Backup/Restore CRs hold the live status, and the storage backend holds the durable record next to the data. There is no database in the backup path.

System Diagram

Retour system architecture overview

Where state lives

The Backup and Restore CRs are the live state. The worker is the only writer of non-terminal status: phase, per-PV progress (file and byte counters), errors, and the backup identity fields status.backupUUID, status.jobUUID, status.backendPath, and status.operations[]. The operator watches worker pods and steps in only when a worker dies without reporting, writing a terminal Failed or Completed status with condition reason OperatorFallback.

The storage backend is the durable source of truth. Next to each backup's data the carrier writes a metadata JSON document (backup-operation-<id>-metadata.json, format frozen in pkg/backupmeta with a schemaVersion) plus the cluster manifest YAMLs (operation-cluster-metadata-<id>/). A restore needs nothing but these documents — that's what lets you restore backups from another cluster, or backups whose CR has since been deleted.

Components

operator

Reconciles the Backup, Restore, BackupSchedule, and StorageBackend CRs. For each backup or restore it creates least-privilege RBAC — a ServiceAccount, ClusterRole, and ClusterRoleBinding whose rules are scoped with resourceNames so the worker can only touch its own CR's status and the Secrets it actually needs — and then spawns the worker pod. It validates BackupSchedule cron expressions through a webhook and checks StorageBackend connectivity.

The operator doesn't track progress. Its only status writes are the terminal fallback described above, driven by watching worker pods.

worker

Runs the backup or restore end to end. For a backup it creates CSI VolumeSnapshots, spawns one carrier Job per volume, tails each carrier's logs for progress, and patches the Backup CR's status live. For a restore it parses the metadata documents fetched from the backend, recreates PVCs and Kubernetes resources through a resource-kind registry, and spawns carrier pods to copy data back. When it finishes, the worker writes the terminal phase itself and exits non-zero on failure, so the pod phase tells the truth.

carrier

The data plane: an rclone wrapper that copies volume data to or from the storage backend. During backup it also writes the metadata JSON document and the cluster manifest YAMLs alongside the data. It reports progress as single-line {"retourProgress":{...}} JSON on stdout, which the worker tails to update CR status. Optional crypt remotes give encryption at rest.

server (retour-server)

  • REST API: Gin framework, Swagger docs at /swagger/index.html
  • Authentication: any number of OIDC/OAuth2 and SAML identity providers (Okta, Keycloak, Entra ID, Ping, JumpCloud, OneLogin, CyberArk, Dex, or a generic OIDC issuer), each rendered as its own login button; JWT remains for deployments with no IdP
  • Storage backends: CRUD, sync with StorageBackend CRs
  • Kubernetes integration: Lists backups and restores by reading the CRs
  • UI: Web interface for backups, restores, storage backends; scaffolds YAML

PostgreSQL (UI only)

PostgreSQL serves only the UI backend: Dex/OIDC session state and UI connection settings. The operator, worker, and carrier never connect to it. If Postgres goes down, backups and restores keep working — the chaos E2E kills it mid-backup to prove exactly that. If you don't deploy the UI, you don't need Postgres at all.

db-migrator

Runs schema migrations for the UI database, as an init container of the UI deployment. Clusters upgraded from the pre-refactor architecture may still have old state tables; dropping them is opt-in via RETOUR_CONFIRM_STATE_TABLE_DROP=true.

Dex (optional)

  • Identity providers for OIDC/SAML authentication with the UI (one or many)
  • Federates with LDAP, GitHub, Google, and other IdPs