Helm Charts (OCI)

Helm 3 stores charts as OCI artifacts, and a CloudRepo Docker repository is an OCI registry. So your charts live in the same repository as your images, reached with the same credential, and there is nothing extra to enable.

There is no separate “Helm repository” type in CloudRepo, and you do not need helm repo add or an index.yaml. You push to and pull from a Docker repository over oci://.

Hosted at CloudRepo — see private Docker registry hosting for plans and pricing.

Prerequisites

  • A Docker repository (see Docker Repositories)

  • A Generic repository token (crp_v1_*) from Repository Tokens in the admin portal — the same token class the Docker CLI uses

  • Helm 3.8 or newer (OCI support is on by default from 3.8; on older 3.x releases export HELM_EXPERIMENTAL_OCI=1)

Log in

helm registry login takes the host alone, exactly like docker login. No repository path here:

helm registry login [your-organization-name].mycloudrepo.io \
  --username [your-minting-account-email] \
  --password-stdin <<< "$CLOUDREPO_TOKEN"

The username is the email of the account that minted the token, and the password is the repository token. An organization owner’s portal password is not accepted here, for the same reason it is not accepted at docker login — see Docker Repositories.

Helm writes the credential to its own registry config, not to ~/.docker/config.json.

Package and push a chart

Package the chart, then push it to the repository, not to the chart:

helm package ./my-chart
# -> my-chart-0.1.0.tgz

helm push my-chart-0.1.0.tgz \
  oci://[your-organization-name].mycloudrepo.io/repositories/[your-repository-name]

Helm reports the full reference it created:

Pushed: [your-organization-name].mycloudrepo.io/repositories/[your-repository-name]/my-chart:0.1.0
Digest: sha256:...

Important

The push URL ends at the repository — Helm appends the chart name itself. The chart name comes from Chart.yaml and the tag from the chart version, so a URL ending in /my-chart would produce .../my-chart/my-chart:0.1.0. This is the opposite of docker push, where you name the full image reference yourself.

Important

The literal repositories/ segment is required, just as it is for images. A reference without it fails at the first request:

Error: failed to perform "Exists" on destination: HEAD
".../v2/[your-repository-name]/my-chart/manifests/sha256:...":
response status code 400: Bad Request

A 400 Bad Request from a helm push is almost always this: check the reference carries /repositories/ before the repository name.

Pull and install a chart

helm pull addresses the chart, and the version is a flag rather than a tag:

helm pull oci://[your-organization-name].mycloudrepo.io/repositories/[your-repository-name]/my-chart \
  --version 0.1.0

That writes my-chart-0.1.0.tgz into the current directory. To install straight from the registry:

helm install my-release \
  oci://[your-organization-name].mycloudrepo.io/repositories/[your-repository-name]/my-chart \
  --version 0.1.0

If the repository is marked public, helm pull and helm install work with no helm registry login at all.

Important

Which commands take the repository, and which take the chart. Helm is not consistent here, and getting it backwards produces a confusing “not found”:

  • helm push and a Chart.yaml repository: end at the repository — Helm appends the chart name itself.

  • helm pull and helm install take the full chart path.

A repository: with the chart name appended makes Helm ask for .../my-chart/my-chart and fail with not found.

Chart dependencies

Reference a CloudRepo-hosted chart from another chart’s Chart.yaml with the oci:// repository URL — again ending at the repository, because Helm appends the dependency’s name:

dependencies:
  - name: my-chart
    version: "0.1.0"
    repository: "oci://[your-organization-name].mycloudrepo.io/repositories/[your-repository-name]"

Then helm dependency update resolves it using the credential from helm registry login.

CI/CD

Non-interactive login takes the token on standard input, keeping it out of process listings and shell history:

echo "$CLOUDREPO_TOKEN" | helm registry login [your-organization-name].mycloudrepo.io \
  --username "$CLOUDREPO_EMAIL" \
  --password-stdin

helm package ./my-chart
helm push my-chart-*.tgz \
  oci://[your-organization-name].mycloudrepo.io/repositories/[your-repository-name]

Store the repository token as a CI secret. It is the same token class your Docker builds use, so one secret can serve both.

Troubleshooting

``helm push`` returns 400 Bad Request

  • The reference is missing the repositories/ segment — see above

  • Or the URL includes the chart name; the push URL ends at the repository

``helm push`` returns 401 after a successful ``helm registry login``

  • Verify the username is the email of the account that minted the token

  • Verify you used a Generic (crp_v1_) token; the npm format (crn_v1_) is not accepted here

  • Verify the token is scoped to the repository you are pushing to

``helm pull`` cannot find a chart you just pushed

  • Check the chart name and version: helm push reported the exact reference it created, and the tag is the chart version, not a name you choose

  • helm pull takes --version; a chart reference with :0.1.0 appended is not the form Helm expects

Next Steps