Kubernetes Controllers

The OCM controllers reconcile OCM component versions into a Kubernetes cluster. They form a chain of four custom resources, each depending on the previous one becoming Ready:

  • Repository validates that an OCM repository is reachable.
  • Component resolves and verifies a component version from that repository.
  • Resource fetches a specific resource from the component version.
  • Deployer downloads the resource content and applies it to the cluster.

A fifth resource, Replication, sits alongside the chain rather than within it. Instead of delivering content into the cluster, it transfers a resolved component version from one OCM repository to another, mirroring ocm transfer as a controller.

Architecture

The OCM controllers act as a bridge between an OCM repository and a Kubernetes cluster. Rather than pulling manifests from a Git repository or a plain OCI image, they consume structured OCM component versions — complete with provenance, signatures, and access metadata — and translate them into running workloads.

This separation means that the same component version can be deployed to multiple clusters, across air-gapped environments, or with different credential configurations, without changing what was published. The controllers handle resolution, verification, and delivery. The component author controls what gets shipped and how it is signed.

Show architecture diagram
Artifact Storage
Artifact Storage
GitOps Sources
GitOps Sources
Cluster Administrator
Cluster Administrator
Kubernetes Cluster
Kubernetes Cluster


















3rd Party Deployers
3rd Party Deployers...
GitOps 
Deployer
GitOps...
API Server
















OCM Controller
OCM Controller...
Repository ControllerComponent ControllerResource ControllerDeployerControllerRComponent Descriptor Resolution And Verification Service
Component Descriptor Cache (In-Memory)
Component Descriptor Cache (In-Memory)
OCM Repositories
OCM Repositories
OCI RegistryRRFluxKROArgoCDR
Poll 
Descriptors
Poll...
OCI RegistryHelmRepository
 
Verify And 
Fetch
Resources
...
RRR
Health
Check
Health...
RR
Watch /
CRUD
API Objects
Watch /...
API Objects
API Objects
Repository
Repository
Resource
Resource
RBAC
RBAC
Deployment
Deployment
RR
Watch /
CRUD
API Objects
Watch /...
Push API 
Objects To 
GitOps
Push API...
Component
Component
Deployer
Deployer
Watch /
CRUD
API Objects
Watch /...
Poll API Objects
Poll API O...
Text is not SVG - cannot display

The Reconciliation Chain

Each custom resource in the chain depends on the previous one being Ready. A child object declares a reference to its parent by name, and will not proceed until the parent has been successfully reconciled. This gives each step a clear responsibility and a clear failure surface.

  flowchart LR
    classDef crd fill:#e8f4fd,stroke:#2c7be5,color:#1a1a2e

    Repository["Repository"]
    Component["Component"]
    Resource["Resource"]
    Deployer["Deployer\n(cluster-scoped)"]

    Repository -->|referenced by| Component
    Component  -->|referenced by| Resource
    Resource   -->|referenced by| Deployer

    class Repository,Component,Resource,Deployer crd

Repository

A Repository validates that a given OCM repository is reachable and healthy. It is the entry point for the entire chain — without a Ready repository, nothing downstream can proceed. You configure it with the registry base URL and type, plus any credentials needed to reach it.

API reference

Component

A Component tracks a specific OCM component within a repository. You give it a component name and a semver constraint, and it continuously resolves the highest matching version. Once resolved, the controller verifies the component descriptor and optionally checks cryptographic signatures. The resolved version and digest are published in its status for downstream objects to consume.

You can control whether the controller is allowed to move to a lower version if the constraint is relaxed (downgradePolicy), and narrow the candidate set further with a regex filter on version strings.

API reference

Resource

A Resource selects a specific artifact from within a resolved component version. You identify the artifact by name (and optionally by a path through nested component references). Once the parent Component is Ready, the controller fetches the resource descriptor — access information, digest, type, labels — and stores it in its status.

You can attach CEL expressions via additionalStatusFields to extract values from the descriptor and surface them in status.additional. This is useful when downstream tools like Kro need to wire specific values — such as an image reference or chart version — into their own resources without parsing the full descriptor themselves.

API reference

Deployer

A Deployer is a cluster-scoped object that watches a Resource and, once it is Ready, downloads the resource blob and applies the contained Kubernetes manifests to the cluster using server-side apply. It manages the full lifecycle of what it deploys: creating resources on first apply, updating them when the component version changes, and pruning resources that are no longer part of the manifest set.

See Kubernetes Deployer for a full description of its apply semantics, drift detection, and caching behavior.

API reference

Replication

A Replication references a Component for its source and a Repository for its target. It watches the source Component’s status and, whenever the resolved version changes, transfers that version, together with the full graph of components it references (if recursion is enabled), into the target repository. It is the controller equivalent of ocm transfer, and fits delivery pipelines, promotion between environments, and backup or air-gap scenarios where a management cluster mirrors content into a downstream registry.

  flowchart LR
    classDef crd fill:#e8f4fd,stroke:#2c7be5,color:#1a1a2e
    classDef ext fill:#f5f5f5,stroke:#888,color:#1a1a2e

    Source["Repository\n(source)"]
    Component["Component"]
    Target["Repository\n(target)"]
    Replication["Replication"]

    Source -->|referenced by| Component
    Component -->|source version| Replication
    Target -->|target| Replication
    Replication -->|transfers to| TargetReg[("Target OCM\nrepository")]

    class Source,Component,Target,Replication crd
    class TargetReg ext

A few more things about replication:

  • The transferred version is the one recorded in the source Component’s status, meaning a version that has already been successfully reconciled (and verified, if verification is configured). It does not re-evaluate the Component’s semver constraint itself.
  • A successful transfer records the source digest in status.lastTransferredDigest. A reconciliation observing the same digest is a no-op, so re-applying or requeueing does not re-transfer unchanged content.
  • First, it walks the component’s reference graph through the resolution worker pool, reporting ResolutionInProgress until every referenced descriptor is available. Then, it executes the transfer, reporting TransferInProgress until completion. Per-transformation failures are recorded in status.lastFailedTransferEvents and cleared on the next success.
  • Recursion depth, copy mode, upload type, and the credentials for the target registry are supplied as OCM configuration referenced from spec.ocmConfig (a Secret or a ConfigMap object carrying a transfer.config.ocm.software entry). See Replicate Component Versions with the Controller for a concrete example.

API reference

Asynchronous Component Resolution

Component version resolution happens in a background worker pool. When a controller needs a component version, it submits a request and receives a sentinel error (ErrResolutionInProgress). The controller returns early without blocking. Once the worker finishes, it broadcasts an event that re-triggers reconciliation for all waiting objects.

Requests for the same component version are deduplicated across multiple subscribers.

Configuration Propagation

OCM configuration such as credentials and resolvers flows through the reconciliation chain. Each object can declare its own config references and inherit configs from its parent:

spec:
  ocmConfig:
    - kind: Secret
      name: registry-credentials
      policy: Propagate

Propagate makes the config available to child objects in the chain. DoNotPropagate scopes it to that object only. Supported sources are Kubernetes Secrets, ConfigMaps, and OCMConfiguration objects.

Additional Status Fields

The Resource object supports additionalStatusFields, a map of field names to CEL expressions evaluated against the resource descriptor:

spec:
  additionalStatusFields:
    registry: "resource.access.toOCI().registry"

Results are stored under status.additional.<fieldName> and can be consumed by downstream tools like Kro to wire values between resources in a ResourceGraphDefinition.

Installation

See Install the OCM Controllers for a step-by-step guide including prerequisites and verification.

Deployer tools

While the OCM controllers technically can be used standalone, it requires kro and a deployer, e.g. Flux, to deploy an OCM resource into a Kubernetes cluster. The OCM controllers deployment, however, does not contain kro or any deployer. Please refer to the respective installation guides for these tools: