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