Set up Controller Environments

This guide helps you set up a local Kubernetes environment for testing OCM controller-based deployments. You’ll install the OCM Controllers, kro, and Flux to enable GitOps workflows with OCM component versions.

Not all components are always required

Depending on your use case, you may not need the full setup. For example, if you’re only deploying raw k8s deployment from an ocm resource, you may be able to skip kro and a deployer like FluxCD or ArgoCD.

Check the prerequisites of the tutorial or how-to you’re following to see what’s actually needed. This guide installs everything so you’re covered for any scenario.

You’ll end up with

  • A local or remote Kubernetes cluster with OCM Controllers, kro, and a deployer like FluxCD or ArgoCD installed

Estimated time

~15 minutes

Prerequisites

Setup Workflow

  1. Create a Local Kubernetes Cluster

    Skip this step if you’re using a remote Kubernetes cluster.

    Create a local kind cluster:

    kind create cluster
    You should see this output
    Creating cluster "kind" ...
     βœ“ Ensuring node image (kindest/node:v1.35.0) πŸ–Ό
     βœ“ Preparing nodes πŸ“¦
     βœ“ Writing configuration πŸ“œ
     βœ“ Starting control-plane πŸ•ΉοΈ
     βœ“ Installing CNI 
     βœ“ Installing StorageClass πŸ’Ύ
    Set kubectl context to "kind-kind"
    You can now use your cluster with:
    
    kubectl cluster-info --context kind-kind
    Have a nice day! πŸ‘‹

    Verify the cluster is running:

    kubectl cluster-info
    You should see this output
    Kubernetes control plane is running at https://127.0.0.1:53348
    CoreDNS is running at https://127.0.0.1:53348/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'
  2. Install the OCM Controllers

    Use Helm to install the OCM controllers:

    helm install ocm-k8s-toolkit "oci://ghcr.io/open-component-model/kubernetes/controller/chart" \
      --namespace ocm-k8s-toolkit-system \
      --create-namespace
    Resource names follow the release name

    The release name ocm-k8s-toolkit used above gives the controller’s resources predictable names, such as the service account ocm-k8s-toolkit-controller-manager. If you install under a different release name β€” or via a GitOps tool such as Flux that alters the effective release name β€” add --set fullnameOverride=ocm-k8s-toolkit to keep these names stable. This matters when you configure custom RBAC, which binds to the service account by name.

    You should see this output
    Pulled: ghcr.io/open-component-model/kubernetes/controller/chart:0.4.0
    Digest: sha256:eac0dc587a1d288f36ef1961bb69f0ffb2791e0153f86d1fdbe54ae2f36f1194
    NAME: ocm-k8s-toolkit
    LAST DEPLOYED: Tue Apr 28 17:42:51 2026
    NAMESPACE: ocm-k8s-toolkit-system
    STATUS: deployed
    REVISION: 1
    DESCRIPTION: Install complete
    TEST SUITE: None

    Verify the OCM controller is running:

    kubectl get pods -n ocm-k8s-toolkit-system
    You should see this output
    NAME                                                  READY   STATUS    RESTARTS   AGE
    ocm-k8s-toolkit-controller-manager-79b7975755-vxtqt   1/1     Running   0          59s
  3. Install kro

    Install kro following the official installation guide. The easiest way is via Helm:

    helm install kro oci://registry.k8s.io/kro/charts/kro \
      --namespace kro-system \
      --create-namespace
    Security consideration

    This default installation grants kro cluster-wide access to all resources, which is suitable for local development but not recommended for production environments. See the kro documentation for guidance on configuring more restrictive RBAC.

    You should see this output
    Pulled: registry.k8s.io/kro/charts/kro:0.8.5
    Digest: sha256:c9a9dc0133f43a25711f4bdbce1eeb4b6448015958f901c6fad61a049e54415e
    NAME: kro
    LAST DEPLOYED: Wed Feb 25 12:02:15 2026
    NAMESPACE: kro-system
    STATUS: deployed
    REVISION: 1
    DESCRIPTION: Install complete
    TEST SUITE: None

    Verify kro is running:

    kubectl get pods -n kro-system
    You should see this output
    NAME                   READY   STATUS    RESTARTS   AGE
    kro-5644d5759f-82nsx   1/1     Running   0          2m22s
  4. Install a Deployer

    The following examples of Flux and Argo CD are demonstrating how they can be used as deployers. In theory, you could use any other deployer that is able to apply a deployable resource to a Kubernetes cluster.

    Choose the one you’re already using or prefer.

    Install the controllers using the Flux CLI:

    flux install
    You should see this output
    ✚ generating manifests
    βœ” manifests build completed
    β–Ί installing components in flux-system namespace
    CustomResourceDefinition/alerts.notification.toolkit.fluxcd.io created
    CustomResourceDefinition/buckets.source.toolkit.fluxcd.io created
    CustomResourceDefinition/externalartifacts.source.toolkit.fluxcd.io created
    CustomResourceDefinition/gitrepositories.source.toolkit.fluxcd.io created
    CustomResourceDefinition/helmcharts.source.toolkit.fluxcd.io created
    CustomResourceDefinition/helmreleases.helm.toolkit.fluxcd.io created
    CustomResourceDefinition/helmrepositories.source.toolkit.fluxcd.io created
    CustomResourceDefinition/kustomizations.kustomize.toolkit.fluxcd.io created
    CustomResourceDefinition/ocirepositories.source.toolkit.fluxcd.io created
    CustomResourceDefinition/providers.notification.toolkit.fluxcd.io created
    CustomResourceDefinition/receivers.notification.toolkit.fluxcd.io created
    Namespace/flux-system created
    ClusterRole/crd-controller-flux-system created
    ClusterRole/flux-edit-flux-system created
    ClusterRole/flux-view-flux-system created
    ClusterRoleBinding/cluster-reconciler-flux-system created
    ClusterRoleBinding/crd-controller-flux-system created
    ResourceQuota/flux-system/critical-pods-flux-system created
    ServiceAccount/flux-system/helm-controller created
    ServiceAccount/flux-system/kustomize-controller created
    ServiceAccount/flux-system/notification-controller created
    ServiceAccount/flux-system/source-controller created
    Service/flux-system/notification-controller created
    Service/flux-system/source-controller created
    Service/flux-system/webhook-receiver created
    Deployment/flux-system/helm-controller created
    Deployment/flux-system/kustomize-controller created
    Deployment/flux-system/notification-controller created
    Deployment/flux-system/source-controller created
    NetworkPolicy/flux-system/allow-egress created
    NetworkPolicy/flux-system/allow-scraping created
    NetworkPolicy/flux-system/allow-webhooks created
    β—Ž verifying installation
    βœ” helm-controller: deployment ready
    βœ” kustomize-controller: deployment ready
    βœ” notification-controller: deployment ready
    βœ” source-controller: deployment ready
    βœ” install finished

    Verify Flux is running:

    kubectl get pods -n flux-system
    You should see this output
    NAME                                         READY   STATUS      RESTARTS        AGE
    helm-controller-b6767d66-zbwws               1/1     Running     0               3h29m
    kustomize-controller-57c7ff5596-v6fvr        1/1     Running     0               3h29m
    notification-controller-58ffd586f7-pr65t     1/1     Running     0               3h29m
    source-controller-6ff87cb475-2h2lv           1/1     Running     0               3h29m

    Install Argo CD via Helm Chart:

    helm repo add argo https://argoproj.github.io/argo-helm
    # ... "argo" has been added to your repositories
    
    helm repo update argo
    
    helm upgrade --install argocd argo/argo-cd \
      --namespace argocd \
      --create-namespace \
      --wait \
      --timeout 5m
    # ...

    Wait for all pods to become ready:

    kubectl wait --for=condition=Ready pods --all -n argocd --timeout=120s
    You should see this output
    pod/argocd-application-controller-0 condition met
    pod/argocd-applicationset-controller-68fd97ccb6-nkcbg condition met
    pod/argocd-dex-server-99ff57675-9qk2l condition met
    pod/argocd-notifications-controller-8596549fb6-bldjz condition met
    pod/argocd-redis-6f6867546c-vs6c6 condition met
    pod/argocd-repo-server-59444f4bbb-gbzxn condition met
    pod/argocd-server-765575f778-j8krk condition met

    Verify Argo CD is running:

    kubectl get pods -n argocd
    You should see this output
    NAME                                                READY   STATUS    RESTARTS   AGE
    argocd-application-controller-0                     1/1     Running   0          42s
    argocd-applicationset-controller-68fd97ccb6-nkcbg   1/1     Running   0          43s
    argocd-dex-server-99ff57675-9qk2l                   1/1     Running   0          43s
    argocd-notifications-controller-8596549fb6-bldjz    1/1     Running   0          43s
    argocd-redis-6f6867546c-vs6c6                       1/1     Running   0          42s
    argocd-repo-server-59444f4bbb-gbzxn                 1/1     Running   0          42s
    argocd-server-765575f778-j8krk                      1/1     Running   0          42s

    OCI registry credentials

    To deploy Helm charts from a private OCI registry (e.g. ghcr.io), create an Argo CD repository Secret with enableOCI: "true":

    apiVersion: v1
    kind: Secret
    metadata:
      name: ghcr-helm
      namespace: argocd
      labels:
        argocd.argoproj.io/secret-type: repository
    stringData:
      name: ghcr-helm
      type: oci
      url: oci://<path-to-helm-chart>
      enableOCI: "true"
      username: <your-username>
      password: <your-token>

    Public OCI registries require no configuration. Read more about ArgoCD OCI Support here.

  5. Verify Complete Setup

    Check all components are running:

    kubectl get pods --all-namespaces | grep -E '(kro-system|flux-system|argocd|ocm-k8s-toolkit-system)'
    You should see this output

    Depending on what deployer you installed the output could differ:

    NAMESPACE                NAME                                                 READY    STATUS             RESTARTS        AGE
    argocd-application-controller-0                                                1/1     Running            0               24m
    argocd-applicationset-controller-85f58f44f4-g85xv                              1/1     Running            0               24m
    argocd-dex-server-69884b6f8b-vf5s9                                             1/1     Running            0               24m
    argocd-notifications-controller-8669567fb-47fcj                                1/1     Running            0               24m
    argocd-redis-5d9668fdff-v2pbp                                                  1/1     Running            0               24m
    argocd-repo-server-95465d997-mbmcp                                             1/1     Running            0               24m
    argocd-server-767b9d54cf-q7bbz                                                 1/1     Running            0               24m
    flux-system              helm-controller-b6767d66-zbwws                        1/1     Running            0               3h39m
    flux-system              kustomize-controller-57c7ff5596-v6fvr                 1/1     Running            0               3h39m
    flux-system              notification-controller-58ffd586f7-pr65t              1/1     Running            0               3h39m
    flux-system              source-controller-6ff87cb475-2h2lv                    1/1     Running            0               3h39m
    kro-system               kro-86d5b5b5bd-6gmvr                                  1/1     Running            0               3h38m
    ocm-k8s-toolkit-system   ocm-k8s-toolkit-controller-manager-788f58d4bd-ntbx8   1/1     Running            0               57s

Registry Access

The OCM Controllers need access to an OCI registry to fetch component versions.

Tip

We recommend using a publicly accessible registry like ghcr.io. Using a local registry requires additional configuration to ensure it’s accessible both from your CLI and from within the cluster.

For private registries, you’ll need to configure credentials. See Configure Credentials for Private Registries for details.

Cleanup

To remove the local kind cluster after testing, run the following command. If you plan to continue with the next tutorial steps, you can keep the cluster.

kind delete cluster
You should see this output
Deleting cluster "kind" ...
Deleted nodes: ["kind-control-plane"]

Next Steps