Deploy Helm Charts

This tutorial walks you through deploying a Helm chart from an OCM component version to a Kubernetes cluster, using the OCM Controllers with kro and Flux.

What You’ll Learn

By the end of this tutorial, you will:

  • Create and publish an OCM component version that references a Helm chart
  • Define a ResourceGraphDefinition to orchestrate OCM and Flux resources
  • Deploy the Helm chart to your cluster using the OCM Controllers

Estimated time

~20 minutes

Scenario

You as a developer create an application, packaged as a Helm chart, and publish it as OCM component version in an OCI registry. Then, you as an operator deploy the Helm chart into a Kubernetes cluster using the OCM Controllers. You define a ResourceGraphDefinition that tells kro how to orchestrate the OCM and Flux resources to deploy the Helm chart.

Prerequisites

Create and Publish a Component Version

  1. Create a working directory

    mkdir /tmp/helm-deploy && cd /tmp/helm-deploy
  2. Define the component

    Create a component-constructor.yaml file that includes a Helm chart resource:

    components:
      - name: ocm.software/ocm-k8s-toolkit/simple
        provider:
          name: ocm.software
        version: "1.0.0"
        resources:
          - name: helm-resource
            type: helmChart
            version: 1.0.0
            access:
              type: ociArtifact
              imageReference: "ghcr.io/stefanprodan/charts/podinfo:6.9.1@sha256:565d310746f1fa4be7f93ba7965bb393153a2d57a15cfe5befc909b790a73f8a"

    This component references the podinfo Helm chart, a simple web application that displays pod information.

  3. Build the component version

    Now add the component version to a local Common Transport Format (CTF) archive. By default, the ocm add cv command looks for a file called component-constructor.yaml in the current directory, and creates a CTF archive in a folder called transport-archive:

    ocm add cv
    You should see this output
    time=2026-02-25T12:47:09.622+01:00 level=WARN msg="could not get credential consumer identity for component version repository" repository=transport-archive error="failed to get component version repository: cannot resolve consumer identity for ctf: credentials not supported"
    COMPONENT                           │ VERSION │ PROVIDER
    ─────────────────────────────────────┼─────────┼──────────────
    ocm.software/ocm-k8s-toolkit/simple │ 1.0.0   │ ocm.software
  4. Transfer to your registry

    Use ocm transfer cv and specify the correct reference (<path-to-your-ctf>//<component>:<version>) and target repository. Replace <your-namespace> with your registry namespace:

    📣 Note: 📣
    If your registry requires authentication, configure Credentials for OCM CLI first.

    ocm transfer cv transport-archive//ocm.software/ocm-k8s-toolkit/simple:1.0.0 ghcr.io/<your-namespace>
    You should see this output
    Transferring component versions...
      ✓ transformOcmSoftwareOcmK8sToolkitSimple100Upload [OCIAddComponentVersion]
      [████████████████████████████████████████] 100% 1/1

    To make your component public in GitHub Container Registry, go to the packages tab in your GitHub repository https://github.com/<your-namespace>?tab=packages, select the package component-descriptors/ocm.software/ocm-k8s-toolkit/simple, and under “Package settings” change the visibility to public.

  5. Verify the upload

    ocm get cv ghcr.io/<your-namespace>//ocm.software/ocm-k8s-toolkit/simple:1.0.0
    You should see this output
     COMPONENT                           │ VERSION │ PROVIDER
    ─────────────────────────────────────┼─────────┼──────────────
     ocm.software/ocm-k8s-toolkit/simple │ 1.0.0   │ ocm.software

Deploy Helm chart using the OCM Controllers

  1. Create ResourceGraphDefinition

    The ResourceGraphDefinition tells kro how to orchestrate the OCM and Flux resources.
    Create rgd.yaml with the following content, replacing <your-namespace> with your registry namespace:

    apiVersion: kro.run/v1alpha1
    kind: ResourceGraphDefinition
    metadata:
      name: simple
    spec:
      schema:
        apiVersion: v1alpha1
        # The name of the CRD that is created by this ResourceGraphDefinition when applied
        kind: Simple
        spec:
          # This spec defines values that can be referenced in the ResourceGraphDefinition and that can be set in the
          # instances of this ResourceGraphDefinition.
          # We will use it to pass a value to the Helm chart and configure the message the application shows
          # (see resource HelmRelease).
          message: string | default="foo"
      resources:
        # Repository points to the OCM repository in which the OCM component version is stored and checks if it is
        # reachable by pinging it.
        - id: repository
          template:
            apiVersion: delivery.ocm.software/v1alpha1
            kind: Repository
            metadata:
              name: simple-repository
            spec:
              repositorySpec:
                  baseUrl: ghcr.io/<your-namespace>
                  type: OCIRegistry
              interval: 1m
              # ocmConfig is required, if the OCM repository requires credentials to access it.
              # ocmConfig:
        # Component refers to the Repository, downloads and verifies the OCM component version descriptor.
        - id: component
          template:
            apiVersion: delivery.ocm.software/v1alpha1
            kind: Component
            metadata:
              name: simple-component
            spec:
              repositoryRef:
                name: ${repository.metadata.name}
              component: ocm.software/ocm-k8s-toolkit/simple
              semver: 1.0.0
              interval: 1m
              # ocmConfig is required, if the OCM repository requires credentials to access it.
              # ocmConfig:
        # Resource points to the Component, downloads the resource passed by reference-name and verifies it. It then
        # publishes the location of the resource in its status.
        - id: resourceChart
          template:
            apiVersion: delivery.ocm.software/v1alpha1
            kind: Resource
            metadata:
              name: simple-resource
            spec:
              componentRef:
                name: ${component.metadata.name}
              resource:
                byReference:
                  resource:
                    name: helm-resource # This must match the resource name set in the OCM component version (see above)
              additionalStatusFields:
                # The additional status fields are useful for splitting the imageReference into its components, so that
                # they can be used in depending deployers
                # Example: ghcr.io/stefanprodan/charts/podinfo:6.7.1 would be
                # registry: ghcr.io
                # repository: stefanprodan/charts/podinfo
                # reference/tag: 6.7.1
                registry: resource.access.imageReference.toOCI().registry
                repository: resource.access.imageReference.toOCI().repository
                tag: resource.access.imageReference.toOCI().tag
              interval: 1m
              # ocmConfig is required, if the OCM repository requires credentials to access it.
              # ocmConfig:
        # OCIRepository watches and downloads the resource from the location provided by the Resource status.
        # The Helm chart location (url) refers to the status of the above resource.
        - id: ocirepository
          template:
            apiVersion: source.toolkit.fluxcd.io/v1
            kind: OCIRepository
            metadata:
              name: simple-ocirepository
            spec:
              interval: 1m0s
              layerSelector:
                mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
                operation: copy
              url: oci://${resourceChart.status.additional.registry}/${resourceChart.status.additional.repository}
              ref:
                tag: ${resourceChart.status.additional.tag}
              # secretRef is required, if the OCI repository requires credentials to access it.
              # secretRef:
        # HelmRelease refers to the OCIRepository, lets you configure the Helm chart and deploys the Helm chart into the
        # Kubernetes cluster.
        - id: helmrelease
          template:
            apiVersion: helm.toolkit.fluxcd.io/v2
            kind: HelmRelease
            metadata:
              name: simple-helmrelease
            spec:
              releaseName: simple
              interval: 1m
              timeout: 5m
              chartRef:
                kind: OCIRepository
                name: ${ocirepository.metadata.name}
                namespace: default
              values:
                # We configure the Helm chart using Flux's HelmRelease 'values' field. We pass the value that we set in
                # the instance of the CRD created by the ResourceGraphDefinition (see below).
                ui:
                  message: ${schema.spec.message}
  2. Apply the ResourceGraphDefinition

    kubectl apply -f rgd.yaml
    You should see this output
    resourcegraphdefinition.kro.run/simple created

    Verify it’s active:

    kubectl get rgd
    You should see this output
    NAME     APIVERSION   KIND     STATE    AGE
    simple   v1alpha1     Simple   Active   19s

    A new Custom Resource Definition called Simple that you can now instantiate has been created.

  3. Create an instance

    Create instance.yaml to deploy the application:

    apiVersion: kro.run/v1alpha1
    kind: Simple
    metadata:
      name: simple
    spec:
      message: "Deployed with OCM!"
  4. Deploy the application

    kubectl apply -f instance.yaml
    You should see this output
    simple.kro.run/simple created

    Wait for the deployment to complete:

    kubectl get simple -w
    You should see this output
    NAME     STATE    SYNCED   AGE
    simple   ACTIVE   True     2m
  5. Verify the deployment

    Check that the pod is running:

    kubectl get deployments

    Output:

    NAME             READY   UP-TO-DATE   AVAILABLE   AGE
    simple-podinfo   1/1     1            1           3m

    Verify the custom message you configured in the HelmRelease values is showing up in the application:

    kubectl get pods -l app.kubernetes.io/name=simple-podinfo \
      -o jsonpath='{.items[0].spec.containers[0].env[?(@.name=="PODINFO_UI_MESSAGE")].value}'

    Output:

    Deployed with OCM!

Troubleshooting

Authentication Errors

If you see errors like:

failed to list versions: response status code 401: unauthorized

Your registry package may be private. Either:

  • Make the package public in your registry settings
  • Configure credentials for the OCM Controller resources

Resource Not Found

If the component isn’t found, verify:

  • The component was transferred successfully: ocm get cv ghcr.io/<your-namespace>//ocm.software/ocm-k8s-toolkit/simple:1.0.0
  • The baseUrl in the ResourceGraphDefinition matches your registry

Cleanup

Remove the deployed resources:

kubectl delete -f instance.yaml
kubectl delete -f rgd.yaml

Next Steps