Hosting a Helm Chart Repository on GitHub with chart-releaser

GitHub Pages plus the helm/chart-releaser-action is the de facto way to turn a GitHub repository into a Helm chart repository. No extra hosting, no separate registry — just a gh-pages branch and a workflow file.

This post walks through the layout I use in practice: one repository for the application, a separate one for the chart.


Why a Dedicated Chart Repository?

It is tempting to keep the Helm chart inside the application repository, in a charts/ or deploy/ folder. It works, but in practice the two artifacts have different lifecycles:

  • The application is versioned by what its source code does. A patch release fixes a bug in the binary.
  • The chart is versioned by what gets deployed. A patch release might tweak a probe, a label, or a default value — without changing a single line of application code.

Mixing both in the same repository means every chart-only change ends up in the application’s git history, and every application release pressures you to bump the chart even when nothing relevant changed. Splitting them keeps each history readable and lets the chart evolve independently.

So for one application myApplication, I create two GitHub repositories:

  • myApplication — the source code
  • helm-myApplication — the Helm chart

Bootstrapping the Chart Repository

Here is the sequence I follow when I create the chart repository.

1. Create the repository

Create helm-myApplication on GitHub. Initialize it however you want — even an empty repo is fine.

2. Configure GitHub Pages

In the repository Settings → Pages, set the source to the gh-pages branch (root). Once Pages is enabled, your chart repo URL will be:

https://<your-account>.github.io/helm-myApplication

This URL is what users will pass to helm repo add. You can also surface it in the repository sidebar via Settings → General → Website, so visitors land on the chart index directly.

3. Create an orphan gh-pages branch

The chart-releaser action publishes the packaged charts and the index.yaml to a branch — gh-pages by default. That branch must exist and should not share history with main:

git checkout --orphan gh-pages
git rm -rf .

Add a minimal README.md to the branch so visitors landing on the GitHub repository’s gh-pages view get a pointer back to the application:

# helm-myApplication

Helm chart for [myApplication](https://github.com/<your-account>/myApplication).

Add the repository:

​```bash
helm repo add myapp https://<your-account>.github.io/helm-myApplication
helm repo update
helm search repo myapp
​```

Commit and push:

git add README.md
git commit -m "chore: initialize gh-pages branch"
git push -u origin gh-pages

4. Switch back to main and add the chart

git checkout main
mkdir -p charts
helm create charts/myApplication

Edit charts/myApplication/Chart.yaml to set the version, app version, description, and maintainers. Commit and push.

The repository should now look like:

helm-myApplication/
├── .github/
│   └── workflows/
│       └── release.yml      # added next
├── charts/
│   └── myApplication/
│       ├── Chart.yaml
│       ├── values.yaml
│       └── templates/
└── README.md

Wiring chart-releaser

Add .github/workflows/release.yml:

name: Release Charts

on:
  push:
    branches:
      - main

permissions:
  contents: write   # required to push to gh-pages and create releases
  pages: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0   # chart-releaser needs full history

      - 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@v4

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

What this does on every push to main:

  1. Detects which charts under charts/ had their version bumped in Chart.yaml.
  2. Packages them into .tgz archives.
  3. Creates a GitHub Release per chart version, with the .tgz attached as a release asset.
  4. Updates index.yaml on the gh-pages branch to point at those release assets.

The crucial detail is that the chart tarballs live as GitHub Release assets, not on the gh-pages branch itself. Only index.yaml and an optional README.md are committed to gh-pages. This keeps the branch tiny no matter how many chart versions you publish.

⚠️ The action only triggers a new release when Chart.yaml’s version: field changes. Forgetting to bump it is the most common reason “the workflow ran but no release appeared”.


Consuming the Chart

Once the first release has been published:

helm repo add myapp https://<your-account>.github.io/helm-myApplication
helm repo update
helm install myapp myapp/myApplication

You can verify the index manually:

curl https://<your-account>.github.io/helm-myApplication/index.yaml

It should list every released chart version with a urls: entry pointing to the GitHub Release asset.


Keeping the Pipeline Healthy with Dependabot

The chart repo has very few moving parts, but the few it has — actions/checkout, azure/setup-helm, helm/chart-releaser-action — drift over time. Add .github/dependabot.yml so Dependabot opens PRs whenever a new version is published:

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

For a repository whose only purpose is releasing charts, this is the cheapest possible way to stay current.


Recap

The full bootstrapping checklist for one application:

  1. Create helm-myApplication on GitHub.
  2. Enable GitHub Pages (gh-pages branch, root).
  3. Create the orphan gh-pages branch with a README.md pointing to the application.
  4. On main, add the chart under charts/.
  5. Add .github/workflows/release.yml calling helm/chart-releaser-action.
  6. Add .github/dependabot.yml for the github-actions ecosystem.
  7. Bump Chart.yaml’s version: and push to main — the action does the rest.

Splitting the application from its chart costs one extra repository, but it pays back every time you release: the application history stays focused on code, and chart changes ship through a workflow that does one thing well.