on
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:
- Package a chart and push it to an OCI registry (we’ll use GitHub Container Registry, GHCR).
- Install directly from the registry and wire up OCI‑based dependencies.
- Add supply‑chain trust with signatures and provenance using Cosign and/or Helm provenance.
- Optionally, publish metadata so your OCI chart appears nicely on Artifact Hub.
Along the way I’ll point out the “gotchas” that tend to trip people up the first time.
Why this matters now
- Helm enabled OCI support by default in v3.8.0. It’s no longer experimental—and the CLI now speaks “oci://” fluently across push, pull, install, upgrade, template, and show. (v3.helm.sh)
- Popular publishers have shifted distribution to OCI. Bitnami made OCI the default for their charts and later announced catalog changes in 2025 that affect how many free artifacts and charts are delivered and from where. If your automation still expects the old repo flow, this impacts you. (blog.bitnami.com)
- Cloud registries are retiring legacy Helm repo features. For example, Azure Container Registry is removing the old Helm repository endpoints and deleting non‑OCI charts; the call is to migrate to OCI now. (learn.microsoft.com)
- Security tooling has coalesced around Sigstore/Cosign for signing and verifying OCI artifacts, including Helm charts, and platforms like Flux can verify Cosign signatures before reconciling. (fluxcd.io)
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
- Helm v3.8+ (OCI support on by default). (v3.helm.sh)
- An OCI registry account; we’ll use GHCR. To push, your token needs write:packages; to pull private packages, read:packages. (docs.github.com)
- Optional: GPG (for Helm provenance) and Cosign (for Sigstore signing). (helm.sh)
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):
- Azure Artifact Registry (GAR): use gcloud to mint a token and pipe it to helm registry login. (cloud.google.com)
- Azure Container Registry (ACR): classic helm repo features are going away; use OCI with helm push and helm pull going forward. (learn.microsoft.com)
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:
- The chart name is inferred from the archive; don’t append it to the push target.
- The tag must match the chart’s semantic version—no “latest” tags for Helm charts.
- If a .prov file sits next to your .tgz, Helm will push it as an extra layer in the OCI manifest. (v3.helm.sh)
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.
- Create artifacthub-repo.yml with your repository metadata (and optionally enable “Verified publisher” once you claim ownership). (artifacthub.io)
- Push the metadata as an OCI artifact tagged artifacthub.io:
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
- Note: With OCI, you must register one Artifact Hub “repository” per chart because the OCI distribution spec doesn’t define a way to list all references under a namespace. (artifacthub.io)
Common pitfalls and fixes
- I pushed 0.1.0 but helm pull fails without a version.
- With OCI, you must supply –version; “latest” isn’t a thing for Helm charts. (v3.helm.sh)
- helm push returns 401/404 the first time.
- Pre‑create the repo/namespace in your registry UI or via API; some registries require it. (v3.helm.sh)
- cosign verify fails on a version with a plus sign.
- Replace + with _ in the tag when verifying (Helm’s OCI translation); or avoid + in chart versions you intend to sign with Cosign. (docs.cloudbees.com)
- My GitHub token can’t push/pull.
- Ensure the PAT has write:packages to push and read:packages to pull private packages; public GHCR packages allow anonymous pulls. (docs.github.com)
- I can’t “search” an OCI registry with helm search.
- The OCI spec doesn’t define a generic search/list protocol; use Artifact Hub to discover, and then install with the oci:// path and specific version. (artifacthub.io)
- We rely on Bitnami charts—what changes?
- Bitnami made OCI the default and, as of late 2025, adjusted how community artifacts are delivered. Plan to reference OCI chart locations and pin/update image repositories or move to their Secure Images subscription where appropriate. (blog.bitnami.com)
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:
- Less bespoke infra: the same ACLs and audit you use for images apply to charts.
- Better performance for big catalogs (no more fetching a giant index.yaml just to find one chart).
- Supply‑chain parity: sign and verify charts with the same tools you use for images. Some registries (e.g., Harbor) have moved from Notary to Cosign for artifact signing. (jfrog.com)
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!