Helm Plugin
The Helm plugin provides support for Kubernetes Helm chart repositories.
Overview
Status: ✅ Available
The Helm plugin consists of:
HelmSyncer - Syncs Helm charts from upstream chart repositories
HelmPublisher - Publishes Helm chart repositories with metadata
Features
✅ index.yaml parsing
✅ Helm chart downloading (.tgz files)
✅ SHA256 checksum verification
✅ Pattern-based chart filtering
✅ Version filtering (only latest)
✅ Metadata generation (index.yaml)
✅ Chart deduplication via content-addressed storage
✅ Snapshot support
✅ Mirror Mode - Byte-for-byte identical repositories with snapshot versioning
✅ Hosted Mode - Upload-only repositories for self-hosted charts (
chantal package upload)ℹ️ OCI ingest only —
oci://chart URLs referenced inside an upstream HTTPindex.yamlare pulled via thehelmbinary (which must be installed). You cannot configure anoci://feed, and Chantal never publishes charts over OCI (published repos are always plain HTTP with anindex.yaml).ℹ️ Charts are served unmodified; Chantal does not sign or re-sign charts
⛔ Not supported: Helm provenance — upstream
.provfiles are not downloaded or republished, andhelm pull --verifyagainst a mirror will fail
Repository Modes
The default mode is filtered. Set mode: mirror explicitly for a byte-for-byte
identical repository copy.
Note on the Helm index: the sync step always stores the upstream
index.yamlin the content-addressed pool as aRepositoryFile, but how the publishedindex.yamlis produced is driven by the mode: mirror republishes the stored upstream index verbatim, while filtered (and hosted) regenerate the index from the published charts so it lists exactly what was published.
Mirror Mode
Status: ✅ Available
In mirror mode, Chantal stores the original index.yaml metadata file in the content-addressed pool as a RepositoryFile. When publishing, the original metadata is hardlinked from the pool to the published directory.
Benefits:
Byte-for-byte identical to upstream repository
Snapshot versioning of metadata (track index.yaml changes over time)
Metadata deduplication across repositories and snapshots
Historical tracking of metadata changes
How it works:
Sync Process:
Downloads index.yaml from upstream
Stores index.yaml in content-addressed pool by SHA256
Creates RepositoryFile database record
Links metadata to repository/snapshot
Publish Process:
Queries RepositoryFile for stored index.yaml
Hardlinks original index.yaml from pool to published directory
Creates hardlinks for all chart .tgz files
Result: Byte-for-byte identical copy of upstream
Example:
repositories:
- id: ingress-nginx
name: Ingress NGINX Helm Charts
type: helm
feed: https://kubernetes.github.io/ingress-nginx
enabled: true
mode: mirror # explicit; the default is 'filtered'
Use Cases:
Offline/air-gapped environments requiring exact upstream mirrors
Compliance requirements for unmodified upstream metadata
Snapshot versioning for reproducible deployments
Bandwidth optimization (metadata reused across snapshots)
Dynamic Index Generation
In filtered and hosted mode the publisher generates index.yaml from the
chart metadata in the database rather than republishing an upstream index. (Mirror
mode also falls back to generation if no stored index.yaml is available.)
This path:
Generates index.yaml from HelmMetadata in the database
Produces an index covering exactly the charts that were published — a
filteredrepo’s index lists only the charts that passed the filtersSupports post-processing (e.g., only latest versions), so the index reflects the post-processed set as well
Hosted Mode
An upload-only repository with no upstream feed. Custom-built charts are
added with chantal package upload; there is nothing to sync, so chantal sync
skips hosted repositories. Publishing generates index.yaml from the uploaded
charts (the dynamic-generation path above), so the result is consumable by a
real helm client.
repositories:
- id: internal-charts
name: Internal Helm Charts
type: helm
enabled: true
mode: hosted
# note: no 'feed' - hosted repos hold only uploaded charts
Upload one or more local chart .tgz files and publish:
# A single chart
chantal package upload --repo-id internal-charts --file ./demo-0.1.0.tgz
# A whole directory (optionally recursive)
chantal package upload --repo-id internal-charts --directory ./charts/ --recursive
# Replace an existing chart of the same name/version with different content
chantal package upload --repo-id internal-charts --file ./demo-0.1.0.tgz --force
# Regenerate index.yaml so clients can pull
chantal publish repo --repo-id internal-charts --target /srv/repos/internal-charts
Chart metadata is read from the chart’s top-level Chart.yaml in pure
Python (the gzipped tar is opened directly) - no helm binary is required.
Uploads are content-addressed and deduplicated by SHA-256: re-uploading
identical bytes just links the existing pool entry, while a different build of a
chart name/version already present requires --force.
Clients consume it like any other Helm HTTP repository:
helm repo add internal http://mirror.example.com/repos/internal-charts
helm repo update
helm pull internal/demo --version 0.1.0
Configuration
Basic Helm Repository
repositories:
- id: ingress-nginx
name: Ingress NGINX Helm Charts
type: helm
feed: https://kubernetes.github.io/ingress-nginx
enabled: true
With Filters
repositories:
- id: bitnami-databases
name: Bitnami Charts - Databases Only
type: helm
feed: https://charts.bitnami.com/bitnami
enabled: true
filters:
patterns:
include: ["^postgresql$", "^mysql$", "^mongodb$", "^redis$"]
post_processing:
only_latest_version: true
With Authentication
Some Helm repositories require authentication:
repositories:
- id: private-charts
name: Private Helm Charts
type: helm
feed: https://charts.example.com/
enabled: true
ssl:
ca_bundle: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
client_cert: /etc/pki/helm/client.pem
client_key: /etc/pki/helm/client-key.pem
verify: true
How It Works
Sync Process
Fetch index.yaml
GET https://charts.example.com/index.yaml
Parse to find available charts and versions
Parse chart metadata
Chart name and version
Chart URL (may be relative or absolute)
Chart digest (SHA256)
Dependencies, maintainers, etc.
Apply filters
Pattern matching (include/exclude regex)
Version filtering (only latest)
Download charts
Download .tgz files to content-addressed pool
Verify SHA256 checksums
Deduplicate identical charts
Store metadata
Create ContentItem records
Store Helm metadata in database
Link charts to repository
Publish Process
Query database for charts in repository/snapshot
Create directory structure for published repository
Create hardlinks from pool to published directory
Generate index.yaml with chart metadata and URLs
Set correct file permissions for web server access
Chart Filtering
Pattern Filters
Include specific charts by name:
filters:
patterns:
include:
- "^nginx-.*" # All nginx-related charts
- "^prometheus$" # Exact match: prometheus chart
- "^grafana-.*" # All grafana-related charts
Exclude charts by pattern:
filters:
patterns:
exclude:
- ".*-alpha$" # Exclude alpha versions
- ".*-beta$" # Exclude beta versions
- "^deprecated-.*" # Exclude deprecated charts
Version Filtering
Keep only the latest version of each chart:
filters:
post_processing:
only_latest_version: true
This is useful for:
Reducing storage usage
Simplifying chart selection for users
Automatically staying current with upstream
Common Use Cases
Mirror Official Kubernetes Charts
repositories:
- id: kubernetes-charts
name: Official Kubernetes Charts
type: helm
feed: https://kubernetes.github.io/ingress-nginx
enabled: true
Mirror Bitnami Charts (Selective)
repositories:
- id: bitnami-webservers
name: Bitnami - Web Servers
type: helm
feed: https://charts.bitnami.com/bitnami
enabled: true
filters:
patterns:
include: ["^nginx$", "^apache$"]
post_processing:
only_latest_version: true
Mirror Harbor Registry
repositories:
- id: harbor
name: Harbor Helm Charts
type: helm
feed: https://helm.goharbor.io
enabled: true
Private Chart Repository
repositories:
- id: company-charts
name: Company Internal Charts
type: helm
feed: https://charts.internal.company.com/
enabled: true
ssl:
client_cert: /etc/pki/helm/company-client.pem
client_key: /etc/pki/helm/company-client-key.pem
verify: true
Publishing Helm Repositories
Publish Latest Repository
chantal publish repo --repo-id ingress-nginx
Published structure:
/var/www/repos/ingress-nginx/latest/
├── index.yaml
├── ingress-nginx-4.0.15.tgz
├── ingress-nginx-4.0.14.tgz
└── ...
Publish Snapshot
chantal snapshot create --repo-id ingress-nginx --name 2025-01-10
chantal publish snapshot --snapshot 2025-01-10 --repo-id ingress-nginx
Published structure:
/var/www/repos/ingress-nginx/snapshots/2025-01-10/
├── index.yaml
└── ingress-nginx-4.0.15.tgz
Configure Helm Client
Point Helm CLI to your mirrored repository:
# Add repository
helm repo add ingress-nginx http://mirror.example.com/repos/ingress-nginx/latest/
# Update repository index
helm repo update
# Install chart
helm install my-ingress ingress-nginx/ingress-nginx
Configure Helm with Snapshot
Use a specific snapshot for reproducible deployments:
# Add snapshot repository
helm repo add ingress-nginx-2025-01-10 \
http://mirror.example.com/repos/ingress-nginx/snapshots/2025-01-10/
# Install from snapshot
helm install my-ingress ingress-nginx-2025-01-10/ingress-nginx
Chart Metadata
Chantal stores comprehensive metadata for each chart:
name - Chart name
version - Chart version (SemVer)
description - Chart description
home - Project home page URL
sources - Source code URLs
keywords - Chart keywords
maintainers - Maintainer information
icon - Chart icon URL
appVersion - Application version
deprecated - Deprecation status
annotations - Chart annotations
dependencies - Chart dependencies
type - Chart type (application/library)
apiVersion - Helm API version
This metadata is:
Stored in the database for querying
Included in published index.yaml
Available via Chantal CLI commands
Troubleshooting
Charts Not Syncing
Check index.yaml is accessible:
curl -I https://charts.example.com/index.yaml
Verify repository configuration:
chantal repo show --repo-id my-helm-repo
Check sync logs for errors:
chantal repo sync --repo-id my-helm-repo
Publishing Issues
Verify charts were synced:
chantal content list --repo-id my-helm-repo
Check published directory permissions:
ls -la /var/www/repos/my-helm-repo/latest/
Verify index.yaml was generated:
cat /var/www/repos/my-helm-repo/latest/index.yaml
Client Certificate Issues
Verify certificate files exist and are readable:
ls -la /etc/pki/helm/client.pem
ls -la /etc/pki/helm/client-key.pem
Test with curl:
curl --cert /etc/pki/helm/client.pem \
--key /etc/pki/helm/client-key.pem \
https://charts.example.com/index.yaml
Best Practices
Use snapshots for production - Create dated snapshots for reproducible deployments
Filter by patterns - Only mirror charts you need to reduce storage
Keep latest only - Use
only_latest_version: trueunless you need version historyRegular syncing - Schedule regular syncs to stay current
Verify checksums - Chantal automatically verifies SHA256 checksums
Document your mirrors - Keep notes on why specific charts are mirrored
Test before production - Test snapshots in staging before promoting to production
Integration with CI/CD
GitOps Workflow
# ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
spec:
source:
repoURL: http://mirror.example.com/repos/ingress-nginx/snapshots/2025-01-10/
chart: ingress-nginx
targetRevision: 4.0.15
Jenkins Pipeline
pipeline {
stages {
stage('Sync Helm Charts') {
steps {
sh 'chantal repo sync --repo-id ingress-nginx'
}
}
stage('Create Snapshot') {
steps {
sh 'chantal snapshot create --repo-id ingress-nginx --name ${BUILD_ID}'
}
}
stage('Publish Snapshot') {
steps {
sh 'chantal publish snapshot --snapshot ${BUILD_ID} --repo-id ingress-nginx'
}
}
}
}
Further Reading
Plugins Overview - Plugin architecture
Custom Plugins - Creating custom plugins
Repository Configuration - Repository settings
CLI Commands - Command reference