Build an AI-Powered Helm Chart Generator with Claude API
Writing a Helm chart from scratch is tedious. Build a tool that takes a service description and generates a production-ready Helm chart with values.yaml, templates, and a test suite.
A complete Helm chart has 8+ files. Writing them all from scratch for every new service is repetitive. Here's how to build an AI tool that does it in seconds.
What You'll Generate
my-service/
├── Chart.yaml
├── values.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── configmap.yaml
│ └── _helpers.tpl
└── tests/
└── test-connection.yaml
The Generator
# helm_chart_generator.py
import anthropic
import os
import sys
import json
import zipfile
from pathlib import Path
from dataclasses import dataclass
SYSTEM_PROMPT = """You are a Helm chart expert. Generate complete, production-ready Helm charts.
Rules:
1. Follow Helm chart best practices (helm.sh/docs)
2. Use proper Go template syntax — all templates must be valid
3. Add comprehensive values.yaml with sensible defaults
4. Include HPA by default (set autoscaling.enabled=false in values)
5. Include Ingress template (set ingress.enabled=false by default)
6. Use named templates in _helpers.tpl for labels and selectors
7. Add resource requests/limits in values.yaml with reasonable defaults
8. Add probes (readiness + liveness) configurable via values
9. ServiceAccount creation with automountServiceAccountToken: false
10. Security context (runAsNonRoot: true, readOnlyRootFilesystem where possible)
Output format: JSON object with filename as key, file content as value.
Example:
{
"Chart.yaml": "apiVersion: v2\\nname: my-service\\n...",
"values.yaml": "replicaCount: 1\\n...",
"templates/deployment.yaml": "{{- if .Values...}}\\n..."
}"""
@dataclass
class ServiceConfig:
name: str
description: str
port: int
language: str
has_database: bool = False
has_cache: bool = False
has_ingress: bool = True
chart_version: str = "0.1.0"
app_version: str = "1.0.0"
def generate_helm_chart(config: ServiceConfig) -> dict:
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
prompt = f"""Generate a complete Helm chart for this service:
Name: {config.name}
Description: {config.description}
Port: {config.port}
Language/Runtime: {config.language}
Has database dependency: {config.has_database}
Has cache dependency: {config.has_cache}
Needs ingress: {config.has_ingress}
Chart version: {config.chart_version}
App version: {config.app_version}
Return ONLY valid JSON with file paths as keys and file contents as values.
Include: Chart.yaml, values.yaml, templates/_helpers.tpl, templates/deployment.yaml,
templates/service.yaml, templates/serviceaccount.yaml, templates/hpa.yaml
{"templates/ingress.yaml" if config.has_ingress else ""}
templates/NOTES.txt, tests/test-connection.yaml"""
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=8192,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": prompt}]
)
response_text = message.content[0].text
# Extract JSON from response
start = response_text.find('{')
end = response_text.rfind('}') + 1
json_str = response_text[start:end]
return json.loads(json_str)
def write_chart_files(files: dict, output_dir: Path, chart_name: str):
chart_dir = output_dir / chart_name
for file_path, content in files.items():
full_path = chart_dir / file_path
full_path.parent.mkdir(parents=True, exist_ok=True)
full_path.write_text(content)
print(f" Created: {full_path}")
return chart_dir
def create_zip(chart_dir: Path) -> Path:
zip_path = chart_dir.parent / f"{chart_dir.name}.tgz"
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
for file_path in chart_dir.rglob('*'):
if file_path.is_file():
zf.write(file_path, file_path.relative_to(chart_dir.parent))
return zip_path
def main():
print("Helm Chart Generator")
print("=" * 40)
# Collect service details
name = input("Service name (lowercase, hyphens): ").strip()
description = input("Service description: ").strip()
port = int(input("Container port [8080]: ").strip() or "8080")
language = input("Language/runtime [python/nodejs/java/go]: ").strip() or "nodejs"
has_db = input("Needs database connection? [y/N]: ").lower() == 'y'
has_cache = input("Needs Redis/cache? [y/N]: ").lower() == 'y'
config = ServiceConfig(
name=name,
description=description,
port=port,
language=language,
has_database=has_db,
has_cache=has_cache
)
print(f"\n⚙️ Generating Helm chart for '{name}'...")
files = generate_helm_chart(config)
print(f"\n📁 Writing {len(files)} files:")
chart_dir = write_chart_files(files, Path("."), name)
print(f"\n✅ Chart generated: ./{name}/")
print(f"\nNext steps:")
print(f" helm lint ./{name}/")
print(f" helm template ./{name}/ --debug")
print(f" helm install {name} ./{name}/ --dry-run")
if __name__ == "__main__":
main()Example Output
For input: payment-api, Python, port 8000, with database:
Chart.yaml:
apiVersion: v2
name: payment-api
description: Payment processing API service
type: application
version: 0.1.0
appVersion: "1.0.0"values.yaml:
replicaCount: 2
image:
repository: payment-api
pullPolicy: IfNotPresent
tag: ""
serviceAccount:
create: true
automount: false
name: ""
service:
type: ClusterIP
port: 80
targetPort: 8000
ingress:
enabled: false
className: nginx
hosts:
- host: payment-api.local
paths:
- path: /
pathType: Prefix
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: false
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
securityContext:
runAsNonRoot: true
runAsUser: 1000
env: []
database:
host: ""
port: 5432
name: payments
secretName: db-credentials
secretKey: connection-stringtemplates/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "payment-api.fullname" . }}
labels:
{{- include "payment-api.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "payment-api.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "payment-api.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "payment-api.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.securityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.targetPort }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- if .Values.database.host }}
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: {{ .Values.database.secretName }}
key: {{ .Values.database.secretKey }}
{{- end }}
{{- with .Values.env }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}Validate and Test the Generated Chart
# Validate chart structure
helm lint ./payment-api/
# Render templates to check output
helm template payment-api ./payment-api/ \
--set image.tag=v1.0.0 \
--set ingress.enabled=true \
--debug
# Install dry run
helm install payment-api ./payment-api/ \
--namespace payments \
--create-namespace \
--dry-run
# If looks good, install
helm install payment-api ./payment-api/ \
--namespace payments \
--create-namespace \
--set image.tag=v1.0.0Add to CI Pipeline for New Services
# .github/workflows/scaffold-service.yml
name: Scaffold New Service
on:
workflow_dispatch:
inputs:
service_name:
description: 'Service name'
required: true
port:
description: 'Container port'
default: '8080'
jobs:
scaffold:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install anthropic
- name: Generate Helm Chart
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
python helm_chart_generator.py \
--name "${{ inputs.service_name }}" \
--port "${{ inputs.port }}" \
--non-interactive
- name: Validate Chart
run: helm lint ./${{ inputs.service_name }}/
- name: Create PR with Generated Chart
uses: peter-evans/create-pull-request@v6
with:
title: "chore: scaffold Helm chart for ${{ inputs.service_name }}"
branch: "scaffold/${{ inputs.service_name }}"Generated charts aren't perfect — always review before deploying to production. But they give you a 90% complete starting point that would take 30–45 minutes to write manually.
For Helm hands-on labs including chart development, templating, and testing, KodeKloud has Helm courses that cover everything from basic installs to advanced chart authoring.
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
AI-Driven Capacity Planning for Kubernetes Clusters (2026)
How to use AI and machine learning for Kubernetes capacity planning. Covers predictive autoscaling, cost optimization, tools like StormForge and Kubecost, and building custom ML models for resource forecasting.
AI-Powered Kubernetes Anomaly Detection: Beyond Static Thresholds
Static alerts miss 40% of real incidents. Learn how AI and ML-based anomaly detection — using tools like Prometheus + ML, Dynatrace, and custom LLM runbooks — catches what thresholds can't.
Build a DevOps AI Agent with LangGraph on Kubernetes (2026)
Build a stateful DevOps agent using LangGraph that can plan multi-step infrastructure tasks, use tools, handle errors, and maintain conversation context — deployed on Kubernetes with a FastAPI interface.