Shared sub-objects¶
Types reused across multiple CRDs. For the terse type/default table see the field reference.
Backend¶
The storage backend for a kopia repository. Exactly one backend block is set —
the wire shape is externally tagged, so you write backend: { s3: {...} },
backend: { filesystem: {...} }, and so on, and kubectl enforces "exactly one
backend" at admission. See the backend guides for
provider-specific setup.
Variants:
s3— Amazon S3 or any S3-compatible store (MinIO, RustFS, Ceph RGW, …).bucketis required;prefixlets several repositories share one bucket (e.g.clusters/prod/);endpointis omitted for AWS and set for S3-compatible stores;regionis required by AWS and some providers;tlscarries TLS overrides;authis a BackendAuth.azure— Azure Blob Storage.containeris required;storageAccountis set when it can't be inferred from credentials.gcs— Google Cloud Storage.bucketis required.b2— Backblaze B2.bucketis required; auth is Secret-only (B2 has no cloud IAM federation).filesystem— kopia writes the repository to apathinside the mover pod, populated by a RepoVolume. kopia has no NFS backend; NFS is reached through the filesystem backend by mounting the export atpath.sftp— SFTP server (host,path, optionalportdefaulting to 22,username); Secret-only auth (SSH key / known-hosts).webdav— WebDAV endpoint (url); Secret-only HTTP basic-auth.rclone— any rclone remote inremote:pathform, with a Secret holding therclone.conf. kopia shells out torclone, broadening reach to providers without a native kopia backend.
RepoVolume¶
What backs a filesystem repository's mount path. Externally tagged, exactly one of:
pvc—volume: { pvc: { name: "repo-pvc" } }: aPersistentVolumeClaimmounted read-write at the repo path (in the mover's namespace).nfs—volume: { nfs: { server: "nas.lan", path: "/export/kopia" } }: an inline NFS export mounted directly, no PVC and no StorageClass.
volume may be absent when the path is already present on the node/image (a
baked-in mount, mainly the e2e harness).
BackendAuth¶
Credentials for a cloud object-store backend that has an IAM plane (S3, Azure, GCS). Set exactly one of:
secretRef— a SecretRef to the Secret holding static access credentials. The operator reads well-known keys (e.g.AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYfor S3).workloadIdentity— a cloud-federatedServiceAccountinstead of static keys.
An absent auth is also legal — the well-known keys may ride the
encryption-password Secret.
Backends without a cloud IAM plane (B2, SFTP, WebDAV, rclone) use a Secret-only auth surface instead, so a workload identity on them is structurally unrepresentable.
workloadIdentity¶
A cloud workload-identity binding (AWS IRSA / EKS Pod Identity, AKS Workload
Identity, GKE Workload Identity): the mover Job runs as a user-supplied
Kubernetes ServiceAccount federated to the cloud IAM role that grants backend
access, instead of reading static keys from a Secret.
serviceAccountName— theServiceAccountthe mover pod runs as, resolved in the mover Job's own namespace.
The ServiceAccount must already exist in each namespace mover Jobs run in (the
operator never creates it) and carry the cloud-specific annotation
(eks.amazonaws.com/role-arn, azure.workload.identity/client-id,
iam.gke.io/gcp-service-account).
MoverDefaults¶
Repository-wide mover defaults inherited by every mover the repository spawns —
bootstrap, backup, restore, maintenance, verification, replication. Each field
is overridable per-recipe via the recipe's mover block. This is
the single place a repository defines mover identity, hardening, resources, and
cache. See movers.
securityContext— container security-context base for every mover.podSecurityContext— pod security-context base (notablyfsGroup).resources— resource requests/limits base for the mover container.cache— kopia CacheDefaults for every mover.scratch— ScratchDefaults for the deep-verification scratch volume.nodeSelector,tolerations,affinity— pod scheduling for every mover.sourceColocation— RWO source-PVC node co-location (see below).ttlSecondsAfterFinished—Job.spec.ttlSecondsAfterFinishedfor every mover Job so finished Jobs self-GC. Defaults to 1h when neither the repo nor the recipe sets one.throttle— repository throttle limits (kopia repository throttle set) applied by every mover after it connects, so a run doesn't saturate the link or hammer the object store:uploadBytesPerSecond,downloadBytesPerSecond,readOpsPerSecond,writeOpsPerSecond(each unset leaves kopia's current limit).
The securityContext/podSecurityContext/resources/cache fields resolve by
field-wise merge: a repo-wide default composes with a partial per-recipe
override, layered over a hardened base. See
security context for the hardened defaults and how
overrides only tighten them.
sourceColocation¶
Controls how a mover co-locates with the node its ReadWriteOnce (RWO)
source/destination PVC is attached to, to avoid a Kubernetes Multi-Attach error
(an RWO PVC can only attach to one node at a time). The mode is one of:
Auto(default) — pin to the attached node when discoverable, otherwise schedule freely (ReadWriteMany/ReadOnlyManyare never pinned). Fixes the Multi-Attach error with no configuration.Required— likeAuto, but fail the run with an actionable error when an RWO PVC's node cannot be determined.Disabled— never compute a node pin; the mover uses only the explicitnodeSelector/affinity/tolerations.
MoverSpec¶
Per-recipe mover overrides on a recipe's mover field. Each field overlays the
repository's MoverDefaults field-wise (recipe wins, the repo
default fills, the hardened base underneath):
resources— resource requests/limits for the mover container.cache— override the repository's CacheDefaults.securityContext— container security context; set only the fields you want to change.podSecurityContext— pod security context, notablyfsGroup, which makes a freshly-provisioned volume group-writable so an unprivileged mover can populate it on restore without root.privilegedMode— opt-in, namespace-gated; preserves UID/GID on restore.inheritSecurityContextFrom— see below.ttlSecondsAfterFinished— per-recipe override of the Job TTL.
A pod-level runAsUser: 0 / runAsNonRoot: false is still gated as privileged,
exactly like the container-level setting.
inheritSecurityContextFrom¶
Copies the UID/GID security context from a live workload instead of setting an
explicit securityContext/podSecurityContext. Combines with both: they are the
higher merge layer, so an explicit field overrides the inherited one and stands in
alone when no pod resolves. Requires the workload to pin runAsUser — a UID from
the image's USER line is invisible in the pod spec. Externally tagged, exactly one
of:
workloadSelector— match the workload pod(s) by a label selector (podSelector+ optionalcontainer) and inherit the chosen container'ssecurityContextplus the pod'sspec.securityContext. Works for both backup and restore (restore inherits from the pod that will read the data).pvcConsumer— backup sources only. Auto-derive the workload pod from the PVC this snapshot backs up: the operator finds the pod(s) mounting the source claim and inherits their security context — no hand-written selector. Meaningless on a restore (the consuming pod may not exist yet), so restore must useworkloadSelector.pvcConsumeraccepts an optionalcontainer.
The resolved context still enters as the recipe layer, so the hardened base
(drop:[ALL], seccomp, fsGroup) still applies.
CacheDefaults¶
How a mover's kopia cache volume is provisioned and sized.
capacity— size of the PVC backing the cache (e.g.10Gi).storageClassName— StorageClass for the cache PVC; absent uses the cluster default.metadataCacheSizeMb— kopia metadata cache budget in MiB (--metadata-cache-size-mb).contentCacheSizeMb— kopia content cache budget in MiB (--content-cache-size-mb).mode—Ephemeral(default; the cache lives only for the run, fresh each time) orPersistent(a warm cache reused across runs in a controller-ownedReadWriteOncePVC, which assumes non-overlapping runs for a given owner).
ScratchDefaults¶
Defaults for the deep-verification scratch volume — the throwaway restore
target a deep restore-test writes into and then discards. Inherited by a
recipe's verification.deep unless overridden there.
storageClassName— StorageClass for the ephemeral scratch PVC; only applies whencapacityis set.capacity— size of the ephemeral scratch PVC (e.g.100Gi); size it to comfortably hold the restored snapshot. When absent, scratch falls back to a node-ephemeralemptyDir.
Unlike the cache, scratch is always ephemeral (auto-deleted with the verify
Job), so it has no mode/persistent-PVC form.
CredentialProjection¶
Opt-in projection of a repository's credential Secret(s) into the namespace
where each mover Job runs (default off). When enabled: true, before each run
the operator reads the source Secret(s) and writes a kopiur-managed copy into the
Job's namespace, owned by the consuming CR (garbage-collected with it) and
refreshed from source every run.
Even when enabled, projection is a no-op where the source Secret already lives in
the Job's namespace (the common namespaced-Repository layout) — there is
nothing to copy. It only actually copies for the cross-namespace case (a shared
ClusterRepository whose Secret is pinned to one namespace). Keeping it off by
default preserves the namespace-as-trust-boundary posture.
CreateBehavior¶
Behavior when the repository does not yet exist (a repository's create block).
enabled— create the repository if it does not exist yet (off by default, so a typo'd backend can't silently spin up a brand-new empty repository).encryption— kopia encryption algorithm for a freshly-created repository (e.g.AES256-GCM-HMAC-SHA256).splitter— kopia object splitter for a freshly-created repository.hash— kopia content hash algorithm for a freshly-created repository.ecc— Reed-Solomon error-correcting-code parity guarding repo blobs against backend bit-rot:algorithm(e.g.REED-SOLOMON-CRC32) andoverheadPercent.
All of these are consulted only at creation time and are immutable after the repository is created.
CatalogBounds¶
Bounds on materialization of origin: discovered Snapshot CRs (a repository's
catalog block). Expiring a CR row never deletes the kopia snapshot behind it —
discovered snapshots are always deletionPolicy: Retain, and stay restorable via
Restore.source.identity.
retain— how many discoveredSnapshotCRs to keep materialized (bounds etcd footprint for large repositories):perIdentitykeeps the most-recent N perusername@hostname:pathidentity (snapshots this cluster produced don't count;0disables discovered-Snapshot materialization entirely), andmaxAgeDaysexpires discovered CRs older than N days (minimum 1).periodicRefresh— opt in to repeated re-scans (bool, defaultfalse). An initial scan always runs (first bootstrap + on any spec change); set this to keep re-scanning so out-of-band snapshots keep appearing. Off by default because each re-scan re-runs the bootstrap Job for object-store / volume-backed repos.refreshInterval— how often to re-scan whenperiodicRefreshis on (Go-style duration like30s/5m/1h; minimum30s, default1h; inert when off).fallbackNamespace— where to materialize discoveredSnapshots whose identity hostname doesn't map to an allowed namespace (ClusterRepository only; rejected on a namespacedRepository, which always materializes into its own namespace).
ServerSpec¶
Optional kopia web-UI server configuration on a Repository /
ClusterRepository. Presence of spec.server means enabled — there is no
enabled bool. The operator runs kopia server start in a Deployment and
exposes it via a Service; networking (Ingress/HTTPRoute) is left to the user. See
server.
auth— UI authentication mode (see below). Omitted defaults togenerate— never no-auth.readOnly— connect the server's repository read-only so the UI cannot create, delete, or alter backups (browse + restore-download only). A repository withspec.mode: ReadOnlyforces this on; setting an explicitreadOnly: falseon aReadOnlyrepository is rejected. Read-only blocks mutation, not reading — the server still holds the decryption key, so anyone reaching the UI can read and restore every backup.service— how the server is exposed as aService:type(ClusterIPdefault,NodePort,LoadBalancer),port(default51515), andannotations(the seam users wire their own Ingress/LoadBalancer onto).resources— resource requests/limits for the server pod.securityContext— override the hardened default container security context.podSecurityContext— pod-level security context. Notably carriessupplementalGroups, the way to grant the long-lived server write access to a group-owned filesystem export —fsGroupis silently a no-op on NFS (the kubelet does not recursively chown in-tree NFS mounts).
On a ClusterRepository the server config additionally requires a
namespace (a cluster-scoped server has no implicit namespace).
By default the server holds a read-write repository connection. kopia's UI is
full read-write-delete, and the server process holds the repository decryption
key, so exposing it exposes full mutation of all backups — hence the
generate-by-default and the mandatory acknowledgeInsecure on no-auth.
Server auth¶
UI authentication mode, externally tagged, exactly one of:
generate— the operator mints random UI credentials into an ownedSecretand pins the reference tostatus.server.generatedSecretRef. The safe default. Optionalusername(defaultkopia).secretRef— the user supplies UI credentials via aSecret:name,usernameKey,passwordKey(all required, so "keys are present" is a structural guarantee).insecure— no UI authentication. Requires an explicitacknowledgeInsecure: true(webhook-rejected otherwise) because it exposes full read/write/delete of the repository with no login.
Retention¶
GFS (grandfather-father-son) retention — how many snapshots to keep per time
bucket. The single successful-retention driver. All fields are optional counts:
keepLatest (the N most-recent regardless of age), keepHourly, keepDaily,
keepWeekly, keepMonthly, keepAnnual (one snapshot per bucket for the
most-recent N of that bucket).
Identity¶
What kopia records as username@hostname:path for a snapshot. Resolved at
admission and pinned to status, never re-rendered.
username— override theusernameportion; absent uses the resolved default (the repository'sidentityDefaultsCEL expression, or the object name).hostname— override thehostnameportion; absent uses the resolved default (theidentityDefaultsexpression, or the namespace).
The fully-resolved values are pinned in status as username, hostname, and
(where applicable) the sourcePath.
CronSpec¶
A single cron entry with optional deterministic jitter, shared by Maintenance's
quick/full schedules.
cron— the cron expression, parsed bycroner. May contain anHplaceholder for deterministic per-schedule jitter.jitter— optional deterministic jitter window as a Go-style duration string (e.g.30m), derived from(scheduleUID, slot)so it is stable across restarts.
RepositoryRef¶
A reference from a consumer CR (SnapshotPolicy, Snapshot, Restore,
Maintenance) to a Repository or ClusterRepository. See
repositories.
kind—Repository(default, namespaced) orClusterRepository(cluster-scoped).name— name of the referenced repository.namespace— cross-namespaceRepositoryreference. Whenkind: ClusterRepository,namespaceMUST be absent (webhook-enforced — a cluster-scoped repository has no namespace).
SecretKeyRef and SecretRef¶
SecretKeyRef references a single key within a Secret (name, optional
namespace, optional key); a repository password (via Encryption) is always
a SecretKeyRef, never inline. SecretRef references an entire Secret (the
operator reads well-known keys from it; name + optional namespace). For both,
an absent namespace means the same namespace as the referrer.
FailurePolicy¶
Per-run failure controls passed through to the mover Job (on a recipe's
failurePolicy):
backoffLimit—Job.spec.backoffLimit, retries before a failed run is marked failed.activeDeadlineSeconds—Job.spec.activeDeadlineSeconds, a wall-clock cap after which a still-running run is killed (meant for long-running work).podStartupDeadlineSeconds— how long a mover pod may sit in a non-starting state (a containerCreateContainerConfigError/ImagePullBackOff/InvalidImageName, orUnschedulable) before the run is failed with an actionable reason. A wedged pod never reaches a terminal phase, sobackoffLimitnever trips — this is the only thing that bounds it. Default 300s.