Packaging and Publishing Charts
Package your Helm charts and publish them to repositories. Share charts with your team or the open source community.
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:
- Create an
artifacthub-repo.ymlin your chart repo:
# artifacthub-repo.yml
repositoryID: your-uuid-here
owners:
- name: Your Name
email: you@example.com
-
Register your repository at artifacthub.io → Add Repository.
-
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.