🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

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.

DevOpsBoysMay 18, 20265 min read
Share:Tweet

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

python
# 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:

yaml
apiVersion: v2
name: payment-api
description: Payment processing API service
type: application
version: 0.1.0
appVersion: "1.0.0"

values.yaml:

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-string

templates/deployment.yaml:

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

bash
# 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.0

Add to CI Pipeline for New Services

yaml
# .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.

Newsletter

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

Comments