Package & Push a Helm Chart to an OCI Registry
This guide walks you through a practical CI scenario: your Git repository already contains a Helm chart (Chart.yaml, templates/, values.yaml, etc.).
We'll use Pipeline to:
- Clone the repository with the
git-clone Task
- Package the chart with Helm
- Push the built chart to an OCI registry (e.g., Harbor)
You will create a Pipeline with two Tasks:
git-clone: clone your repo into a shared workspace
helm: packages the chart and pushes the .tgz to your OCI registry
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 and repository path (e.g., oci://registry.example.com/charts).
- Push credentials for your OCI registry as a Docker config JSON:
- Create a Kubernetes Secret of type
kubernetes.io/dockerconfigjson (example below).
- A Git repository that contains a valid Helm chart (directory with
Chart.yaml, templates/, values.yaml).
- Git access to the repository:
- Public repo: nothing special.
- Private repo: create a Git credential Secret (SSH or basic-auth).
Why Docker config JSON? Helm uses the same auth config as Docker. We'll point Helm to it via HELM_REGISTRY_CONFIG.
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: 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 3: Define the Pipeline
This Pipeline uses git-clone to fetch your repo, then calls helm to package and push your chart.
Please replace <helm-image> with your Helm image.
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: package-and-push-helm-oci
spec:
description: Clone repo, package Helm chart, and push to an OCI registry.
params:
- name: repo_url
type: string
description: Git URL of your repository
- name: revision
type: string
description: Git revision (branch, tag, or SHA)
default: main
- name: chart_dir
type: string
description: Path to the Helm chart in the repo
default: "."
- name: oci_repo
type: string
description: OCI repo, e.g., oci://registry.example.com/charts
- name: package_flags
type: string
description: Extra flags for `helm package`
default: ""
workspaces:
- name: source
- name: registry-creds
optional: true
- name: basic-auth
optional: true
tasks:
- name: git-clone
taskRef:
resolver: hub
params:
- name: catalog
value: catalog
- name: kind
value: task
- name: name
value: git-clone
- name: version
value: "0.9"
workspaces:
- name: output
workspace: source
- name: basic-auth
workspace: basic-auth
params:
- name: url
value: $(params.repo_url)
- name: revision
value: $(params.revision)
- name: helm
runAfter:
- "git-clone"
taskRef:
resolver: hub
params:
- name: catalog
value: catalog
- name: kind
value: task
- name: name
value: run-script
- name: version
value: "0.1"
workspaces:
- name: source
workspace: source
- 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
echo "Packaging chart from $(params.chart_dir)"
tempDir=$(mktemp -d)
helm package $(params.chart_dir) --destination ${tempDir} $(params.package_flags)
chart_tgz="$(ls ${tempDir}/*.tgz | head -n1)"
if [ -z "${chart_tgz}" ]; then
echo "No packaged chart found!"
exit 1
fi
echo "Packaged: ${chart_tgz}"
echo "Pushing to $(params.oci_repo)..."
helm push "${chart_tgz}" "$(params.oci_repo)"
echo "Done."
Step 4: Run It with a PipelineRun
Bind workspaces and pass your parameters.
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: package-and-push-helm-oci-
spec:
pipelineRef:
name: package-and-push-helm-oci
workspaces:
- name: source
volumeClaimTemplate:
spec:
## Specify StorageClassName (as needed)
# storageClassName: <storage-class-name>
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: registry-creds
secret:
secretName: registry-creds
- name: basic-auth
secret:
secretName: basic-auth
params:
- name: repo_url
value: https://github.com/your-org/your-repo.git
- name: revision
value: main
- name: oci_repo
value: oci://registry.example.com/charts
- name: chart_dir
value: .
Troubleshooting
helm: command not found: Ensure your image actually contains the Helm binary.
Error: unknown command "push": Your Helm image lacks OCI push support. Use a newer Helm (3.8+) image.
unauthorized: authentication required: Ensure the Secret is correct and mounted to registry-creds. Confirm HELM_REGISTRY_CONFIG is set to that path.
Next Steps