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