Helm with GitHub Actions
A comprehensive guide to Helm with GitHub Actions covering CI/CD pipelines, linting, testing, publishing charts, and best practices for automated Helm workflows.
GitHub Actions
CI/CD Pipelines
Chart Publishing
Why GitHub Actions for Helm?
GitHub Actions provides a powerful, integrated CI/CD platform for automating Helm chart workflows. It offers:
- Native GitHub Integration: Deep integration with GitHub repositories
- Large Marketplace: Thousands of reusable actions
- Matrix Builds: Test across multiple Kubernetes versions
- Secrets Management: Secure handling of credentials
- Artifact Storage: Store and share chart packages
- Cost-Effective: Free for public repositories
Key Concept: GitHub Actions enables you to automate the entire Helm chart lifecycle: linting, testing, packaging, signing, and publishing—all triggered by Git events.
Basic Helm Workflow
# .github/workflows/helm.yml
name: Helm Chart CI
on:
push:
branches: [ main ]
paths:
- 'charts/**'
pull_request:
branches: [ main ]
paths:
- 'charts/**'
jobs:
lint:
name: Lint Helm Charts
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Lint charts
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Linting $chart"
helm lint "$chart"
fi
done
- name: Validate chart templates
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Validating $chart"
helm template test-release "$chart" > /dev/null
fi
done
test:
name: Test Helm Charts
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Install helm-unittest
run: |
helm plugin install https://github.com/helm-unittest/helm-unittest
- name: Run unit tests
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Testing $chart"
helm unittest "$chart"
fi
done
- name: Validate with kubeconform
run: |
wget https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz
tar xf kubeconform-linux-amd64.tar.gz
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Validating $chart"
helm template test-release "$chart" | ./kubeconform -strict
fi
done
package:
name: Package Helm Charts
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Package charts
run: |
mkdir -p dist
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Packaging $chart"
helm package "$chart" --destination dist
fi
done
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: helm-charts
path: dist/*.tgz
Basic Workflow Benefits:
- Automated linting on every push
- Unit testing with helm-unittest
- Validation with kubeconform
- Chart packaging for distribution
- Artifact storage for later use
Publishing Workflows
Publish to GitHub Pages (Traditional Helm Repo)
# .github/workflows/publish-gh-pages.yml
name: Publish Helm Charts to GitHub Pages
on:
push:
branches: [ main ]
paths:
- 'charts/**'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.5.0
with:
charts_dir: charts
config: cr.yaml
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: Verify published charts
run: |
helm repo add my-repo https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}
helm repo update
helm search repo my-repo
Publish to OCI Registry (GHCR)
# .github/workflows/publish-oci.yml
name: Publish Helm Charts to OCI
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Login to GitHub Container Registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | \
helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Package and Push Charts
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Publishing $chart"
helm package "$chart" --destination dist
helm push dist/$(basename $chart)-*.tgz oci://ghcr.io/${{ github.repository }}/charts
fi
done
- name: Verify published charts
run: |
helm pull oci://ghcr.io/${{ github.repository }}/charts/$(basename $chart) --version $(helm show chart charts/$chart | grep version | awk '{print $2}')
# Publish to AWS ECR
- name: Login to AWS ECR
run: |
aws ecr get-login-password --region us-east-1 | \
helm registry login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com
- name: Push to ECR
run: |
helm push dist/*.tgz oci://${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/helm
# Publish to Azure ACR
- name: Login to Azure ACR
uses: azure/docker-login@v1
with:
login-server: ${{ secrets.ACR_REGISTRY }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- name: Push to ACR
run: |
helm registry login ${{ secrets.ACR_REGISTRY }} -u ${{ secrets.ACR_USERNAME }} -p ${{ secrets.ACR_PASSWORD }}
helm push dist/*.tgz oci://${{ secrets.ACR_REGISTRY }}/helm
GitHub Pages
Traditional Helm repository hosted on GitHub Pages. Free and simple.
GHCR
GitHub Container Registry. OCI-compliant, integrated with GitHub.
AWS ECR
Amazon Elastic Container Registry. Enterprise-grade with IAM.
Azure ACR
Azure Container Registry. Integrated with Azure AD and DevOps.
Chart Testing Workflow
# .github/workflows/test-charts.yml
name: Test Helm Charts
on:
pull_request:
paths:
- 'charts/**'
jobs:
test:
name: Test Charts
runs-on: ubuntu-latest
strategy:
matrix:
k8s-version: [v1.27.0, v1.28.0, v1.29.0]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Install helm-unittest
run: helm plugin install https://github.com/helm-unittest/helm-unittest
- name: Run unit tests
run: helm unittest charts/*
- name: Create kind cluster
uses: helm/kind-action@v1
with:
node_image: kindest/node:${{ matrix.k8s-version }}
- name: Install chart
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Installing $chart"
helm install test-release "$chart" --wait --timeout 5m
fi
done
- name: Run helm test
run: |
for release in $(helm list -q); do
echo "Testing $release"
helm test "$release" --timeout 5m
done
- name: Check pod status
run: |
kubectl get pods --all-namespaces
kubectl get svc --all-namespaces
kubectl get deployments --all-namespaces
- name: Cleanup
run: |
for release in $(helm list -q); do
helm uninstall "$release"
done
kind delete cluster
# Matrix testing across Kubernetes versions
strategy:
matrix:
k8s-version: [v1.27.0, v1.28.0, v1.29.0]
chart: [nginx, postgresql, redis]
# Use chart-testing tool
- name: Run chart-testing
uses: helm/chart-testing-action@v2.4.0
- name: List changed charts
id: list-changed
run: |
changed=$(ct list-changed --config ct.yaml)
if [[ -n "$changed" ]]; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: Lint charts
if: steps.list-changed.outputs.changed == 'true'
run: ct lint --config ct.yaml
- name: Create kind cluster
if: steps.list-changed.outputs.changed == 'true'
uses: helm/kind-action@v1
- name: Install and test charts
if: steps.list-changed.outputs.changed == 'true'
run: ct install --config ct.yaml
chart-testing (ct) Tool:
- Automatically detects changed charts
- Runs linting and validation
- Installs charts in a kind cluster
- Runs helm tests
- Supports matrix testing across K8s versions
Advanced Workflows
Chart Signing Workflow
# .github/workflows/sign-charts.yml
name: Sign Helm Charts
on:
push:
tags:
- 'v*'
jobs:
sign:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Import GPG Key
run: |
echo "${{ secrets.GPG_PRIVATE_KEY }}" | \
gpg --batch --import
echo "${{ secrets.GPG_PASSPHRASE }}" | \
gpg --batch --passphrase-fd 0 --pinentry-mode loopback --import
env:
GPG_TTY: /dev/tty
- name: Package and Sign
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
helm package "$chart" \
--sign \
--key "${{ secrets.GPG_KEY_NAME }}" \
--destination dist
fi
done
- name: Verify Signatures
run: |
for chart in dist/*.tgz; do
echo "Verifying $chart"
helm verify "$chart"
done
- name: Upload Signed Charts
uses: actions/upload-artifact@v3
with:
name: signed-charts
path: |
dist/*.tgz
dist/*.tgz.prov
Security Scanning Workflow
# .github/workflows/security-scan.yml
name: Security Scan Helm Charts
on:
pull_request:
paths:
- 'charts/**'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Install Trivy
uses: aquasecurity/setup-trivy@v0.2.0
- name: Scan charts for vulnerabilities
run: |
for chart in charts/*; do
if [ -d "$chart" ]; then
echo "Scanning $chart"
trivy config "$chart"
fi
done
- name: Validate with kube-score
run: |
wget https://github.com/zegl/kube-score/releases/latest/download/kube-score_linux_amd64
chmod +x kube-score_linux_amd64
for chart in charts/*; do
if [ -d "$chart" ]; then
helm template test "$chart" | ./kube-score_linux_amd64 score -
fi
done
- name: Check for secrets in charts
uses: gitleaks/gitleaks-action@v2
with:
config-path: .gitleaks.toml
- name: Upload scan results
uses: actions/upload-artifact@v3
with:
name: security-scan-results
path: |
trivy-results/
kube-score-results/
Automated Release Workflow
# .github/workflows/release.yml
name: Release Helm Charts
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.14.0'
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Package Charts
run: |
mkdir -p dist
for chart in charts/*; do
if [ -d "$chart" ]; then
helm package "$chart" --destination dist
fi
done
- name: Generate Changelog
id: changelog
run: |
echo "## What's Changed" > CHANGELOG.md
git log --oneline --no-merges $(git describe --tags --abbrev=0 HEAD^)..HEAD >> CHANGELOG.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
dist/*.tgz
dist/*.tgz.prov
body_path: CHANGELOG.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to OCI
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | \
helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
for chart in dist/*.tgz; do
helm push "$chart" oci://ghcr.io/${{ github.repository }}/charts
done
- name: Update Helm Repository
run: |
helm repo index dist --url https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}
git checkout gh-pages
cp dist/index.yaml .
git add index.yaml
git commit -m "Update Helm repository index"
git push origin gh-pages
GitHub Actions Best Practices
Use Secrets
Store credentials in GitHub Secrets, never in workflow files.
Pin Action Versions
Use specific action versions (e.g., @v4) instead of @master.
Matrix Testing
Test against multiple Kubernetes versions and chart combinations.
Use Caching
Cache Helm dependencies and Go modules for faster builds.
Path Filters
Trigger workflows only when chart files change.
Validate Charts
Always lint and validate charts before publishing.
Sign Charts
Sign charts with GPG keys for authenticity.
Security Scans
Scan charts for vulnerabilities and secrets.
# Best practices summary
# 1. Use secrets for credentials
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | helm registry login ...
# 2. Pin action versions
uses: azure/setup-helm@v3 # Specific version
# uses: azure/setup-helm@master # Avoid this
# 3. Use path filters
on:
push:
paths:
- 'charts/**'
- '.github/workflows/helm.yml'
# 4. Use caching
- name: Cache Helm dependencies
uses: actions/cache@v3
with:
path: ~/.cache/helm
key: ${{ runner.os }}-helm-${{ hashFiles('**/Chart.lock') }}
# 5. Matrix testing
strategy:
matrix:
k8s-version: [v1.27.0, v1.28.0, v1.29.0]
# 6. Use concurrency control
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# 7. Use environment protection
environment:
name: production
url: https://myapp.example.com
# 8. Require approvals for production
environment:
name: production
# Configure protection rules in GitHub settings
Frequently Asked Questions
How do I trigger a Helm workflow only when charts change?
Use path filters in the
on section: paths: - 'charts/**'. This ensures the workflow only runs when chart files are modified. How do I publish Helm charts to GitHub Pages?
Use the
helm/chart-releaser-action action. It packages charts, creates GitHub releases, and updates the gh-pages branch with the repository index. How do I publish charts to OCI registries?
Use
helm registry login and helm push commands. For GHCR, use GITHUB_TOKEN; for AWS ECR, use AWS credentials; for Azure ACR, use Azure credentials. How do I test Helm charts in GitHub Actions?
Use helm-unittest for unit tests, kind-action for integration testing, and chart-testing (ct) for comprehensive testing. Validate with kubeconform and kube-score.
How do I sign Helm charts in GitHub Actions?
Import your GPG private key from GitHub Secrets, then use
helm package --sign --key. Store the key and passphrase as secrets. How do I scan Helm charts for vulnerabilities?
Use Trivy, kube-score, and gitleaks. Run security scans in your CI/CD pipeline before publishing charts.
What is chart-testing (ct)?
chart-testing is a tool that detects changed charts, runs linting, installs charts in a kind cluster, and runs helm tests. It's ideal for PR validation.
How do I use matrix builds for Helm charts?
Define a matrix in your workflow with different Kubernetes versions or chart names. Use
strategy.matrix to run tests in parallel across multiple configurations.
Related Topics
GitHub Actions provides a powerful platform for automating Helm chart workflows. Implement these CI/CD pipelines to ensure quality, security, and reliability in your Helm deployments.