Helm charts used to be published as static HTTP repos: an index.yaml manifest and a set of .tgz archives behind a CDN. Since Helm 3.8 (stable in 3.11), charts can be pushed to any OCI-compliant registry — the same registry you use for container images. This is now the default and recommended path.
Packaging
helm package ./my-app
# Successfully packaged chart and saved it to: my-app-1.4.2.tgz
The version comes from Chart.yaml; override with --version 1.4.2-rc.1. The output is a deterministic gzip-compressed tarball.
Pushing to an OCI Registry
The URL scheme is oci://<registry>/<namespace>. The chart name and version become part of the artifact reference:
helm registry login ghcr.io -u USERNAME -p TOKEN
helm push my-app-1.4.2.tgz oci://ghcr.io/acme/charts
# Pushed: ghcr.io/acme/charts/my-app:1.4.2
# Digest: sha256:9c2...
Pull and install directly from OCI:
helm pull oci://ghcr.io/acme/charts/my-app --version 1.4.2
helm install my-app oci://ghcr.io/acme/charts/my-app --version 1.4.2
helm show values oci://ghcr.io/acme/charts/my-app --version 1.4.2
You do not helm repo add for OCI — the URL is self-describing.
Cloud Registry Specifics
| Registry | Login |
|---|---|
| GitHub (GHCR) | echo $GITHUB_TOKEN | helm registry login ghcr.io -u $USER --password-stdin |
| AWS ECR | aws ecr get-login-password | helm registry login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com |
| Google Artifact Registry | gcloud auth print-access-token | helm registry login -u oauth2accesstoken --password-stdin <region>-docker.pkg.dev |
| Azure ACR | az acr login --name <registry> |
| Harbor / Artifactory | Username + token / password |
ECR requires the chart's repository path to exist as an ECR repository first (or, with newer ECR, with ecr:CreateRepository permission, it is created on first push).
CI/CD Pattern
A typical GitHub Actions workflow:
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # to push to GHCR
steps:
- uses: actions/checkout@v4
- uses: azure/setup-helm@v4
with: { version: 'v3.16.0' }
- name: Lint
run: helm lint ./charts/my-app
- name: Package
run: helm package ./charts/my-app
- name: Login and push
env: { GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} }
run: |
echo "$GITHUB_TOKEN" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
helm push my-app-*.tgz oci://ghcr.io/${{ github.repository_owner }}/charts
Pin chart versions to your Git SHA or your release tag — never push 0.0.0-latest.
Signing Charts
OCI registries store everything as content-addressable artifacts, which makes signature attachment natural. Use Sigstore / cosign:
cosign sign --yes ghcr.io/acme/charts/my-app:1.4.2
# Verifier side:
cosign verify ghcr.io/acme/charts/my-app:1.4.2 \
--certificate-identity-regexp 'https://github.com/acme/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
This is keyless signing: cosign uses your OIDC identity (GitHub Actions, GitLab, Google) and Sigstore's transparency log. No private keys to manage.
Helm's legacy helm package --sign uses PGP and a separate .prov file — works but few teams operate the key custody to make it useful. Cosign on OCI is the practical modern choice.
Legacy HTTP Repositories
You will still encounter the older model. Add a repo, then install:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo bitnami/postgresql
helm install pg bitnami/postgresql --version 13.4.1
Behind the scenes the repo serves an index.yaml file listing every chart and version with their .tgz URLs. Hosting is just static files — GitHub Pages, S3, GCS, NGINX all work. Many vendors still publish HTTP for compatibility but are dual-publishing to OCI.
Versioning Discipline
Treat chart versions as a published API:
- Patch (1.4.2 → 1.4.3): bug fix, template adjustment, no values surface change.
- Minor (1.4.x → 1.5.0): new feature, new values key, backwards compatible default.
- Major (1.x → 2.0.0): values surface breaking change, removed keys, mandatory upgrade notes.
Document every change in CHANGELOG.md. Use artifacthub.io annotations in Chart.yaml if publishing publicly — they drive the Artifact Hub listing UI.
Internal Chart Catalogues
For organisations with many internal apps, run a central registry (Harbor, JFrog, Artifactory, ECR) and treat it like an internal npm. Pair with:
- A chart linter and policy check (kubeconform, datreeio, kyverno) in CI
- Mandatory
maintainersandiconfields - Required JSON schema for values
- Automated PR scaffolding from a chart template
The chart catalogue becomes the company's internal app-deploy contract.