Packaging and Publishing Charts

Package your Helm charts and publish them to repositories. Share charts with your team or the open source community.

6 min read

Packaging and Publishing Charts

In the previous tutorial, we explored Helm repositories from the consumer side. Now let's switch roles — you're the chart author, and you want to share your work. Time to package and publish.

Packaging a Chart

The helm package command bundles your chart directory into a versioned .tgz archive:

helm package ./my-chart

# Successfully packaged chart and saved it to: /current/dir/my-chart-1.0.0.tgz

The filename is <name>-<version>.tgz, pulled from Chart.yaml:

# Chart.yaml
name: my-chart
version: 1.0.0

Package Options

# Output to a specific directory
helm package ./my-chart --destination ./dist/

# Override the version (useful in CI)
helm package ./my-chart --version 1.2.3

# Override the appVersion
helm package ./my-chart --app-version "2.0.0"

# Update dependencies before packaging
helm package ./my-chart --dependency-update

The --dependency-update flag is handy — it runs helm dep update automatically, ensuring all sub-charts are downloaded before packaging.

Signing Charts

For production, sign your charts so consumers can verify authenticity:

# Generate a GPG key (if you don't have one)
gpg --gen-key

# Package with signing
helm package ./my-chart --sign \
  --key "Your Name" \
  --keyring ~/.gnupg/secring.gpg

# This creates two files:
# my-chart-1.0.0.tgz           (the chart)
# my-chart-1.0.0.tgz.prov      (the provenance file)

Consumers verify with:

helm verify my-chart-1.0.0.tgz --keyring public-keys.gpg
helm install my-release my-chart-1.0.0.tgz --verify --keyring public-keys.gpg

Publishing to OCI Registries

The simplest way to publish charts today. Use any OCI-compatible registry you already have:

GitHub Container Registry (GHCR)

# Login
echo $GITHUB_TOKEN | helm registry login ghcr.io -u USERNAME --password-stdin

# Package
helm package ./my-chart

# Push
helm push my-chart-1.0.0.tgz oci://ghcr.io/your-org/charts

# Verify it's there
helm show chart oci://ghcr.io/your-org/charts/my-chart --version 1.0.0

Docker Hub

helm registry login registry-1.docker.io -u USERNAME
helm push my-chart-1.0.0.tgz oci://registry-1.docker.io/your-username

AWS ECR

# Create repository (one-time)
aws ecr create-repository --repository-name my-chart --region us-east-1

# Login
aws ecr get-login-password --region us-east-1 | \
  helm registry login --username AWS --password-stdin \
  123456789.dkr.ecr.us-east-1.amazonaws.com

# Push
helm push my-chart-1.0.0.tgz oci://123456789.dkr.ecr.us-east-1.amazonaws.com/

Google Artifact Registry

gcloud auth print-access-token | helm registry login -u oauth2accesstoken \
  --password-stdin https://us-central1-docker.pkg.dev

helm push my-chart-1.0.0.tgz oci://us-central1-docker.pkg.dev/my-project/charts

Hosting a Traditional Chart Repository

If you prefer HTTP-based repos, you just need a web server serving an index.yaml and .tgz files.

GitHub Pages (Free and Easy)

This is the most popular approach for open-source charts:

# 1. Create a GitHub repo for your charts
# 2. Create a gh-pages branch
git checkout --orphan gh-pages
git rm -rf .
git commit --allow-empty -m "Init"
git push origin gh-pages

# 3. Switch back and package your chart
git checkout main
helm package ./my-chart --destination ./dist/

# 4. Generate the index
helm repo index ./dist/ --url https://your-username.github.io/charts/

# 5. Copy to gh-pages and push
git checkout gh-pages
cp dist/* .
git add .
git commit -m "Release my-chart 1.0.0"
git push origin gh-pages

# 6. Enable GitHub Pages in repo settings (source: gh-pages branch)

Users add your repo:

helm repo add your-charts https://your-username.github.io/charts/
helm install my-release your-charts/my-chart

Updating the Index

When you publish new versions, merge into the existing index:

# Package new version
helm package ./my-chart --destination ./dist/

# Merge with existing index
helm repo index ./dist/ --url https://your-username.github.io/charts/ --merge ./index.yaml

The --merge flag keeps existing entries and adds new ones, so you don't lose older versions.

ChartMuseum

ChartMuseum is a dedicated Helm chart repository server:

# Run ChartMuseum
docker run -d -p 8080:8080 \
  -e STORAGE=local \
  -e STORAGE_LOCAL_ROOTDIR=/charts \
  -v $(pwd)/charts:/charts \
  ghcr.io/helm/chartmuseum:latest

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

# Add as a repo
helm repo add local http://localhost:8080

ChartMuseum auto-generates the index.yaml and supports multiple storage backends (S3, GCS, Azure Blob, etc.).

Harbor

Harbor is a full-featured registry that supports both container images and Helm charts:

# Push via OCI
helm push my-chart-1.0.0.tgz oci://harbor.example.com/my-project

# Or use the traditional API
helm repo add harbor https://harbor.example.com/chartrepo/my-project

Versioning Strategy

Your Chart.yaml has two version fields:

version: 1.2.3      # Chart version (your packaging version)
appVersion: "4.5.6"  # Application version (what's being deployed)

Best practices:

  • Chart version — Follows SemVer strictly. Bump it whenever anything in the chart changes (templates, values, dependencies).
  • App version — Matches the application's release version. Changes when you update the Docker image tag.
  • They change independently. You can release chart version 2.0.0 while app version stays at 1.5.0 (e.g., major chart restructuring).
# Bump chart version in CI
helm package ./my-chart --version $(git describe --tags)

# Or use a tool like chart-releaser

Automating with chart-releaser

The chart-releaser tool (cr) automates the GitHub Pages workflow:

# Install
brew install chart-releaser

# Package all charts in charts/ directory
cr package charts/my-chart

# Upload chart archives as GitHub releases
cr upload -o your-org -r charts --token $GITHUB_TOKEN

# Update index.yaml on gh-pages
cr index -o your-org -r charts --push

There's also a GitHub Action:

# .github/workflows/release.yml
name: Release Charts

on:
  push:
    branches: [main]

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Configure Git
        run: |
          git config user.name "$GITHUB_ACTOR"
          git config user.email "$GITHUB_ACTOR@users.noreply.github.com"

      - name: Install Helm
        uses: azure/setup-helm@v3

      - name: Run chart-releaser
        uses: helm/chart-releaser-action@v1
        env:
          CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

This automatically packages any chart that has a version bump, creates a GitHub release, and updates the gh-pages index.

Publishing to Artifact Hub

Artifact Hub is the central discovery platform for Helm charts. To list your charts there:

  1. Create an artifacthub-repo.yml in your chart repo:
# artifacthub-repo.yml
repositoryID: your-uuid-here
owners:
  - name: Your Name
    email: you@example.com
  1. Register your repository at artifacthub.io → Add Repository.

  2. Artifact Hub periodically scans your repo and indexes your charts.

Add rich metadata to your chart for better discoverability:

# Chart.yaml
annotations:
  artifacthub.io/changes: |
    - kind: added
      description: Support for HPA
    - kind: fixed
      description: Incorrect service port
  artifacthub.io/license: Apache-2.0
  artifacthub.io/links: |
    - name: Documentation
      url: https://docs.example.com

Pre-Publish Checklist

Before publishing a chart, verify:

# 1. Lint the chart
helm lint ./my-chart

# 2. Template renders without errors
helm template test-release ./my-chart

# 3. Template with different value combinations
helm template test-release ./my-chart -f values-production.yaml
helm template test-release ./my-chart --set ingress.enabled=true

# 4. Package successfully
helm package ./my-chart

# 5. Install in a test cluster
helm install test ./my-chart --dry-run
helm install test ./my-chart  # live test

# 6. Run chart tests
helm test test

What's Next?

You now know how to package charts, publish them to OCI registries or HTTP repositories, and automate the release process with chart-releaser.

In the next tutorial, we'll formalize quality assurance with testing Helm charts — linting, unit testing templates, and running integration tests.