Replicate Component Versions with the Controller
Goal
Use the OCM Kubernetes controller to transfer a component version, together with
the full graph of components it references (if recursion is enabled), from one OCM
repository to another. The transfer re-runs automatically whenever the source
Component resolves to a new version.
Prerequisites
- Controller environment set up
- For this example, you don’t need Kro or Flux. Just a cluster and ocm-controller running.
- The OCM CLI installed, to build and push the source component
- A source and a target OCI registry you can push to, with credentials that have
write access (for example a
ghcr.io
token with
write:packages)
If you already have a component version in a source repository, skip the first
step and point the Component at it.
How it works
A Replication references two objects:
spec.componentRef, aComponentthat resolves and verifies the source version. The version that gets transferred is the one recorded in thatComponent’s status, so it has already been successfully reconciled.spec.targetRepositoryRef, theRepositorythe content is written to.
When the source Component’s resolved version changes, the controller walks the
component’s reference graph and transfers the component version into the target.
It records the transferred version and digest in its status and treats an unchanged
digest as a no-op, so it never re-transfers content that is already present.
Transfer behaviour (recursion depth, copy mode, upload type) and the registry
credentials are supplied as OCM configuration through ocmConfig. In the steps
below they live in two Secrets: the Component carries the credentials and
propagates them down, while the Replication declares the transfer settings
itself and references the Component to merge in those propagated credentials.
The configuration influences the way the transfer happens: recursive controls following references,
copyMode controls which resources are transferred, and uploadType controls how they are uploaded.
These options mirror the flags on the ocm transfer component-version CLI command, mapped out in the
configuration step below.
Steps
Build a source component graph
To show recursion in action, build a small graph: a parent component that references a child, each carrying a blob. Replicating the parent pulls the child along with it.
component-constructor.yaml
echo "parent payload" > parent.txt echo "child payload" > child.txt cat <<EOF > component-constructor.yaml components: - name: ocm.software/examples/replication/child version: 1.0.0 provider: name: ocm.software resources: - name: data type: blob version: 1.0.0 input: type: file path: ./child.txt - name: ocm.software/examples/replication/parent version: 1.0.0 provider: name: ocm.software resources: - name: data type: blob version: 1.0.0 input: type: file path: ./parent.txt componentReferences: - name: child componentName: ocm.software/examples/replication/child version: 1.0.0 EOFPush both components to the source registry:
ocm add cv --repository ghcr.io/<source-namespace> --constructor component-constructor.yamlCreate the source and target
RepositoryBoth repositories are plain
Repositoryobjects pointing at OCI registries. The source holds the component you already published; the target is where the transfer writes.repositories.yaml
cat <<EOF > repositories.yaml apiVersion: delivery.ocm.software/v1alpha1 kind: Repository metadata: name: replication-source spec: repositorySpec: baseUrl: ghcr.io/<source-namespace> type: OCIRepository interval: 10m --- apiVersion: delivery.ocm.software/v1alpha1 kind: Repository metadata: name: replication-target spec: repositorySpec: baseUrl: ghcr.io/<target-namespace> type: OCIRepository interval: 10m EOFkubectl apply -f repositories.yamlCreate the transfer configuration and credentials
Store the configuration in two
Secrets under the.ocmconfigkey. Theocm-credentialsSecret holds the registry credentials and is propagated by theComponent; theocm-transfer-configSecret holds the transfer settings and is referenced directly by theReplication. The controller reads both as OCM configuration.config.yaml
cat <<EOF > config.yaml apiVersion: v1 kind: Secret metadata: name: ocm-credentials type: Opaque stringData: .ocmconfig: | type: generic.config.ocm.software/v1 configurations: - type: credentials.config.ocm.software consumers: - identity: type: OCIRegistry hostname: ghcr.io credentials: - type: Credentials properties: username: <username> password: <token> --- apiVersion: v1 kind: Secret metadata: name: ocm-transfer-config type: Opaque stringData: .ocmconfig: | type: generic.config.ocm.software/v1 configurations: - type: transfer.config.ocm.software/v1alpha1 recursive: -1 copyMode: localBlob EOFkubectl apply -f config.yamlThe
transfer.config.ocm.softwareentry controls the transfer itself. Each key maps directly to a flag on theocm transfer component-versionCLI command:Config key CLI flag Effect recursive-r, --recursive-1follows component references without limit;0disables recursion. The CLI flag is a boolean: set means-1, unset means0.copyMode--copy-resourceslocalBlob(default) includes only local-blob resources;allResourcesalso pulls in remote OCI artifacts and Helm charts. Set the CLI flag forallResources, leave it unset forlocalBlob.uploadType-u, --upload-asHow resources are stored in the target: defaultlets the transfer decide,localBlobembeds them in the descriptor,ociArtifactuploads them as separate OCI artifacts (OCI target only). Only relevant for resources thatcopyModeincludes.The example sets
recursive: -1andcopyMode: localBlobbecause the sample components carry blob resources, and omitsuploadTypeto let the transfer choose the right storage for the OCI target. To use streaming for resources that are themselves OCI artifacts, setcopyMode: allResourceswithuploadType: ociArtifact.Propagating a single configSplitting the configuration is optional. If you set
policy: Propagate, you can keep everything (transfer settings and credentials) in one config on theComponentand propagate the whole thing down to theReplication, instead of having theReplicationdeclare its own transfer config.Create the source
ComponentThe
Componentresolves the version to replicate. Reference theocm-credentialsSecretwithpolicy: Propagateso the credentials flow on to theReplication.component.yaml
cat <<EOF > component.yaml apiVersion: delivery.ocm.software/v1alpha1 kind: Component metadata: name: replication-component spec: component: ocm.software/examples/replication/parent repositoryRef: name: replication-source semver: 1.0.0 interval: 10m ocmConfig: - kind: Secret name: ocm-credentials policy: Propagate EOFkubectl apply -f component.yamlWait for it to become ready:
kubectl get component replication-component -o wideCreate the
ReplicationThe
Replicationties the sourceComponentto the targetRepository. It declares the transfer settings through its ownocmConfig. Because declaring anocmConfigopts out of automatic inheritance from the parent, it also references theComponentexplicitly to pull in the registry credentials theComponentpropagates.replication.yaml
cat <<EOF > replication.yaml apiVersion: delivery.ocm.software/v1alpha1 kind: Replication metadata: name: replication-example spec: componentRef: name: replication-component targetRepositoryRef: name: replication-target ocmConfig: - kind: Secret name: ocm-transfer-config - kind: Component apiVersion: delivery.ocm.software/v1alpha1 name: replication-component EOFkubectl apply -f replication.yamlEffective configurationThe effective configuration the
Replicationreconciles with is the combination of what it declares in its ownspec.ocmConfig, what theComponentpropagates, and the targetRepository’s configuration.Watch it run
kubectl get replication replication-example -owideThe transfer proceeds in two stages:
- While the controller walks the component’s reference graph through the
resolution service, the
Readycondition staysFalsewith reasonResolutionInProgress(one pass per graph level, event-driven). - While the transfer executes, the
TransferInProgresscondition isTrue. - On completion the
Readycondition flips toTrue,status.lastTransferredVersionandstatus.lastTransferredDigestare set, andTransferInProgressreturns toFalse.
kubectl get replication replication-example -o yamlRe-applying with the same source digest is a no-op, the controller short-circuits on
lastTransferredDigest.- While the controller walks the component’s reference graph through the
resolution service, the
Verify the target
Confirm the parent landed in the target repository:
ocm get cv ghcr.io/<target-namespace>//ocm.software/examples/replication/parent:1.0.0Because the transfer is recursive, the referenced child is present too:
ocm get cv ghcr.io/<target-namespace>//ocm.software/examples/replication/child:1.0.0
Troubleshooting
When a transfer fails, the Ready condition is set to False with the error
message, and per-transformation failures are recorded in
status.lastFailedTransferEvents:
kubectl get replication replication-example -o jsonpath='{.status.lastFailedTransferEvents}'Symptom: Ready=False stuck on ResolutionInProgress
Cause: The controller is still discovering the component’s reference graph, or a referenced component version cannot be resolved from the source repository.
Fix: Confirm the source Component is ready and that every referenced
component version actually exists in the source repository. Resolution is
event-driven and advances one graph level per pass, so a large graph takes
several reconciliations.
Symptom: authentication or “unauthorized” errors during transfer
Cause: The credentials in the transfer Secret do not cover one of the hosts
involved, or lack push permission on the target.
Fix: Ensure a credentials.config.ocm.software consumer exists for every
registry hostname (source and target), and that the target token has write
access (for example write:packages on ghcr.io).
Symptom: nothing happens after applying the Replication
Cause: The source Component or the target Repository is not yet Ready.
The controller waits for both before transferring.
Fix: Check both objects:
kubectl get component replication-component -o wide
kubectl get repository replication-target -o wideNext Steps
- Verify Component Versions in the Controller - Verify signatures on the source version before it is replicated
Related Documentation
- Concept: Kubernetes Controllers - How Replication fits alongside the reconciliation chain
- Reference: Replication CRD - Full API specification
- How-To: Configure Credentials for OCM Controllers - Set up registry credentials for the controller
- Example:
replication-simple- A complete, runnable manifest set used by the controller end-to-end tests