Goodbye index.yaml, Hello OCI: a hands‑on Helm guide to pushing and signing your charts

If you’ve been packaging Kubernetes apps with Helm for a while, you’ve probably used classic “chart repositories” backed by an index.yaml. That workflow still works—but the center of gravity has shifted. Major publishers are moving (or have already moved) their charts to OCI registries, and cloud registries are sunsetting legacy Helm repo features. In short: it’s time to get comfortable with OCI for Helm. (v3.helm.sh)

In this hands‑on guide you’ll:

Along the way I’ll point out the “gotchas” that tend to trip people up the first time.

Why this matters now

Think of OCI as moving your charts into the same “record store” as your container images. Same shelves, same check‑out counter, better tooling.

What we’ll build

We’ll create a tiny chart, push it to GHCR, install it back from the registry, and sign it. You can adapt the same steps to ACR, ECR, GCR/Artifact Registry, Harbor, Artifactory, and others—the Helm CLI flow is the same. (v3.helm.sh)

Prerequisites

Step 1: Create and package a chart

Start a demo chart and give it a semantic version:

helm create hello-helm
# edit hello-helm/Chart.yaml and set:
# name: hello-helm
# version: 0.1.0

helm lint hello-helm
helm package hello-helm
# Produces: hello-helm-0.1.0.tgz

If you want a provenance file (.prov) using GPG:

helm package --sign \
  --key "Your GPG Key Name" \
  --keyring ~/.gnupg/pubring.gpg \
  hello-helm
# Produces: hello-helm-0.1.0.tgz and hello-helm-0.1.0.tgz.prov

Helm’s provenance lets others verify integrity with helm verify or with –verify flags in pull/install. We’ll also show a Cosign workflow in a bit. (helm.sh)

Step 2: Log in to your OCI registry

For GHCR, log in with a PAT that has write:packages to push:

export GHCR_USER=<your-gh-username>
export GHCR_PAT=<a PAT with write:packages>

# Use stdin to avoid exposing tokens in process lists:
echo -n "$GHCR_PAT" | helm registry login ghcr.io \
  -u "$GHCR_USER" --password-stdin

helm registry login supports –password-stdin (like docker login), which keeps your token out of shell history. (helm.sh)

Notes for other registries (keep the same pattern):

Step 3: Push your chart as an OCI artifact

helm push hello-helm-0.1.0.tgz oci://ghcr.io/<org-or-user>/charts
# Remote becomes: ghcr.io/<org-or-user>/charts/hello-helm:0.1.0

A couple of rules OCI enforces here:

Some registries require you to pre‑create the repository/namespace before the first push; if you see a 401/404 on push, create it then retry. (v3.helm.sh)

Step 4: Install directly from the registry

Now pull or install back from GHCR:

# Pull and inspect:
helm pull oci://ghcr.io/<org-or-user>/charts/hello-helm --version 0.1.0

# Or install straight from the registry:
helm upgrade --install hello oci://ghcr.io/<org-or-user>/charts/hello-helm \
  --version 0.1.0

The version flag is mandatory with OCI references; you can’t pull by “latest.” (v3.helm.sh)

Tip: If you made the GHCR package private, ensure the requesting identity has read:packages; public GHCR packages can be pulled anonymously. (docs.github.com)

Step 5: Use OCI‑based dependencies (subcharts)

You can point dependencies at OCI registries in Chart.yaml:

# Chart.yaml (in your parent chart)
dependencies:
  - name: my-subchart
    version: "2.7.0"
    repository: "oci://ghcr.io/<org-or-user>/charts"

Then fetch them:

helm dependency update ./your-parent-chart

Helm will resolve the full reference oci://…/charts/my-subchart:2.7.0 and pull the OCI artifact. (helm.sh)

Step 6: Add signatures and provenance

There are two common paths: classic Helm provenance (GPG) and Sigstore/Cosign.

Option A: Verify Helm provenance (GPG)

If you pushed a chart with a .prov file, you can verify the chart’s integrity:

helm pull --verify oci://ghcr.io/<org-or-user>/charts/hello-helm \
  --version 0.1.0
# or after pulling:
helm verify hello-helm-0.1.0.tgz

Helm will check the .prov file and the signer’s public key in your keyring. If verification fails, Helm will abort. (helm.sh)

Option B: Sign and verify with Cosign (Sigstore)

Cosign lets you sign the chart artifact stored in the OCI registry, using the same tool you use for container images.

# Generate a keypair for demo (alternatively use keyless/OIDC):
cosign generate-key-pair

# Sign the chart OCI artifact:
cosign sign --key cosign.key \
  ghcr.io/<org-or-user>/charts/hello-helm:0.1.0

# Verify later (locally or in CI):
cosign verify --key cosign.pub \
  ghcr.io/<org-or-user>/charts/hello-helm:0.1.0

Flux can be configured to verify these Cosign signatures before installing or upgrading a HelmRelease—add verify: provider: cosign and a secret with your cosign.pub. (fluxcd.io)

Heads‑up on SemVer “+” build metadata: OCI tags can’t contain “+”. Helm converts “+” to “_” on push, but Cosign doesn’t know that, so use the underscore form when verifying. For example, verify 3.43.1+build as 3.43.1_build. (docs.cloudbees.com)

Prefer a Helm‑native Sigstore flow? The helm‑sigstore plugin can publish and verify Rekor transparency log entries tied to your Helm provenance. (v3.helm.sh)

Step 7: Optional—list your OCI chart on Artifact Hub

Artifact Hub can index OCI‑hosted Helm charts. You’ll add a tiny metadata file and push it to your registry using a special tag.

oras push ghcr.io/<org-or-user>/charts/hello-helm:artifacthub.io \
  --config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \
  artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml

Common pitfalls and fixes

A quick detour: why OCI is nicer than index.yaml

Using OCI keeps storage and distribution for charts in the same registry as your images. That means:

On the Helm side, the CLI’s registry commands are first‑class: helm registry login/logout, helm push, pull, install, and even dependency resolution work with oci:// references. Provenance uploads as a manifest layer; Sigstore integration is available via a plugin. (v3.helm.sh)

Put it all together (a mini “from zero” script)

# 0) Login
echo -n "$GHCR_PAT" | helm registry login ghcr.io -u "$GHCR_USER" --password-stdin

# 1) Create chart
helm create hello-helm && yq -i '.version="0.1.0"' hello-helm/Chart.yaml

# 2) Package (+ provenance optional)
helm package hello-helm
# OR: helm package --sign --key "Your GPG Key" --keyring ~/.gnupg/pubring.gpg hello-helm

# 3) Push to GHCR
helm push hello-helm-0.1.0.tgz oci://ghcr.io/$GHCR_USER/charts

# 4) Install from GHCR
helm upgrade --install hello oci://ghcr.io/$GHCR_USER/charts/hello-helm --version 0.1.0

# 5) Sign with Cosign and verify
cosign sign --key cosign.key ghcr.io/$GHCR_USER/charts/hello-helm:0.1.0
cosign verify --key cosign.pub ghcr.io/$GHCR_USER/charts/hello-helm:0.1.0

References for the commands above live in Helm’s OCI docs, Helm provenance docs, and Sigstore/Flux docs linked throughout this article. (v3.helm.sh)

Closing thoughts

OCI for Helm isn’t just a new URL scheme—it’s a simpler mental model: charts become artifacts alongside your images, and all the modern registry features (RBAC, retention, signing) just apply. With publishers (like Bitnami) and registries (like ACR) shifting decisively toward OCI, now’s the perfect moment to make it your default. Once you push your first chart to an OCI registry and see helm install working against oci://… with version pinning and signature checks, it feels like switching from a messy stack of mixtapes to a neat digital library. Same songs, far less friction. (blog.bitnami.com)

Happy Helming!