Deploying a Helm Chart (with Bootstrap)

The concept and the previous guide showed a basic example of how to deploy a Helm chart from an OCM component. By defining a ResourceGraphDefinition that contains all the required resources to deploy the Helm chart into a Kubernetes cluster.

However, there are scenarios, where the developer already knows how the deployment instructions for the Helm chart should look like and what should be configured. Accordingly, the developer can create a ResourceGraphDefinition that contains all the required resources to deploy and configure the Helm chart, and deliver it with the OCM component version itself. This way, the deployment instructions can also be delivered securely through OCM and the operator does not need to know which resources are required.

In such a case, we need to bootstrap the ResourceGraphDefinition from the OCM component and apply it to the cluster.

To do so, we use the OCM Controller resource Deployer. By referencing the Resource containing the ResourceGraphDefinition by name, the deployer will download the content from the OCM component and apply it to the cluster.

The following guide demonstrates how to deploy a Helm chart using a ResourceGraphDefinition that is also delivered with the same OCM component. Additionally, it shows how to localize a Helm chart.

Localization describes the process of inserting a new image reference into the deployment instructions, e.g. a Helm chart. It is a two-step process:

  1. When an OCM component and its resources are transferred to another registry, referential resources can potentially update their reference to the new location. For instance, a resource with an access type ociArtifact will update its image reference in the component descriptor to the new registry location, if the OCM transfer is done with the flag --copy-resources.
  2. However, the deployment using the image is not aware of this change. Accordingly, we need to insert the new image reference into the deployment instruction. This can be done using deployment tools like FluxCDs HelmRelease and Kustomization or ArgoCDs Helm and Kustomize.

The following diagram shows an overview of the resources and their relationships of this guide:

  flowchart TB
    classDef cluster fill:white,color:black,stroke:black;
    classDef reconciledBy fill:#dedede,stroke:black,stroke-dasharray: 5,color:black;
    classDef k8sObject fill:#b3b3b3,color:black,stroke:black;
    classDef information fill:#b3b3b3,color:black,stroke:none;
    classDef templateOf fill:#b3b3b3,color:black,stroke:black,stroke-dasharray: 2;
    classDef ocm fill:white,stroke:black,color:black;
    classDef legendStyle fill:white,stroke:black,color:black,stroke-dasharray: 2;
    classDef legendStartEnd height:0px;
    classDef legendItems fill:#b3b3b3,stroke:none,color:black;

    subgraph legend[Legend]
        start1[ ] ---references[referenced by] --> end1[ ]
        start2[ ] -.-creates -.-> end2[ ]
        start3[ ] ---instanceOf[instance of] --> end3[ ]
        start4[ ] ~~~reconciledBy[reconciled by] ~~~ end4[ ]
        start5[ ] ~~~k8sObject[k8s object] ~~~ end5[ ]
        start6[ ] ~~~templateOf[template of] ~~~ end6[ ]
    end

    subgraph background[ ]
        direction TB
        subgraph ocmRepo[OCM Repository]
            subgraph ocmCV[OCM Component Version]
                direction RL 
                subgraph ocmResourceHelm[OCM Resource: HelmChart]
                end
                subgraph ocmResourceImage[OCM Resource: Image]
                end
                subgraph ocmResourceRGD[OCM Resource: RGD]
                end
            end
        end

        subgraph k8sCluster[Kubernetes Cluster]
            subgraph bootstrap[OCM Controllers]
                k8sRepo[OCMRepository]
                k8sComponent[Component]
                k8sResourceRGD[Resource: RGD]
                k8sDeployer[Deployer]
            end
            subgraph kro[kro]
                subgraph rgd[RGD: Bootstrap]
                    rgdResourceHelm[Resource: HelmChart]
                    rgdResourceImage[Resource: Image]
                    rgdSource[FluxCD: OCI Repository]
                    rgdHelmRelease[FluxCD: HelmRelease]
                end
                crdBootstrap[CRD: Bootstrap]
                subgraph instanceBootstrap[Instance: Bootstrap]
                    subgraph ocmControllers[OCM Controllers]
                        k8sResourceHelm[Resource: HelmChart]
                        k8sResourceImage[Resource: Image]
                    end
                    subgraph fluxCD[FluxCD]
                        source[OCI Repository]
                        helmRelease[HelmRelease]
                    end
                    k8sResourceImage ---info[localization reference] --> helmRelease
                end
            end
            helmRelease --> deployment[Deployment: Helm chart]
        end

        ocmRepo --> k8sRepo --> k8sComponent --> k8sResourceRGD --> k8sDeployer --> rgd --> crdBootstrap --> instanceBootstrap
        k8sComponent --> k8sResourceHelm & k8sResourceImage
        k8sResourceHelm --> source --> helmRelease
    end

    linkStyle default fill:none,stroke:black,color:black;
    linkStyle 2,3,20 stroke:black,fill:none,color:black,stroke-dasharray: 10;
    linkStyle 4,5,21 stroke:black,fill:none,color:black,stroke-dasharray: 4;

    class start1,end1,start2,end2,start3,end3,start4,end4,start5,end5,start6,end6 legendStartEnd;
    class references,creates,instanceOf legendItems;
    class templateOf,rgdResourceHelm,rgdResourceImage,rgdSource,rgdHelmRelease templateOf;
    class info information;
    class reconciledBy,ocmK8sToolkit,bootstrap,fluxCD,kro reconciledBy;
    class k8sObject,rgd,k8sRepo,k8sComponent,k8sResourceRGD,k8sDeployer,k8sResourceHelm,k8sResourceImage,source,helmRelease,deployment,crdBootstrap,instanceBootstrap k8sObject;
    class ocmRepo,ocmCV,ocmResourceHelm,ocmResourceRGD,ocmResourceImage ocm;
    class k8sCluster cluster;
    class legend legendStyle;

As the diagram shows, we will start by creating an OCM component that contains three resources:

  • An OCM Resource containing the “HelmChart” we want to deploy.
  • An OCM Resource containing an access specification to an “Image” we want to use for the deployment and localization.
  • An OCM Resource containing the ResourceGraphDefinition (RGD) that will deploy the Helm chart and configure the localization.

To enable the bootstrap of the ResourceGraphDefinition, we will create the respective OCM Controller resources that point to the OCM repository (“Repository”), the OCM component version (“Component”), and the OCM Resource (“Resource: RGD”) that contains the ResourceGraphDefinition. The OCM Controller resource “Deployer” will refer to the aforementioned “Resource: RGD”, download the ResourceGraphDefinition, and apply it to the cluster.

After applying the ResourceGraphDefinition, kro will reconcile it and create a Custom Resource Definition (“CRD: Bootstrap”). By creating an instance of that CRD, we will deploy the resources as defined in the ResourceGraphDefinition:

  • An OCM Controller resource “HelmChart” of type Resource that contains the location of the Helm chart in its status.
  • An OCM Controller resource “Image” of type Resource that contains the localized image reference in its status.
  • A FluxCD resource of type OCIRepository that points to the location of the Helm chart retrieved from the status of the resource “HelmChart”.
  • A FluxCD resource of type HelmRelease that points to FluxCDs OCIRepository, gets the Helm chart, and replaces the image location in the deployment using its spec.values-field and the status of the resource “Image” that contains the localized image reference.

Finally, we will check if the deployment was successful and if the localization was applied correctly.

Before starting, make sure you have set up your environment as described in the setup guide.

Create the OCM component version

First, we will create an OCM component version containing a Helm chart, the application image that is used in the Helm chart, and the ResourceGraphDefinition that contains all required resource to deploy the Helm chart and configure the localization. For this example, we will use the podinfo Helm chart and image, which is a simple web application that serves a pod information page. For more details on how to create an OCM component version, please refer to the OCM documentation.

To create the OCM component version, we will use the following component-constructor.yaml file:

components:
  - name: ocm.software/ocm-k8s-toolkit/bootstrap
    version: "1.0.0"
    provider:
      name: ocm.software
    resources:
      - name: helm-resource
        type: helmChart
        version: "1.0.0"
        access:
          type: ociArtifact
          imageReference: "ghcr.io/stefanprodan/charts/podinfo:6.9.1@sha256:565d310746f1fa4be7f93ba7965bb393153a2d57a15cfe5befc909b790a73f8a"
      - name: image-resource
        type: ociArtifact
        version: "1.0.0"
        access:
          type: ociRegistry
          imageReference: "ghcr.io/stefanprodan/podinfo:6.9.1@sha256:262578cde928d5c9eba3bce079976444f624c13ed0afb741d90d5423877496cb"
      - name: resource-graph-definition
        type: blob
        version: "1.0.0"
        input:
          type: file
          path: ./resourceGraphDefinition.yaml

As you can see, the resource resource-graph-definition is of type blob and contains the path to a file resourceGraphDefinition.yaml. Before we can create the OCM component version, we need to create this file, with the following content:

apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
  name: bootstrap
spec:
  schema:
    apiVersion: v1alpha1
    kind: Bootstrap
  resources:
    # In this guide, we will not create a "Repository" and "Component" resource in this ResourceGraphDefinition. Those
    # resources will be created to bootstrap the ResourceGraphDefinition itself and will be present in the Kubernetes
    # cluster to be referenced by the following resources (see the bootstrap resource in one of the following sections).

    # This resource refers to the resource "helm-resource" defined in the OCM component version. It will be downloaded,
    # verified, and its location is made available in the status of the resource.
    - id: resourceChart
      template:
        apiVersion: delivery.ocm.software/v1alpha1
        kind: Resource
        metadata:
          name: bootstrap-helm-resource
        spec:
          # This component will be part of the bootstrap resources that will be created later.
          componentRef:
            name: bootstrap-component
          resource:
            byReference:
              resource:
                name: helm-resource
          interval: 1m
          # ocmConfig is required, if the OCM repository requires credentials to access it.
          # ocmConfig:
    # This resource refers to the resource "image-resource" defined in the OCM component version. It will be downloaded,
    # verified, and its location is made available in the status of the resource.
    - id: resourceImage
      template:
        apiVersion: delivery.ocm.software/v1alpha1
        kind: Resource
        metadata:
          name: bootstrap-image-resource
        spec:
          # This component will be part of the bootstrap resources that will be created later.
          componentRef:
            name: bootstrap-component
          resource:
            byReference:
              resource:
                name: image-resource
          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 resource helm-resource.
    - id: ocirepository
      template:
        apiVersion: source.toolkit.fluxcd.io/v1beta2
        kind: OCIRepository
        metadata:
          name: bootstrap-ocirepository
        spec:
          interval: 1m0s
          insecure: true
          layerSelector:
            mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
            operation: copy
          url: oci://${resourceChart.status.reference.registry}/${resourceChart.status.reference.repository}
          ref:
            tag: ${resourceChart.status.reference.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: bootstrap-helmrelease
        spec:
          releaseName: bootstrap-release
          interval: 1m
          timeout: 5m
          chartRef:
            kind: OCIRepository
            name: ${ocirepository.metadata.name}
            namespace: default
          values:
            # This is the second step of the localization. We use the image reference from the resource "image-resource"
            # and insert it into the Helm chart values.
            image:
              repository: ${resourceImage.status.reference.registry}/${resourceImage.status.reference.repository}
              tag: ${resourceImage.status.reference.tag}

If you plan to push your OCM component version to a private registry, you need to provide credentials for the OCM Controllers and FluxCDs OCIRepository (if the Helm chart is also stored in a private registry). Accordingly, you have to specify the ocmConfig field in the Resource resources and the secretRef field in the OCIRepository.

If you want to use the same credentials for FluxCD and for the OCM Controller resources, create a Kubernetes secret of type dockerconfigjson and keep all the resources in the same namespace.

After creating both files, we can create the OCM component version using the following command:

ocm add componentversion --create --file ./ctf component-constructor.yaml

This will create a local CTF (Component Transfer Format) directory ./ctf containing the OCM component version. Since the OCM component version must be accessible for the OCM Controllers, we will transfer the CTF to a registry. For this example, we will use GitHub’s container registry, but you can use any OCI registry. Additionally, we will use the flag --copy-resources to make sure that all referential resources, for instance the Helm chart, will be localized in the first step - so, the image reference is updated to the new registry location:

ocm transfer ctf --copy-resources ./ctf ghcr.io/<your-namespace>

If you are using a registry that requires authentication, you need to provide credentials for ocm. Please refer to the [OCM CLI credentials documentation][ocm-credentials] for more information on how to set up and use credentials.

If everything went well, you should see the following output:

ocm get componentversion ghcr.io/<your-namespace>//ocm.software/ocm-k8s-toolkit/bootstrap:1.0.0 -o yaml | yq .component.resources
# Output is truncated for brevity
- access:
    imageReference: ghcr.io/<your-namespace>/stefanprodan/charts/podinfo:6.9.1@sha256:565d310746f1fa4be7f93ba7965bb393153a2d57a15cfe5befc909b790a73f8a
    type: ociArtifact
  digest:
    ...
  name: helm-resource
  relation: external
  type: helmChart
  version: 1.0.0
- access:
    imageReference: ghcr.io/<your-namespace>/stefanprodan/podinfo:6.9.1@sha256:262578cde928d5c9eba3bce079976444f624c13ed0afb741d90d5423877496cb
    type: ociArtifact
  digest:
    ...
  name: image-resource
  relation: external
  type: ociArtifact
  version: 1.0.0
- access:
    localReference: sha256:ed5252ff70bfe93e763ff6afeafe8dafd14c128981e4ae1472e35afc3ebe7a63
    mediaType: application/octet-stream
    type: localBlob
  digest:
     ...
  name: resource-graph-definition
  relation: local
  type: blob
  version: 1.0.0

Deploy the Helm Chart

To deploy the Helm chart from the OCM component, we first need to create all resources that are required to bootstrap the ResourceGraphDefinition from the OCM component. Afterwards, we will create an instance of the resulting Custom Resource Definition (CRD) which will deploy the Helm chart and configure the localization.

Bootstrapping

The bootstrap process consists of creating the OCM Controller resources that will download and apply the ResourceGraphDefinition. First, we will create a Repository and Component resource that point to the OCM component in the registry (the Component resource is reused in the ResourceGraphDefinition (see above) as reference for the Resource resources). Then, we create the Resource resource that references by name to the OCM resource containing the ResourceGraphDefinition. Finally, we will create a Deployer resource that will download the ResourceGraphDefinition and apply it to the cluster.

To proceed, create the following file named bootstrap.yaml containing the above-mentioned resources:

apiVersion: delivery.ocm.software/v1alpha1
kind: Repository
metadata:
  name: bootstrap-repository
spec:
  repositorySpec:
    # Adjust to your OCM repository
    baseUrl: ghcr.io/<your-namespace>
    type: OCIRegistry
  interval: 1m
  # ocmConfig is required, if the OCM repository requires credentials to access it.
  # ocmConfig:
---
apiVersion: delivery.ocm.software/v1alpha1
kind: Component
metadata:
  name: bootstrap-component
spec:
  component: ocm.software/ocm-k8s-toolkit/bootstrap
  repositoryRef:
    name: bootstrap-repository
  semver: 1.0.0
  interval: 1m
  # ocmConfig is required, if the OCM repository requires credentials to access it.
  # ocmConfig:
---
apiVersion: delivery.ocm.software/v1alpha1
kind: Resource
metadata:
  name: bootstrap-rgd
  namespace: default
spec:
  componentRef:
    name: bootstrap-component
  resource:
    byReference:
      resource:
        name: resource-graph-definition
  interval: 1m
  # ocmConfig is required, if the OCM repository requires credentials to access it.
  # ocmConfig:
---
apiVersion: delivery.ocm.software/v1alpha1
kind: Deployer
metadata:
  name: bootstrap-deployer
spec:
  resourceRef:
    # Reference to the Kubernetes resource OCM resource that contains the ResourceGraphDefinition.
    name: bootstrap-rgd
    # As kro processes resources in cluster-scope*, the deployer must also be cluster-scoped. Accordingly, we have to
    # set the namespace of the resource here (usually, when the namespace is not specified, it is derived from the
    # referencing Kubernetes resource).
    # Check out the kro documentation for more details:
    # https://github.com/kro-run/kro/blob/8f53372bfde232db7ddd6809eebb6a1d69b34f2e/website/docs/docs/concepts/20-access-control.md
    namespace: default
  # ocmConfig is required, if the OCM repository requires credentials to access it.
  # (You also need to specify the namespace of the reference as the 'deployer' is cluster-scoped.)
  # ocmConfig:

Again, if your OCM component version is stored in a private registry, you need to provide credentials for the OCM Controller resources to access the OCM repository. You can do so by specifying the ocmConfig field in the Repository, Component, Resource, and Deployer resources. For more information on how to set up credentials, please refer to the OCM Controllers credentials guide.

Afterwards, apply the bootstrap.yaml to the cluster:

kubectl apply -f bootstrap.yaml

This will create all the defined resources in the cluster and reconcile them. This can take a few seconds. As a result, you should see the ResourceGraphDefinition being created in the cluster:

kubectl get rgd
NAME        APIVERSION   KIND        STATE    AGE
bootstrap   v1alpha1     Bootstrap   Active   2m56s

By applying the ResourceGraphDefinition successfully, a Custom Resource Definition (CRD) named Bootstrap is created in the cluster. Check if the CRD is available by using the following command:

kubectl get crd bootstraps.kro.run
NAME                 CREATED AT
bootstraps.kro.run   2025-05-28T11:40:38Z

Troubleshooting

You can check the status of the ResourceGraphDefinition by investigating the status of the resources or the logs of the ocm-k8s-toolkit-controller-manager.

One common issue, when using GitHub’s container registry, is that the transferred OCM component is by default a private package. If so, you might see an error like the following:

failed to list versions: failed to list tags: GET "https://ghcr.io/v2...": response status code 401: unauthorized: authentication required

You can resolve this issue by making the package public or by providing credentials to the respective resources.

Creating an instance

After applying the ResourceGraphDefinition and making sure that the resulting CRD is available, we can create an instance of the CRD, which will deploy the Helm chart and configure the localization. To do so, create a file containing the following content and name it instance.yaml:

apiVersion: kro.run/v1alpha1
kind: Bootstrap
metadata:
  name: bootstrap

Then, apply the instance to the cluster:

kubectl apply -f instance.yaml

If successful, you should see the following output:

kubectl get bootstrap
NAME        STATE    SYNCED   AGE
bootstrap   ACTIVE   True     3m23s

If the instance is in the ACTIVE state, the resources defined in the ResourceGraphDefinition were created and reconciled. This includes the OCM Controller resources for the Helm chart and the image, as well as FluxCDs resources for the OCI repository and the Helm release. Accordingly, you should see the following deployment in the cluster. To see, if the deployment was successful, you can use the following command:

kubectl get deployments
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
bootstrap-release-podinfo   1/1     1            1           4m25s

Finally, you can check the pod itself to see if the localization was applied correctly by checking the image name in the container::

kubectl get pods -l app.kubernetes.io/name=bootstrap-release-podinfo -o jsonpath='{.items[0].spec.containers[0].image}'
ghcr.io/<your-namespace>/stefanprodan/podinfo:6.9.1

You now have successfully created an OCM component containing a Helm chart, the respective image for localization, and a ResourceGraphDefintion to deploy your Helm chart and localize the image. By creating the required bootstrap-resources you bootstrapped the ResourceGraphDefinition from the OCM component and created the resulting CRD. Finally, you created an instance of the CRD which deployed the Helm chart and configured the localization using the OCM Controllers, kro, and FluxCD.