Packaging and publishing to OCI registries

Helm · Advanced · 8 min

Sharing charts across teams or publicly means publishing versioned, verifiable artifacts.

The modern way to distribute charts is an OCI registry: GHCR, Docker Hub, ECR, Artifact Registry, ACR, Harbor. Charts sit next to images, with the same authentication and access control.

  • helm registry login ghcr.io -u USER --password-stdin
  • helm package ./shop produces shop-1.4.0.tgz (the version comes from Chart.yaml; --version and --app-version override it in CI).
  • helm push shop-1.4.0.tgz oci://ghcr.io/acme/charts stores it as ghcr.io/acme/charts/shop:1.4.0.
  • Consumers install with helm install shop oci://ghcr.io/acme/charts/shop --version 1.4.0, or list it as a dependency with that repository URL.

Classic HTTP repos (an index.yaml plus .tgz files, often on GitHub Pages via chart-releaser) still work, but they need the index to be rebuilt and fetched as a whole, while OCI needs nothing extra.

Versioning: follow SemVer for the chart version. A breaking change to values (renamed keys, removed features) is a major bump; document migrations in the chart's README or changelog.

Integrity: Helm's own --sign/--verify uses PGP provenance files; many teams instead sign OCI artifacts with cosign (Sigstore) and verify the digest. Referencing a chart by digest pins it immutably.

Example

echo "$GITHUB_TOKEN" | helm registry login ghcr.io -u acme-bot --password-stdin

helm package ./shop --version 1.4.0 --app-version 1.5.0
# Successfully packaged chart and saved it to: shop-1.4.0.tgz

helm push shop-1.4.0.tgz oci://ghcr.io/acme/charts
# Pushed: ghcr.io/acme/charts/shop:1.4.0
# Digest: sha256:9c1e...

helm show chart oci://ghcr.io/acme/charts/shop --version 1.4.0
cosign sign --yes ghcr.io/acme/charts/shop@sha256:9c1e...

Remember

  • Package, then push to oci://registry/path; install with --version
  • OCI registries need no index file
  • SemVer the chart; sign artifacts (cosign) and pin by digest for integrity

Watch out for

  • Re-pushing a changed chart under an existing version tag, so consumers get different content for the same version.

Check yourself

  1. What determines the version tag a chart gets when pushed to an OCI registry?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: helm.sh/docs/topics/registries/