Deploy/Upgrade from an OCI-hosted Helm Chart
This guide shows a practical CD path where your Helm chart is already pushed to an OCI registry (e.g., Harbor).
You'll use a Tekton Pipeline to pull/install that chart into your Kubernetes cluster.
You'll Build a reusable Pipeline named helm-oci-deploy that:
- Authenticates to your OCI registry
- Runs
helm upgrade --install directly from the OCI chart reference
- Optionally applies extra values files and
--set overrides
- Works with a Kubeconfig secret or a ServiceAccount
TOC
Prerequisites
- A Kubernetes cluster (you can use minikube for local testing).
- Tekton Pipelines installed on your cluster.
- A Helm 3.8+ container image (Helm v3 with OCI support).
- An OCI registry with the OCI chart reference and version you intend to deploy, e.g.:
oci://registry.example.com/charts/myapp
- Version like
1.2.3 (must exist in the registry)
- Registry credentials for your OCI registry as a Docker config JSON:
- Create a Kubernetes Secret of type
kubernetes.io/dockerconfigjson (example below).
- Cluster access for Helm (choose one):
- Mount a Kubeconfig Secret,
- Run the Task under a ServiceAccount with sufficient RBAC.
Step-by-Step Instructions
Step 1: Create the Registry Credential Secret
You need a registry credential for your OCI registry as a Docker config JSON.
You can refer to the Prepare Registry Credential.
Step 2: Create Cluster Access Credential
You need a cluster access credential for Helm.
You can refer to the Prepare Cluster Access Credential.
Step 3: Prepare helm image
You need a Helm 3.8+ container image (Helm v3 with OCI support) to run the helm command.
You can refer to the Discover Tool Image.
When searching by label, specify the image as helm, for example: -l operator.tekton.dev/tool-image=helm.
Step 4: Define the Pipeline
This Pipeline installs/upgrades a release directly from an OCI chart reference using helm upgrade --install.
Helm 3.8+ supports referencing charts via oci://... with --version. This keeps the step stateless and fast.
If you prefer to pre-pull the chart, you can helm pull to a temp dir and install from the .tgz path instead.
You can use --wait to blocks until Kubernetes reports the release's resources are ready (or until the operation times out).
Pair it with --timeout to control how long Helm will wait.
It's common to combine with --atomic, which rolls back automatically if the wait fails or times out—so you don't leave a half-upgraded release.
Please replace <helm-image> with your Helm image.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: helm-oci-deploy
spec:
description: Clone repo, package Helm chart, and push to an OCI registry.
params:
- name: oci_chart
type: string
description: OCI chart ref, e.g. oci://registry.example.com/charts/myapp
- name: version
type: string
description: Chart version to install/upgrade to (must exist in the registry)
- name: release_name
type: string
description: Helm release name
- name: namespace
type: string
description: Target namespace
default: default
- name: extra_args
type: string
description: Extra helm args (e.g., "--atomic --timeout 5m --set key=val -f values.yaml")
default: ""
workspaces:
- name: registry-creds
description: Workspace with docker config at config.json (for OCI auth)
optional: true
- name: kubeconfig
description: Workspace containing kubeconfig file at ./kubeconfig
optional: true
tasks:
- name: helm
taskRef:
resolver: hub
params:
- name: catalog
value: catalog
- name: kind
value: task
- name: name
value: run-script
- name: version
value: "0.1"
workspaces:
- name: config
workspace: kubeconfig
- name: secret
workspace: registry-creds
params:
- name: image
## Replace with your Helm image
value: <helm-image>
- name: script
value: |
if [ "$(workspaces.secret.bound)" = "true" ]; then
echo "Using registry credentials in $(workspaces.secret.path)"
export HELM_REGISTRY_CONFIG=$(workspaces.secret.path)/.dockerconfigjson
fi
if [ "$(workspaces.config.bound)" = "true" ]; then
echo "Using kubeconfig in $(workspaces.config.path)"
export KUBECONFIG=$(workspaces.config.path)/kubeconfig
fi
echo "Upgrading/Installing release..."
echo " Release: $(params.release_name)"
echo " Namespace: $(params.namespace)"
echo " Chart: $(params.oci_chart):$(params.version)"
# Direct install from OCI (no need to helm pull first)
helm upgrade --install "$(params.release_name)" "$(params.oci_chart)" \
--version "$(params.version)" \
--namespace "$(params.namespace)" --create-namespace \
$(params.extra_args)
echo "Done."
Step 5: Run It with a PipelineRun
- Cluster access for Helm (choose one):
- Mount a Kubeconfig Secret,
- Run the Task under a ServiceAccount with sufficient RBAC.
Please choose one of cluster access credentials.
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: helm-oci-deploy-
spec:
workspaces:
- name: registry-creds
secret:
secretName: registry-creds
## If you choose to use the kubeconfig Secret
# - name: kubeconfig
# secret:
# secretName: <kubeconfig-secret-name>
params:
- name: oci_chart
value: oci://registry.example.com/charts/myapp
- name: version
value: "1.1.0"
- name: release_name
value: myapp
- name: namespace
value: my-namespace
pipelineRef:
name: helm-oci-deploy
## If you choose to use the ServiceAccount
# taskRunTemplate:
# serviceAccountName: <service-account-name>
Troubleshooting
helm: command not found: Ensure your image actually contains the Helm binary.
unauthorized: authentication required: Ensure the Secret is correct and mounted to registry-creds. Confirm HELM_REGISTRY_CONFIG is set to that path.
Error: chart "myapp" version "x.y.z" not found: The version doesn't exist in the OCI repo or the oci_chart path is wrong. Verify the pushed tag/version and path.
failed to create resource: (…RBAC…) forbidden: The kubeconfig/ServiceAccount lacks permissions. Grant the necessary roles to create/update the resources the chart manages.
Next Steps