Lesson 18 Chart Packaging & Registries
Theme:

Chart Packaging & Registries

Packaging Helm charts for distribution, hosting them in repositories and OCI registries, and securing them with cryptographic signatures.

Introduction

A Helm chart starts its life as a directory of YAML templates, a Chart.yaml, and a values.yaml. That is perfectly fine for local development, but the moment you need to share a chart with teammates, deploy it across environments, or distribute it to customers, you need a packaging and distribution strategy. Helm provides a complete toolchain for this: helm package creates versioned archives, chart repositories and OCI registries host them, and provenance files verify their integrity.

This lesson covers the full lifecycle from local chart directory to signed, published artifact.

Creating Chart Archives with helm package

The helm package command takes a chart directory and produces a compressed .tgz archive named <name>-<version>.tgz. The name and version are read from Chart.yaml.

# Basic packaging
helm package ./mychart/
# Output: mychart-1.2.0.tgz

# Override version at package time
helm package ./mychart/ --version 2.0.0-rc1

# Write output to a specific directory
helm package ./mychart/ -d ./dist/

# Package with dependency update
helm package ./mychart/ -u

The -u flag runs helm dependency update before packaging, ensuring all subchart archives in charts/ are current. This is essential in CI pipelines where the charts/ directory may not be checked into version control.

What Gets Included

Everything in the chart directory is included in the archive except files matched by .helmignore. A typical .helmignore looks like this:

# .helmignore
.git
.gitignore
.helmignore
*.md
tests/
ci/
.env
secrets.yaml
*.swp
*.bak
__pycache__/

The syntax follows .gitignore conventions. Always exclude documentation, test fixtures, CI configuration, editor swap files, and anything containing secrets. A bloated chart archive slows down installs and wastes registry storage.

SemVer Versioning: version vs appVersion

Every Chart.yaml has two version fields, and confusing them is one of the most common Helm mistakes:

FieldTracksWhen to BumpExample
versionThe chart itselfAny change to templates, values, helpers, or dependencies1.2.0
appVersionThe deployed applicationWhen the app image tag changes"3.8.1"

Both fields should follow Semantic Versioning. The version field is used by Helm to resolve dependencies, sort releases, and name archives. The appVersion is informational and commonly injected into image tags via .Chart.AppVersion.

Tip: A chart version bump does not require an appVersion bump, and vice versa. If you fix a template bug without changing the app image, bump version only. If you update the app image but nothing else in the chart changes, bump appVersion and version (since the chart contents did change).

Classic Chart Repositories

A classic Helm chart repository is simply an HTTP server hosting .tgz chart archives alongside an index.yaml file. The index file is a machine-readable catalog of every chart version available in the repository.

# index.yaml structure
apiVersion: v1
entries:
  mychart:
    - name: mychart
      version: 1.2.0
      appVersion: "3.8.1"
      created: "2026-04-01T10:00:00Z"
      digest: sha256:a1b2c3d4...
      urls:
        - https://charts.example.com/mychart-1.2.0.tgz
    - name: mychart
      version: 1.1.0
      appVersion: "3.7.0"
      created: "2026-03-15T08:30:00Z"
      digest: sha256:e5f6a7b8...
      urls:
        - https://charts.example.com/mychart-1.1.0.tgz

Generate or update the index with:

helm repo index ./dist/ --url https://charts.example.com

Users interact with repos through these commands:

helm repo add myrepo https://charts.example.com
helm repo update
helm search repo myrepo/mychart
helm repo remove myrepo

ChartMuseum

ChartMuseum is a lightweight, open-source chart repository server. It auto-generates index.yaml and provides an upload API, eliminating the need to manually manage index files. It supports local storage, S3, GCS, Azure Blob, and other backends.

# Run with Docker
docker run -d -p 8080:8080 \
  -e STORAGE=local \
  -e STORAGE_LOCAL_ROOTDIR=/charts \
  -v $(pwd)/charts:/charts \
  chartmuseum/chartmuseum

# Upload a chart
curl --data-binary "@mychart-1.2.0.tgz" http://localhost:8080/api/charts

GitHub Pages

A popular free hosting option is to use a gh-pages branch (or the docs/ folder on main) as a chart repository. Package your charts, generate index.yaml with the GitHub Pages URL, commit, and push. GitHub serves the static files over HTTPS, which is all Helm needs.

helm package ./mychart/ -d ./docs/
helm repo index ./docs/ --url https://myorg.github.io/helm-charts
git add docs/ && git commit -m "Release mychart 1.2.0" && git push

Harbor

Harbor is an enterprise container registry that also serves as a chart repository. It supports both the classic chart repo protocol and OCI-based distribution, along with RBAC, vulnerability scanning, image signing, and cross-datacenter replication. If your organization already runs Harbor for container images, it is the natural choice for chart hosting.

OCI Registries

Starting with Helm 3.8, OCI (Open Container Initiative) support is stable and is now the recommended distribution method. OCI registries store charts as OCI artifacts alongside container images, eliminating the need for a separate index.yaml.

# Authenticate
helm registry login ghcr.io -u USERNAME

# Push (requires a .tgz file)
helm push mychart-1.2.0.tgz oci://ghcr.io/myorg/charts

# Pull
helm pull oci://ghcr.io/myorg/charts/mychart --version 1.2.0

# Install directly
helm install myrelease oci://ghcr.io/myorg/charts/mychart --version 1.2.0

# Show chart info
helm show chart oci://ghcr.io/myorg/charts/mychart --version 1.2.0

OCI registries that work with Helm charts include: GitHub Container Registry (ghcr.io), Docker Hub, Amazon ECR, Google Artifact Registry, Azure Container Registry, and Harbor. Any registry that supports OCI artifacts will work.

Key difference: With classic repos, you run helm repo add and helm search repo. With OCI registries, there is no repo add step — you reference charts directly by their oci:// URL. The helm search command does not work with OCI; instead, use your registry's web UI or API to browse available charts.

Signing and Verifying Charts

Chart signing adds a cryptographic guarantee that a chart has not been tampered with and was produced by a trusted party. Helm uses GnuPG for signing.

# Package and sign in one step
helm package ./mychart/ --sign \
  --key "Helm Signing Key" \
  --keyring ~/.gnupg/secring.gpg

This produces two files: mychart-1.2.0.tgz (the chart archive) and mychart-1.2.0.tgz.prov (the provenance file). The .prov file contains the chart metadata, a SHA-256 digest of the archive, and a PGP signature.

# Verify a chart
helm verify mychart-1.2.0.tgz --keyring ~/.gnupg/pubring.gpg

# Install with automatic verification
helm install myrelease mychart-1.2.0.tgz \
  --verify --keyring ~/.gnupg/pubring.gpg

When --verify is used, Helm will refuse to install the chart if the signature is missing, invalid, or signed by an untrusted key. In production environments, especially those subject to compliance requirements, chart verification should be mandatory.

Warning: The legacy GnuPG v1 keyring format (secring.gpg) is required by Helm's signing implementation. If you use GnuPG v2, you may need to export your key to the legacy format: gpg --export-secret-keys > ~/.gnupg/secring.gpg.

CI/CD Pipeline for Chart Publishing

A well-structured CI/CD pipeline automates the entire chart lifecycle. A typical pipeline has four stages:

  1. Lint — helm lint ./mychart/ --strict catches syntax errors, missing required fields, and best-practice violations.
  2. Test — helm template ./mychart/ | kubeval --strict validates rendered manifests against the Kubernetes API schema. For integration tests, use ct install from the chart-testing tool.
  3. Package — helm package ./mychart/ --version ${CI_TAG} creates the versioned archive. Optionally sign it with --sign.
  4. Push — helm push mychart-${CI_TAG}.tgz oci://ghcr.io/myorg/charts publishes to the registry.
# GitHub Actions example
name: Release Chart
on:
  push:
    tags: ['v*']
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Helm
        uses: azure/setup-helm@v4
      - name: Lint
        run: helm lint ./mychart/ --strict
      - name: Package
        run: helm package ./mychart/ --version ${GITHUB_REF_NAME#v}
      - name: Login to GHCR
        run: echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
      - name: Push
        run: helm push mychart-${GITHUB_REF_NAME#v}.tgz oci://ghcr.io/${{ github.repository_owner }}/charts

Choosing a Distribution Strategy

The right distribution method depends on your team size, infrastructure, and security requirements:

MethodProsConsBest For
GitHub PagesFree, simple, version-controlledPublic only (unless GitHub Enterprise), manual index managementOpen-source projects
ChartMuseumLightweight, auto-index, multi-backend storageNo RBAC, no scanning, extra infra to maintainSmall teams, internal use
HarborRBAC, scanning, replication, OCI + classicHeavier to operateEnterprise environments
OCI (ghcr.io, ECR, etc.)No index.yaml, reuses container infra, modern standardNo helm search repo, newer ecosystemTeams already using container registries
Recommendation: For new projects, default to OCI. It is the direction the Helm ecosystem is moving, it simplifies infrastructure, and every major cloud provider supports it. Use classic repos only when you need backward compatibility with older Helm clients.

Summary

helm package turns a chart directory into a versioned, distributable archive. Classic chart repositories serve .tgz files behind an index.yaml, while OCI registries use the same infrastructure as container images. ChartMuseum, Harbor, GitHub Pages, and cloud-hosted registries each serve different scales and security postures. Chart signing with provenance files provides tamper detection and origin verification. A robust CI/CD pipeline ties it all together: lint, test, package, sign, and push on every tagged release.

In the next lesson, we will look at advanced dependency management and library charts.